Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for macOS

Download Features

Download Sample Application

This sample demonstrates how you can create a local geodabase (.geodatabase) of the features served from an online feature service. This sample uses the AGSGDBSyncTask to generate a geodatabase on the server, store it in a single geodatabase file and download it to your Mac. Using a geodatabase containing features, your application can go offline. It is important to remember that generating a geodatabase on the server and downloading it to your Mac will take time. The length of time is affected by your chosen geographical extent, number of layers and the number of features in each layer. This sample downloads all features from the uppermost layer that fall within an area around San Francisco, California. You can adapt the code provided in this sample to generate geodatabases for your area of interest.

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

let  kOnlineFeatureServiceURL = "http://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/Wildfire/FeatureServer"
let kCOGBaseMapURL = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"

class CreateOfflineGeodatabaseSwiftSample: NSViewController, AGSMapViewTouchDelegate, AGSMapViewLayerDelegate, AGSLayerDelegate {
    
    @IBOutlet weak var messageLabel:NSTextField!
    @IBOutlet weak var createGeodatabaseButton:NSButton!
    @IBOutlet weak var modeMatrix:NSMatrix!
    @IBOutlet weak var mapView:AGSMapView!
    
    var geodatabasePath:String!
    var geodatabaseTask:AGSGDBSyncTask!
    var areaOfInterest:AGSEnvelope!
    var featureLayer:AGSFeatureLayer!
    var localFeatureTable:AGSGDBFeatureTable!
    var localFeatureTableLayer:AGSFeatureTableLayer!
    var graphicsLayer:AGSGraphicsLayer!
    var geodatabaseJob:AGSCancellable!
    var geodatabase:AGSGDBGeodatabase!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
    }
    
    //// -------------------------------------------------------------------------------
    ////  awakeFromNib
    //// -------------------------------------------------------------------------------
    override func awakeFromNib() {
    
        //enable wrap around
        self.mapView.enableWrapAround()
        
        //set layer delegate to know when mapView loads
        self.mapView.layerDelegate = self
        self.mapView.touchDelegate = self
        self.mapView.showMagnifierOnTapAndHold = true
        
        //add base layer to map and set delegate to know when layer loads or fails to load
        let baseMapLayer = AGSTiledMapServiceLayer(URL: NSURL(string: kCOGBaseMapURL))
        baseMapLayer.delegate = self
        self.mapView.addMapLayer(baseMapLayer, withName:"Base Layer")
        
        //Load tiled map service and zoom to envelope
        var string = "\(kOnlineFeatureServiceURL)/0"
        self.featureLayer = AGSFeatureLayer(URL: NSURL(string: string), mode: .OnDemand)
        self.featureLayer.delegate = self
        self.mapView.addMapLayer(self.featureLayer, withName:"Online Feature Layer")
        
        self.modeMatrix.enabled = false
        
        self.areaOfInterest = AGSEnvelope(xmin: -13637696.600912, ymin:4539924.661945, xmax:-13624293.155808, ymax:4553328.107049, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
        
        // zoom to area of interest
        self.mapView.zoomToGeometry(self.areaOfInterest, withPadding:400, animated:true)
        
        
        //Create a graphic showing the area of interest
        self.graphicsLayer = AGSGraphicsLayer()
        self.mapView.addMapLayer(self.graphicsLayer, withName:"GraphicsLayer")
        var simpleFillSymbol = AGSSimpleFillSymbol(color: nil, outlineColor: NSColor.redColor())
        simpleFillSymbol.outline.width = 5.0
        
        let geom = self.areaOfInterest
        let graphic = AGSGraphic(geometry: geom, symbol: simpleFillSymbol, attributes: nil)
        self.graphicsLayer.addGraphic(graphic)
    }
    
    func layerDidLoad(layer: AGSLayer!) {
        println("Layer did load... \(layer.name)")
    }
    
    @IBAction func changeMode(sender:NSMatrix) {
        if sender.selectedCell().tag() == 0 {
            self.featureLayer.visible = false
            self.localFeatureTableLayer.visible = true
        }
        if sender.selectedCell().tag() == 1 {
            self.featureLayer.visible = true
            self.localFeatureTableLayer.visible = false
        }
    }
    
    @IBAction func createGeodatabase(sender:AnyObject) {
    
        self.createGeodatabaseButton.enabled = false
        
        //Create the task
        if self.geodatabaseTask == nil {
            self.geodatabaseTask = AGSGDBSyncTask(URL: NSURL(string: kOnlineFeatureServiceURL))
        }
        

        
        self.geodatabaseTask.loadCompletion = { [weak self] (error) in
            if let weakSelf = self {
                if error != nil {
                    weakSelf.showErrorWithTitle("AGSGDBSyncTask failed to load!", forMessage:error.localizedDescription)
                }
                else {
                    println("Geodatabase task loaded")
                    //Get path to store geodatabase
                    let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
                    weakSelf.geodatabasePath = paths[0] as String
                    
                    //create the task parameters
                    let params = AGSGDBGenerateParameters(extent: weakSelf.areaOfInterest, layerIDs: [0])
                    params.syncModel = .PerLayer
                    params.outSpatialReference = weakSelf.mapView.spatialReference
                    
                    
                    
                    //create the geodatabase and fetch it from the server
                    
                    weakSelf.geodatabaseTask.generateGeodatabaseWithParameters(params, downloadFolderPath: weakSelf.geodatabasePath, useExisting: false, status: { (status, userInfo) -> Void in
                        weakSelf.messageLabel.stringValue = AGSResumableTaskJobStatusAsString(status);
                    }, completion: { [weak self] (geodatabase, error) -> Void in
                        if let weakSelf = self {
                            if error == nil {
                                weakSelf.featureLayer.visible = false
                                weakSelf.localFeatureTable = geodatabase.featureTables()[0] as AGSGDBFeatureTable
                                weakSelf.localFeatureTableLayer = AGSFeatureTableLayer(featureTable: weakSelf.localFeatureTable)
                                weakSelf.localFeatureTableLayer.delegate = weakSelf
                                weakSelf.mapView.addMapLayer(weakSelf.localFeatureTableLayer, withName:"Offline Feature Layer")
                                weakSelf.createGeodatabaseButton.enabled = true
                                weakSelf.modeMatrix.enabled = true
                                weakSelf.modeMatrix.selectCellWithTag(0)
                                weakSelf.messageLabel.stringValue = "The Job has completed and the geodatabase is located in this file: \(geodatabase.path)"
                            } else {
                                weakSelf.showErrorWithTitle("Error fetching geodatabase!:", forMessage:error.localizedDescription)
                                weakSelf.messageLabel.stringValue = "Error fetching geodatabase!"
                                weakSelf.createGeodatabaseButton.enabled = true
                            }
                        }
                    })
                }
            }
        }
    }
    
    
    func showErrorWithTitle(title:String, forMessage 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 "CreateOfflineGeodatabaseSample.h"

#define kOnlineFeatureServiceURL @"http://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/Wildfire/FeatureServer"
#define kBaseMapURL @"http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"

@interface CreateOfflineGeodatabaseSample ()
@property (nonatomic,strong) AGSPoint *mapPoint;
@property (nonatomic,strong) id<AGSMapViewTouchDelegate> previousMapViewTouchDelegate;
@end

@implementation CreateOfflineGeodatabaseSample

//// -------------------------------------------------------------------------------
////  awakeFromNib
//// -------------------------------------------------------------------------------
- (void)awakeFromNib {

    //enable wrap around
    [self.mapView enableWrapAround];
    
    //set layer delegate to know when mapView loads
    self.mapView.layerDelegate = self;
    self.mapView.touchDelegate = self;
    self.mapView.showMagnifierOnTapAndHold = YES;
    
    //add base layer to map and set delegate to know when layer loads or fails to load
    AGSTiledMapServiceLayer *baseMapLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kBaseMapURL]];
    baseMapLayer.delegate = self;
    [self.mapView addMapLayer:baseMapLayer withName:@"Base Layer"];
    
    //Load tiled map service and zoom to envelope
    NSMutableString *string = [NSMutableString string];
    [string appendString:kOnlineFeatureServiceURL];
    [string appendString:@"/0"];
	self.featureLayer = [[AGSFeatureLayer alloc] initWithURL:[NSURL URLWithString:string] mode:AGSFeatureLayerModeOnDemand];
    self.featureLayer.delegate = self;
	[self.mapView addMapLayer:self.featureLayer withName:@"Online Feature Layer"];
    
    [self.modeMatrix setEnabled:NO];
    
    self.areaOfInterest = [AGSEnvelope envelopeWithXmin: -13637696.600912 ymin:4539924.661945 xmax:-13624293.155808 ymax:4553328.107049 spatialReference:[AGSSpatialReference webMercatorSpatialReference]];
    
        // zoom to area of interest
    [self.mapView zoomToGeometry:self.areaOfInterest withPadding:400 animated:YES];
    
    //Create a graphic showing the area of interest
    self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
    [self.mapView addMapLayer:self.graphicsLayer withName:@"GraphicsLayer"];
    AGSSimpleFillSymbol *simpleFillSymbol = [AGSSimpleFillSymbol simpleFillSymbolWithColor:nil outlineColor:[NSColor redColor]];
    simpleFillSymbol.outline.width = 5.0;
    
    AGSGeometry *geom = self.areaOfInterest;
    AGSGraphic *graphic = [AGSGraphic graphicWithGeometry:geom symbol:simpleFillSymbol attributes:nil];
    [self.graphicsLayer addGraphic:graphic];
    
}

