Routing Offline

Download Sample Application

This sample demonstrates how you can go offline and calculate point-to-point routes using a network dataset file stored on your Mac. The sample uses the Sketch Layer to allow you to add the start of your route (green points) and to add barriers (red points, lines or polygons) to avoid certain roads and intersections. Click on the Find Route button and move the cursor across the map to generate the route from the start to your cursor. The route is shown on the map as a blue line, and the distance and drive time for the route are shown in a callout.

//header file
@interface RoutingOfflineSample : NSViewController <AGSRouteTaskDelegate>
@property (strong) AGSRouteTask *routeTask;
@property (strong) AGSRouteTaskParameters *routeTaskParameters;
@property (strong) AGSRouteResult *routeResult;
...
@end

//implementation file
//set up the route task and set delegate
//The network dataset file is stored within this application (i.e. within the main bundle of the application).
NSString *databasePath = [[NSBundle mainBundle] pathForResource:@"RuntimeSanDiego" ofType:@"geodatabase"];
self.routeTask = [AGSRouteTask routeTaskWithDatabasePath:databasePath network:@"Streets_ND" error:&error];       
self.routeTask.delegate = self;

//set up the parameters
[self.routeTaskParameters setStopsWithFeatures:stops];
[self.routeTaskParameters setPolylineBarriersWithFeatures:barriers];
self.routeTaskParameters.returnRouteGraphics = YES;
self.routeTaskParameters.returnDirections = YES;
...

//use the route task to solve the route providing the parameters
[self.routeTask solveWithParameters:self.routeTaskParameters];

//process the results when the solve has returned some results
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didSolveWithResult:(AGSRouteTaskResult *)routeTaskResult {
  ...
}

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/routetask urls
let kROBaseMapURL = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"

class RoutingOfflineSwiftSample: NSViewController, AGSRouteTaskDelegate, AGSMapViewTouchDelegate, AGSLayerDelegate {
    
    @IBOutlet weak var mapView:AGSMapView!
    @IBOutlet weak var stopsBarriersSegmentedControl:NSSegmentedControl!
    @IBOutlet weak var sketchTools:NSSegmentedControl!
    @IBOutlet weak var addStopOrBarrierButton:NSButton!
    @IBOutlet weak var clearSketchButton:NSButton!
    @IBOutlet weak var messageLabel:NSTextField!
    @IBOutlet weak var message2Label:NSTextField!
    
    var startGraphicsLayer:AGSGraphicsLayer!
    var endGraphicsLayer:AGSGraphicsLayer!
    var barriersGraphicsLayer:AGSGraphicsLayer!
    var routeGraphicsLayer:AGSGraphicsLayer!
    var sketchLayer:AGSSketchGraphicsLayer!
    var routeTask:AGSRouteTask!
    var routeTaskParameters:AGSRouteTaskParameters!
    var routeResult:AGSRouteResult!
    var currentDirectionGraphic:AGSDirectionGraphic!
    var stops = Array<AGSStopGraphic>()
    var pointBarriers = Array<AGSGraphic>()
    var polylineBarriers = Array<AGSGraphic>()
    var polygonBarriers = Array<AGSGraphic>()
    var numStops:UInt = 0
    var numBarriers:UInt = 0
    var mapPoint:AGSPoint!
    var awaken = false
    var showRoute = false
    var hasEnd = false
    
    //MARK: - awakeFromNib
    
    // -------------------------------------------------------------------------------
    //  awakeFromNib
    // -------------------------------------------------------------------------------
    override func awakeFromNib() {
    
        if !self.awaken {
            
            //add base layer to map
            let baseMapLayer = AGSTiledMapServiceLayer(URL: NSURL(string: kROBaseMapURL))
            self.mapView.addMapLayer(baseMapLayer, withName:"Base Layer")
            
            //add graphics layers for barriers, route, start adn end graphics
            //these are kept as single graphics layers to make the code more simple
            //you could combine them into one single graphicslayer
            
            self.barriersGraphicsLayer = AGSGraphicsLayer()
            self.mapView.addMapLayer(self.barriersGraphicsLayer, withName:"Barriers Graphics Layer")
            
            self.routeGraphicsLayer = AGSGraphicsLayer()
            self.mapView.addMapLayer(self.routeGraphicsLayer, withName:"Route Graphics Layer")
            
            self.startGraphicsLayer = AGSGraphicsLayer()
            self.mapView.addMapLayer(self.startGraphicsLayer, withName:"Start Graphics Layer")
            
            self.endGraphicsLayer = AGSGraphicsLayer()
            self.mapView.addMapLayer(self.endGraphicsLayer, withName:"End Graphics Layer")
            
            
            //add sketch graphics layer and set as mapView's touch delegate so it begin tracking touch events
            self.sketchLayer = AGSSketchGraphicsLayer(geometry: AGSMutablePoint(spatialReference: AGSSpatialReference.webMercatorSpatialReference()))
            self.mapView.touchDelegate = self.sketchLayer
            self.mapView.addMapLayer(self.sketchLayer, withName:"Sketch Layer")
            
            //Network Dataset--------------//
            //The network dataset file is stored within this application (i.e. within the main bundle of the application).
            //pathForResource will locate the physical path to the RuntimeSanDiego.geodatabase file
            let databasePath = NSBundle.mainBundle().pathForResource("RuntimeSanDiego", ofType:"geodatabase")
            
            // Setup the route task
            var error:NSError?
            self.routeTask = AGSRouteTask(databasePath: databasePath, network:"Streets_ND", error:&error)
            // assign delegate to this view controller
            self.routeTask.delegate = self
            
            if error != nil {
                self.showErrorWithTitle("Offline Network Dataset failed to load!", message:error!.localizedDescription)
            }
            else {
                //kick off asynchronous method to retrieve default parameters for the route task
                self.routeTask.retrieveDefaultRouteTaskParameters()
                self.routeTask.retrieveNetworkDescription()
            }
            
            //register for "Geometry Changed" notifications
            //we want to enable/disable UI elements when sketch geometry is modified
            NSNotificationCenter.defaultCenter().addObserver(self, selector:"respondToGeometryChanged:", name:AGSSketchGraphicsLayerGeometryDidChangeNotification, object:self.sketchLayer)
            
            //zoom to predefined extend with known spatial reference of the map
            let envelope = AGSEnvelope(xmin: -13044000, ymin:3855000, xmax:-13040000, ymax:3858000, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
            self.mapView.zoomToEnvelope(envelope, animated:true)
            
            
            self.message2Label.hidden = true
            
            self.awaken = true
            self.showRoute = false
            self.hasEnd = false
        }
    }
    
    //MARK: - AGSMapViewTouchDelegate methods
    func mapView(mapView: AGSMapView!, didTapAndHoldAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
        self.mapPoint = mappoint
        
        if self.showRoute {
            self.calculateRoute()
            self.enableStartAndBarriers(false)
        }
    }
    
    func mapView(mapView: AGSMapView!, didMoveTapAndHoldAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
        
        self.mapPoint = mappoint
        
        if self.showRoute {
            self.calculateRoute()
            self.enableStartAndBarriers(false)
        }
    
    }
    
    //MARK: - AGSLayerDelegate Delegate
    
    // -------------------------------------------------------------------------------
    //  layer:didFailToLoadWithError:error
    // -------------------------------------------------------------------------------
    func layer(layer: AGSLayer!, didFailToLoadWithError error: NSError!) {
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = "Layer '\(layer.name)' failed to load"
            alert.informativeText = error.localizedDescription
            alert.beginSheetModalForWindow(viewWindow, modalDelegate:self, didEndSelector:nil, contextInfo:nil)
        }
    }
    
