Geoprocessing task

The Geoprocessing task allows you to perform geoprocessing in ArcGIS API for Windows Phone applications by executing geoprocessing models that you've created with ArcGIS. Geoprocessing models chain together geographic information system (GIS) analysis operations so that they can be executed automatically. These models generally consist of a series of tools in ArcToolbox, chained together logically to accomplish a specific task. For example, a model might interpolate an elevation surface from a set of input spot heights, then create contours from that surface. Instead of manually opening two separate tools from ArcToolbox, you just run the model. If you need to perform this operation on 100 datasets, you can automate the model to run 100 times. This makes geoprocessing both powerful and convenient.

The Geoprocessor class in the ArcGIS API for Windows Phone gives you access to geoprocessing models that have been published to ArcGIS Server as geoprocessing services. These services are useful because geoprocessing can be computationally intensive and requires GIS software to run the tools and models. It's often more efficient to send geoprocessing jobs to a centralized server rather than relying on the processing power of client machines to perform the analysis. It can also be impractical to implement geoprocessing logic in your web application or to expect users to install additional software.

Creating a Geoprocessing task

As with all tasks, you'll initialize, execute, and process the results of the Geoprocessing task in your Windows Phone application's .NET code (that is, code-behind). The Geoprocessing task does not contain visual components, so you can't use XAML to interact with it.

Typically, the task's input and output interfaces are defined primarily in XAML. For instance, you might use a Map and a Button that initializes a Draw object to accept input points, then use a GraphicsLayer and InfoWindow to display the geoprocessing output. For step-by-step implementation of similar input and output interfaces, see Find task, Query task, and Identify task. For examples of geoprocessing applications, complete with input and output interfaces, see the Geoprocessing (GP) section of the Interactive SDK.

The remainder of this topic focuses on manipulating the task in .NET code, assuming that the input and output interfaces are already defined, and covers the following geoprocessing task steps:

  1. Gathering service information
  2. Initializing the task
  3. Specifying the Geoprocessing task's input parameters
  4. Using the Geoprocessing task and handling the results
    1. Using a synchronous geoprocessing service
    2. Using an asynchronous geoprocessing service

Gathering service information

Before you can use the Geoprocessing task, you need to have the following information about the geoprocessing service you want to use:

  • The URL of the service
  • The required inputs and outputs of the task
  • Whether the task is asynchronous or synchronous

All of these items are described in the ArcGIS Services Directory, which is a browsable depiction of what's available on an ArcGIS Server. The URL for the Services Directory is http://<server name>/<instance name>/rest/services. See Discovering services for general information about how to use the Services Directory.

A Services Directory page for a Geoprocessing task is shown in the following screen shot. When you navigate to this page, the URL of the service is displayed in the browser's address bar. The Execution Type shows whether the task is synchronous (esriExecutionTypeSynchronous) or asynchronous (esriExecutionTypeAsynchronous). For each parameter, you'll need to know the parameter's name, data type, and whether it's used for input or returned as output. This information is circled in the screen shot.

GP Services Directory

NoteNote:

The Data Types shown for the parameters match the classes you'll use when initializing parameters in your code. Input parameters have a direction of esriGPParameterDirectionInput, while output parameters have a direction of esriGPParameterDirectionOutput.

Initializing the task

To initialize a Geoprocessing task, declare a Geoprocessor object, instantiate it with the new keyword, and pass the URL of the geoprocessing service's REST endpoint to the constructor as shown in the following code:

Geoprocessor geoprocessorTask = new Geoprocessor("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/" + 
    "Specialty/ESRI_Currents_World/GPServer/MessageInABottle");

Specifying the Geoprocessing task's input parameters

The Geoprocessing task's execution methods take a list of GPParameter objects as input. All the geoprocessing parameter classes (for example, GPFeatureRecordSetLayer, GPDouble, GPRecordSet, GPRasterData, and so on) derive from this class. The MessageInABottle service, shown in the Services Directory screen shot above, has two input parameters: Input_Point, which is of type GPFeatureRecordSetLayer, and Days, which is of type GPDouble.

The following code assumes that there is a MapPoint in the current scope named MyMapPoint and a TextBox named DaysTextBox that are used for input in the application.

