Geoprocessor

The Geoprocessor allows you to perform geographic analysis and batch operations using geoprocessing tools and models on ArcGIS Server. These models and tools are exposed through Geoprocessing services.

Geoprocessing allows you to automate and chain together GIS analysis and operations. You do this by creating custom tools and models with ArcGIS. A model generally consists of a series of tools you might see in ESRI's ArcToolbox, chained together in a logical way to accomplish a specific thing. For example, a model might interpolate an elevation surface from a set of input spot heights, then create contours from that surface. Instead of individually executing the tools in sequence, you just run the model. If you need to do this operation on 100 datasets, you can automate the model to run 100 times. This makes geoprocessing both powerful and convenient.

ArcGIS Server Geoprocessing services are accessible on the web as SOAP and REST web services. A Geoprocessing service is a collection of one or more Geoprocessing tasks. Each task is based on a geoprocessing model or a tool and performs a pre-defined function. When publishing the service, the administrator chooses either to allow geoprocessing tasks to be invoked asynchronously or synchronously. The service will then either support a Submit Job operation or an Execute Task operation respectively. The latter is a blocking call and suitable for geoprocessing tasks that take only a little time to complete. The client waits till the operation is complete on the server. When the operation is complete, the client is given the results. The former is a non-blocking call and is suitable for long running geoprocessing tasks. It allows the client to do other things while the task is running on the server. The client can check back periodically to see if the job is finished and then get the results. The difference between these two modes is not so relevant for developers working with ArcGIS API for iOS. Both the methods on the Geoprocessor to execute a task and submit a job are asynchronous. The developer is shielded from the asynchronous/synchronous nature of the geoprocessing task.

To successfully use the Geoprocessor, you must know

• The URL of the Geoprocessing service’s REST web service endpoint

• The Geoprocessing Task which you want to execute, and whether that Task can be executed asynchronously or synchronously

• The inputs expected by the service’s Task, and the results that will be returned

You can use the ArcGIS Server Services Directory to find out these details about the service you wish to use. If you are familiar with the ArcGIS Server REST API, you can even invoke operations on the service directly from a browser using the Services Directory.

Creating a Geoprocessor

To instantiate a Geoprocessor, you need to provide a URL to a Geoprocessing Task in a Geoprocessing service REST endpoint. This URL is usually of the form http://<server:port>/<instance>/services/<service>/GPServer/<task>

If the web service is secured, you will also need to provide the credentials that can be used to access the service. In this tutorial, we’ll be using the Mailing List geoprocessing task from a sample server on ArcGIS Online. The task is available in both synchronous and asynchronous services –ESRI_CadastralData_PortlandSync and ESRI_CadastralData_Portland.

NSURL* url = [NSURL URLWithString: @"http://sampleserver2.arcgisonline.com/ArcGIS/rest/services/Portland/ESRI_CadastralData_Portland/GPServer/MailingList"];

When you create the geoprocessor, you need to ensure you take ownership of it. Otherwise, it might get deallocated before it has a chance to execute. If you instantiate the geoprocessor using alloc , you automatically have ownership. However, if you instantiate the geoprocessor using the convenience constructor, you need to take ownership by either sending it a retain message, or by assigning it to a property that will retain it.

//sending a retain message
AGSGeoprocessor* geoprocessor = [AGSGeoprocessor geoprocessorWithURL:url];
[geoprocessor retain];

//OR assigning to a property that will retain the object
self.geoprocessor = [AGSGeoprocessor geoprocessorWithURL:url];

When you are finished using the geoprocessor, you should relinquish ownership so that its memory can be reclaimed. You do this typically in the dealloc method of your view controller by sending the geoprocessor a release message or by setting the corresponding property to nil.

- (void)dealloc {  
//sending a release message 
[geoprocessor release];	  

//OR setting the property to nil  
self.geoprocessor = nil;  

...
}

NoteNote:

Please refer to Apple's Memory Management Programming Guide for more information on how to manage application memory.

Preparing the input