    //MARK: - RouteTask Delegate
    
    // -------------------------------------------------------------------------------
    //  routeTask:didRetrieveDefaultRouteTaskParameters
    //
    //  this will be called when route task retrieve default parameters successfully
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didRetrieveDefaultRouteTaskParameters routeParams: AGSRouteTaskParameters!) {
        self.routeTaskParameters = routeParams
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didFailToRetrieveDefaultRouteTaskParametersWithError
    //
    //  this will be called when route tssk fails to retrieve default parameters
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didFailToRetrieveDefaultRouteTaskParametersWithError error: NSError!) {
        // let the user know the retrieval failed
        self.showErrorWithTitle("Failed to retrieve default route parameters", message:error.localizedDescription)
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didSolveWithResult
    //
    //  this will be called when route task solve route successfully
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didSolveWithResult routeTaskResult: AGSRouteTaskResult!) {
        // we know that we are only dealing with 1 route...
        self.routeResult = routeTaskResult.routeResults.last as AGSRouteResult
        if self.routeResult != nil {
            
            self.mapView.callout.showCalloutAt(self.mapPoint, screenOffset:CGPointMake(0,0), animated:true)
            
            self.mapView.callout.title = String(format: "Distance = %.02f Miles", self.routeResult.totalMiles)
            self.mapView.callout.detail = String(format: "Drive Time = %.02f Minutes", self.routeResult.totalMinutes)
            
            //disable sketch tools
            self.sketchTools.setEnabled(false, forSegment: 0)
            self.sketchTools.setEnabled(false, forSegment: 1)
            self.sketchTools.setEnabled(false, forSegment: 2)
            
            // symbolize the returned route graphic
            self.routeResult.routeGraphic.symbol = self.routeSymbol()
            
            // add the route graphic to the graphic's layer
            self.routeGraphicsLayer.addGraphic(self.routeResult.routeGraphic)
            
            //remove sketch layer as mapView's touch delegate
            self.mapView.touchDelegate = self
        }
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didFailSolveWithError
    //
    //  this will be called when route tssk fails to solve route
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didFailSolveWithError error: NSError!) {
        
        self.mapView.callout.showCalloutAt(self.mapPoint, screenOffset:CGPointMake(0,0), animated:true)
        self.mapView.callout.title = "Routing is not possible here"
        self.mapView.callout.detail = ""
        
        //disable sketch tools
        self.sketchTools.setEnabled(false, forSegment: 0)
        self.sketchTools.setEnabled(false, forSegment: 1)
        self.sketchTools.setEnabled(false, forSegment: 2)
            
        //remove sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = self
    }
    
    //MARK: - Actions
    
    // -------------------------------------------------------------------------------
    //  stopsBarriersAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func stopsBarriersAction(sender:NSSegmentedControl) {
        
        if sender.selectedSegment == 0 {
            //clear sketch geometry
            self.sketchLayer.clear()
            
            //disable polyline and polygon sketch tools
            self.sketchTools.setEnabled(true, forSegment:0)
            self.sketchTools.setEnabled(false, forSegment:1)
            self.sketchTools.setEnabled(false, forSegment:2)
            self.sketchTools.setSelected(true, forSegment: 0)
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePoint(spatialReference: self.mapView.spatialReference)
        }
        else {
            
            //clear sketch geometry
            self.sketchLayer.clear()
            
            //enable all sketch tools
            self.sketchTools.setEnabled(true, forSegment:0)
            self.sketchTools.setEnabled(true, forSegment:1)
            self.sketchTools.setEnabled(true, forSegment:2)
        }
    }
    
    // -------------------------------------------------------------------------------
    //  sketchToolsAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func sketchToolsAction(sender:NSSegmentedControl) {
        
        switch sender.selectedSegment {
        case 0://point tool
            //sketch layer should begin tracking touch events to sketch a point
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePoint(spatialReference: self.mapView.spatialReference)
            break
            
        case 1://polyline tool
            //sketch layer should begin tracking touch events to sketch a polyline
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePolyline(spatialReference: self.mapView.spatialReference)
            break
            
        case 2://polygon tool
            //sketch layer should begin tracking touch events to sketch a polygon
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePolygon(spatialReference: self.mapView.spatialReference)
            break
            
        default:
            break
        }
        
    }
    
    // -------------------------------------------------------------------------------
    //  addStopOrBarrierAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func addStopOrBarrierAction(sender:AnyObject) {
        
        //grab the geometry, then clear the sketch
        let geometry = self.sketchLayer.geometry.copy() as AGSGeometry
        self.sketchLayer.clear()
        
        //Prepare symbol and attributes for the Stop/Barrier
        let attributes = NSMutableDictionary()
        
        if self.stopsBarriersSegmentedControl.selectedSegment == 0 {
            
            //increase counter
            self.numStops++
            
            //set stop symbol
            let symbol = self.stopSymbolWithNumber(1)
            
            //create stop graphic
            let stopGraphic = AGSStopGraphic(geometry: geometry, symbol: symbol, attributes: attributes)
            //set stop graphic sequence
            //You can set additional properties on the stop here
            //refer to the conceptual helf for Routing task
            stopGraphic.sequence = 1
            
            self.stops.removeAll(keepCapacity: false)
            
            //add graphic to stop graphics array
            self.stops.append(stopGraphic)
            
            self.startGraphicsLayer.removeAllGraphics()
            
            //add stop graphic to graphics layer
            self.startGraphicsLayer.addGraphic(stopGraphic)
            
            //enable all sketch tools
            self.sketchTools.setEnabled(true, forSegment: 0)
            self.sketchTools.setEnabled(true, forSegment: 1)
            self.sketchTools.setEnabled(true, forSegment: 2)
            self.sketchTools.setSelected(false, forSegment: 0)
            self.sketchTools.setSelected(false, forSegment: 1)
            self.sketchTools.setSelected(false, forSegment: 2)
            self.stopsBarriersSegmentedControl.setSelected(true, forSegment: 1)
        }
        else {
            
            //increase counter
            self.numBarriers++
            
            //set barrierNumber attribute
            //you can set additional properties on the barrier here
            //refer to the conceptual help for Routing task
            attributes["barrierNumber"] = NSNumber(unsignedLong:self.numBarriers)
            
            //create a barrier graphic
            let g = AGSGraphic(geometry: geometry, symbol: nil, attributes: attributes)
            
            //set symbol based on type of geometry
            if geometry is AGSPoint {
                
                //set symbol
                g.symbol = self.pointBarrierSymbol()
                
                //add to point barriers array
                self.pointBarriers.append(g)
            }
            else if geometry is AGSPolyline {
                
                //set symbol
                g.symbol = self.polylineBarrierSymbol()
                
                //add to point barriers array
                self.polylineBarriers.append(g)
            }
            else if geometry is AGSPolygon {
                
                //set symbol
                g.symbol = self.polygonBarrierSymbol()
                
                //add to point barriers array
                self.polygonBarriers.append(g)
            }
            
            //add to graphics layer
            self.barriersGraphicsLayer.addGraphic(g)
        }
        
        if self.stops.count > 0 {
            self.mapView.touchDelegate = self
            self.showRoute = true
            self.message2Label.hidden = false
        }
    }
    
    // -------------------------------------------------------------------------------
    //  clearSketchAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func clearSketchAction(sender:AnyObject) {
        //clear sketch geometry
        self.sketchLayer.clear()
        self.message2Label.hidden = true
        self.mapView.callout.dismiss()
    }
    
    // -------------------------------------------------------------------------------
    //  resetAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func resetAction(sender:AnyObject) {
        
        // set stop counter back to 0
        self.numStops = 0
        
        // set barrier counter back to 0
        self.numBarriers = 0
        
        self.stopsBarriersSegmentedControl.selectedSegment = 0
        self.sketchTools.setSelected(true, forSegment: 0)
        self.sketchTools.setEnabled(true, forSegment: 0)
        self.sketchTools.setEnabled(false, forSegment: 1)
        self.sketchTools.setEnabled(false, forSegment: 2)
        self.sketchLayer.geometry = AGSMutablePoint(spatialReference: self.mapView.spatialReference)
        
        //reset stops and barriers array
        self.stops = Array<AGSStopGraphic>()
        self.pointBarriers = Array<AGSGraphic>()
        self.polylineBarriers = Array<AGSGraphic>()
        self.polygonBarriers = Array<AGSGraphic>()
        
        // remove all graphics
        self.startGraphicsLayer.removeAllGraphics()
        self.endGraphicsLayer.removeAllGraphics()
        self.barriersGraphicsLayer.removeAllGraphics()
        self.routeGraphicsLayer.removeAllGraphics()
        
        //clear sketch layer
        self.sketchLayer.clear()
        
        //set sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = self.sketchLayer
        
        self.enableStartAndBarriers(true)
        self.message2Label.hidden = true
        self.mapView.callout.dismiss()
        
        self.showRoute = false
        self.hasEnd = false
    }
    
    // -------------------------------------------------------------------------------
    //  routeAction:sender
    // -------------------------------------------------------------------------------
    func calculateRoute() {
        
        // update message
//        self.messageLabel.stringValue = "Routing..."
        
        //clear sketch layer
        self.sketchLayer.clear()
        self.routeGraphicsLayer.removeAllGraphics()
        
        //remove sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = nil
        
        //Prepare symbol and attributes for the Stop/Barrier
        var attributes = NSMutableDictionary()
        
        //set stop symbol
        let symbol = self.stopSymbolWithNumber(2)
        
        //create stop graphic
        let stopGraphic = AGSStopGraphic(geometry: self.mapPoint, symbol: symbol, attributes: attributes)
        
        self.endGraphicsLayer.removeAllGraphics()
        self.endGraphicsLayer.addGraphic(stopGraphic)
        
        //set stop graphic sequence
        //You can set additional properties on the stop here
        //refer to the conceptual helf for Routing task
        stopGraphic.sequence = 2
        
        //add graphic to stop graphics array
        if self.hasEnd {

            self.stops[1] = stopGraphic
        }
        else{
            self.stops.append(stopGraphic)
            self.hasEnd = true
        }
        
        // set the stops and  barriers on the parameters object
        if self.stops.count > 0 {
            self.routeTaskParameters.setStopsWithFeatures(self.stops)
        }
        if self.pointBarriers.count > 0 {
            self.routeTaskParameters.setPointBarriersWithFeatures(self.pointBarriers)
        }
        if self.polylineBarriers.count > 0 {
            self.routeTaskParameters.setPolylineBarriersWithFeatures(self.polylineBarriers)
        }
        if self.polygonBarriers.count > 0 {
            self.routeTaskParameters.setPolygonBarriersWithFeatures(self.polygonBarriers)
        }
        
        // this generalizes the route graphics that are returned
        self.routeTaskParameters.outputGeometryPrecision = 5.0
        self.routeTaskParameters.outputGeometryPrecisionUnits = .Meters
        
        // return the graphic representing the entire route, generalized by the previous
        // 2 properties: outputGeometryPrecision and outputGeometryPrecisionUnits
        self.routeTaskParameters.returnRouteGraphics = true
        
        // this returns turn-by-turn directions
        self.routeTaskParameters.returnDirections = true
        
        // the next 3 lines will cause the task to find the
        // best route regardless of the stop input order
        self.routeTaskParameters.findBestSequence = true
        self.routeTaskParameters.preserveFirstStop = true
        self.routeTaskParameters.preserveLastStop = false
        
        // since we used "findBestSequence" we need to
        // get the newly reordered stops
        self.routeTaskParameters.returnStopGraphics = true
        
        // ensure the graphics are returned in our map's spatial reference
        self.routeTaskParameters.outSpatialReference = self.mapView.spatialReference
        
        // let's ignore invalid locations
        self.routeTaskParameters.ignoreInvalidLocations = true
        
        self.routeTaskParameters.accumulateAttributeNames = ["Meters"]
        
        // you can also set additional properties here that should
        // be considered during analysis.
        // See the conceptual help for Routing task.
        
        // execute the route task
        self.routeTask.solveWithParameters(self.routeTaskParameters)
    }
    
    //MARK: - Symbols
    
    // -------------------------------------------------------------------------------
    //  stopSymbolWithNumber:stopNumber
    // -------------------------------------------------------------------------------
    func stopSymbolWithNumber(stopNumber:Int) -> AGSCompositeSymbol {
        
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        // create main circle
        let sms = AGSSimpleMarkerSymbol()
        sms.color = NSColor.greenColor()
        sms.size = CGSizeMake(20, 20)
        sms.style = .Circle
        cs.addSymbol(sms)
        
        // add number as a text symbol
        let ts = AGSTextSymbol(text: "\(stopNumber)", color: NSColor.blackColor())
        ts.vAlignment = .Middle
        ts.hAlignment = .Center
        ts.fontSize	= 16
        cs.addSymbol(ts)
        
        return cs
    }
    
    // -------------------------------------------------------------------------------
    //  pointBarrierSymbol
    // -------------------------------------------------------------------------------
    func pointBarrierSymbol() -> AGSSimpleMarkerSymbol {
        //create a simple marker symbol
        let sms = AGSSimpleMarkerSymbol()
        sms.color = NSColor.redColor()
        sms.size = CGSizeMake(20, 20)
        sms.style = .Circle
        return sms
    }
    
    // -------------------------------------------------------------------------------
    //  polylineBarrierSymbol
    // -------------------------------------------------------------------------------
    func polylineBarrierSymbol() -> AGSSimpleLineSymbol {
        //create a simple line symbol
        let sls = AGSSimpleLineSymbol()
        sls.color = NSColor.redColor()
        sls.style = .Solid
        sls.width = 4
        return sls
    }
    
    // -------------------------------------------------------------------------------
    //  polygonBarrierSymbol
    // -------------------------------------------------------------------------------
    func polygonBarrierSymbol() -> AGSSimpleFillSymbol {
        //create simple fill symbol
        let sfs = AGSSimpleFillSymbol()
        sfs.style = .Solid
        sfs.color = NSColor.redColor().colorWithAlphaComponent(0.5)
        sfs.outline.width = 4
        sfs.outline.color = NSColor.redColor()
        return sfs
    }
    
    // -------------------------------------------------------------------------------
    //  routeSymbol
    // -------------------------------------------------------------------------------
    func routeSymbol() -> AGSCompositeSymbol {
        
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        let sls1 = AGSSimpleLineSymbol()
        sls1.color = NSColor.blueColor()
        sls1.style = .Solid
        sls1.width = 6
        cs.addSymbol(sls1)
        
        let sls2 = AGSSimpleLineSymbol()
        sls2.color = NSColor(red: 0/255.0, green:170/255.0, blue:255/255.0, alpha:1.0)
        sls2.style = .Solid
        sls2.width = 4
        cs.addSymbol(sls2)
        
        return cs
    }
    
    // -------------------------------------------------------------------------------
    //  currentDirectionSymbol
    // -------------------------------------------------------------------------------
    func currentDirectionSymbol() -> AGSCompositeSymbol {
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        let sls1 = AGSSimpleLineSymbol()
        sls1.color = NSColor.blackColor()
        sls1.style = .Solid
        sls1.width = 8
        cs.addSymbol(sls1)
        
        let sls2 = AGSSimpleLineSymbol()
        sls2.color = NSColor.yellowColor()
        sls2.style = .Solid
        sls2.width = 4
        cs.addSymbol(sls2)
        
        return cs
    }
    
    //MARK: - Respond To Geometry Changed
    
    // -------------------------------------------------------------------------------
    //  respondToGeometryChanged:aNotification
    // -------------------------------------------------------------------------------
    func respondToGeometryChanged(aNotification:NSNotification) {
        
        //enable/disable add/clear buttons
        self.clearSketchButton.enabled = !self.sketchLayer.geometry.isEmpty() && self.sketchLayer.geometry != nil
        self.addStopOrBarrierButton.enabled = self.sketchLayer.geometry.isValid()
    }
    
    deinit {
        //remove observer
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    //MARK: - Maintain UI
    
    func enableStartAndBarriers(enable:Bool) {
        self.stopsBarriersSegmentedControl.enabled = enable
        self.sketchTools.enabled = enable
        self.addStopOrBarrierButton.enabled = enable
    }
    
    //MARK: - Show Error
    
    func showErrorWithTitle(title:String, message:String) {
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = title
            alert.informativeText = message
            alert.beginSheetModalForWindow(viewWindow, modalDelegate:self, didEndSelector:nil, contextInfo:nil)
        }
    }
}
//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 "RoutingOfflineSample.h"