List<GPParameter> parameters = new List<GPParameter>();
parameters.Add(new GPFeatureRecordSetLayer("Input_Point", MyMapPoint));
parameters.Add(new GPDouble("Days", Convert.ToDouble(DaysTextBox.Text)));

Using the Geoprocessing task

There are two sets of members for executing and handling the results of a Geoprocessing task. Which members you use depends on whether the geoprocessing service executes synchronously or asynchronously. In ArcGIS Server, synchronous geoprocessing services return the results of each operation to the client as soon as the operation is complete—no results are stored on the server. Alternatively, asynchronous services store results on the server and return a job ID to the client. The client can then use this ID to retrieve the results at a convenient time. Synchronous services are intended for geoprocessing operations that take a short amount of time to execute (for example, less than three seconds), while asynchronous services should be used for longer-running operations.

Using a synchronous geoprocessing service

When using a service that executes synchronously, initiate the operation using the ExecuteAsync method. To retrieve results, you can either specify a handler for the ExecuteCompleted event or use the ExecuteLastResult property. In this example, the ExecuteCompleted handler is used. The following code attaches the handler to the task:

geoprocessorTask.ExecuteCompleted += GeoprocessorTask_ExecuteCompleted;

The ExecuteCompleted event handler is then implemented; in this example it loops through the results features, adding each to a GraphicsLayer. Each GPParameter in the results that are passed to the handler is assumed to be a GPFeatureRecordSetLayer object since this is the output type listed in the service's information page.

The following code assumes that a Map control (named MyMap, and that contains a GraphicsLayer with an ID of MyGraphicsLayer) and a Symbol (named PathLineSymbol) have been created and are available to the handler.

private void GeoprocessorTask_ExecuteCompleted(object sender, GPExecuteCompleteEventArgs e)
{
    GraphicsLayer graphicsLayer = MyMap.Layers["MyGraphicsLayer"] as GraphicsLayer;

    foreach (GPParameter gpParameter in e.Results.OutParameters)
    {
        GPFeatureRecordSetLayer gpLayer = gpParameter as GPFeatureRecordSetLayer;
        foreach (Graphic graphic in gpLayer.FeatureSet.Features)
        {
            graphic.Symbol = graphic.Symbol = LayoutRoot.Resources["PathLineSymbol"] as Symbol;
            graphicsLayer.Graphics.Add(graphic);
        }
    }
}

After the ExecuteCompleted handler is attached, the task can be executed using the ExecuteAsync method and by passing in the parameters defined in the previous section.

geoprocessorTask.ExecuteAsync(parameters);
For a functional example, see the Message in a Bottle sample in the Geoprocessing (GP) section of the Interactive SDK.

Using an asynchronous geoprocessing service

As previously explained, when a client initiates an operation on an asynchronous geoprocessing service, the service returns a job ID to the client, which can then be used to retrieve the operation's results. The results are not automatically returned to the client when the operation is completed, but rather are stored on the server and are sent to the client only when the client requests them. Accordingly, you initiate an operation on an asynchronous geoprocessing service by calling the Geoprocessing task's SubmitJobAsync method, rather than ExecuteAsync, as shown in the following code.

NoteNote:

The code in this section is based on using the ClipCounties asynchronous service available at http://serverapps.esri.com/ArcGIS/rest/services/SamplesNET/USA_Data_ClipTools/GPServer/ClipCounties. For a functional example, see the Clip Features sample in the Geoprocessing (GP) section of the Interactive SDK.

geoprocessorTask.SubmitJobAsync(parameters);

A job's results can only be retrieved after the operation has completed. Asynchronous geoprocessing services do not notify the client when a job is complete, so it's up to the client to check with the service to determine the status of the job. By default, the ArcGIS API for Windows Phone does this for you automatically and fires the Geoprocessing task's JobCompleted event once an operation is done executing. A handler for JobCompleted is typically where you'll request an asynchronous operation's results. The following code attaches the handler to the task:

geoprocessorTask.JobCompleted += GeoprocessorTask_JobCompleted;

To get those results from an asynchronous Geoprocessing task, the task defines the following methods, each of which returns the results in a different format:

  • GetResultDataAsync—Retrieves a collection of objects that expose all the result data (for example, a collection of graphics containing geometry and attributes).
  • GetResultImageLayerAsync—Retrieves a GPResultImageLayer. This can be used to display the results by adding it directly to the map but does not contain information about individual results.
  • GetResultImageAsync—Retrieves a GPResultImage, which contains a MapImage with the geometry of the results.

