Create Local Runtime Geodatabase

This sample demonstrates the services pattern for generating a runtime geodatabase from a feature service.

Sample Design

This sample is designed to allow users to generate a runtime geodatabase on the device from a pre-defined feature service. Once successfully downloaded the features from the local runtime geodatabase are added to the MapView as an operational layer. The main API features used are the GenerateGeodatabaseParameters class to define the parameters to allow you to control features in the generated runtime geodatabase and GeodatabaseSyncTask class which creates the runtime geodatabase and downloads the file when it has been successfully created.

How to use the Sample

The sample starts up with a MapView containing the World Topographic Basemap. The action bar as a download icon that when pressed will initiate the creation, download, and adding the local runtime geodatabase features from the Wildfire Response Points Wildfire feature service. The runtime geodatabase file directory will be displayed in a TextView above the MapView. The default download location is <EXTERNAL-STORAGE-DIR>/ArcGIS/samples/CRGdb/wildfire.geodatase.

Sample Code

/* Copyright 2013 ESRI
 *
 * All rights reserved under the copyright laws of the United States
 * and applicable international laws, treaties, and conventions.
 *
 * You may freely redistribute and use this sample code, with or
 * without modification, provided you include the original copyright
 * notice and use restrictions.
 *
 * See the Sample code usage restrictions document for further information.
 *
 */

package com.esri.arcgis.android.sample.runtimegeodb;

import java.io.File;
import java.io.FileNotFoundException;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import com.esri.android.map.FeatureLayer;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISFeatureLayer;
import com.esri.core.ags.FeatureServiceInfo;
import com.esri.core.geodatabase.Geodatabase;
import com.esri.core.geodatabase.GeodatabaseFeatureTable;
import com.esri.core.map.CallbackListener;
import com.esri.core.tasks.geodatabase.GenerateGeodatabaseParameters;
import com.esri.core.tasks.geodatabase.GeodatabaseStatusCallback;
import com.esri.core.tasks.geodatabase.GeodatabaseStatusInfo;
import com.esri.core.tasks.geodatabase.GeodatabaseSyncTask;

public class CreateRuntimeGeodatabaseActivity extends Activity {

	static MapView mMapView;
	String fLayerUrl;
	String fServiceUrl;
	static ArcGISFeatureLayer wildfireFL;
	static GeodatabaseSyncTask gdbSyncTask;

	static ProgressDialog mProgressDialog;
	static TextView pathView;

	private static File demoDataFile;
	private static String offlineDataSDCardDirName;
	private static String filename;
	static String localGdbFilePath;
	
	protected static final String TAG = "CRGdb";
	protected static String OFFLINE_FILE_EXTENSION = ".geodatabase";
	
	private static Context mContext;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// set app context so it can be obtained to update progress
		CreateRuntimeGeodatabaseActivity.setContext(this);

		// get sdcard resource names
		demoDataFile = Environment.getExternalStorageDirectory();
		offlineDataSDCardDirName = this.getResources().getString(
				R.string.config_data_sdcard_offline_dir);
		filename = this.getResources().getString(
				R.string.config_geodatabase_name);

		// Retrieve the map and map options from XML layout
		mMapView = (MapView) findViewById(R.id.map);
		// create service layer
		fServiceUrl = this.getResources()
				.getString(R.string.featureservice_url);
		
		mProgressDialog = new ProgressDialog(CreateRuntimeGeodatabaseActivity.this);
		mProgressDialog.setTitle("Create local runtime geodatabase");
		