//layer/routetask urls
#define kBaseMapURL @"http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"

#pragma mark - RoutingOfflineSample

@interface RoutingOfflineSample () {
    BOOL _awaken;
    BOOL showRoute;
    BOOL hasEnd;
}
@end

@implementation RoutingOfflineSample

#pragma mark - awakeFromNib

// -------------------------------------------------------------------------------
//  awakeFromNib
// -------------------------------------------------------------------------------
- (void)awakeFromNib {
    
    if (!_awaken) {
        
        //add base layer to map
        AGSTiledMapServiceLayer *baseMapLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kBaseMapURL]];
        [self.mapView addMapLayer:baseMapLayer withName:@"Base Layer"];
        
        //add graphics layers for barriers, route, start adn end graphics
        //these are kept as single graphics layers to make the code more simple
        //you could combine them into one single graphicslayer
        
        self.barriersGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.barriersGraphicsLayer withName:@"Barriers Graphics Layer"];
        
        self.routeGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.routeGraphicsLayer withName:@"Route Graphics Layer"];

        self.startGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.startGraphicsLayer withName:@"Start Graphics Layer"];

        self.endGraphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.endGraphicsLayer withName:@"End Graphics Layer"];
        
        
        //add sketch graphics layer and set as mapView's touch delegate so it begin tracking touch events
        self.sketchLayer = [[AGSSketchGraphicsLayer alloc] initWithGeometry:[[AGSMutablePoint alloc] initWithSpatialReference:[AGSSpatialReference webMercatorSpatialReference]]];
        self.mapView.touchDelegate = self.sketchLayer;
        [self.mapView addMapLayer:self.sketchLayer withName:@"Sketch Layer"];
        
        //Network Dataset--------------//
        //The network dataset file is stored within this application (i.e. within the main bundle of the application).
        //pathForResource will locate the physical path to the RuntimeSanDiego.geodatabase file
        NSString *databasePath = [[NSBundle mainBundle] pathForResource:@"RuntimeSanDiego" ofType:@"geodatabase"];
   
        // Setup the route task
        NSError *error = nil;
        self.routeTask = [AGSRouteTask routeTaskWithDatabasePath:databasePath network:@"Streets_ND" error:&error];
        // assign delegate to this view controller
        self.routeTask.delegate = self;
        
        if (error) {
            [self  showErrorWithTitle:@"Offline Network Dataset failed to load!" message:error.localizedDescription];
        }
        else {
            //kick off asynchronous method to retrieve default parameters for the route task
            [self.routeTask retrieveDefaultRouteTaskParameters];
            [self.routeTask retrieveNetworkDescription];
        }
        
        //Add layers to the map------------------//
        //init stops and barriers array
        self.stops = [NSMutableArray array];
        self.pointBarriers = [NSMutableArray array];
        self.polylineBarriers = [NSMutableArray array];
        self.polygonBarriers = [NSMutableArray array];
        
        //register for "Geometry Changed" notifications
        //we want to enable/disable UI elements when sketch geometry is modified
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondToGeometryChanged:) name:AGSSketchGraphicsLayerGeometryDidChangeNotification object:self.sketchLayer];
        
        //zoom to predefined extend with known spatial reference of the map
        AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-13044000 ymin:3855000 xmax:-13040000 ymax:3858000 spatialReference:[AGSSpatialReference webMercatorSpatialReference]];
        [self.mapView zoomToEnvelope:envelope animated:YES];
        [self.mapView.maxEnvelope envelope];
        
        [self.message2Label setHidden:YES];
        
        _awaken = YES;
        showRoute = NO;
        hasEnd = NO;
        

    }
}

