Skip to content
Elevation API ATLAS Elevation 2 Tileendpointarchitecture

Elevation API ATLAS Elevation 2 Tileendpointarchitecture

Andi Lamprecht Andi Lamprecht ·· 7 min read· Accepted
ADR-0053 · Author: Sybil Melton · Date: 2025-02-07 · Products: platform
Originally ADR-0057 ELEVATION_API_ATLAS_ELEVATION_2_TileEndpointArchitecture (v4) · Source on Confluence ↗

Architecture of Tile Endpoints

Context

Atlas Customers will request for the elevation data for predefined area.

The elevation data is sourced from 3rd party providers in raster files. Elevation API loads this data and hosts on GCS.

Despite of the data source the elevation data has the following characteristics.

  • each file (eg. geotiff) covers predefined area defined by it’s boundary property
  • reference system is source-specific (eg. USGS refers each file in it’s UTM zone [1])
  • data files are often large (eg. USGS 1/3 arcsec dataset consist of 900 GeoTiffs of total size 333GB).

The data is served as a Tiles. A tile is a data structure defined in Storage Layer ADR.

Atlas has designed an API to support serving this data for 2 use cases:

  • data for display - data is served as RGB Tiles in webp format.
  • data for programmatic consumption - data is served as RGB tiles in json format.

Decision

The elevation API endpoints dynamically retrieve raw data from Cloud Optimized GeoTiff files stored in Google Cloud Storage (GCS), based on the customer’s requested area of interest. The data is loaded to GCS by a scheduled Cloud Composer job. After the job is done the MosaicJSON file is generate which is a geo-indexed lookup table for loaded data. The data structure within MosaicJSON is replicated to redis via MosaicSync service which creates a MosaicDB. For more information about process above see [11]. Next the Elevation Service processes the request by identifying the assets (GeoTiffs) covering the requested area of interests by checking a MosaicDB keys. Subsequently, the data undergoes re-projection from the source reference system into either EPSG:4326 (WSG84, lat/lon) or EPSG:3857 (WebMercator) before being returned. To optimize performance, the service creates an index in Redis (Memorycache) to store tile data for subsequent requests.

Additionally, considering the anticipated high request spikes from web browsers when serving data for display, a Content Delivery Network (CDN) can be used to cache image responses. This approach aims to offload traffic from the service, enhancing the overall user experience.

Invalid Image Path

Requesting a tile

In order to request for a tile in any endpoint a customer must provide X, Y and Z which describes the query area. XYZ are referenced in Tile Matrix Set [2] specific to requested output reference system. Tile Matrix Set defines how tiles are generated (a cutting schema).

Data for display

  • The data is requested in X/Y/Z pattern, where X, Y and Z are well-known slippy tiles [3][8] defined by OGC WebMercatorQuad TMS [4]
  • Data output and slippy tiles are referenced in WebMercator
  • The data used for visualization is served as RGB webp image with colors mapped acc. to mapbox elevation display spec [9]
  • Each tile has 256x256 resolution

Data for processing

  • The data is requested in X/Y/Z pattern, where X and Y are the tile index specific for each zoom level (Z), defined by OGC WGS1984Quad TMS [4]
  • Data output and slippy tiles are referenced in EPSG:4326, which is based on WGS84’s ellipsoid.
  • The data used for processing is served as JSON with raw data.
  • Each tile has 256x256 resolution

Note: Tile indexes (XYZ) are different for display endpoint and data endpoint since their grids are build by different TMS

Serving a slippy tile

The service follows a multi-step process to deliver slippy tiles for map rendering:

  • Cache Check:

Before generating a tile, the service checks if it already exists in the cache. This step aims to optimize performance by avoiding the recreation of tiles that have been generated before.

  • Tile Generation and Rendering:

In cases where the tile is not found in the cache, the service proceeds to generate and render it. This involves locating the necessary raw data, and using this data to create the specific map tile.

  • Caching the Output:

Once the tile is rendered, the resulting image is stored in the cache. This caching mechanism is crucial for efficiency, as it allows subsequent requests for the same tile to be fulfilled more rapidly by retrieving the pre-rendered version from the cache.

By implementing this approach, the service minimizes redundant computational efforts and enhances response times.

Invalid Image Path

Locating apropriate file on GCS

The raw data is distributed between multiple cloud optimized geotiff files. Each file has a boundaries information and stores the data in location-specific reference system.

