Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I have a silverlight application that allows the user to select points to make a polyline. The polyline is densified per the users input (say every two feet). The new points are sent to an esri elevation service to get the elevations at those points. I have the lat/long/elevation in a c# dictionary with a incremented number as the key.

What is the best way to send this over to a geoprocessing service and what should I have my gp service take? I want to send something lightweight like json but I haven't fused with json much (except for trying to deserialize it with c# and failing so I just parsed the return string myself using c#). If I call the gp service (similar to how my code is calling the densify and elevation services, what does the gp service need to take? Are there alternatives?

What we will be doing is finding the average percent slope across the polyine. Once the average percent slope is found, we need to generate a polyline shapefile or feature class with the associated percent slope attributed and we'll send this over to autocad folks.

EDIT: Here's my code..!
//This is defined as a constant string at the top of the Densify class Densify.SUMBITGEOMETRY = "http://gis.mustangeng.com/ArcGIS/rest/services/Geoprocessing/gpServiceTest/GPServer/gpService/submitJob?Input=";

string dog = string.Join(",", colorList.ToArray());
//String.Join(",", dictElevat.Keys.Select(o => o.ToString()).ToArray());
//HtmlPage.Window.Alert(string.Format("Count: {0}", dog));
HtmlPage.Window.Alert(String.Join(",", dictElevat.Keys.Select(o => o.ToString()).ToArray()));
//UriBuilder builder1 = new UriBuilder(Densify.SUBMITGEOMETRY);
//builder1.Query = string.Format(String.Join(",", dictElevat.Keys.Select(o => o.ToString()).ToArray()));
WebClient webClient1 = new WebClient();
webClient1.Headers["Content-Type"] = "text/plain";
Uri serviceUri = new Uri(Densify.SUBMITGEOMETRY);
string s = String.Join(",", dictElevat.Keys.Select(o => o.ToString()).ToArray());
webClient1.UploadStringAsync(serviceUri, "POST", s);                            
HtmlPage.Window.Alert(serviceUri.ToString());
share|improve this question
    
If you've not seen this question (from me) then it may be worth looking at because it too involves passing long GeoJSON objects to a GP service via the REST API: gis.stackexchange.com/questions/19721/… - just realised that you don't have control of the Esri Elevation Service so my comment may be irrelevant. –  PolyGeo Feb 28 '12 at 6:59
    
PolyGeo, will http://<server>/ArcGIS/rest/services/OEMS/test/GPServer/myGpService hit the gp service and ArcGIS Server will know what "/execute" means? What is the "?geometry" keyword doing here? Thanks. –  Justin Feb 28 '12 at 13:27
    
I wasn't sure if I should reply my questions here or the other post you linked. –  Justin Feb 28 '12 at 13:28
    
I think I need to tidy my answer up on the other question but I did a quick proof of concept a month or so back which wilol only get followed up on in a few weeks or so. My recollection is that "geometry" is the name of parameter of the Python script that gets exposed by the GP service and is read within the Python script by GetParameterAsText (which seems to be missing from what I posted). I think the "/execute" part is always part of such URLs but I have not looked into its exact meaning. –  PolyGeo Feb 28 '12 at 22:36
add comment

2 Answers 2

I have implemented GP services that expose custom GP tools written in C#. As requirements evolved - and the input parameters changed - I grew weary of changing code that packed and unpacked the parameter arrays. I decided it would be easier to have the GP services accept a single (string) parameter and return a single (string) parameter.

The strings were Data Transfer Objects (DTOs) serialized using Json.NET (same as @awesomo).

Since I couldn't reference a non-silverlight dll (i.e. the gp tool project) from my silverlight project, I had to choose "Add existing item..." click the drop down box and choose "add as link". The linked files allowed the two projects to share the same source code used by the DTO classes.

In the silverlight project you can have extension methods on the DTO's to convert to/from Esri silverlight polyline objects. Likewise on the GP service project file you could have extension methods on the DTO's to arcobjects IPolylines

There was no requirement stating the gp service's json had to be consumable by javascript. I never determined how much my use of Json.NET's TypeNameHandling.Objects option complicated life for javascript clients. So if you've got javascript clients you might want to look into that.

This approach would allow you to send an array of points to the server as a generic list of LatLong objects, and receive a list of Xyz objects - the details of serialization are hidden by Json.NET. To reduce network i/o you might want to densify on the server.

The result of the gp service might be a url that references a zipped shapefile on the server which can be downloaded via a browser.

share|improve this answer
    
The last statement answers another of my se questions. How to use AGS to cross the network and send spatial data. I have intranet A and intranet B. I need to not use sneaker-net as someone called it but send the data electronically. I thought surely AGS could do this. gis.stackexchange.com/questions/19534/… maybe I'm missing something. Thanks! –  Justin Feb 29 '12 at 3:59
    
@Justin I also implemented services using Microsoft's WCF REST sdk. It allows you to transmit streams. The hard part was fixing a bug that turned out was caused by a config file setting specifying a max file size that was too small - maxReceivedMessageSize or maxBufferSize. –  Kirk Kuykendall Feb 29 '12 at 4:18
add comment