#pragma mark - AGSMapViewTouchDelegate methods

-(void) mapView:(AGSMapView *)mapView didTapAndHoldAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features{

    self.mapPoint = mappoint;
    
    if(showRoute){
        [self calculateRoute];
        [self enableStartAndBarriers:NO];
    }
}


-(void) mapView:(AGSMapView *)mapView didMoveTapAndHoldAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features{
    
    self.mapPoint = mappoint;
    
    if(showRoute){
        [self calculateRoute];
        [self enableStartAndBarriers:NO];
    }

}

#pragma mark - AGSLayerDelegate Delegate

// -------------------------------------------------------------------------------
//  layer:didFailToLoadWithError:error
// -------------------------------------------------------------------------------
- (void)layer:(AGSLayer *)layer didFailToLoadWithError:(NSError *)error {
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:[NSString stringWithFormat:@"Layer '%@' failed to load",layer.name]];
    [alert setInformativeText:[NSString stringWithFormat:@"%@",error]];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
}

#pragma mark - RouteTask Delegate

// -------------------------------------------------------------------------------
//  routeTask:didRetrieveDefaultRouteTaskParameters
//
//  this will be called when route task retrieve default parameters successfully
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didRetrieveDefaultRouteTaskParameters:(AGSRouteTaskParameters *)routeParams {
	self.routeTaskParameters = routeParams;
}