Each method requires you to specify the operation's job ID and the name of the output parameter to retrieve. For each, the results are passed to an event of the form <operation name>Completed (for example, GetResultImageCompleted) and a property of the form <operation name>LastResult (for example, GetResultImageLastResult) as soon as they are returned to the client.

The following code demonstrates use of JobCompleted, GetResultDataAsync, and GetResultDataCompleted. The code for JobCompleted requests data for an output parameter called Clipped_Counties. In the GetResultDataCompleted handler that's shown, the following is assumed:

  • There is a Map named MyMap.
  • The Map contains a GraphicsLayer with an ID of MyResultGraphicsLayer.
  • A symbol named DefaultFillSymbol has been created.
  • The output parameters are of type GPFeatureRecordSetLayer.

The GetResultDataCompleted handler shown in the following code iterates through the results features, adding each to a GraphicsLayer:

private void GeoprocessorTask_JobCompleted(object sender, JobInfoEventArgs e)
{
    Geoprocessor geoprocessorTask = sender as Geoprocessor;
    geoprocessorTask.GetResultDataCompleted += GeoprocessorTask_GetResultDataCompleted;
    geoprocessorTask.GetResultDataAsync(e.JobInfo.JobId, "Clipped_Counties");
}

private void GeoprocessorTask_GetResultDataCompleted(object sender, GPParameterEventArgs e)
{
    GraphicsLayer graphicsLayer = MyMap.Layers["MyResultGraphicsLayer"] as GraphicsLayer;

    GPFeatureRecordSetLayer gpLayer = e.Parameter as GPFeatureRecordSetLayer;
    foreach (Graphic graphic in gpLayer.FeatureSet.Features)
    {
        graphic.Symbol = LayoutRoot.Resources["DefaultFillSymbol"] as Symbol;
        graphicsLayer.Graphics.Add(graphic);
    }
}

By default, a Geoprocessing task checks the job status of asynchronous operations every five seconds. To change this, set the UpdateDelay property. This property is specified in milliseconds. The following statement sets the interval to ten seconds:

geoprocessorTask.UpdateDelay = 10000;

If you don't want the Geoprocessing task to check the status of asynchronous operations automatically, you can use the CancelJobStatusUpdates method to disable the default behavior, then check the status with the CheckJobStatusAsync method. The StatusUpdated event is used to get the job ID and call CancelJobStatusUpdates and to handle the result of the call to CheckJobStatusAsync. This approach is shown in the following code:

// Wire status updated event and initiate the asynchronous GP operation.
geoprocessorTask.GetResultDataCompleted += new EventHandler<GPParameterEventArgs>(geoprocessorTask_GetResultDataCompleted);
geoprocessorTask.StatusUpdated += geoprocessorTask_StatusUpdated;
geoprocessorTask.SubmitJobAsync(parameters);
// Manually check the job's status. The result is passed to the StatusUpdated handler.
// This is often done at a user's request (for example, when an app bar button is pressed).
// jobID is a global variable set when the job status becomes submitted and automatic
//    updates are disabled.
if (jobID != null) 
    geoprocessorTask.CheckJobStatusAsync(jobID);
void geoprocessorTask_StatusUpdated(object sender, JobInfoEventArgs e)
{
    Geoprocessor geoprocessorTask = sender as Geoprocessor;
    switch (e.JobInfo.JobStatus)
    {
        case esriJobStatus.esriJobSubmitted:            
            // Disable automatic status checking.
            geoprocessorTask.CancelJobStatusUpdates(e.JobInfo.JobId);
            // Keep track of the jobID so that it can be used to retrieve info later.
            jobID = e.JobInfo.JobID;
            break;
        case esriJobStatus.esriJobSucceeded:
            // Get the results.
            geoprocessorTask.GetResultDataAsync(e.JobInfo.JobId, "Clipped_Counties");
            break;
        case esriJobStatus.esriJobFailed:
        case esriJobStatus.esriJobTimedOut:
            MessageBox.Show("operation failed");
            break;
    }
}

1/23/2012