-(void)layerDidLoad:(AGSLayer *)layer{
    
    NSLog(@"Layer did load... %@", layer.name);
    
}

-(IBAction)changeMode:(id)sender {
    if ([[sender selectedCell] tag]==0)
    {
        self.featureLayer.visible = NO;
        self.localFeatureTableLayer.visible = YES;

    }
    if ([[sender selectedCell] tag]==1)
    {
        self.featureLayer.visible = YES;
        self.localFeatureTableLayer.visible = NO;
    }
    
}


-(IBAction)createGeodatabase:(id)sender {

    [self.createGeodatabaseButton setEnabled:NO];
	
    //Create the task
	if (!self.geodatabaseTask) {
		self.geodatabaseTask = [[AGSGDBSyncTask alloc] initWithURL:[NSURL URLWithString:kOnlineFeatureServiceURL] credential:nil];
	}
	
    __weak CreateOfflineGeodatabaseSample *weakSelf = self;
    
    self.geodatabaseTask.loadCompletion = ^(NSError *error){
		if (error) {
            [weakSelf showErrorWithTitle:@"AGSGDBSyncTask failed to load!" message:[NSString stringWithFormat:@"%@",error]];
			
		} else {
            NSLog(@"Geodatabase task loaded");
            //Get path to store geodatabase
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
			weakSelf.geodatabasePath = [paths objectAtIndex:0];
            
            //create the task parameters
            AGSGDBGenerateParameters *params = [[AGSGDBGenerateParameters alloc]initWithExtent:weakSelf.areaOfInterest layerIDs:@[@0]];
            params.syncModel = AGSGDBSyncModelPerLayer;
            params.outSpatialReference = weakSelf.mapView.spatialReference;
            
            //create the geodatabase and fetch it from the server
            weakSelf.geodatabaseJob = [weakSelf.geodatabaseTask generateGeodatabaseWithParameters:params
                                                                               downloadFolderPath:weakSelf.geodatabasePath
                                                                                      useExisting:NO
                                       
                                                                                           status:^(AGSResumableTaskJobStatus status, NSDictionary *userInfo){
                                                                                               
                                                                                               weakSelf.messageLabel.stringValue = AGSResumableTaskJobStatusAsString(status);
                                                                                           }
                                                                                       completion:^(AGSGDBGeodatabase *geodatabase, NSError *error){
                                                                                           if (!error) {
                                                                                               weakSelf.featureLayer.visible = NO;
                                                                                               weakSelf.localFeatureTable =
                                                                                               [[geodatabase featureTables] objectAtIndex:0];
                                                                                               weakSelf.localFeatureTableLayer = [[AGSFeatureTableLayer alloc] initWithFeatureTable: weakSelf.localFeatureTable];
                                                                                               weakSelf.localFeatureTableLayer.delegate = weakSelf;
                                                                                               [weakSelf.mapView addMapLayer:weakSelf.localFeatureTableLayer withName:@"Offline Feature Layer"];
                                                                                               [weakSelf.createGeodatabaseButton setEnabled:YES];
                                                                                               [weakSelf.modeMatrix setEnabled:YES];
                                                                                               [weakSelf.modeMatrix selectCellWithTag:0];
                                                                                               NSMutableString *string = [NSMutableString string];
                                                                                               [string appendString:@"The Job has completed and the geodatabase is located in this file: "];
                                                                                               [string appendString:geodatabase.path];
                                                                                               weakSelf.messageLabel.stringValue = string;
                                                                                           } else {
                                                                                               [weakSelf showErrorWithTitle:@"Error fetching geodatabase!:" message:[NSString stringWithFormat:@"%@",error]];
                                                                                               weakSelf.messageLabel.stringValue = @"Error fetching geodatabase!";
                                                                                               [weakSelf.createGeodatabaseButton setEnabled:YES];
                                                                                           }
                                                                                           
                                                                                       }
                                       ];
        }
    };
}