// -------------------------------------------------------------------------------
//  routeTask:didFailToRetrieveDefaultRouteTaskParametersWithError
//
//  this will be called when route tssk fails to retrieve default parameters
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didFailToRetrieveDefaultRouteTaskParametersWithError:(NSError *)error {
	// let the user know the retrieval failed
    [self  showErrorWithTitle:@"Failed to retrieve default route parameters" message:error.localizedDescription];
}

// -------------------------------------------------------------------------------
//  routeTask:didSolveWithResult
//
//  this will be called when route task solve route successfully
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didSolveWithResult:(AGSRouteTaskResult *)routeTaskResult {
	
	// we know that we are only dealing with 1 route...
	self.routeResult = [routeTaskResult.routeResults lastObject];
	if (self.routeResult) {
        
        [self.mapView.callout showCalloutAt:self.mapPoint screenOffset:CGPointMake(0,0) animated:YES];
        
        self.mapView.callout.title = [NSString stringWithFormat:@"Distance = %.02f Miles", self.routeResult.totalMiles];
        self.mapView.callout.detail = [NSString stringWithFormat:@"Drive Time = %.02f Minutes", self.routeResult.totalMinutes];
        
        //disable sketch tools
        [self.sketchTools setEnabled:NO forSegment:0];
        [self.sketchTools setEnabled:NO forSegment:1];
        [self.sketchTools setEnabled:NO forSegment:2];
        
		// symbolize the returned route graphic
		self.routeResult.routeGraphic.symbol = [self routeSymbol];
        
        // add the route graphic to the graphic's layer
		[self.routeGraphicsLayer addGraphic:self.routeResult.routeGraphic];
        
        //remove sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = self;
	}
}

