How to change elevation exaggeration at runtime?

374
1
09-25-2019 08:35 PM
zhiminzhang
New Contributor II

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?

0 Kudos
1 Reply
VictorBerchet
New Contributor III

(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

0 Kudos