To find out the data for requested tile, the service has to locate the appropriate raster file (or files, since they could overlay on each other) on GCS and then read the values for each pixel. To do this efficiently each file URI is stored in a lookup json created according to MosaicJSON spec [6]. The mosaicJSON is generated by converting the geographic boundary box of a GeoTIFF into a QuadKey [8] geoindex. Subsequently, it associates the GeoTIFF URI with the corresponding QuadKey.

Failure Modes

  • Redis is offline

Elevation API is using GCP MemoryStore for Redis as a service. GCP Memorystore allows to deploy redis with few replicas to ensure high availability. In rare cases when redis is offline the app should return error 500, to not interrupt the data consistency in-between switching the replicas. We want to avoid the scenario when the main service is offline, the service reads the data from gcs and in subsequent request serves old data when traffic is redirected to replica, that was not aware of GCS reading operation. [10]

Regarding the asset retrieval step, in case redis (MosaicDB) is not responsive, the service falls back to get the assets list directly from MosaicJSON stored on GCS, until the service is back online.

  • GCS is offline

GCS serves the Geotiffs which are the source of truth for the application. If the service is offline and the tile is not cached in redis, then the service should return error 500

  • Redis RAM memory overflow

Invalid Image Path

Consequences

Dynamic creation of tiles

  • Up-to-Date Data:

Dynamic tile generation ensures that the tiles are always generated from the latest version of the source data, providing real-time updates without the need repopulate the tile store.

  • Data needs to be cached

For current change frequency the TTL-driven cache invalidation seem to be sufficient

Alternatives Considered

Pre-Computed tiles.

Atlas could pre-render all possible tiles and store them, however this would end up in processing and maintenance of the data that is not used by anyobody. Precreating tiles for multiple zoom levels and resolutions can results in storage redundancy. The number of tiles that needs to be pre-generated raises rapidly with each zoom level. [7]

Store raw data in DB instead of GCS

GeoTiffs can be very large (from few MB up to TB size) so GCS seem to be more appropriate place to store them. Additionally GCS supports HTTP RANGE requests header that allows to read only the part of large file instead of processing it as a whole.

Cloud optimized geotiffs are optimized to being read through HTTP. Storing them in regular database is possible but it would require to re-create some structures that already exist in those files in order to optimize reads. It might be a valid alternative in the future during the fine tuning phase, however atm the costs of implementing whole framework around that does not justifies the value gained.

Use custom file map algorithm

The MosaicJSON is not perfect, since usage of it generates 3 hits to GCS for every fresh tile.

  1. Parse MosaicJSON from the GCS and find out the URIs for raw data
  2. Read geotiff header in order to find byte offset for requested tile raw data
  3. Read geotiff with byte offset.

This process can be improved by moving a lookup data that MosaicJSON contains into Redis and adding geotiff headers to lookup. This modification would reduce the reading process for fresh data just to one GCS hit.
However, it would require to write a custom lib and due to the current project timelines we decided to pick ready-to use solution for now.

Event driven cache invalidation

The frequency of changes for USGS data is low and creating event driven cache invalidation might in fact almost never be triggered. Although this is overkill for current state of the service, this is probable path for future development of the app.

Links

  1. UTM Zone https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system
  2. OGC Tile Matrix Set Specification https://docs.ogc.org/is/17-083r4/17-083r4.html
  3. Slippy Tile https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
  4. WebMercatorQuad TMS https://schemas.opengis.net/tms/2.0/json/examples/tilematrixset/WebMercatorQuad.json
  5. WGS1984Quad TMS https://schemas.opengis.net/tms/2.0/json/examples/tilematrixset/WGS1984Quad.json
  6. MosaicJSON Spec https://github.com/developmentseed/mosaicjson-spec
  7. Zoom Levels https://wiki.openstreetmap.org/wiki/Zoom_levels
  8. Tile / QuadKey vizualizer https://labs.mapbox.com/what-the-tile/
  9. Mapbox RGB spec https://docs.mapbox.com/data/tilesets/reference/mapbox-terrain-dem-v1/#elevation-data
  10. Redis Failover Docs https://cloud.google.com/memorystore/docs/redis/high-availability-for-memorystore-for-redis
  11. ReadFromGCS ADR https://github.com/droneup/utm-atlas-architecture/tree/trunk/ADRs/ELEVATION_API/ATLAS-ELEVATION-3-ReadFromGCS
Last updated on