// -------------------------------------------------------------------------------
//  routeTask:didFailSolveWithError
//
//  this will be called when route tssk fails to solve route
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didFailSolveWithError:(NSError *)error {
    
    [self.mapView.callout showCalloutAt:self.mapPoint screenOffset:CGPointMake(0,0) animated:YES];
    self.mapView.callout.title = @"Routing is not possible here";
    self.mapView.callout.detail = @"";
    
    //disable sketch tools
    [self.sketchTools setEnabled:NO forSegment:0];
    [self.sketchTools setEnabled:NO forSegment:1];
    [self.sketchTools setEnabled:NO forSegment:2];
    
    //remove sketch layer as mapView's touch delegate
    self.mapView.touchDelegate = self;
    
}


#pragma mark - Actions

// -------------------------------------------------------------------------------
//  stopsBarriersAction:sender
// -------------------------------------------------------------------------------
- (IBAction)stopsBarriersAction:(id)sender {
    
    if ([sender selectedSegment] == 0) {
        
        //clear sketch geometry
        [self.sketchLayer clear];
        
        //disable polyline and polygon sketch tools
        [self.sketchTools setEnabled:YES forSegment:0];
        [self.sketchTools setEnabled:NO forSegment:1];
        [self.sketchTools setEnabled:NO forSegment:2];
        [self.sketchTools setSelected:YES forSegment:0];
        self.mapView.touchDelegate = self.sketchLayer;
        self.sketchLayer.geometry = [[AGSMutablePoint alloc] initWithSpatialReference:self.mapView.spatialReference];
        
    }
    else {
        
        //clear sketch geometry
        [self.sketchLayer clear];
        
        //enable all sketch tools
        [self.sketchTools setEnabled:YES forSegment:0];
        [self.sketchTools setEnabled:YES forSegment:1];
        [self.sketchTools setEnabled:YES forSegment:2];
    }
}

// -------------------------------------------------------------------------------
//  sketchToolsAction:sender
// -------------------------------------------------------------------------------
- (IBAction)sketchToolsAction:(id)sender {
    
    switch ([sender selectedSegment]) {
		case 0://point tool
			//sketch layer should begin tracking touch events to sketch a point
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePoint alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;
            
		case 1://polyline tool
			//sketch layer should begin tracking touch events to sketch a polyline
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePolyline alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;
            
		case 2://polygon tool
			//sketch layer should begin tracking touch events to sketch a polygon
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePolygon alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;

		default:
			break;
	}
    
}

// -------------------------------------------------------------------------------
//  addStopOrBarrierAction:sender
// -------------------------------------------------------------------------------
- (IBAction)addStopOrBarrierAction:(id)sender {
    
    //grab the geometry, then clear the sketch
	AGSGeometry *geometry = [self.sketchLayer.geometry copy];
	[self.sketchLayer clear];
	
	//Prepare symbol and attributes for the Stop/Barrier
	NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    
    if ([self.stopsBarriersSegmentedControl selectedSegment] == 0) {
        
        //set stop symbol
        AGSSymbol *symbol = [self stopSymbolWithNumber:1];
        
        //create stop graphic
        AGSStopGraphic *stopGraphic = [AGSStopGraphic graphicWithGeometry:geometry
                                                                   symbol:symbol
                                                               attributes:attributes];
        
        //set stop graphic sequence
        //You can set additional properties on the stop here
        //refer to the conceptual helf for Routing task
        stopGraphic.sequence = 1;
        
        [self.stops removeAllObjects];
        
        //add graphic to stop graphics array
        [self.stops addObject:stopGraphic];
        
        [self.startGraphicsLayer removeAllGraphics];

        //add stop graphic to graphics layer
        [self.startGraphicsLayer addGraphic:stopGraphic];
        

        //enable all sketch tools
        [self.sketchTools setEnabled:YES forSegment:0];
        [self.sketchTools setEnabled:YES forSegment:1];
        [self.sketchTools setEnabled:YES forSegment:2];
        [self.sketchTools setSelected:NO forSegment:0];
        [self.sketchTools setSelected:NO forSegment:1];
        [self.sketchTools setSelected:NO forSegment:2];
        [self.stopsBarriersSegmentedControl setSelected:YES forSegment:1];
        
    }
    else {
                
        //increase counter
        self.numBarriers++;
        
        //set barrierNumber attribute
        //you can set additional properties on the barrier here
        //refer to the conceptual helf for Routing task
        [attributes setValue:[NSNumber numberWithInt:self.numBarriers] forKey:@"barrierNumber"];
        
        //create a barrier graphic
        AGSGraphic *g = [AGSGraphic graphicWithGeometry:geometry symbol:nil attributes:attributes];
        
        //set symbol based on type of geometry
        if ([geometry isKindOfClass:[AGSPoint class]]) {
            
            //set symbol
            g.symbol = [self pointBarrierSymbol];
            
            //add to point barriers array
            [self.pointBarriers addObject:g];
        }
        else if ([geometry isKindOfClass:[AGSPolyline class]]) {
            
            //set symbol
            g.symbol = [self polylineBarrierSymbol];
            
            //add to point barriers array
            [self.polylineBarriers addObject:g];
        }
        else if ([geometry isKindOfClass:[AGSPolygon class]]) {
            
            //set symbol
            g.symbol = [self polygonBarrierSymbol];
                       
            //add to point barriers array
            [self.polygonBarriers addObject:g];
        }

        //add to graphics layer
        //[self.barriersGraphicsLayer removeAllGraphics];
        [self.barriersGraphicsLayer addGraphic:g];
    }
    
    if ([self.stops count] > 0) {
        self.mapView.touchDelegate = self;
        showRoute = YES;
        [self.message2Label setHidden:NO];
    }
}

