Geocoding Offline

Download Sample Application

This sample demonstrates how you can perform geocoding offline. You can either type in an address into the search control or select one from the drop down list and press enter. The resulting matched addresses are displayed as pushpins on the map. Or you can tap and hold on the map to locate the nearest address. Both functions are acheived using a locator file stored locally on your Mac.

@property (strong) AGSLocator *locator;

//The locator file is stored within this application (i.e. within the main bundle of the application).
//pathForResource will locate the physical path to the SanFrancisco.loc file
NSString *locatorPath = [[NSBundle mainBundle] pathForResource:@"SanFranciscoLocator" ofType:@"loc"];
//create the locator
self.locator = [AGSLocator locatorWithPath:locatorPath error:&error];
//set the locators delegate so the application will know when locations or addresses have been found
self.locator.delegate = self;

//Find the LOCATION of an ADDRESS
//-------------------------------

//create the address dictionary providing the search field text
NSDictionary *addressDictionary = @{@"Single Line Input":self.searchField.stringValue};
        
//create the address parameters for the locator to use
AGSLocationsForAddressParameters *locationsForAddressParams = [[AGSLocationsForAddressParameters alloc]init];
locationsForAddressParams.address = addressDictionary;
locationsForAddressParams......

//find geographical locations for the address parameters
[self.locator locationsForAddressWithParameters:locationsForAddressParams];

