Elevation queries in Giro3D

By Friday October 4th, 2024NewsFR, Open Source

This article covers features released with version 0.36 of Giro3D.

Giro3D 0.36 introduced an elevation query system on Maps. We will explore this API along with a few typical use cases.

Prerequisite: Elevation Layers

To take advantage of the elevation query API, a Map must be equipped with an elevation layer. Elevation layers can read data from various sources, such as GeoTIFFs or tiled layers (like those provided by IGN or Mapbox).

Querying Elevation at a Coordinate

Once our Map and elevation layer are ready, we can start querying elevations. This is done through the getElevation() method.

// Mont Blanc
const coordinates = new Coordinates('EPSG:4326', 6.864594, 45.833641);

const elevationResult = map.getElevation({ coordinates });

getElevation() does not directly return a single value but an object containing a series of samples. We can sort these samples by resolution and then retrieve the one with the best resolution.

if (result.samples.length > 0) {
    result.samples.sort((a, b) => a.resolution - b.resolution);

    const best = result.samples[0];
    const elevation = best.elevation;

    console.log(`Elevation for coordinate: ${elevation} m`);
}

This allows querying multiple Maps simultaneously, aggregating their samples, and retrieving the best one.

Querying Multiple Maps?

In a typical context, a scene contains only one map. However, there are scenarios where several maps with elevation data are present simultaneously in a scene, possibly overlapping.

For example, in a scene containing a heterogeneous mosaic of elevation rasters (each represented by a Map), we need to create an object to store the results rather than letting getElevation() do it for us. We then query all the maps by passing them this object:

const coordinates = new Coordinates('EPSG:4326', 6.864594, 45.833641);

const result = { samples: [], coordinates: options.coordinates };

for (const map of allMaps) {
    map.getElevation({ coordinates }, result);
}

if (result.samples.length > 0) {
    result.samples.sort((a, b) => a.resolution - b.resolution);

    const best = result.samples[0];
    const elevation = best.elevation;

    console.log(`Best sample for coordinate: ${elevation} m`);
}

Positioning Labels on Terrain

For more details, see the complete example on the Giro3D website.

A typical use case for elevation queries is positioning labels on the terrain. Suppose we want to place a label at the summit of Mont Blanc, but we only have its latitude and longitude, not its elevation:

const labelObject = new CSS2DObject(/** options */);

// Mont Blanc
const coordinates = new Coordinates('EPSG:4326', 6.864594, 45.833641).as('EPSG:3857');

labelObject.position.setX(coordinates.x);
labelObject.position.setY(coordinates.y);

// ... After elevation query on the coordinates

labelObject.position.setZ(elevation);

This way, you can quickly position a large number of objects on the surface of the terrain.

Positioning labels on terrain

Positioning labels on terrain

Limitations

Elevation queries only read data that is currently in memory and do not make any HTTP requests. The data loaded depends on the current viewpoint: if the camera is far from the terrain, lower-resolution data will be loaded. This may impact the accuracy of positioning, especially when zooming in.

To address this issue, you should make a new elevation query when the currently loaded data changes. We can add a listener for the map’s elevation change event and update the labels within the updated area (extent).

function updateLabels(extent) {
    // Code
}

map.addEventListener('elevation-changed', ({ extent }) => updateLabels(extent));

Creating an Elevation Profile

For more details, see the complete example on the Giro3D website.

Another typical scenario for using elevation queries is, of course, the elevation profile. It is relatively simple to create an elevation profile using 3 components:

Drawing an elevation profile

Drawing an elevation profile

The profile is created by querying dozens or even hundreds of elevations in a very short time (usually less than a second).

Of course, the same limitations apply: it queries only the data already loaded, and no additional HTTP requests are made by Giro3D. This is an ideal scenario to give users a very quick preview without overloading servers.

Performance

Elevation queries are generally very fast: on the order of tens of milliseconds.

In the Giro3D example of elevation profiles, 200 samples are obtained in less than 200 milliseconds. A profile with 10,000 samples is generated in less than 400 milliseconds.

Conclusion

Elevation queries provide a flexible and efficient way to bridge the gap between 2D and 3D geomatics, without requiring additional services such as OGC WPS.