You are using a browser that is no longer supported. Please use the latest version of Google Chrome, Mozilla Firefox, Apple Safari, or Microsoft Edge. For more information please see the System Requirements.

Unsupported browser

You are using a browser that is not supported. JavaScript API works on the latest versions of Google Chrome, Mozilla Firefox, Apple Safari, or Microsoft Edge. Use one of these browsers and provide your feedback through GeoNet, the Esri Community.

  • {i18n.unsupportedBrowser.chrome}
  • Firefox
  • Safari
  • undefined
Loading...

Note: Support for 3D on mobile devices may vary, view the system requirements for more information.

Overview

This sample demonstrates how to create a custom ElevationLayer by creating a subclass of BaseElevationLayer. In some cases, you may want to exaggerate elevation in a SceneView to make it more pronounced at smaller scales.

No exaggeration (TopoBathy3D elevation service)

default-elevation

Elevation exaggerated 70x with custom layer

exaggerated-elevation

Creating a custom elevation layer

To create a custom ElevationLayer you must call createSubclass() on BaseElevationLayer. We'll name the custom layer ExaggeratedElevationLayer.

Since this sample exaggerates existing elevation values, we require ElevationLayer and load the default ArcGIS TopoBathy3D service as a dependency of the custom layer. This is done inside the load() method because it is a loadable resource.

We'll also create an exaggeration property to specify the factor by which we'll exaggerate the elevation.

const ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
  properties: {
    // exaggerates the actual elevations by 70x
    exaggeration: 70
  },

  load: function() {
    this._elevation = new ElevationLayer({
      url: "//elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/TopoBathy3D/ImageServer"
    });

    // wait for the elevation layer to load before resolving load()
    this.addResolvingPromise(
      this._elevation.load().then(() => {
        // get tileInfo, spatialReference and fullExtent from the elevation service
        // this is required for elevation services with a custom spatialReference
        this.tileInfo = this._elevation.tileInfo;
        this.spatialReference = this._elevation.spatialReference;
        this.fullExtent = this._elevation.fullExtent;
      })
    );

    return this;
  }
});

Then we must manipulate the fetchTile() method of ExaggeratedElevationLayer so that it returns the proper values for each pixel. We can fetch the actual elevation values using ElevationLayer.fetchTile(), multiply them by the exaggeration value, and return them to fetchTile since the data object specification for fetchTile() in ElevationLayer is the same as it is for BaseElevationLayer. The final code for the custom elevation layer should look like the following.

const ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
  properties: {
    // exaggerates the actual elevations by 70x
    exaggeration: 70
  },

  load: function() {
    this._elevation = new ElevationLayer({
      url: "//elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/TopoBathy3D/ImageServer"
    });

    // wait for the elevation layer to load before resolving load()
    this.addResolvingPromise(
      this._elevation.load().then(() => {
        // get tileInfo, spatialReference and fullExtent from the elevation service
        // this is required for elevation services with a custom spatialReference
        this.tileInfo = this._elevation.tileInfo;
        this.spatialReference = this._elevation.spatialReference;
        this.fullExtent = this._elevation.fullExtent;
      })
    );

    return this;

  },

  // Fetches the tile(s) visible in the view
  fetchTile: function(level, row, col, options) {
    return this._elevation.fetchTile(level, row, col, options).then(
      function(data) {
        var exaggeration = this.exaggeration;
        for (var i = 0; i < data.values.length; i++) {
          data.values[i] = data.values[i] * exaggeration;
        }

        return data;
      }.bind(this)
    );
  }
});

Once the layer is created, you must add it to the layers of the Map.ground property and add the map to a SceneView instance.

const map = new Map({
  basemap: basemap,
  ground: {
    layers: [new ExaggeratedElevationLayer()]
  }
});
sceneView.map = map;

Sample search results

TitleSample
Loading...