//process results when they are returned
- (void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFindLocationsForAddress:(NSArray *)candidates {  
 ...
}

//Find an ADDRESS at a LOCATION
//-----------------------------

- (void)mapView:(AGSMapView *)mapView didClickAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features {
    //find the addresses near the point that the user has clicked on
    [self.locator addressForLocation:mappoint maxSearchDistance:50];
}

//process results when they are returned
-(void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFindAddressForLocation:(AGSAddressCandidate *)candidate{
...
}

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 url
let kGOBaseMapURL = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"

class GeocodingOfflineSwiftSample: NSViewController, AGSMapViewTouchDelegate, AGSLocatorDelegate {

    @IBOutlet weak var mapView:AGSMapView!
    @IBOutlet weak var searchField:NSSearchField!
    var locator:AGSLocator!
    var graphicsLayer:AGSGraphicsLayer!
    var calloutTemplate:AGSCalloutTemplate!
    var recentSearches:Array<String>!
    var mapEnvelope:AGSEnvelope!
    var locationSymbol:AGSPictureMarkerSymbol!
    var mapPoint:AGSPoint!
    
    //MARK: - awakeFromNib
    
    // -------------------------------------------------------------------------------
    //  awakeFromNib
    // -------------------------------------------------------------------------------
    override func awakeFromNib() {
        
        self.createMenuForSearchField()
        
        //add base layer
        let baseMapLayer = AGSTiledMapServiceLayer(URL: NSURL(string: kGBaseMapURL))
        self.mapView.addMapLayer(baseMapLayer, withName:"Base Layer")
        self.mapView.enableWrapAround()
        
        //create picture marker symbol
        self.locationSymbol = AGSPictureMarkerSymbol(imageNamed: "bluepin")
        self.locationSymbol.leaderPoint = CGPoint(x: 4,y: 17)
        
        //add graphics layer using the picture marker symbol above
        self.graphicsLayer = AGSGraphicsLayer()
        self.graphicsLayer.renderer = AGSSimpleRenderer(symbol: self.locationSymbol)
        //setup callout template
        self.calloutTemplate = AGSCalloutTemplate()
        self.calloutTemplate.titleTemplate = "${Match_addr}"
        
        //set layer callout delegate
        self.graphicsLayer.calloutDelegate = self.calloutTemplate
        self.mapView.addMapLayer(self.graphicsLayer, withName:"Graphics Layer")
        
        //zoom to san francisco
        self.mapEnvelope = AGSEnvelope(xmin: -13633923.548231, ymin:4544474.291277, xmax:-13624673.399336, ymax:4550864.586036, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
        self.mapView.zoomToEnvelope(self.mapEnvelope, animated:true)
        
        //set the map's touch delegate so that the application can respond to the user's interaction
        self.mapView.touchDelegate = self
        
        //The locator file is stored within this application (i.e. within the main bundle of the application).
        //pathForResource will locate the physical path to the SanFrancisco.loc file
        let locatorPath = NSBundle.mainBundle().pathForResource("SanFranciscoLocator", ofType:"loc")
        
        //create the locator and fetch it's LocatorInfo
        var error:NSError?
        self.locator = AGSLocator(path: locatorPath, error: &error)
        self.locator.delegate = self
        if error != nil {
            self.showErrorWithTitle("Offline locator failed to load!", message:error!.localizedDescription)
        }
        else {
            self.locator.fetchLocatorInfo()
        }
        
    }
    
    //MARK: - Actions
    
    // -------------------------------------------------------------------------------
    //  searchAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func searchAction(sender:AnyObject) {
        
        //determine if there is text in the search field, if so, find the locations for the address
        if !self.searchField.stringValue.isEmpty {
            
            //create the address dictionary providing the search field text
            let addressDictionary = ["Single Line Input": self.searchField.stringValue]
            
            //create the address parameters for the locator to use
            let locationsForAddressParams = AGSLocationsForAddressParameters()
            locationsForAddressParams.address = addressDictionary
            locationsForAddressParams.outFields = ["*"]
            locationsForAddressParams.outSpatialReference = self.mapView.spatialReference
            
            //find geographical locations for the address parameters
            self.locator.locationsForAddressWithParameters(locationsForAddressParams)
        }
    }
    
    //MARK: - AGSMapViewTouchDelegate Methods
    
    func mapView(mapView: AGSMapView!, didTapAndHoldAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
        
        //store the mappoint so the callout is displayed at this point
        self.mapPoint = mappoint
        
        //find the addresses near the mappoint
        self.locator.addressForLocation(mappoint, maxSearchDistance:50)
    }
    
    func mapView(mapView: AGSMapView!, didMoveTapAndHoldAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
        
        //store the mappoint so the callout is displayed at this point
        self.mapPoint = mappoint
        
        //find the addresses near the mappoint
        self.locator.addressForLocation(mappoint, maxSearchDistance:50)
    }
    
    
    //MARK: - Locator Delegate
    
    func locator(locator: AGSLocator!, operation op: NSOperation!, didFindAddressForLocation candidate: AGSAddressCandidate!) {
        
        //Find the nearest address (candidate) to the location clicked
        //Display a callout at the point that the user clicked.
        //NOTE: If you want to display the callout at the location of the address use candidate.location
        //instead of self.mapPoint
        self.mapView.callout.showCalloutAt(self.mapPoint, screenOffset:CGPointMake(0,0), animated:true)
        
        //add the address information to the callout title
        let streetKey = candidate.address["Street"] as String
        let cityKey = candidate.address["City"] as String
        let stateKey = candidate.address["State"] as String
        let zipKey = candidate.address["ZIP"] as String
        self.mapView.callout.title = "\(streetKey), \(cityKey), \(stateKey), \(zipKey)"
    }
    
    
    func locator(locator: AGSLocator!, operation op: NSOperation!, didFindLocationsForAddress candidates: [AnyObject]!) {
        
        //found the addresses with a high match score (>90)
        if candidates.count > 0 {
            
            //remove the existing pushpins
            self.graphicsLayer.removeAllGraphics()
            
            //create a graphics array
            var graphics = Array<AGSGraphic>()
            
            //get all the addresses that have a score greater than 90
            for candidate in candidates as [AGSAddressCandidate] {
                if candidate.score > 90 {
                    let graphic = AGSGraphic(geometry: candidate.location, symbol:self.locationSymbol, attributes:candidate.attributes)
                    graphics.append(graphic)
                }
            }
            
            //add graphics
            self.graphicsLayer.addGraphics(graphics)
            
            //zoom to graphics layer
            self.mapView.zoomToGeometry(self.graphicsLayer.fullEnvelope, withPadding:10000, animated:true)
        }
    }
    
    func locator(locator: AGSLocator!, operation op: NSOperation!, didFailLocationsForAddress error: NSError!) {
        //No addresses found for the address string
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = "Failed to find addresses to match"
            alert.beginSheetModalForWindow(viewWindow, modalDelegate:self, didEndSelector:nil, contextInfo:nil)
        }
    }
    
    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)
        }
    }
    
    func createMenuForSearchField() {
        
        //create a drop down menu to add to the searchfield that contains a set of known addresses
        self.searchField.recentSearches = ["1455 Market St, San Francisco, CA 94103",
            "2011 Mission St, San Francisco  CA  94110",
            "820 Bryant St, San Francisco  CA  94103",
            "1 Zoo Rd, San Francisco, 944132",
            "1201 Mason Street, San Francisco, CA 94108",
            "151 Third Street, San Francisco, CA 94103",
            "1050 Lombard Street, San Francisco, CA 94109"]
        
        let cellMenu = NSMenu(title: "Search Menu")
        var item:NSMenuItem!
        
        
        //Title
        item = NSMenuItem(title: "Example Addresses in San Francisco", action:nil, keyEquivalent:"")
        //TODO: had to downcast from Int32 to Int
        item.tag = Int(NSSearchFieldRecentsTitleMenuItemTag)
        cellMenu.insertItem(item, atIndex: 0)
        
        //Seperator
        item = NSMenuItem.separatorItem()
        //TODO: had to downcast from Int32 to Int
        item.tag = Int(NSSearchFieldRecentsTitleMenuItemTag)
        cellMenu.insertItem(item, atIndex:1)
        
        //As set by setRecentSearches
        item = NSMenuItem(title: "Recents", action: nil, keyEquivalent: "")
        //TODO: had to downcast from Int32 to Int
        item.tag = Int(NSSearchFieldRecentsMenuItemTag)
        cellMenu.insertItem(item, atIndex:2)
        
        if let searchCell = self.searchField.cell() as? NSSearchFieldCell {
            searchCell.searchMenuTemplate = cellMenu
        }
    }
    
}
//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 "GeocodingOfflineSample.h"

