Edit

Editable ArcGIS REST Feature Service

vector56 esri4 ArcGIS5 REST3 Feature10 Service2 loading6 server4 edit10 updateFeature1 addFeature1

Example of using an ArcGIS REST Feature Service in an editing application.

This example loads features from ArcGIS REST Feature Service and allows to add new features or update existing features.

main.js
import 'ol/ol.css';
import EsriJSON from 'ol/format/EsriJSON';
import Map from 'ol/Map';
import VectorSource from 'ol/source/Vector';
import View from 'ol/View';
import XYZ from 'ol/source/XYZ';
import {
  Draw,
  Modify,
  Select,
  defaults as defaultInteractions,
} from 'ol/interaction';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {createXYZ} from 'ol/tilegrid';
import {fromLonLat} from 'ol/proj';
import {tile as tileStrategy} from 'ol/loadingstrategy';

var serviceUrl =
  'https://services.arcgis.com/rOo16HdIMeOBI4Mb/arcgis/rest/' +
  'services/PDX_Pedestrian_Districts/FeatureServer/';
var layer = '0';

var esrijsonFormat = new EsriJSON();

var vectorSource = new VectorSource({
  loader: function (extent, resolution, projection) {
    var url =
      serviceUrl +
      layer +
      '/query/?f=json&' +
      'returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=' +
      encodeURIComponent(
        '{"xmin":' +
          extent[0] +
          ',"ymin":' +
          extent[1] +
          ',"xmax":' +
          extent[2] +
          ',"ymax":' +
          extent[3] +
          ',"spatialReference":{"wkid":102100}}'
      ) +
      '&geometryType=esriGeometryEnvelope&inSR=102100&outFields=*' +
      '&outSR=102100';
    $.ajax({
      url: url,
      dataType: 'jsonp',
      success: function (response) {
        if (response.error) {
          alert(
            response.error.message + '\n' + response.error.details.join('\n')
          );
        } else {
          // dataProjection will be read from document
          var features = esrijsonFormat.readFeatures(response, {
            featureProjection: projection,
          });
          if (features.length > 0) {
            vectorSource.addFeatures(features);
          }
        }
      },
    });
  },
  strategy: tileStrategy(
    createXYZ({
      tileSize: 512,
    })
  ),
});

var vector = new VectorLayer({
  source: vectorSource,
});

var raster = new TileLayer({
  source: new XYZ({
    attributions:
      'Tiles © <a href="https://services.arcgisonline.com/ArcGIS/' +
      'rest/services/World_Topo_Map/MapServer">ArcGIS</a>',
    url:
      'https://server.arcgisonline.com/ArcGIS/rest/services/' +
      'World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
  }),
});

var draw = new Draw({
  source: vectorSource,
  type: 'Polygon',
});

var select = new Select();
select.setActive(false);
var selected = select.getFeatures();

var modify = new Modify({
  features: selected,
});
modify.setActive(false);

var map = new Map({
  interactions: defaultInteractions().extend([draw, select, modify]),
  layers: [raster, vector],
  target: document.getElementById('map'),
  view: new View({
    center: fromLonLat([-122.619, 45.512]),
    zoom: 12,
  }),
});

var typeSelect = document.getElementById('type');

/**
 * Let user change the interaction type.
 */
typeSelect.onchange = function () {
  draw.setActive(typeSelect.value === 'DRAW');
  select.setActive(typeSelect.value === 'MODIFY');
  modify.setActive(typeSelect.value === 'MODIFY');
};

var dirty = {};

selected.on('add', function (evt) {
  var feature = evt.element;
  feature.on('change', function (evt) {
    dirty[evt.target.getId()] = true;
  });
});

selected.on('remove', function (evt) {
  var feature = evt.element;
  var fid = feature.getId();
  if (dirty[fid] === true) {
    var payload =
      '[' +
      esrijsonFormat.writeFeature(feature, {
        featureProjection: map.getView().getProjection(),
      }) +
      ']';
    var url = serviceUrl + layer + '/updateFeatures';
    $.post(url, {f: 'json', features: payload}).done(function (data) {
      var result = JSON.parse(data);
      if (result.updateResults && result.updateResults.length > 0) {
        if (result.updateResults[0].success !== true) {
          var error = result.updateResults[0].error;
          alert(error.description + ' (' + error.code + ')');
        } else {
          delete dirty[fid];
        }
      }
    });
  }
});

draw.on('drawend', function (evt) {
  var feature = evt.feature;
  var payload =
    '[' +
    esrijsonFormat.writeFeature(feature, {
      featureProjection: map.getView().getProjection(),
    }) +
    ']';
  var url = serviceUrl + layer + '/addFeatures';
  $.post(url, {f: 'json', features: payload}).done(function (data) {
    var result = JSON.parse(data);
    if (result.addResults && result.addResults.length > 0) {
      if (result.addResults[0].success === true) {
        feature.setId(result.addResults[0]['objectId']);
        vectorSource.clear();
      } else {
        var error = result.addResults[0].error;
        alert(error.description + ' (' + error.code + ')');
      }
    }
  });
});
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Editable ArcGIS REST Feature Service</title>
    <!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
    <script src="https://unpkg.com/elm-pep"></script>
    <!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
    <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    <style>
      .map {
        width: 100%;
        height:400px;
      }
    </style>
  </head>
  <body>
    <div id="map" class="map"></div>
    <form class="form-inline">
      <label>Draw or modify &nbsp;</label>
      <select id="type">
        <option value="DRAW">Draw</option>
        <option value="MODIFY">Modify</option>
      </select>
    </form>
    <script src="main.js"></script>
  </body>
</html>
package.json
{
  "name": "vector-esri-edit",
  "dependencies": {
    "ol": "6.4.3"
  },
  "devDependencies": {
    "parcel": "1.11.0"
  },
  "scripts": {
    "start": "parcel index.html",
    "build": "parcel build --experimental-scope-hoisting --public-url . index.html"
  }
}