Custom ElevationLayer - Exaggerating elevation
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. See the image below for an example of this. Both images were captured at a view scale of 1:36,876,121
.
No exaggeration (default world elevation service)
Elevation exaggerated 100x with custom layer
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 world elevation 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.
var ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
properties: {
// exaggerates the actual elevations by 100x
exaggeration: 100
},
load: function() {
this._elevation = new ElevationLayer({
url: "//elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
});
// wait for the elevation layer to load before resolving load()
this.addResolvingPromise(this._elevation.load());
}
});
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.
var ExaggeratedElevationLayer = BaseElevationLayer.createSubclass({
properties: {
// exaggerates the actual elevations by 100x
exaggeration: 100
},
load: function() {
this._elevation = new ElevationLayer({
url: "//elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
});
// wait for the elevation layer to load before resolving load()
this.addResolvingPromise(this._elevation.load());
},
// Fetches the tile(s) visible in the view
fetchTile: function(level, row, col) {
return this._elevation.fetchTile(level, row, col)
.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.
var map = new Map({
basemap: "satellite",
ground: {
layers: [ new ExaggeratedElevationLayer() ]
}
});
sceneView.map = map;