Feature layer update geometry
Update a feature's location in an online feature service.
Use case
Sometimes users may want to edit features in an online feature service by moving them.
How to use the sample
Tap a feature to select it. Tap again to set the updated location for that feature. An alert will be shown confirming success or failure.
How it works
- Create a
ServiceFeatureTable
object from a URL. - Create a
FeatureLayer
object from theServiceFeatureTable
. - Identify layers from the feature layer to find features, and select a feature with
selectFeatures(identifiedFeature)
. - Change the selected feature's location using
identifiedFeature.setGeometry(...)
. - After the change, update the table on the server using
applyEditsAsync()
.
Relevant API
- Feature
- FeatureLayer
- ServiceFeatureTable
Tags
editing, feature layer, feature table, moving, service, updating
Sample Code
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* Copyright 2016 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.arcgisruntime.samples.featurelayerupdategeometry;
import java.util.List;
import java.util.concurrent.ExecutionException;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.ArcGISFeature;
import com.esri.arcgisruntime.data.FeatureTableEditResult;
import com.esri.arcgisruntime.data.ServiceFeatureTable;
import com.esri.arcgisruntime.data.ServiceGeodatabase;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.BasemapStyle;
import com.esri.arcgisruntime.mapping.GeoElement;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.IdentifyLayerResult;
import com.esri.arcgisruntime.mapping.view.MapView;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private MapView mMapView;
private FeatureLayer mFeatureLayer;
private boolean mFeatureSelected = true;
// objects that implement Loadable must be class fields to prevent being garbage collected before loading
private ArcGISFeature mIdentifiedFeature;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// authentication with an API key or named user is required to access basemaps and other
// location services
ArcGISRuntimeEnvironment.setApiKey(BuildConfig.API_KEY);
// get a reference to the map view
mMapView = findViewById(R.id.mapView);
// create a map with the streets basemap
ArcGISMap map = new ArcGISMap(BasemapStyle.ARCGIS_STREETS);
// set the map to be displayed in the MapView
mMapView.setMap(map);
// set an initial viewpoint
mMapView.setViewpoint(new Viewpoint(34.057386, -117.191455, 100000000));
// create and load the service geodatabase
ServiceGeodatabase serviceGeodatabase = new ServiceGeodatabase(getString(R.string.sample_service_url));
serviceGeodatabase.loadAsync();
serviceGeodatabase.addDoneLoadingListener(() -> {
// create a feature layer using the first layer in the ServiceFeatureTable
ServiceFeatureTable serviceFeatureTable = serviceGeodatabase.getTable(0);
// create a feature layer from table
mFeatureLayer = new FeatureLayer(serviceFeatureTable);
// add the layer to the map
mMapView.getMap().getOperationalLayers().add(mFeatureLayer);
});
Toast.makeText(getApplicationContext(), "Tap on a feature to select it.", Toast.LENGTH_LONG).show();
// set an on touch listener to listen for click events
mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
@Override
public boolean onSingleTapConfirmed(MotionEvent motionEvent) {
if (mFeatureSelected) {
android.graphics.Point screenCoordinate = new android.graphics.Point(Math.round(motionEvent.getX()),
Math.round(motionEvent.getY()));
double tolerance = 20;
// identify Layers to find features
final ListenableFuture<IdentifyLayerResult> identifyFuture = mMapView
.identifyLayerAsync(mFeatureLayer, screenCoordinate, tolerance, false, 1);
identifyFuture.addDoneListener(() -> {
try {
// call get on the future to get the result
IdentifyLayerResult layerResult = identifyFuture.get();
List<GeoElement> resultGeoElements = layerResult.getElements();
if (!resultGeoElements.isEmpty()) {
if (resultGeoElements.get(0) instanceof ArcGISFeature) {
mIdentifiedFeature = (ArcGISFeature) resultGeoElements.get(0);
// select the identified feature
mFeatureLayer.selectFeature(mIdentifiedFeature);
mFeatureSelected = false;
Toast.makeText(getApplicationContext(), "Feature Selected. Tap on map to update its geometry.",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "No Features Selected. Tap on a feature.", Toast.LENGTH_LONG)
.show();
}
}
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "Update feature failed: " + e.getMessage());
}
});
} else {
Point movedPoint = mMapView.screenToLocation(new android.graphics.Point(Math.round(motionEvent.getX()), Math.round(motionEvent.getY())));
final Point normalizedPoint = (Point) GeometryEngine.normalizeCentralMeridian(movedPoint);
mIdentifiedFeature.addDoneLoadingListener(() -> {
mIdentifiedFeature.setGeometry(normalizedPoint);
final ListenableFuture<Void> updateFuture = mFeatureLayer.getFeatureTable().updateFeatureAsync(mIdentifiedFeature);
updateFuture.addDoneListener(() -> {
try {
// track the update
updateFuture.get();
// apply edits once the update has completed
if (updateFuture.isDone()) {
applyEditsToServer();
mFeatureLayer.clearSelection();
mFeatureSelected = true;
} else {
Log.e(TAG, "Update feature failed!");
}
} catch (InterruptedException | ExecutionException e1) {
Log.e(TAG, "Update feature failed: " + e1.getMessage());
}
});
});
mIdentifiedFeature.loadAsync();
}
return super.onSingleTapConfirmed(motionEvent);
}
});
}
/**
* Applies edits to the FeatureService
*/
private void applyEditsToServer() {
final ListenableFuture<List<FeatureTableEditResult>> applyEditsFuture = ((ServiceFeatureTable) mFeatureLayer
.getFeatureTable()).getServiceGeodatabase().applyEditsAsync();
applyEditsFuture.addDoneListener(() -> {
try {
// get results of edit
List<FeatureTableEditResult> featureEditResultsList = applyEditsFuture.get();
Toast.makeText(this,
"Applied Geometry Edits to Server. ObjectID: " + featureEditResultsList.get(0).getEditResult().get(0).getObjectId(),
Toast.LENGTH_SHORT).show();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "Update feature failed: " + e.getMessage());
}
});
}
@Override
protected void onPause() {
mMapView.pause();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
mMapView.resume();
}
@Override protected void onDestroy() {
mMapView.dispose();
super.onDestroy();
}
}