Input to the Geoprocessor is supplied as an array. The array contains values for parameters listed in Parameters section of Services Directory for the Geoprocessing Task. The section contains all the information needed to prepare the input.

Parameters section in Services Directory

Only input parameters, i.e. parameters with Direction = esriGPParameterDirectionInput, need to be present in the array. Of these, some parameters are required (parameters with Type = esriGPParameterTypeRequired). These parameters must be specified. Others are optional (Type = esriGPParameterTypeOptional) and may be omitted.

Each parameter value is specified using an AGSGPParameterValue object. This object requires the parameter’s name, type, and value. The name and type information are available in the Service Directory. The value is an Objective-C object whose type is determined by the data type of the parameter. The following table provides a mapping between possible Parameter Data Types and their Objective-C counterparts.

Parameter Data Type

Objective-C Type

GPBoolean

NSNumber

GPDataFile

NSURL

GPDate

NSDate

GPDouble

NSNumber

GPFeatureRecordSetLayer

AGSFeatureSet

GPLinearUnit

AGSGPLinearUnit

GPLong

NSNumber

GPRasterData

AGSGPRasterData

GPRasterLayer

AGSGPRasterData

GPRecordSet

AGSFeatureSet

GPString

NSString

GPMultivalue

NSArray

When the data type of the parameter is GPFeatureRecordSetLayer or GPRecordSet, the Default Value in the Services Directory gives clues about the fields and the geometry type of the corresponding AGSFeatureSet.GPFeatureRecordSetLayer Parameter

For the Geoprocessing Task that we are using, we need to provide a parcel ID and a search distance as input parameters.

AGSGPParameterValue *parcel = [AGSGPParameterValue parameterWithName:@"Parcel_ID" type:AGSGPParameterTypeString value:@"1N1E34CC  -06600"]; 

AGSGPParameterValue *distance = [AGSGPParameterValue parameterWithName:@"SearchDistance_ft" type:AGSGPParameterTypeLong value:[NSNumber numberWithInt:100]];

NSArray *params = [NSArray arrayWithObjects:parcel,distance,nil];

Executing a geoprocessing task synchronously

To execute a geoprocessing task, you need to provide an array of input parameters to the Geoprocessor’s executeWithParameters: method.

[geoprocessor executeWithParameters:params];

Submitting a geoprocessing job for asynchronous execution

To submit a job, you need to provide the array of input parameters to the Geoprocessor’s submitJobWithParameters: method. You can also specify how often you want the geoprocessor to check the status of the job. By default, the geoprocessor checks every 5 seconds but you should use a higher interval if you know your geoprocessing task takes longer to complete.

geoprocessor.interval = 20;
[geoprocessor submitJobWithParameters:params];

Getting results and handling errors

The geoprocessor informs its delegate when operations complete successfully or when errors are encountered. To get results from the geoprocessor and to properly handle any errors, you must set one of your classes as the geoprocessor’s delegate. You do this by making your class (typically the view controller which uses the geoprocessor) adopt the AGSGeoprocessorDelegate protocol.

@interface MyViewController : UIViewController <AGSGeoprocessorDelegate> { 
  ...
 }

An instance of your class must also be set as the geoprocessor’s delegate. This will allow the geoprocessor to invoke methods on your class in response to operations that it performs.

geoprocessor.delegate = self;

Finally, your class must implement one ore more methods defined in the protocol which pertain to the operation being performed.

For synchronous task execution, you need to implement the geoprocessor:operation:didExecuteWithResults:messages: method if you want to be notified when the task finishes execution successfully. The results of the operation, as well as any messages that were generated during geoprocessing are provided to this method. The results are an array of AGSGPParameterValue objects, and the messages are an array of AGSGPMessage objects.

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor   operation:(NSOperation*) op didExecuteWithResults:(NSArray*) results  messages:(NSArray*) messages {	
 for (AGSGPParameterValue* param in results) { 
   NSLog(@"Parameter: %@, Value: %@", param.name,param.value);
	}
}