// -------------------------------------------------------------------------------
//  clearSketchAction:sender
// -------------------------------------------------------------------------------
- (IBAction)clearSketchAction:(id)sender {
    //clear sketch geometry
    showRoute = NO;
    [self.sketchLayer clear];
    [self.message2Label setHidden:YES];
    [self.mapView.callout dismiss];
}


// -------------------------------------------------------------------------------
//  resetAction:sender
// -------------------------------------------------------------------------------
- (IBAction)resetAction:(id)sender {
    
    // set stop counter back to 0
	self.numStops = 0;
	
	// set barrier counter back to 0
	self.numBarriers = 0;
    
    //Reset the 
    [self.stopsBarriersSegmentedControl setSelectedSegment:0];
    [self.sketchTools setSelected:YES forSegment:0];
    [self.sketchTools setEnabled:YES forSegment:0];
    [self.sketchTools setEnabled:NO forSegment:1];
    [self.sketchTools setEnabled:NO forSegment:2];
    self.sketchLayer.geometry = [[AGSMutablePoint alloc] initWithSpatialReference:self.mapView.spatialReference];
    
    //reset stops and barriers array
    self.stops = [NSMutableArray array];
    self.pointBarriers = [NSMutableArray array];
    self.polylineBarriers = [NSMutableArray array];
    self.polygonBarriers = [NSMutableArray array];
    
	// remove all graphics
	[self.startGraphicsLayer removeAllGraphics];
    [self.endGraphicsLayer removeAllGraphics];
	[self.barriersGraphicsLayer removeAllGraphics];
    [self.routeGraphicsLayer removeAllGraphics];
    
    //clear sketch layer
    [self.sketchLayer clear];
    
    //set sketch layer as mapView's touch delegate
    self.mapView.touchDelegate = self.sketchLayer;
    
    [self enableStartAndBarriers:YES];
    [self.message2Label setHidden:YES];
    [self.mapView.callout dismiss];
    
    showRoute = NO;
    hasEnd = NO;
    
}

#pragma mark - Calculate Route

-(void)calculateRoute {
    
    
    // update message
    //self.messageLabel.stringValue = @"Click on the map to stop solving the Route.";
    
    //clear sketch layer and route graphics layer
    [self.sketchLayer clear];
    [self.routeGraphicsLayer removeAllGraphics];
    
    //remove sketch layer as mapView's touch delegate
    self.mapView.touchDelegate = nil;
    
    //Prepare symbol and attributes for the Stop/Barrier
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    
    //set stop symbol
    AGSSymbol *symbol = [self stopSymbolWithNumber:2];
    
    //create stop graphic
    AGSStopGraphic *stopGraphic = [AGSStopGraphic graphicWithGeometry:self.mapPoint
                                                               symbol:symbol
                                                           attributes:attributes];
    [self.endGraphicsLayer removeAllGraphics];
    [self.endGraphicsLayer addGraphic:stopGraphic];
    
    //set stop graphic sequence
    //You can set additional properties on the stop here
    //refer to the conceptual helf for Routing task
    stopGraphic.sequence = 2;
    
    //add graphic to stop graphics array
    if (hasEnd) {
        [self.stops replaceObjectAtIndex:1 withObject:stopGraphic];
    }
    else{
        [self.stops addObject:stopGraphic];
        hasEnd = YES;
    }
    
    // set the stops and  barriers on the parameters object
    if (self.stops.count > 0) {
        [self.routeTaskParameters setStopsWithFeatures:self.stops];
    }
    if (self.pointBarriers.count > 0) {
        [self.routeTaskParameters setPointBarriersWithFeatures:self.pointBarriers];
    }
    if (self.polylineBarriers.count > 0) {
        [self.routeTaskParameters setPolylineBarriersWithFeatures:self.polylineBarriers];
    }
    if (self.polygonBarriers.count > 0) {
        [self.routeTaskParameters setPolygonBarriersWithFeatures:self.polygonBarriers];
    }
    
    // this generalizes the route graphics that are returned
    self.routeTaskParameters.outputGeometryPrecision = 5.0;
    self.routeTaskParameters.outputGeometryPrecisionUnits = AGSUnitsMeters;
    
    // return the graphic representing the entire route, generalized by the previous
    // 2 properties: outputGeometryPrecision and outputGeometryPrecisionUnits
    self.routeTaskParameters.returnRouteGraphics = YES;
    
    // this returns turn-by-turn directions
    self.routeTaskParameters.returnDirections = YES;
    
    // the next 3 lines will cause the task to find the
    // best route regardless of the stop input order
    self.routeTaskParameters.findBestSequence = YES;
    self.routeTaskParameters.preserveFirstStop = YES;
    self.routeTaskParameters.preserveLastStop = NO;
    
    // since we used "findBestSequence" we need to
    // get the newly reordered stops
    self.routeTaskParameters.returnStopGraphics = YES;
    
    // ensure the graphics are returned in our map's spatial reference
    self.routeTaskParameters.outSpatialReference = self.mapView.spatialReference;
    
    // let's ignore invalid locations
    self.routeTaskParameters.ignoreInvalidLocations = YES;
    
    self.routeTaskParameters.accumulateAttributeNames = [NSArray arrayWithObject:@"Meters"];
    
    // you can also set additional properties here that should
    // be considered during analysis.
    // See the conceptual help for Routing task.
    
    // execute the route task
    [self.routeTask solveWithParameters:self.routeTaskParameters];
    
}

#pragma mark - Symbols