		// attribute app and pan across dateline
		addAttributes();
	}

	private void addAttributes() {
		// attribute ESRI logo to map
		mMapView.setEsriLogoVisible(true);
		// enable map to wrap around date line
		mMapView.enableWrapAround(true);

	}
	
	// methods to ensure context is available when updating the progress dialog
	public static Context getContext(){
		return mContext;
	}
	
	public static void setContext(Context context){
		mContext = context;
	}

	/*
	 * Create the geodatabase file location and name structure
	 */
	static String createGeodatabaseFilePath() {
		StringBuilder sb = new StringBuilder();
		sb.append(demoDataFile.getAbsolutePath());
		sb.append(File.separator);
		sb.append(offlineDataSDCardDirName);
		sb.append(File.separator);
		sb.append(filename);
		sb.append(OFFLINE_FILE_EXTENSION);
		return sb.toString();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// inflate action bar menu
		getMenuInflater().inflate(R.menu.menu, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// handle menu item selection
		switch (item.getItemId()) {
		case R.id.action_download:
			downloadData(fServiceUrl);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}

	}

	/**
	 * Create the GeodatabaseTask from the feature service URL w/o credentials.
	 */
	private void downloadData(String url) {
		Log.i(TAG, "Create GeoDatabase");
		// create a dialog to update user on progress
		mProgressDialog.show();
		// create the GeodatabaseTask

		gdbSyncTask = new GeodatabaseSyncTask(url, null);
		gdbSyncTask
				.fetchFeatureServiceInfo(new CallbackListener<FeatureServiceInfo>() {

					@Override
					public void onError(Throwable arg0) {
						Log.e(TAG, "Error fetching FeatureServiceInfo");
					}

					@Override
					public void onCallback(FeatureServiceInfo fsInfo) {
						if (fsInfo.isSyncEnabled()) {
							createGeodatabase(fsInfo);
						}
					}
				});

	}

	/**
	 * Set up parameters to pass the the {@link #submitTask()} method. A
	 * {@link CallbackListener} is used for the response.
	 */
	private static void createGeodatabase(FeatureServiceInfo featureServerInfo) {
		// set up the parameters to generate a geodatabase
		GenerateGeodatabaseParameters params = new GenerateGeodatabaseParameters(
				featureServerInfo, mMapView.getExtent(),
				mMapView.getSpatialReference());

		// a callback which fires when the task has completed or failed.
		CallbackListener<String> gdbResponseCallback = new CallbackListener<String>() {
			@Override
			public void onError(final Throwable e) {
				Log.e(TAG, "Error creating geodatabase");
				mProgressDialog.dismiss();
			}

			@Override
			public void onCallback(String path) {
				Log.i(TAG, "Geodatabase is: " + path);
				mProgressDialog.dismiss();
				// update map with local feature layer from geodatabase
				updateFeatureLayer(path);
				// log the path to the data on device
				Log.i(TAG, "path to geodatabase: " + path);
			}
		};

		// a callback which updates when the status of the task changes
		GeodatabaseStatusCallback statusCallback = new GeodatabaseStatusCallback() {
			@Override
			public void statusUpdated(final GeodatabaseStatusInfo status) {
				// get current status
				String progress = status.getStatus().toString();
				// get activity context
				Context context = CreateRuntimeGeodatabaseActivity.getContext();
				// create activity from context
				CreateRuntimeGeodatabaseActivity activity = (CreateRuntimeGeodatabaseActivity) context;
				// update progress bar on main thread
				showProgressBar(activity, progress);

			}
		};

		// create the fully qualified path for geodatabase file
		localGdbFilePath = createGeodatabaseFilePath();

		// get geodatabase based on params
		submitTask(params, localGdbFilePath, statusCallback,
				gdbResponseCallback);
	}

	/**
	 * Request database, poll server to get status, and download the file
	 */
	private static void submitTask(GenerateGeodatabaseParameters params,
			String file, GeodatabaseStatusCallback statusCallback,
			CallbackListener<String> gdbResponseCallback) {
		// submit task
		gdbSyncTask.generateGeodatabase(params, file, false, statusCallback,
				gdbResponseCallback);
	}

	/**
	 * Add feature layer from local geodatabase to map
	 * 
	 * @param featureLayerPath
	 */
	private static void updateFeatureLayer(String featureLayerPath) {
		// create a new geodatabase
		Geodatabase localGdb = null;
		try {
			localGdb = new Geodatabase(featureLayerPath);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		// Geodatabase contains GdbFeatureTables representing attribute data
		// and/or spatial data. If GdbFeatureTable has geometry add it to
		// the MapView as a Feature Layer
		if (localGdb != null) {
			for (GeodatabaseFeatureTable gdbFeatureTable : localGdb
					.getGeodatabaseTables()) {
				if (gdbFeatureTable.hasGeometry()){
					mMapView.addLayer(new FeatureLayer(gdbFeatureTable));

				}
			}
		}
	}
	
	private static void showProgressBar(final CreateRuntimeGeodatabaseActivity activity, final String message){
		activity.runOnUiThread(new Runnable(){

			@Override
			public void run() {
				mProgressDialog.setMessage(message);
			}
			
		});
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	protected void onPause() {
		super.onPause();
		mMapView.pause();
	}

	@Override
	protected void onResume() {
		super.onResume();
		mMapView.unpause();
	}

}
Feedback on this topic?