So one way, if I understand your question, would be to accept a GPRecordset as input to the geoprocess with x, y and z as the columns. You could then process the table and do your logic to find average percent slope. Then, you could download feature class and parse. (Alternately, you could change to asynchronous service and save on server for download out of the jobs directory.)

Setting up the service on AGS, it should be synchronous.

In the toolbox accept GPRecordset, output FeatureClass

#python script
Table = arcpy.GetParameterAsText(0)
#process table, do logic and create feature class using in_memory designator
OutputFC = "in_memory\\OutputFC"
#send back feature class
arcpy.SetParameterAsText(1, OutputFC)

Silverlight client creates JSON request (so in my example take a list of address and make a json formatted table out of them to pass as a GPRecordset) (You don't really need to send urlencoded, but my example is)

using System.Windows.Browser; //HttpUtility UrlEncode

public string toJSON(List<Address> la)
{
  //HttpUtility.UrlEncode seems to have a limit of about 2000 characters,
  //so have to keep calling it every record.. lame
  //Uri.EscapeUriString
  StringBuilder sb = new StringBuilder(HttpUtility.UrlEncode("{\"features\":["));
  foreach (Address a in la)
  {
      sb.Append(HttpUtility.UrlEncode("{\"attributes\":{" +
        "\"id\":\"" + a.UniqueID +
        "\",\"a\":\"" + a.Street +
        "\",\"c\":\"" + a.City +
        "\",\"s\":\"" + a.State +
        "\",\"z\":\"" + a.ZIP +
        //"\",\"z4\":\"" + a.ZIP4 +
        "\"}},"));
  }
  sb.Remove(sb.Length - 3, 3); //remove last comma %2C
  sb.Append(HttpUtility.UrlEncode("]}"));
  return sb.ToString();
}

Silverlight client makes request

using System.Net;

public void requestGeocoding(string strJSONRequest)
{
   Uri serviceUri = new Uri(http://myserver.com/ArcGIS/rest/services/myfolder/myGP/GPServer/myGP/execute/);
   WebClient client = new WebClient();
   //client.Headers["Content-Type"] = "text/plain";
   client.Headers["Content-Type"] = "application/x-www-form-urlencoded";
   client.Headers["Pragma"] = "no-cache";
   client.UploadStringCompleted += new UploadStringCompletedEventHandler(client_UploadStringCompleted);
   client.UploadStringAsync(serviceUri, "POST", strJSONRequest));
}

Silverlight client receives string

void client_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{      
    if (!e.Result.Contains("error"))
    {
      List<Dictionary<string, string>> ld = fromJSON(e.Result);
    }
}

Silverlight parses feature class json (some extra stuff in here from my own app -- ignore). Using a library I found called Newtonsoft.Json. There are other ways to do this, probably better... Objective is to get to the field names and values.

using Newtonsoft.Json;

public List<Dictionary<string, string>> fromJSON(string s)
{
  List<Dictionary<string, string>> la = new List<Dictionary<string, string>>(BATCH_SIZE);
  StringReader sr = new StringReader(s);
  JsonTextReader reader = new JsonTextReader(sr);
  int count = 0;
  string outBatchId = "";
  string serverTime = "";
  while (reader.Read())
  {
    if (null != reader.Value && reader.Value.ToString() == "attributes")
    {
      Dictionary<string, string> fields = new Dictionary<string, string>(23);
      reader.Read(); //start object
      reader.Read(); //read first fieldname
      while (reader.TokenType != JsonToken.EndObject)
      {
        string fieldName = reader.Value.ToString();
        reader.Read();
        object myVal = "";
        if (null != reader.Value) myVal = reader.Value;
        fields.Add(fieldName, myVal.ToString());
        reader.Read();
      }
      la.Add(fields);
      count++;
    }
    if (null != reader.Value && reader.Value.ToString() == "OutBatchID")
    {
      reader.Read(); //dataType
      reader.Read();
      reader.Read();
      reader.Read(); //OutBatchID value
      outBatchId = reader.Value.ToString();
    }
    if (null != reader.Value && reader.Value.ToString().StartsWith("Succeeded"))
    {
      int index = reader.Value.ToString().LastIndexOf(':') + 2;
      serverTime = reader.Value.ToString().Substring(index, reader.Value.ToString().Length - index);
    }
  }
  sendMsg("Received Batch: " + outBatchId + " - Rows Received: " + count + " - Server Process Time (" + serverTime);
  return la;
}
share|improve this answer
    
If I paste this uri in my browser it works for me: /ArcGIS/rest/services/Geoprocessing/gpServiceTest/GPServer/gpService/submitJob?I‌​nput=WOWee! But mimicking what you have doesn't: client.UploadStringAsync(serviceUri, "POST", s). where serviceUri= "/ArcGIS/rest/services/Geoprocessing/gpServiceTest/GPServer/gpService/submitJob?‌​Input=" and s= "Wowee!" –  Justin Mar 1 '12 at 21:59
    
@Justin You'll want it more like this... s = "Input=Wowee"; serviceUri = "/ArcGIS/rest/services/Geoprocessing/gpServiceTest/GPServer/gpService/submitJob"‌​; Also, do you use fiddler. If not, you should download, it will be a great help in debugging. It can also save request/responses as visual studio test cases. –  awesomo Mar 1 '12 at 23:09
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.