- (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];
    
}
@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 CreateOfflineGeodatabaseSample: NSViewController < AGSMapViewLayerDelegate, AGSLayerDelegate, AGSMapViewTouchDelegate, AGSPopupsContainerDelegate, NSTableViewDataSource, NSTableViewDelegate>

@property (strong) IBOutlet NSTextField *messageLabel;
@property (nonatomic, strong) IBOutlet NSButton *createGeodatabaseButton;
@property (nonatomic, strong) IBOutlet NSMatrix *modeMatrix;
@property (strong) IBOutlet AGSMapView *mapView;
@property (nonatomic,strong) NSString *geodatabasePath;
@property (strong,nonatomic) AGSGDBSyncTask *geodatabaseTask;
@property (strong,nonatomic) AGSEnvelope *areaOfInterest;
@property (strong, nonatomic) AGSFeatureLayer *featureLayer;
@property (strong,nonatomic) AGSGDBFeatureTable *localFeatureTable;
@property (strong, nonatomic) AGSFeatureTableLayer *localFeatureTableLayer;
@property (strong, nonatomic) AGSGraphicsLayer *graphicsLayer;
@property (nonatomic,strong) id<AGSCancellable> geodatabaseJob;
@property (strong, nonatomic) AGSGDBGeodatabase *geodatabase;


//IBActions - methods called by IBOutlets
- (IBAction)createGeodatabase:(id)sender;
- (IBAction)changeMode:(id)sender;

@end

Feedback on this topic?