Asynchronous Geoprocessing App

5452
16
05-09-2012 01:31 PM
LukeCatania
New Contributor III
I am trying to write an app to display results from an Asynchronous GP Task.  I looked at the Viewshed Example and tried to change that, but that is synchronous and changing the parameters passed and the URL to access my Asynchronous GP Tool results in an "execute operation is not allowed on this service" error, which I found is the result of calling the Asynchronous GP Tool using the .execute method rather than the .submitJob method. 


Other things need to be changed in the code to get check the status of the job and get the results and display them, but I don't really know what to change.  Has anyone done anything like this and would you please post some code?
0 Kudos
16 Replies
DanO_Neill
Occasional Contributor III
Are you specifically wanting to use the Viewshed service?  If so, it does not support asynchronous execution.  Before you can use the Geoprocessing task you have to know whether the task is asynchronous or synchronous.  This is described in the ArcGIS Services Directory.
MarkDeaton
Esri Contributor
Coincidentally, I just found myself needing to call an async GP service, too. In some other Esri APIs (such as Flex), the mechanism for using synchronous and asynchronous services is almost identical. But in Android, there's no event that calls a listener when processing is done, so it's up to you, the developer, to create a loop of some sort to check for results.

Here's a little incomplete bit of code; I adapted the Viewshed sample in the appropriate places. I hope it helps you.


gpJobID = gp.submitJob(params).getJobID();
// If no response in 60 seconds, cancel out
gpTimeout = new Timer();   
gpTimeout.schedule(new TimerTask() {
 @Override
 public void run() {
  uiHandler.sendEmptyMessage(CANCEL_LOADING_WINDOW);
 }
}, 60000);
// Check periodically for results
checkGPResults = new Timer();
checkGPResults.scheduleAtFixedRate(new TimerTask() {
 @Override
 public void run() {
  try {
   GPJobResource gpJR = gp.checkJobStatus(gpJobID);
   if ( gpJR.getJobStatus() == JobStatus.succeeded ) {
    uiHandler.sendEmptyMessage(CLOSE_LOADING_WINDOW);
    checkGPResults.cancel();
    lyrOutputResults.removeAll();
    GPParameter result = 
     gp.getResultData( gpJobID, "SewerMainsTraced" );
    GPFeatureRecordSetLayer resultFeats = (GPFeatureRecordSetLayer) result;
    for (Graphic feature : resultFeats.getGraphics()) {
     Geometry geom = feature.getGeometry();
     final Graphic g = new Graphic(geom, new SimpleLineSymbol(Color.CYAN, 2));
     lyrOutputResults.addGraphic(g);
    }
   }
   else if ( gpJR.getJobStatus() == JobStatus.failed ) {
    uiHandler.sendEmptyMessage(CLOSE_LOADING_WINDOW);
    checkGPResults.cancel();
    showToast( "Error in tracing: " + gpJR.getMessages()[ gpJR.getMessages().length-1 ].toString(), Toast.LENGTH_LONG );
   }
  }
  catch (final Exception exc) {
   Log.e(TAG, exc.getMessage());
   showToast("Error in tracing: " + exc.getMessage(), Toast.LENGTH_LONG);
  }
 }
}, 2000, 2000);
0 Kudos
AndyGup
Esri Regular Contributor
In addition to Mark's code snippet, here's some additional code taken from a Clip and Ship demo that shows another pattern for coding against an asynchronous GP service. The full sample code will be included with the v3 release of our SDK later this year. Or, if you want to see the full app sooner then ping me: agup at esri dot com.

strmv.setParamName("Layers_to_Clip");

strmv.setValues(arrayList);

final ArrayList<GPParameter> paramlist = new ArrayList<GPParameter>();
paramlist.add(strmv);
paramlist.add(areaofinterest);
paramlist.add(featureformat);

