Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

ElevationTileLayer

kircher1 edited this page Apr 10, 2020 · 22 revisions

The elevation data used to render the map can be configured via the ElevationTileLayer system. The map calls into the ElevationTileLayer to request the elevation data relevant for the given view. The layer is responsible for retrieving and providing elevation data on a per-tile basis.

Hillshaded terrain of the Deschutes River valley from the DefaultElevationTileLayer. Using the ElevationTileLayer to provide higher detail data based on LIDAR collection, sourced from DOGAMI.

The DefaultElevationTileLayer uses Bing Maps elevation data. This layer has global coverage with varying degrees of resolution.

As shown in the image above, a custom data set can provide higher detail. The following sections describe how to construct an ElevationTile and provide this data through an ElevationTileLayer implementation.

Anatomy of an ElevationTile

Similar to a TextureTile, an ElevationTile covers the extents of a specified Web Mercator tile. However, instead of the tile representing colors, values reperesent elevations. In this sense, the elevation tile is simply a heightmap.

A 5x5 grid of elevation values. Each elevation value can be mapped to a vertex position in a mesh.

For rendering, the elevation data is used to offset the height axis of a grid mesh. This offset is performed in the vertex shader by sampling the elevation tile as a texture and applying a corresponding shift to the vertical axis of each vertex.

ElevationTile requirements

  • 257x257 dimension. This restriction may be removed in future versions.
  • Origin (0, 0) is in northwest corner (or top-left) of the tile.
  • Elevation values are provided in meters relative to WGS84 ellipsoid (not geoid-based).
  • Uses EPSG:3857 projection with individual tile data covering the extents as described here.
  • (Recommended) Pixels along the tile edge should share the same values as corresponding pixels in neighboring tile edges. This will reduce seams between tiles.

Constructing ElevationTiles

ElevationTiles can be created in one of two ways.

  • ElevationTile.FromDataInMeters: Provide elevation values in meters as 32-bit floats.
  • ElevationTile.FromNormalizedData: Provide elevation values which have been normalized into 16-bit ushorts. A min and max elevation for the tile must also be provided.

Implementing a custom ElevationTileLayer

While the DefaultElevationTileLayer provides global elevation coverage with varying amounts of detail, there may be areas where higher detail data is availble from a different souce like LIDAR scans, drone capture, etc.

A custom ElevationTileLayer implementation can be used to provide this sort of data to the map, and it can be served from a variety of sources:

  • Remote servers
  • Local disk
  • Textures generated procedurally

Exactly how the data is requested and processed into ElevationTiles is up to the implementation, which must provide HasElevationTile and GetElevationTile methods.

public class FooElevationTileLayer : ElevationTileLayer
{
    public override async Task<bool> HasElevationTile(TileId tileId, CancellationToken cancellationToken = default)
    {
        // If tiles are in a known area and LOD range, return true or false based on the TileIds inclusion.
        // If working with many tiles stored on local disk, it is recommended to create an in-memory lookup table to know which tiles are present.
        // If no lookup data structure is available, the ElevationTile data itself could be requested here and put in a pool that can be consumed in GetElevationTile.
        // There may be instances where HasElevationTile is called for a TileId but GetElevationTile is not, so it is best to track items in this pool and age them out if necessary.
    }

    public override async Task<ElevationTile> GetElevationTile(TileId tileId, CancellationToken cancellationToken = default)
    {
        // Request and decode elevation data asynchronously...

        // return ElevationTile.FromDataInMeters(...);
        return ElevationTile.FromNormalizedData(...);
    }
}

A note on performance

  • HasElevationTile and GetElevationTile are called asynchronously-- not on the Unity thread. Web requests or file IO can be performed in these methods without directly impacting framerate. If it is required to interop with Unity APIs, the UnityTaskFactory can be used to run tasks on Unity's main thread. Avoid long running work on the main thread however as it will impact framerate.
  • Serving large elevation data sets with thousands of tiles can be resource intensive both for network/file IO and CPU processing. Host or store the elevation data in a format that is easily decodable to the 16-bit normalized input as this format has a smaller memory footprint than the unnormalized 32-bit float alternative. In general, aim to minimize size of data and decrease time needed to transcode the data on the client to a format usable by the ElevationTile.

Clone this wiki locally