Building Applications with the ArcGIS Runtime SDK for Android Andy Gup
Who am I?
Andy Gup, Esri U.S. Tech Lead for Web APIs, Android Esri Developer Network
[email protected] @agup
Agenda •
Introduction
•
Runtime SDK -
Tools and features
•
Maps & Layers
•
GPS
•
Tasks
•
Editing
•
Offline Capabilities
•
Summary
Assumptions
Basic understanding of the Google Android SDK Understand how to create ArcGIS Android Project Familiar with Eclipse Some knowledge of Java
Who are you?
Maps Can Be Shared Across Devices Making Maps available to all
Windows Phone
Android
iOS
Web Sites
ArcGIS Server
Desktop
Browsers
One Map
Online Services
. . . Enhancing Access and Collaboration
SDK Features
•
Create new ArcGIS Android projects
•
Create new ArcGIS Android Sample projects
•
Convert existing Android projects into ArcGIS for Android projects
•
Callout Localization -
•
Right click project > ArcGIS tools > UI Localization
Integrated Help System and Javadoc
Android SDK
http://developer.android.com
Download the SDK
http://esriurl.com/androidsdk
Project Configuration - res/layout/main.xml
Project Configuration - AndroidManifest.xml
... ...
<uses-permission android:name= "android.permission.INTERNET" /> <uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" />
Performance and the UI Thread
•
ALL UI work must be done on UI thread!
•
AsyncTask – easily run background thread
•
Handler() – bound to creation thread
•
ExecutorService – manage multiple AsyncTasks
•
FutureTask – cancellable asynchronous task
•
Thread
The main mapping component - MapView class
public class HelloWorld extends Activity { MapView map = null; /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); map = (MapView) findViewById(R.id.map); } }
http://esriurl.com/AndroidMapView
Adding map layers
map = new MapView(this); map.addLayer(new ArcGISTiledMapServiceLayer( "http://services.arcgisonline.com/ArcGIS/rest/services/...”)); setContentView(map);
Adding map layers Tiled Map Service map = new MapView(this); map.addLayer(new ArcGISTiledMapServiceLayer( "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer")); setContentView(map);
Dynamic Map Service map.addLayer(new ArcGISDynamicMapServiceLayer( "http://.../ArcGIS/rest/services/Demographics/ESRI_Population_World/MapServer"));
Feature Service map.addLayer(new ArcGISFeatureLayer(url,MODE.SNAPSHOT));
Image Service map.addLayer(new ArcGISImageServicesLayer(url, null));
Adding layers…
GraphicsLayer GroupLayer ArcGISLocalTiledLayer
WebMapLayer
Listening for MapView events
INTIALIZED INITIALIZATION_FAILED OnStatusChangedListener.STATUS.INITIALIZED LAYER_LOADED OnStatusChangedListener.STATUS.INITIALIZATION_FAILED LAYER_LOADING_FAILED
OnStatusChangedListener.STATUS.LAYER_LOADED OnStatusChangedListener.STATUS.LAYER_LOADING_FAILED
Listening for MapView events
map.setOnStatusChangedListener(new OnStatusChangedListener() { private static final long serialVersionUID = 1L; public void onStatusChanged(Object source, STATUS status) { if (OnStatusChangedListener.STATUS.INITIALIZED == status && source == map) { //TODO } if (OnStatusChangedListener.INITIALIZATION_FAILED == status && source == map){ //TODO } } }
Listening for Layer events
INTIALIZED INITIALIZATION_FAILED OnStatusChangedListener.STATUS.INITIALIZED LAYER_LOADED
LAYER_LOADING_FAILED OnStatusChangedListener.STATUS.INITIALIZATION_FAILED
Listening for Layer events
tiledLayer.setOnStatusChangedListener(new OnStatusChangedListener() { private static final long serialVersionUID = 1L; public void onStatusChanged(Object source, STATUS status) { if (OnStatusChangedListener.STATUS.INITIALIZED == status && source == tiledLayer) { //TODO } if (OnStatusChangedListener.STATUS.INITIALIZATION_FAILED == status && source == tiledLayer){ //TODO } } }
Map Initialized Event Demo
Map touch events - MapOnTouchListener
Listening for map touch events
mMapView.setOnSingleTapListener(new OnSingleTapListener() { public void onSingleTap(float arg0, float arg1) { //TO-DO } });
Custom Event Event Listener
Class DragTouchListener extends MapOnTouchListener {
public DragTouchListener(Context arg0, MapView arg1) { super(arg0, arg1); } public boolean onDragPointerMove(MotionEvent from, MotionEvent to) { ... return true; } ... }
Switching between touch listeners /** * Sets the DEFAULT MapOnTouchListener */ public void setDefaultTouchListener(){ MapOnTouchListener ml = new MapOnTouchListener(getContext(), _mapView); _mapView.setOnTouchListener(ml); } /** * Set the MyTouchListener which overrides various user touch events. */ public void setDrawTouchListener(){ _myTouchListener = new MyTouchListener(getContext(), _mapView); _mapView.setOnTouchListener(_myTouchListener); }
Remove a default listener
//Remove default listener! _mapView.setOnSingleTapListener(null);
Touch listeners demo
Microsoft Clip art
GPS and Device Location
_locationService = _mapView.getLocationService(); _locationService.setAutoPan(true); _locationService.setAccuracyCircleOn(true); _locationService.setLocationListener(new LocationListener() { //TODO }); _locationService.start();
GPS/Location restart
After application cold start, paused or view change. Step 1: Verify Map + layer(s) are loaded Step 2: Start the LocationManager and/or LocationService Step 3: On location event autoCenter and/or draw graphic
Restart LocationService #1 – easy and quick
map.setOnStatusChangedListener(new OnStatusChangedListener() { public void onStatusChanged(Object source, STATUS status) { if (source == map && status == STATUS.INITIALIZED) { LocationService ls = map.getLocationService(); ls.setAutoPan(false); ls.setLocationListener(new LocationListener() { public void onLocationChanged(Location loc) { //TODO } } } } });
Restart LocationService #2 - more control
In onResume() use Runnable… Step 1: unpause() map after onResume() event Step 2: Implement a Runnable Step 3: Implement a counter & check if map loaded Step 4: Implement if count > X then fail gracefully Step 5: start the Runnable
Example is in the EsriQuickStart on github
Location demo
Microsoft Clip art
Geo-Analysis •
All ArcGIS Task samples are (currently) AsyncTask -
Geocode
-
GeoProcessing
-
Identify
-
Query
Geoprocessing Example – Step 1
class ViewShedQuery extends AsyncTask { GPParameter[] outParams = null; @Override protected void onPostExecute(GPParameter[] result) { //TODO } @Override protected GPParameter[] doInBackground( ArrayList... params1) { //TODO } }
Geoprocessing Example – Step 2
@Override protected GPParameter[] doInBackground( ArrayList... params1) { ... try { GPResultResource rr = gp.execute(params1[0]); outParams = rr.getOutputParameters(); } catch (Exception e) { e.printStackTrace(); } return outParams; }
Geoprocessing Example – Step 3 @Override protected void onPostExecute(GPParameter[] result) { for (int i = 0; i < outParams.length; i++) { if (outParams[i] instanceof GPFeatureRecordSetLayer) { GPFeatureRecordSetLayer fsl = (GPFeatureRecordSetLayer) outParams[i]; for (Graphic feature : fsl.getGraphics()) { Graphic g = new Graphic(feature.getGeometry(), new SimpleFillSymbol(Color.CYAN) ); gLayer.addGraphic(g); } } } }
Geoprocessing Example – Step 4
GPFeatureRecordSetLayer gpf = new GPFeatureRecordSetLayer(“xyz"); gpf.setSpatialReference(map.getSpatialReference()); gpf.setGeometryType(Geometry.Type.Point); // 1st input parameter - Add the point selected by the user Graphic f = new Graphic(mappoint,new SimpleMarkerSymbol(...)); gpf.addGraphic(f); // Second input parameter GPLinearUnit gpl = new GPLinearUnit("Viewshed_Distance"); gpl.setUnits("esriMeters"); gpl.setDistance(8046.72); // Add params params = new ArrayList(); params.add(gpf); params.add(gpl); new ViewShedQuery().execute(params); //ON THE UI THREAD!
Geoprocessing demo
Editing Feature Layers ArcGISFeatureLayer.applyEdits() •
Asynchronous
•
Create new feature
•
Delete features
•
Edit existing geometries
•
Edit attributes
Editing Feature Layers Immediate over-the-air sync (requires internet!) •
Adding
•
Deleting
•
Updating
Feature Service specification support •
geometries
•
renderer
•
attributes
//Access ArcGISFeatureLayer featureLayer.getFields(); //Field[] featureLayer.getTypes(); //FeatureType[] featureLayer.getTypeIdField(); //String
Editing Feature Layers – data integrity
Features must confirm to layer specification •
Geometry type
•
Accuracy
•
Topology rules
Editing Feature Layers
//create a graphic using the template Graphic graphic = featureLayer.createFeatureWithTemplate( template, geometry); featureLayer.applyEdits(new Graphic[] { graphic }, null, null, new CallbackListener() { public void onError(Throwable error) { // TODO implement error code } public void onCallback(FeatureEditResult[][] editResult) { // NOTE: This is ALWAYS called even if error if (editResult[2] != null && editResult[2][0] != null && editResult[2][0].isSuccess()) { // editResult[0] = ADD results // editResult[1] = DELETE results // editResult[2] = UPDATE results } });
AttributeEditor demo
Geometry Editing
Offline Data can be stored on an SD Card •
ArcGIS Compact Cache Format
•
Tile Package (*.tpk)
•
JSON FeatureSet
baseMapLayer = new ArcGISLocalTiledLayer( “file:///mnt/sdcard/ArcGIS/samples/cljf/”+ “OfflineData/ImageryTPK.tpk”);
Offline capabilities •
•
Use in memory FeatureLayer -
Feature set [array of features]
-
Layer definition (JSON from feature service)
Change Features -
Add/remove graphics
-
applyEdits()
•
Write to/from disk in JSON
•
Add/remove graphics
•
Advanced Symbology
Offline – create FeatureLayer
<string name="config.windturbine.layer.definition"> { \"currentVersion\":10.01,\"id \":0,\"name\":\"WindTurbine\”… }
//Java classturbinesFeatureLayer = new ArcGISFeatureLayer( R.string.config.windturbine.layer.definition, FeatureSet, null);
Offline – write JSON to SD Card FileOutputStream outstream = null; try { // create feature set as json string String fsstring = FeatureSet.toJson(result); // create fully qualified path for json file path = createJsonFile(); // create a File from json fully qualified path File outfile = new File(path); // create output stream to write to json file outstream = new FileOutputStream(outfile); outstream.write(fsstring.getBytes()); outstream.close(); } catch (Exception e) { //TODO }
Offline – read JSON from SD Card
//Use Jackson JsonParser JsonFactory factory = new JsonFactory(); JsonParser parser = factory.createJsonParser( new FileInputStream(path) ); parser.nextToken(); fs = FeatureSet.fromJson(parser);
Demo Offline
Webmaps Uses a different pattern map = new MapView( this, "http://www.arcgis.com/home/item.html?id=81d22...”, “name”, “password” ); setContentView(map);
As compared to non-Webmaps…
map = (MapView) findViewById(R.id.map); map.addLayer(new ArcGISTiledMapServiceLayer( "http://services.arcgisonline.com/ArcGIS/rest/services/...”));
ArcGIS for Android Application
Github
Android Quick Start Sample Android GPS Tester https://github.com/esri Samples for this presentation: https://github.com/andygup/getting-startedandroid
GPSTester demo
Microsoft Clip art
Tips-and-tricks •
Test using a phone and tablet vs. Emulator
•
Android Help: http://developer.android.com/
•
Android Help -> User Interface Best Practices
•
Which Android version? Know your users!
•
Troubleshooting ArcGIS? Use the Android Debug Bridge (ADB), DDMS and Logcat
Log.d("Debug", String.valueOf(_currentLocation.getLatitude()));
DevSummit Android sessions include… Agenda Search > Keyword > Android Developing Mapping Apps with Android •
Tue 4pm Pasadena/Venture/Sierra
Android User Group Meeting! •
Wed 12pm Mesquite A
Implementing Analysis, Editing and Offline Apps w/ Android •
Wed 4pm Pasadena/Venture/Sierra
iOS and Android: Let’s have a hug •
Wed 5:30pm Catalina/Madera
Road ahead for Runtime SDKs •
Thur 10am Primrose B
Who am I?
Andy Gup, Esri U.S. Tech Lead for Web APIs, Android Esri Developer Network http://blog.andygup.net
[email protected] @agup