Add features (Feature Service)

View inJavaKotlin
View on GitHub
Sample viewer app

Add features to a feature layer.

Image of adding features

Use case

An end-user performing a survey may want to add features to the map during the course of their work.

How to use the sample

Tap on a location on the map to add a feature at that location.

How it works

  1. Create a ServiceFeatureTable from a URL.
  2. Create a FeatureLayer derived from the ServiceFeatureTable instance.
  3. Create a Feature with attributes and a location using the ServiceFeatureTable.
  4. Add the Feature to the ServiceFeatureTable.
  5. Apply edits to the ServiceFeatureTable which will upload the new feature to the online service.

Relevant API

  • Feature
  • FeatureEditResult
  • FeatureLayer
  • ServiceFeatureTable


edit, feature, online service

Sample Code
 * Copyright 2019 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
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.

package com.esri.arcgisruntime.sample.addfeaturesfeatureservice;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Toast;

import androidx.annotation.Nullable;
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
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.Viewpoint;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.MapView;

public class MainActivity extends AppCompatActivity {

  private static final String TAG = MainActivity.class.getSimpleName();

  private MapView mMapView;

  private ServiceFeatureTable mServiceFeatureTable;

  @Override protected void onCreate(@Nullable Bundle savedInstanceState) {

    // authentication with an API key or named user is required to access basemaps and other
    // location services

    mMapView = findViewById(;

    // create a map with streets basemap
    ArcGISMap map = new ArcGISMap(BasemapStyle.ARCGIS_STREETS);

    // create service feature table from URL
    mServiceFeatureTable = new ServiceFeatureTable(getString(R.string.service_layer_url));

    // create a feature layer from table
    FeatureLayer featureLayer = new FeatureLayer(mServiceFeatureTable);

    // add the layer to the map

    // add a listener to the MapView to detect when a user has performed a single tap to add a new feature to
    // the service feature table
    mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
      @Override public boolean onSingleTapConfirmed(MotionEvent event) {
        // create a point from where the user clicked point = new event.getX(), (int) event.getY());

        // create a map point from a point
        Point mapPoint = mMapView.screenToLocation(point);

        // add a new feature to the service feature table
        addFeature(mapPoint, mServiceFeatureTable);
        return super.onSingleTapConfirmed(event);

    // set map to be displayed in map view
    mMapView.setViewpoint(new Viewpoint( 40.0, -95.0, 10000000.0));

   * Adds a new Feature to a ServiceFeatureTable and applies the changes to the
   * server.
   * @param mapPoint     location to add feature
   * @param featureTable service feature table to add feature
  private void addFeature(Point mapPoint, final ServiceFeatureTable featureTable) {

    // create default attributes for the feature
    Map<String, Object> attributes = new HashMap<>();
    attributes.put("typdamage", "Destroyed");
    attributes.put("primcause", "Earthquake");

    // creates a new feature using default attributes and point
    Feature feature = featureTable.createFeature(attributes, mapPoint);

    // check if feature can be added to feature table
    if (featureTable.canAdd()) {
      // add the new feature to the feature table and to server
      featureTable.addFeatureAsync(feature).addDoneListener(() -> applyEdits(featureTable));
    } else {
      runOnUiThread(() -> logToUser(true, getString(R.string.error_cannot_add_to_feature_table)));

   * Sends any edits on the ServiceFeatureTable to the server.
   * @param featureTable service feature table
  private void applyEdits(ServiceFeatureTable featureTable) {

    // apply the changes to the server
    final ListenableFuture<List<FeatureEditResult>> editResult = featureTable.applyEditsAsync();
    editResult.addDoneListener(() -> {
      try {
        List<FeatureEditResult> editResults = editResult.get();
        // check if the server edit was successful
        if (editResults != null && !editResults.isEmpty()) {
          if (!editResults.get(0).hasCompletedWithErrors()) {
            runOnUiThread(() -> logToUser(false, getString(R.string.feature_added)));
          } else {
            throw editResults.get(0).getError();
      } catch (InterruptedException | ExecutionException e) {
        runOnUiThread(() -> logToUser(true, getString(R.string.error_applying_edits, e.getCause().getMessage())));

   * Shows a Toast to user and logs to logcat.
   * @param isError whether message is an error. Determines log level.
   * @param message message to display
  private void logToUser(boolean isError, String message) {
    Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    if (isError) {
      Log.e(TAG, message);
    } else {
      Log.d(TAG, message);

  @Override protected void onResume() {

  @Override protected void onPause() {

  @Override protected void onDestroy() {