//layer url
#define kBaseMapURL @"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"

@interface GeocodingOfflineSample ()
@end

@implementation GeocodingOfflineSample

#pragma mark - awakeFromNib

// -------------------------------------------------------------------------------
//  awakeFromNib
// -------------------------------------------------------------------------------
- (void)awakeFromNib {
    
    [self createMenuForSearchField];
    
    //add base layer
	AGSTiledMapServiceLayer *baseMapLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kBaseMapURL]];
    [self.mapView addMapLayer:baseMapLayer withName:@"Base Layer"];
    [self.mapView enableWrapAround];
    
    //create picture marker symbol
    self.locationSymbol = [AGSPictureMarkerSymbol pictureMarkerSymbolWithImageNamed:@"bluepin"];
    self.locationSymbol.leaderPoint = CGPointMake(4,17);
    
    //add graphics layer using the picture marker symbol above
    self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
    self.graphicsLayer.renderer = [AGSSimpleRenderer simpleRendererWithSymbol:self.locationSymbol];
    //setup callout template
    self.calloutTemplate = [[AGSCalloutTemplate alloc] init];
    self.calloutTemplate.titleTemplate = @"${Match_addr}";
    
    //set layer callout delegate
    self.graphicsLayer.calloutDelegate = self.calloutTemplate;
    [self.mapView addMapLayer:self.graphicsLayer withName:@"Graphics Layer"];
    
    //zoom to san francisco
    self.mapEnvelope = [AGSEnvelope envelopeWithXmin: -13633923.548231 ymin:4544474.291277 xmax:-13624673.399336 ymax:4550864.586036 spatialReference:[AGSSpatialReference webMercatorSpatialReference]];
    [self.mapView zoomToEnvelope:self.mapEnvelope animated:YES];
    
    //set the map's touch delegate so that the application can respond to the user's interaction
    self.mapView.touchDelegate = self;


    
    //The locator file is stored within this application (i.e. within the main bundle of the application).
    //pathForResource will locate the physical path to the SanFrancisco.loc file
    NSString *locatorPath = [[NSBundle mainBundle] pathForResource:@"SanFranciscoLocator" ofType:@"loc"];
    
    //create the locator and fetch it's LocatorInfo
    NSError *error = nil;
    self.locator = [AGSLocator locatorWithPath:locatorPath error:&error];
    self.locator.delegate = self;
    if (error) {
        [self showErrorWithTitle:@"Offline locator failed to load!" message:[NSString stringWithFormat:@"%@",error.localizedDescription]];
    }
    else {
        [self.locator fetchLocatorInfo];
    }

}

#pragma mark - Actions

// -------------------------------------------------------------------------------
//  searchAction:sender
// -------------------------------------------------------------------------------
- (IBAction)searchAction:(id)sender {
    
    //determine if there is text in the search field, if so, find the locations for the address
    if ([self.searchField.stringValue length] != 0) {
        
        //create the address dictionary providing the search field text
        NSDictionary *addressDictionary = @{@"Single Line Input":self.searchField.stringValue};
        
        //create the address parameters for the locator to use
        AGSLocationsForAddressParameters *locationsForAddressParams = [[AGSLocationsForAddressParameters alloc]init];
        locationsForAddressParams.address = addressDictionary;
        locationsForAddressParams.outFields = @[@"*"];
        locationsForAddressParams.outSpatialReference = self.mapView.spatialReference;
        
        //find geographical locations for the address parameters
        [self.locator locationsForAddressWithParameters:locationsForAddressParams];
    }
}

#pragma mark - AGSMapViewTouchDelegate Methods

