Geoprocessing

Download Sample Application

The Geoprocessing Server gives you the power to perform any geoprocessing task within your application. In this sample, we use a chemical emergency geoprocessing task to calculate isolation and protection zones. Once the job completes, the application is notified with a job id which is used to query the resultant polygons. In this case the resultant polygons are displayed on the map.

@property (strong) AGSGeoprocessor *geoprocessor;

//set up the geoprocessing task with the URL to the Geoprocessing Service
self.geoprocessor = [AGSGeoprocessor geoprocessorWithURL:[NSURL URLWithString:theGeoprocessingServiceURL]];

//add parameters to an arrary (for example)
NSArray *params = [NSArray arrayWithObjects:paramloc, paramDegree, paramTime, paramType, paramMaterial, nil];

//submit the geoprocessing job.
[self.geoprocessor submitJobWithParameters:params];

Sample Code

//SWIFT SAMPLE CODE
/*
Copyright 2014 Esri

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Cocoa
import ArcGIS

//layer urls
let kAGBaseMapURL = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"
let kGeoprocessingServiceURL = "http://sampleserver2.arcgisonline.com/ArcGIS/rest/services/PublicSafety/EMModels/GPServer/ERGByChemical"

class AsyncGeoprocessingSwiftSample: NSViewController, AGSMapViewLayerDelegate, AGSMapViewTouchDelegate, AGSGeoprocessorDelegate {
    
    @IBOutlet weak var mapView:AGSMapView!
    @IBOutlet weak var popover:NSPopover!
    @IBOutlet weak var windDirectionSlider:NSSlider!
    @IBOutlet weak var windDirectionLabel:NSTextField!
    @IBOutlet weak var materialTypePopupButton:NSPopUpButton!
    @IBOutlet weak var timeSegmentedControl:NSSegmentedControl!
    @IBOutlet weak var spillSegmentedControl:NSSegmentedControl!
    @IBOutlet weak var statusMessageLabel:NSTextField!
    
    var graphicsLayer:AGSGraphicsLayer!
    var geoprocessor:AGSGeoprocessor!
    var jobInfo:AGSGPJobInfo!
    
    //MARK: - awakeFromNib
    
    // -------------------------------------------------------------------------------
    //  awakeFromNib
    // -------------------------------------------------------------------------------
    override func awakeFromNib() {
    
        //set mapView's layer delegate so when mapViewDidLoad called, we can setup geoprocessor
        self.mapView.layerDelegate = self
        
        //set mapView's touch delegate so when user tap's on map, we can execute geoprocessing job
        self.mapView.touchDelegate = self
        
        //add base layer
        let baseMapLayer = AGSTiledMapServiceLayer(URL: NSURL(string: kAGBaseMapURL))
        self.mapView.addMapLayer(baseMapLayer, withName:"Base Layer")
        
        //add  graphics layer to result of geoprocessing task
        self.graphicsLayer = AGSGraphicsLayer()
        self.mapView.addMapLayer(self.graphicsLayer, withName:"Chemical ERG")
        
        //zoom to predefined extend with known spatial reference of the map
        let envelope = AGSEnvelope(xmin:-13639984, ymin:4537387, xmax:-13606734, ymax:4558866, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
        self.mapView.zoomToEnvelope(envelope, animated:true)
        
        //set wind direction label
        self.windDirectionLabel.stringValue = "Wind Direction \(self.windDirectionSlider.integerValue)°"
    }
    
    //MARK: - MapView Layer Delegate
    
    // -------------------------------------------------------------------------------
    //  mapViewDidLoad:mapView
    // -------------------------------------------------------------------------------
    func mapViewDidLoad(mapView: AGSMapView!) {
    
        //set up the geoprocessing task
        self.geoprocessor = AGSGeoprocessor(URL: NSURL(string: kGeoprocessingServiceURL))
        self.geoprocessor.delegate = self //required to respond to the gp response.
        self.geoprocessor.processSpatialReference = self.mapView.spatialReference
        self.geoprocessor.outputSpatialReference = self.mapView.spatialReference
    }
    
    //MARK: - MapView Touch Delegate
    
    // -------------------------------------------------------------------------------
    //  mapView:didClickAtPoint:mapPoint:features
    // -------------------------------------------------------------------------------
    func mapView(mapView: AGSMapView!, didClickAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
        
        //clear graphic layer before any update.
        self.graphicsLayer.removeAllGraphics()
        
        //create a symbol to show user tap location on map.
        let sms = AGSSimpleMarkerSymbol(color: NSColor(red: 0.0, green:1.0, blue:0.0, alpha:0.25))
        sms.size = CGSizeMake(10,10)
        sms.outline.width = 1
        sms.outline.color = NSColor.redColor()
        
        //create a graphic
        let graphic = AGSGraphic(geometry: mappoint, symbol: sms, attributes: nil)
        
        //add graphic to graphics layer
        self.graphicsLayer.addGraphic(graphic)
        
        //create a feature set for the input pareameter
        let featureSet = AGSFeatureSet()
        featureSet.features = [graphic]
        
        //create parameters and read values from UI control values
        let paramloc = AGSGPParameterValue(name: "Incident_Point", type:.FeatureRecordSetLayer, value:featureSet)
        let paramDegree = AGSGPParameterValue(name: "Wind_Bearing__direction_blowing_to__0_-_360_", type:.Double, value:self.windDirectionSlider.doubleValue)
        let paramMaterial = AGSGPParameterValue(name: "Material_Type", type:.String, value:self.materialTypePopupButton.titleOfSelectedItem)
        let paramTime = AGSGPParameterValue(name: "Day_or_Night_incident", type:.String, value:self.timeSegmentedControl.labelForSegment(self.timeSegmentedControl.selectedSegment))
        let paramType = AGSGPParameterValue(name: "Large_or_Small_spill", type:.String, value:self.spillSegmentedControl.labelForSegment(self.spillSegmentedControl.selectedSegment))
        
        //add parameters to arrary
        let params = [paramloc, paramDegree, paramTime, paramType, paramMaterial]
        
        //submit the geoprocessing job.
        //the interval property of the geoprocessor is not set to a value explicitly. default is 5 secs.
        self.geoprocessor.submitJobWithParameters(params)
    }
    
    //MARK: - Geoprocessor Delegate
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:didSubmitJob
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, didSubmitJob jobInfo: AGSGPJobInfo!) {
        
        //set job id
        self.jobInfo = jobInfo
        
        //update status
        self.statusMessageLabel.stringValue = "Executing geoprocessing job: \(jobInfo.jobId)..."
    }
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:didCheckJobStatus
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, didCheckJobStatus jobInfo: AGSGPJobInfo!) {
        //update status
        self.statusMessageLabel.stringValue = "Geoprocessing job status: \(jobInfo.jobStatus)..."
    }
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:jobDidSucceed
    //
    //  this gets called when gp job completes successfully
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, jobDidSucceed jobInfo: AGSGPJobInfo!) {
        
        //update status
        self.statusMessageLabel.stringValue = "Geoprocessing job succeeded..."
        
        //job succeed, query result data
        geoprocessor.queryResultData(jobInfo.jobId, paramName:"outerg_shp")
        
        //update status
        self.statusMessageLabel.stringValue = "Querying result data..."
    }
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:didQueryWithResult:jobId
    //
    //  this gets called when result data query completes successfully.
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, didQueryWithResult result: AGSGPParameterValue!, forJob jobId: String!) {
        
        //get the result
        let fs = result.value() as AGSFeatureSet
        
        //init graphics array
        var graphics = Array<AGSGraphic>()
        
        //create a symbol for graphic
        let sfs = AGSSimpleFillSymbol()
        sfs.color = NSColor.purpleColor().colorWithAlphaComponent(0.25)
        
        //loop through all graphics in feature set
        //set symbol and add them to map
        for graphic in fs.features as [AGSGraphic] {
        
            //set a symbol to graphic
            graphic.symbol = sfs
            
            //add graphic to graphics array
            graphics.append(graphic)
        }
        
        //add graphics to graphics layer
        self.graphicsLayer.addGraphics(graphics)
        
        //zoom to graphics layer
        self.mapView.zoomToGeometry(self.graphicsLayer.fullEnvelope, withPadding:600, animated:true)
        
        //update status
        self.statusMessageLabel.stringValue = "Geoprocessing job succeeded with results..."
        
        //reset status after delay
        dispatch_after(4, dispatch_get_main_queue(), {
            self.statusMessageLabel.stringValue = "Tap on the map to get the spill analysis..."
        })
    }
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:ofType:didFailWithError:forJob
    //
    //  if error encountered while executing gp task, show error
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, ofType opType: AGSGPAsyncOperationType, didFailWithError error: NSError!, forJob jobId: String!) {
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = "Failed to exectue job"
            alert.informativeText = error.localizedDescription
            alert.beginSheetModalForWindow(viewWindow, modalDelegate:self, didEndSelector:nil, contextInfo:nil)
        }
    }
    
    // -------------------------------------------------------------------------------
    //  geoprocessor:jobDidFail
    //
    //  if gp job fails, show error
    // -------------------------------------------------------------------------------
    func geoprocessor(geoprocessor: AGSGeoprocessor!, operation op: NSOperation!, jobDidFail jobInfo: AGSGPJobInfo!) {
    
        for message in jobInfo.messages {
            println("\((message as AGSGPMessage).description)")
        }
        
        //Update status
        self.statusMessageLabel.stringValue = "Job failed.."
        
        //reset the status
        dispatch_after(4, dispatch_get_main_queue(), {
            self.statusMessageLabel.stringValue = "Tap on the map to get the spill analysis..."
        })
    }
    
    //MARK: - Actions
    
    // -------------------------------------------------------------------------------
    //  settingsAction
    // -------------------------------------------------------------------------------
    @IBAction func settingsAction(settingsButton:NSButton) {
        
        if !self.popover.shown {
            self.popover.showRelativeToRect(settingsButton.bounds, ofView:settingsButton, preferredEdge:NSMaxYEdge)
        } else {
            self.popover.close()
        }
    }
    
    // -------------------------------------------------------------------------------
    //  windDirectionChangedAction
    // -------------------------------------------------------------------------------
    @IBAction func windDirectionChangedAction(sender:AnyObject) {
        //update wind direction label when user changes value
        self.windDirectionLabel.stringValue = "Wind Direction \(self.windDirectionSlider.integerValue)°"
    }
    
}
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 Esri
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import "AsyncGeoprocessingSample.h"

//layer urls
#define kBaseMapURL @"http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"
#define kGeoprocessingServiceURL @"http://sampleserver2.arcgisonline.com/ArcGIS/rest/services/PublicSafety/EMModels/GPServer/ERGByChemical"

@interface AsyncGeoprocessingSample ()

@end

@implementation AsyncGeoprocessingSample

#pragma mark - awakeFromNib

// -------------------------------------------------------------------------------
//  awakeFromNib
// -------------------------------------------------------------------------------
- (void)awakeFromNib {
    
    //set mapView's layer delegate so when mapViewDidLoad called, we can setup geoprocessor
    self.mapView.layerDelegate = self;
    
    //set mapView's touch delegate so when user tap's on map, we can execute geoprocessing job
    self.mapView.touchDelegate = self;
       
    //add base layer
	AGSTiledMapServiceLayer *baseMapLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kBaseMapURL]];
    [self.mapView addMapLayer:baseMapLayer withName:@"Base Layer"];
    
    //add  graphics layer to result of geoprocessing task
    self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
    [self.mapView addMapLayer:self.graphicsLayer withName:@"Chemical ERG"];
    
    //zoom to predefined extend with known spatial reference of the map
	AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-13639984 ymin:4537387 xmax:-13606734 ymax:4558866 spatialReference:[AGSSpatialReference webMercatorSpatialReference]];
	[self.mapView zoomToEnvelope:envelope animated:YES];
    
    //set wind direction label
    self.windDirectionLabel.stringValue = [NSString stringWithFormat:@"Wind Direction %i°",[self.windDirectionSlider intValue]];
}

#pragma mark - MapView Layer Delegate

// -------------------------------------------------------------------------------
//  mapViewDidLoad:mapView
// -------------------------------------------------------------------------------
- (void)mapViewDidLoad:(AGSMapView *) mapView {
	
    //set up the geoprocessing task
    self.geoprocessor = [AGSGeoprocessor geoprocessorWithURL:[NSURL URLWithString:kGeoprocessingServiceURL]];
	self.geoprocessor.delegate = self; //required to respond to the gp response.
	self.geoprocessor.processSpatialReference = self.mapView.spatialReference;
	self.geoprocessor.outputSpatialReference = self.mapView.spatialReference;
}

#pragma mark - MapView Touch Delegate

// -------------------------------------------------------------------------------
//  mapView:didClickAtPoint:mapPoint:features
// -------------------------------------------------------------------------------

-(void)mapView:(AGSMapView *)mapView didClickAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features{
    
    //clear graphic layer before any update.
	[self.graphicsLayer removeAllGraphics];
    
    //create a symbol to show user tap location on map.
	AGSSimpleMarkerSymbol *sms = [AGSSimpleMarkerSymbol simpleMarkerSymbolWithColor:[NSColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:0.25]];
    sms.size = CGSizeMake(10,10);
    sms.outline.width = 1;
    sms.outline.color = [NSColor redColor];
	
	//create a graphic
	AGSGraphic *graphic = [AGSGraphic graphicWithGeometry:mappoint symbol:sms attributes:nil];
	
	//add graphic to graphics layer
	[self.graphicsLayer addGraphic:graphic];
	
    //create a feature set for the input pareameter
	AGSFeatureSet *featureSet = [[AGSFeatureSet alloc] init];
	featureSet.features = [NSArray arrayWithObjects:graphic, nil];
    
    //create parameters and read values from UI control values
	AGSGPParameterValue *paramloc = [AGSGPParameterValue parameterWithName:@"Incident_Point" type:AGSGPParameterTypeFeatureRecordSetLayer value:featureSet];
	AGSGPParameterValue *paramDegree = [AGSGPParameterValue parameterWithName:@"Wind_Bearing__direction_blowing_to__0_-_360_" type:AGSGPParameterTypeDouble value:[NSNumber numberWithDouble:[self.windDirectionSlider intValue]]];
    AGSGPParameterValue *paramMaterial = [AGSGPParameterValue parameterWithName:@"Material_Type" type:AGSGPParameterTypeString value:[self.materialTypePopupButton titleOfSelectedItem]];
    AGSGPParameterValue *paramTime = [AGSGPParameterValue parameterWithName:@"Day_or_Night_incident" type:AGSGPParameterTypeString value:[self.timeSegmentedControl labelForSegment:[self.timeSegmentedControl selectedSegment]]];
    AGSGPParameterValue *paramType = [AGSGPParameterValue parameterWithName:@"Large_or_Small_spill" type:AGSGPParameterTypeString value:[self.spillSegmentedControl labelForSegment:[self.spillSegmentedControl selectedSegment]]];
    
    //add parameters to arrary
	NSArray *params = [NSArray arrayWithObjects:paramloc, paramDegree, paramTime, paramType, paramMaterial, nil];
    
    //submit the geoprocessing job.
	//the interval property of the geoprocessor is not set to a value explicitly. default is 5 secs.
    [self.geoprocessor submitJobWithParameters:params];
}

#pragma mark - Geoprocessor Delegate

// -------------------------------------------------------------------------------
//  geoprocessor:didSubmitJob
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *)geoprocessor operation:(NSOperation *)op didSubmitJob:(AGSGPJobInfo *)jobInfo {
    
    //set job id
    self.jobInfo = jobInfo;
        
	//update status
    self.statusMessageLabel.stringValue = [NSString stringWithFormat:@"Executing geoprocessing job: %@...",jobInfo.jobId];
}

// -------------------------------------------------------------------------------
//  geoprocessor:didCheckJobStatus
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *)geoprocessor operation:(NSOperation *)op didCheckJobStatus:(AGSGPJobInfo *)jobInfo {
    //update status
    self.statusMessageLabel.stringValue = [NSString stringWithFormat:@"Geoprocessing job status: %@...",jobInfo.jobStatus];
}

// -------------------------------------------------------------------------------
//  geoprocessor:jobDidSucceed
//
//  this gets called when gp job completes successfully
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *) geoprocessor operation:(NSOperation *) op jobDidSucceed:(AGSGPJobInfo *) jobInfo {
	
    //update status
    self.statusMessageLabel.stringValue = [NSString stringWithFormat:@"Geoprocessing job succeeded..."];

	//job succeed, query result data
    [geoprocessor queryResultData:jobInfo.jobId paramName:@"outerg_shp"];
    
    //update status
    self.statusMessageLabel.stringValue = [NSString stringWithFormat:@"Querying result data..."];
}

// -------------------------------------------------------------------------------
//  geoprocessor:didQueryWithResult:jobId
//
//  this gets called when result data query completes successfully.
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *) geoprocessor operation:(NSOperation *) op didQueryWithResult:(AGSGPParameterValue *) result forJob:(NSString *) jobId {
	
	//get the result
    AGSFeatureSet *fs = result.value;
    
    //init graphics array
    NSMutableArray *graphics = [NSMutableArray array];
    
    //create a symbol for graphic
    AGSSimpleFillSymbol *sfs = [AGSSimpleFillSymbol simpleFillSymbol];
    sfs.color = [[NSColor purpleColor] colorWithAlphaComponent:0.25];
	
    //loop through all graphics in feature set
    //set symbol and add them to map
	for(AGSGraphic *graphic in fs.features){
		
		//set a symbol to graphic
		graphic.symbol = sfs;
		
        //add graphic to graphics array
        [graphics addObject:graphic];
	}
    
    //add graphics to graphics layer
    [self.graphicsLayer addGraphics:graphics];
    
	//zoom to graphics layer
    [self.mapView zoomToGeometry:self.graphicsLayer.fullEnvelope withPadding:600 animated:YES];
    
    //update status
    self.statusMessageLabel.stringValue = @"Geoprocessing job succeeded with results...";
    
	//reset status after delay
    [self performSelector:@selector(resetStatusMessageLabel:) withObject:@"Tap on the map to get the spill analysis..." afterDelay:4];
}

// -------------------------------------------------------------------------------
//  geoprocessor:ofType:didFailWithError:forJob
//
//  if error encountered while executing gp task, show error
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *) geoprocessor operation:(NSOperation *)op ofType:(AGSGPAsyncOperationType) opType didFailWithError:(NSError *) error forJob:(NSString *) jobId {
	
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:@"Failed to exectue job"];
    [alert setInformativeText:[NSString stringWithFormat:@"%@",error]];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
}

// -------------------------------------------------------------------------------
//  geoprocessor:jobDidFail
//
//  if gp job fails, show error
// -------------------------------------------------------------------------------
- (void)geoprocessor:(AGSGeoprocessor *) geoprocessor operation:(NSOperation *) op jobDidFail:(AGSGPJobInfo *) jobInfo {
	
    for (AGSGPMessage* msg in jobInfo.messages) {
        NSLog(@"%@", msg.description);
    }
    
    //update staus
    self.statusMessageLabel.stringValue = @"Job failed...";
    
	//reset the status
    [self performSelector:@selector(resetStatusMessageLabel:) withObject:@"Tap on the map to get the spill analysis..." afterDelay:4];
}

#pragma mark - Actions

// -------------------------------------------------------------------------------
//  settingsAction
// -------------------------------------------------------------------------------
- (IBAction)settingsAction:(id)sender {
    
    //show popover when user taps on settings button
    NSButton *settingsButton = (NSButton*)sender;
    if (!self.popover.shown) {
        [self.popover showRelativeToRect:[settingsButton bounds] ofView:settingsButton preferredEdge:NSMaxYEdge];
    } else {
        [self.popover close];
    }
}

// -------------------------------------------------------------------------------
//  windDirectionChangedAction
// -------------------------------------------------------------------------------
- (IBAction)windDirectionChangedAction:(id)sender {
    //update wind direction label when user changes value
    self.windDirectionLabel.stringValue = [NSString stringWithFormat:@"Wind Direction %i°",[self.windDirectionSlider intValue]];
}

// -------------------------------------------------------------------------------
//  resetStatusMessageLabel
// -------------------------------------------------------------------------------
- (void)resetStatusMessageLabel:(NSString*)message {
    self.statusMessageLabel.stringValue = message;
}

@end
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 Esri
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import <Cocoa/Cocoa.h>

@interface AsyncGeoprocessingSample : NSViewController <AGSMapViewLayerDelegate,AGSMapViewTouchDelegate,AGSGeoprocessorDelegate>

@property (strong) IBOutlet AGSMapView *mapView;
@property (strong) IBOutlet NSPopover *popover;
@property (strong) IBOutlet NSSlider *windDirectionSlider;
@property (strong) IBOutlet NSTextField *windDirectionLabel;
@property (strong) IBOutlet NSPopUpButton *materialTypePopupButton;
@property (strong) IBOutlet NSSegmentedControl *timeSegmentedControl;
@property (strong) IBOutlet NSSegmentedControl *spillSegmentedControl;
@property (strong) IBOutlet NSTextField *statusMessageLabel;
@property (strong) AGSGraphicsLayer *graphicsLayer;
@property (strong) AGSGeoprocessor *geoprocessor;
@property (strong) AGSGPJobInfo *jobInfo;

- (IBAction)settingsAction:(id)sender;
- (IBAction)windDirectionChangedAction:(id)sender;

@end
Feedback on this topic?