Hello, I have create a custom elevation layer based on the Custom ElevationLayer Example with TypeScript, here is my code:
import {
subclass, declared, property
} from "esri/core/accessorSupport/decorators";
import BaseElevationLayer = require('esri/layers/BaseElevationLayer');
import ElevationLayer = require('esri/layers/ElevationLayer');
@subclass('beginor.ExaggeratedElevationLayer')
class ExaggeratedElevationLayer extends(declared(BaseElevationLayer)) {
@property()
public exaggeration: number;
@property()
public url: string;
@property()
public get type(): string { return 'exaggerated-elevation'; }
private elevation: ElevationLayer;
constructor(properties: ExaggeratedElevationLayerProperties) {
super(properties);
if (!this.exaggeration) {
this.exaggeration = 1.0;
}
}
public load(signal: AbortSignal): IPromise<any> {
this.elevation = new ElevationLayer({
url: this.url
});
return this.addResolvingPromise(this.elevation.load(signal));
}
fetchTile(
level: number,
row: number,
column: number,
options: __esri.ElevationLayerFetchTileOptions
): IPromise<__esri.ElevationTileData> {
return this.elevation.fetchTile(level, row, column, options).then(
data => {
const exag = this.exaggeration;
for (let i = 0; i < data.values.length; i++) {
data.values[i] = data.values[i] * exag;
}
return data;
}
);
}
}
interface ExaggeratedElevationLayerProperties extends __esri.BaseElevationLayerProperties {
url: string;
exaggeration?: number;
}
export = ExaggeratedElevationLayer;
And I can use it in a webscene like this:
import WebScene = require('esri/Map');
import SceneView = require('esri/views/SceneView');
import externalRenderers = require('esri/views/3d/externalRenderers');
import ExaggeratedElevationLayer = require('beginor/ExaggeratedElevationLayer');
const map = new WebScene({
basemap: 'streets',
ground: {
layers: [
new ExaggeratedElevationLayer({
url: 'http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
exaggeration: 5
})
]
}
});
const view = new SceneView({
map: map,
container: 'viewDiv',
center: [113.2, 23.4],
zoom: 13,
camera: {
position: {
spatialReference: { wkid: 102100 },
x: 12601020.732296046,
y: 2661706.7320940713,
z: 10515.040601080284
},
heading: 0,
tilt: 60
}
});
But when I want to change the exaggeration at runtime, I have tried find the exaggerated elevation layer from the ground , change the exaggeration property, but this does not work.
The only way I found is remove the exaggerated vation layer from the ground, and recreate a new exaggerated elevation layer , and add it the the ground's layers, like this:
var btnTgl = document.getElementById('eleTgl') as HTMLButtonElement;
btnTgl.addEventListener('click', e => {
const ground = view.map.ground;
var eleLayer = ground.layers.getItemAt(0);
ground.layers.remove(eleLayer);
let exLayer = eleLayer as any as ExaggeratedElevationLayer;
if (exLayer.exaggeration == 5) {
exLayer = new ExaggeratedElevationLayer({
exaggeration: 1,
url: exLayer.url
});
}
else {
exLayer = new ExaggeratedElevationLayer({
exaggeration: 5,
url: exLayer.url
});
}
ground.layers.add(exLayer as any);
});
This works, but I think it is not the mose efficient way, because it will reload lots of elevation data from server again.
So my question is: Is there a more efficient way to change elevation aggeration at runtime?
(Edited)
I first tried to remove / re-add the layer as:
this.elevationLayer.factor = 5; this.map.ground.layers.remove(this.elevationLayer); this.map.ground.layers.add(this.elevationLayer);
This works fine for tiles that are not cached.
However cached tiles altitude is not updated and you end up with a mix of old and new altitude when you switch after the layer has already been used.
I ended up re-creating a new Layer every time I change the elevation which fixes the problem