To be notified of task execution failure, you need to implement the geoprocessor:operation:didFailExecuteWithError: method. The reason for failure is provided to the method.

For asynchronous task execution, you need to implement the geoprocessor:operation:didSubmitJob: method if you want to be notified once the job has been submitted.

To be notified when the job completes successfully, you need to implement the geoprocessor:operation:jobDidSucceed: method. The messages generated during the geoprocessing operation are provided to you, but you must fetch the results separately. Both these methods receive a unique ID identifying the job you submitted. This ID will be later needed to fetch the results. You fetch the results by invoking queryResultData:paramName: on the geoprocessor for each output parameter of the geoprocessing task. The result will be returned to your class serving as the the geoprocessor’s delegate. Your class needs to implement the geoprocessor:operation:didQueryWithResult:forJob: method to receive the result, and the geoprocessor:operation:didFailWithError: to handle any errors encountered while retrieving the results

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor operation:(AGSGPRequestOperation*)op jobDidSucceed:(AGSJobInfo*) jobInfo{
  [geoprocessor queryResultData:jobInfo.jobId paramName:@"Report_html"];
  [geoprocessor queryResultData:jobInfo.jobId paramName:@"parcels_Buffer1_shp"];
}

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor operation:(AGSGPRequestOperation*)op didQueryWithResult:(AGSGPParameterValue*) result forJob:(NSString*) jobId {
   NSLog(@"Parameter: %@, Value: %@",result.name, result.value);
}

- (void) geoprocessor: (AGSGeoprocessor *) geoprocessor operation: (AGSGPRequestOperation *) op didFailWithError: (NSError *) error { 
  NSLog(@"Error: %@",error);
}

To be notified when a job fails, you must implement the geoprocessor:operation:jobDidFail: method. The messages generated during geoprocessing are provided to this method, which may provide clues on why the job failed.

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor operation:(AGSGPRequestOperation*)op jobDidFail:(AGSJobInfo*) jobInfo {
for (AGSGPMessage* msg in jobInfo.messages) {
  NSLog(@"%@", msg.description);
}
}

You can also implement the geoprocessor:willCheckJobStatus: and geoprocessor:operation:didCheckJobStatus: methods to display the network activity indicator whenever the geoprocessor polls for status of the submitted job.

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor willCheckJobStatus:(AGSJobInfo*) jobInfo { 
  [UIApplication showNetworkActivityIndicator:true];
}

- (void) geoprocessor:(AGSGeoprocessor*) geoprocessor operation:(AGSGPRequestOperation*) op didCheckJobStatus:(AGSJobInfo*) jobInfo {
 [UIApplication showNetworkActivityIndicator:false]; 
}

Getting results as a layer or an image

Under most circumstances, Geoprocessing services return results to clients in the form of values (text, numbers, dates, etc). The clients then display these results to the user using the platform capabilities available to them. This works fine for results that are not complex to display. For example, a client application built using ArcGIS API for iOS can easily display results which are dates, numbers, or text. Even results that are features can be displayed using the Graphics layer. But sometimes, the results might be complex to visualize, such as a hillshade raster or features using advanced cartography. In such situations, Geoprocessing services can be configured to use an associated Map service to “draw” the result. Clients then access the result as an image, or as a layer that can be added to the map. In both cases, the heavy-lifting is done by ArcGIS Server to transform the result into a format that can be easily displayed on the client.

To get the result as an image, you need to invoke the queryResultImage:paramName:imageParams: method on the geoprocessor. The image will be returned to the geoprocessor delegate’s geoprocessor:operation:didQueryWithResultImage:forJob: method. You can then display this image to users using CocoaTouch UIImageView.

To get the result as a layer, you need to invoke the queryResultImageLayer:paramName: method on the geoprocessor. The layer will be returned as an AGSGPResultLayer object to the geoprocessor delegate through the geoprocessor:operation:didQueryWithResultImageLayer:forJob: method. You can then add this layer to the map to display the results on the map.

12/7/2011