- (void)mapView:(AGSMapView *)mapView didTapAndHoldAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features {
    
    //store the mappoint so the callout is displayed at this point
    self.mapPoint = mappoint;
    
    //find the addresses near the mappoint
    [self.locator addressForLocation:mappoint maxSearchDistance:50];

}

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

        //store the mappoint so the callout is displayed at this point
        self.mapPoint = mappoint;
        
        //find the addresses near the mappoint
        [self.locator addressForLocation:mappoint maxSearchDistance:50];

}


#pragma mark - Locator Delegate

-(void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFindAddressForLocation:(AGSAddressCandidate *)candidate{

    //Find the nearest address (candidate) to the location clicked
    //Display a callout at the point that the user clicked.
    //NOTE: If you want to display the callout at the location of the address use candidate.location
    //instead of self.mapPoint
    [self.mapView.callout showCalloutAt:self.mapPoint screenOffset:CGPointMake(0,0) animated:YES];
    
    //add the address information to the callout title
    self.mapView.callout.title = [NSString stringWithFormat:@"%@, %@, %@, %@", candidate.address[@"Street"],
                                  candidate.address[@"City"],
                                  candidate.address[@"State"],
                                  candidate.address[@"ZIP"]];
}


- (void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFindLocationsForAddress:(NSArray *)candidates {
    
    //found the addresses with a high match score (>90)
    if (candidates > 0) {
        
        //remove the existing pushpins
        [self.graphicsLayer removeAllGraphics];
        
        //create a graphics array
        NSMutableArray *graphics = [NSMutableArray array];
        
        //get all the addresses that have a score greater than 90
        for (AGSAddressCandidate *candidate in candidates) {
            if (candidate.score > 90){
            AGSGraphic *graphic = [AGSGraphic graphicWithGeometry:candidate.location symbol:self.locationSymbol attributes:candidate.attributes];
                [graphics addObject:graphic];}
        }
        
        //add graphics
        [self.graphicsLayer addGraphics:graphics];
        
        //zoom to graphics layer
        [self.mapView zoomToGeometry:self.graphicsLayer.fullEnvelope withPadding:10000 animated:YES];
    }
}

- (void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFailLocationsForAddress:(NSError *)error {
    //No addresses found for the address string
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:@"Failed to find addresses to match"];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
}

- (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];
    
}

- (void)createMenuForSearchField {
    
    //create a drop down menu to add to the searchfield that contains a set of known addresses
    [self.searchField setRecentSearches:[NSMutableArray arrayWithObjects:
                                         @"1455 Market St, San Francisco, CA 94103",
                                         @"2011 Mission St, San Francisco  CA  94110",
                                         @"820 Bryant St, San Francisco  CA  94103",
                                         @"1 Zoo Rd, San Francisco, 944132",
                                         @"1201 Mason Street, San Francisco, CA 94108",
                                         @"151 Third Street, San Francisco, CA 94103",
                                         @"1050 Lombard Street, San Francisco, CA 94109",
                                         nil]];
    
    NSMenu *cellMenu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Search Menu", @"Search Menu title")];
    NSMenuItem *item;
    
    //Title
    item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Example Addresses in San Francisco", @"")
                                      action:NULL keyEquivalent:@""];
    [item setTag:NSSearchFieldRecentsTitleMenuItemTag];
    [cellMenu insertItem:item atIndex:0];
    
    //Seperator
    item = [NSMenuItem separatorItem];
    [item setTag:NSSearchFieldRecentsTitleMenuItemTag];
    [cellMenu insertItem:item atIndex:1];
    
    //As set by setRecentSearches
    item = [[NSMenuItem alloc] initWithTitle:@"Recents"
                                      action:NULL keyEquivalent:@""];
    [item setTag:NSSearchFieldRecentsMenuItemTag];
    [cellMenu insertItem:item atIndex:2];
    
    id searchCell = [self.searchField cell];
    [searchCell setSearchMenuTemplate:cellMenu];
    
}

@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 GeocodingOfflineSample : NSViewController <AGSLocatorDelegate, AGSMapViewTouchDelegate, AGSLayerDelegate>

@property (strong) IBOutlet AGSMapView *mapView;
@property (strong) IBOutlet NSSearchField *searchField;
@property (strong) AGSLocator *locator;
@property (strong) AGSGraphicsLayer *graphicsLayer;
@property (strong) AGSCalloutTemplate *calloutTemplate;
@property (nonatomic, strong) NSMutableArray* recentSearches;
@property (nonatomic, strong) AGSEnvelope *mapEnvelope;
@property (nonatomic, strong) AGSPictureMarkerSymbol *locationSymbol;
@property (strong) AGSPoint *mapPoint;

- (IBAction)searchAction:(id)sender;

@end
Feedback on this topic?