Building Applications with the ArcGIS Runtime SDK for Android

Report 13 Downloads 629 Views
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