handler = new Handler();
submitJobandPolling(gp, paramlist);



 /**
  * Method submitJobandPolling.
  * @param gp Geoprocessor
  * @param params List<GPParameter>
  */
 void submitJobandPolling(final Geoprocessor gp,
   List<GPParameter> params) {
  try {
   GPJobResource gpjr1 = gp.submitJob(params);
   JobStatus jobstatus = gpjr1.getJobStatus();
   final String jobid = gpjr1.getJobID();
   Log.d("Test", "jobid " + jobid);
   Log.d("Test", "jobstatus " + jobstatus);

   // if (!jobstatus.equals("esriJobSucceeded")) {
   if (jobstatus != JobStatus.succeeded) {

    handler.postDelayed(new Runnable() {

     
     @Override
     public void run() {
      try {
       GPJobResource gpjr2 = gp.checkJobStatus(jobid);  
       GPMessage[] messages = gpjr2.getMessages();
       if ( messages!= null && messages.length > 0) {
        for (int i = 0; i < messages.length; i++) {
         Log.d("Test", "Message: " + messages.getDescription());
        }
       }
       Log.d("Test", "Polling thread is: "
         + Thread.currentThread().getName());
       
       JobStatus status = gpjr2.getJobStatus();
       boolean jobcomplete = false;

       if (status == JobStatus.canceled
         || status == JobStatus.deleted
         || status == JobStatus.failed || status == JobStatus.succeeded
         || status == JobStatus.timedOut) {
        jobcomplete = true;

       }
       if (jobcomplete) {
         if (status == JobStatus.succeeded) {
       
           GPDataFile outputZipfile = (GPDataFile) gp
          .getResultData(jobid, "Output_Zip_File");

           Log.d("Test", "zip file is "
          + outputZipfile.getUrl().toURI()
            .toString());
         }else {
          Log.d("Test", "GP failed");
         }

       } else {
        handler.postDelayed(this, 5000);
       }
      } catch (Exception e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }

     }
    }, 4000);

   }
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }
0 Kudos
MarkDeaton
Esri Contributor
Nice--thanks for that, Andy. That's the other route I was considering, as it's safer and probably more efficient.

Am I mistaken in thinking that it's creating a worker thread? If that's true, then any UI-related work would have to be run/posted to the UI thread, if I remember correctly. That includes things like showing toasts or adding graphics to the map...?
0 Kudos
AndyGup
Esri Regular Contributor
Mark, thanks for bringing that up as I was thinking the same thing right after I posted it. Yes, if you use Handler, rather than AsyncTask, you have to use the MessageQueue to send and handle messages relative to the main thread.

For the benefit of others that read this and may be wondering when to use AsyncTask and when to use Handler: I suggest that AsyncTask is perfect for simple jobs such as a straight forward REST request/response. And, that you should consider using Handler for jobs that require an additional level of control, or advanced execution and processing.

This was just a prototype sample, but I believe we do need to tweak it so that it shows how to loop information back into the UI thread and complete the cycle.

If others have any experience with either of these please feel free to share what you have learned.

-Andy
0 Kudos
LukeCatania
New Contributor III
Are you specifically wanting to use the Viewshed service?  If so, it does not support asynchronous execution.  Before you can use the Geoprocessing task you have to know whether the task is asynchronous or synchronous.  This is described in the ArcGIS Services Directory.


Well, I was trying to use a= Viewshed Async Task that someone at my agency created, but she just changed it to sychronous so I can try use the viewshed example and modify it to call her service.  With that, I am still having an issue when the service is called.  I get an error:

com.esri.core.io.EsriServiceException: Error executing task 'ViewShed'. Please check your parameters.

All I am is passing a point to the service with the the attributes (RADIUS1, AZIMUTH1, etc) in the point set through the Graphic.  I don't see what parameter would be wrong.  Here is a few snips of my code.


public void onSingleTap(float x, float y) {
    mappoint = map.toMapPoint(x, y);
    observerAttributes.put(offsetA, 1.0);
    observerAttributes.put(offsetB, 0.0);
    observerAttributes.put(azimmuth1, 0.0);
    observerAttributes.put(azimmuth2, 180.0);
    observerAttributes.put(vert1, 90.0);
    observerAttributes.put(vert2, -90.0);
    observerAttributes.put(radius1, 0.0);
    observerAttributes.put(radius2, 2000.0);
    Graphic g = new Graphic(1, mappoint, new SimpleMarkerSymbol(
      Color.RED, 10, STYLE.CIRCLE), observerAttributes, null);
    gLayer.addGraphic(g);
   }


public void start(Point mappoint) {
  // First input parameter
  GPFeatureRecordSetLayer gpf = new GPFeatureRecordSetLayer(
    "InputPoint");
  gpf.setSpatialReference(map.getSpatialReference());
  gpf.setGeometryType(Geometry.Type.Point);

  // Add the point selected by the user
  Graphic f = new Graphic(2, mappoint, new SimpleMarkerSymbol(Color.RED,
    25, STYLE.DIAMOND), observerAttributes, null);
  gpf.addGraphic(f);

  // Add params
  params = new ArrayList<GPParameter>();
  params.add(gpf);
0 Kudos
AndyGup
Esri Regular Contributor
@lcatania You may have already done this, did you compare the parameters set in your code with the parameters documented in your Geoprocessing Service REST endpoint?

As a second suggestion, do you have another working sample that uses the same service that you can compare the HTTP request against?

-Andy
0 Kudos
MarkDeaton
Esri Contributor
I found it very difficult to troubleshoot GP parameter issues until I pointed my device to a Fiddler proxy running on my PC. Once I was able to spy on the JSON requests going over the wire, I was able to copy/paste those parameters into a web browser at the GP service REST endpoint. The problems quickly became apparent...
0 Kudos
LukeCatania
New Contributor III
I found it very difficult to troubleshoot GP parameter issues until I pointed my device to a Fiddler proxy running on my PC. Once I was able to spy on the JSON requests going over the wire, I was able to copy/paste those parameters into a web browser at the GP service REST endpoint. The problems quickly became apparent...


How exactly do you point the device to fiddler?  I have used fiddler before, but I used it on my PC when I was running a flex client.   How do you have fiddler see the traffic from the device.  And the device is not sending HTTP requests directly since it is using the androi API to make the GP calls.  How do you monitor the traffic?
0 Kudos