// -------------------------------------------------------------------------------
//  stopSymbolWithNumber:stopNumber
// -------------------------------------------------------------------------------
- (AGSCompositeSymbol*)stopSymbolWithNumber:(NSInteger)stopNumber {
    
    //init composite symbol
	AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
	
    // create main circle
	AGSSimpleMarkerSymbol *sms = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
	sms.color = [NSColor greenColor];
	sms.size = CGSizeMake(20, 20);
	sms.style = AGSSimpleMarkerSymbolStyleCircle;
	[cs addSymbol:sms];
	
    // add number as a text symbol
    AGSTextSymbol *ts = [[AGSTextSymbol alloc] initWithText:[NSString stringWithFormat:@"%i", (int)stopNumber] color:[NSColor blackColor]];
    ts.vAlignment = AGSTextSymbolVAlignmentMiddle;
    ts.hAlignment = AGSTextSymbolHAlignmentCenter;
    ts.fontSize	= 16;
    [cs addSymbol:ts];
	
	return cs;
}

// -------------------------------------------------------------------------------
//  pointBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleMarkerSymbol*)pointBarrierSymbol {
    //create a simple marker symbol
    AGSSimpleMarkerSymbol *sms = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
	sms.color = [NSColor redColor];
	sms.size = CGSizeMake(20, 20);
	sms.style = AGSSimpleMarkerSymbolStyleCircle;
    return sms;
}

// -------------------------------------------------------------------------------
//  polylineBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleLineSymbol*)polylineBarrierSymbol {
    //create a simple line symbol
    AGSSimpleLineSymbol *sls = [AGSSimpleLineSymbol simpleLineSymbol];
	sls.color = [NSColor redColor];
	sls.style = AGSSimpleLineSymbolStyleSolid;
	sls.width = 4;
    return sls;
}

// -------------------------------------------------------------------------------
//  polygonBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleFillSymbol*)polygonBarrierSymbol {
    //create simple fill symbol
    AGSSimpleFillSymbol *sfs = [AGSSimpleFillSymbol simpleFillSymbol];
	sfs.style = AGSSimpleFillSymbolStyleSolid;
	sfs.color = [[NSColor redColor] colorWithAlphaComponent:0.5];
    sfs.outline.width = 4;
    sfs.outline.color = [NSColor redColor];
    return sfs;
}

// -------------------------------------------------------------------------------
//  routeSymbol
// -------------------------------------------------------------------------------
- (AGSCompositeSymbol*)routeSymbol {
    
    //init composite symbol
	AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
	
	AGSSimpleLineSymbol *sls1 = [AGSSimpleLineSymbol simpleLineSymbol];
    sls1.color = [NSColor blueColor];
	sls1.style = AGSSimpleLineSymbolStyleSolid;
	sls1.width = 6;
	[cs addSymbol:sls1];
	
	AGSSimpleLineSymbol *sls2 = [AGSSimpleLineSymbol simpleLineSymbol];
	sls2.color = [NSColor colorWithRed:0/255.0f green:170/255.0f blue:255/255.0f alpha:1.0];
	sls2.style = AGSSimpleLineSymbolStyleSolid;
	sls2.width = 4;
	[cs addSymbol:sls2];
	
	return cs;
}


#pragma mark - Show Error

- (void)showErrorWithTitle:(NSString*)title message:(NSString*)message {
    
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:title];
    [alert setInformativeText:message];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
    
}

#pragma mark - Dealloc

// -------------------------------------------------------------------------------
//  dealloc
// -------------------------------------------------------------------------------
- (void)dealloc {
    
    //remove observer...
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark - Maintain UI

-(void)enableStartAndBarriers:(BOOL)enable {
    
    [self.stopsBarriersSegmentedControl setEnabled:enable];
    [self.sketchTools setEnabled:enable];
    [self.addStopOrBarrierButton setEnabled:enable];
    
}


- (void)respondToGeometryChanged:(NSNotification*)aNotification {
    
    //enable/disable add/clear buttons
    self.clearSketchButton.enabled = ![self.sketchLayer.geometry isEmpty] && self.sketchLayer.geometry!=nil;
    self.addStopOrBarrierButton.enabled = [self.sketchLayer.geometry isValid];
}



@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 RoutingOfflineSample : NSViewController <AGSRouteTaskDelegate, AGSMapViewTouchDelegate, AGSLayerDelegate>

@property (strong) IBOutlet AGSMapView *mapView;
@property (strong) IBOutlet NSSegmentedControl *stopsBarriersSegmentedControl;
@property (strong) IBOutlet NSSegmentedControl *sketchTools;
@property (strong) IBOutlet NSButton *addStopOrBarrierButton;
@property (strong) IBOutlet NSButton *clearSketchButton;
@property (strong) IBOutlet NSTextField *messageLabel;
@property (strong) IBOutlet NSTextField *message2Label;

@property (strong) AGSGraphicsLayer *startGraphicsLayer;
@property (strong) AGSGraphicsLayer *endGraphicsLayer;
@property (strong) AGSGraphicsLayer *barriersGraphicsLayer;
@property (strong) AGSGraphicsLayer *routeGraphicsLayer;
@property (strong) AGSSketchGraphicsLayer *sketchLayer;
@property (strong) AGSRouteTask *routeTask;
@property (strong) AGSRouteTaskParameters *routeTaskParameters;
@property (strong) AGSRouteResult *routeResult;
@property (strong) AGSDirectionGraphic *currentDirectionGraphic;
@property (strong) NSMutableArray *stops;
@property (strong) NSMutableArray *pointBarriers;
@property (strong) NSMutableArray *polylineBarriers;
@property (strong) NSMutableArray *polygonBarriers;
@property (assign) int numStops;
@property (assign) int numBarriers;
@property (strong) AGSPoint *mapPoint;

- (IBAction)stopsBarriersAction:(id)sender;
- (IBAction)sketchToolsAction:(id)sender;
- (IBAction)addStopOrBarrierAction:(id)sender;
- (IBAction)clearSketchAction:(id)sender;
- (IBAction)resetAction:(id)sender;

@end
Feedback on this topic?