← back to the explorer

REMA explorer: guide and design

The Reference Elevation Model of Antarctica, read straight from cloud storage by the browser, at any scale, from one DSN.

Guide

Drag to pan, scroll or pinch to zoom, double-click to zoom in (Shift + double-click zooms out). + - and the arrow keys work too. Home shows the whole continent; Link copies a URL that restores the view, styling, level and comparison.

The top-right readout shows the cursor position in EPSG:3031 and longitude/latitude, the elevation from the tile on screen and which level it came from, and the polar stereographic scale factor k. The status bar (bottom right) shows the level(s) on screen and cumulative requests and bytes. The scale bar is corrected for k at the view centre, so it is a ground distance, not a grid distance.

Heights are metres above the WGS84 ellipsoid, as REMA publishes them. Sea level around Antarctica sits tens of metres below the ellipsoid in places, which is why the ocean in the filled mosaics is not zero.

Display

Render as colour, hillshade, colour + hillshade, slope or aspect. Auto range (on by default) stretches the palette to the 2-98% range of what is on screen every time the view settles; typing a min or max turns it off. Equalise uses the histogram of the current view. Hillshade exaggeration grows with pixel size so a 1 km view still shows relief; the slider multiplies that. Contours draw at a nice interval for the current range, or at the interval you type.

Tools

ToolHowWhat you get
Info IClickCoordinates, scale factor, grid convergence; elevation at every level at that point with the file it came from; for the PGC levels, the sibling count, mad, mindate, maxdate values and the list of strip DEMs in that tile's meta.txt. Buttons to zoom to 2 m there, copy coordinates, or produce a recipe for a 1 km box at 2 m.
Profile PClick vertices, double-click or Enter to finishElevation profile sampled at roughly one screen pixel, from the level that matches that spacing; ground length, climb and drop, cost; hover the chart to see the point on the map; CSV download. With the default DSN the bed from Bedmap2 (1 km) is drawn under the surface, with ice thickness, and Cutaway in Globe opens the 3D cutaway.
Measure MClick vertices, finish as aboveGround and grid length, and area when there are three or more points.
Box BDrag a rectangleReads the box at up to 1024 px across from the matching level: stats and histogram, a Float32 GeoTIFF download (EPSG:3031, nodata -9999), and the same box as a GDAL CLI, Python and R recipe against the DSN.

Esc cancels a line or box in progress.

Levels and pinning

The pyramid list in the side panel shows the six levels, where each lives, and whether this page can reach it (green dot). The levels in use for the current view are highlighted. In Auto, each screen tile reads the coarsest level whose pixel is no larger than 1.5 screen pixels, and inside that file the matching internal overview.

Pin fixes one level so it no longer switches with zoom: click a row in the pyramid, choose it in the Level select, or press Pin level on the map toolbar to pin whatever is on screen now. A pinned level is read at its own native pixel size at every zoom and drawn with nearest neighbour, so zooming in shows its real pixels and zooming out shows it decimated. Pinning a fine level and zooming far out would need too many tiles; the page says so rather than fetching them.

Tile boundaries

BoundaryShows
Source filesThe footprint of every file of the level, labelled with the tile name when zoomed in.
COG internal blocksThe 512 px blocks of the image actually read inside each file (full resolution or an internal overview), which is the unit of a range request. Hover to see the overview index and block row/column.
Display tilesThe explorer's own 256 px EPSG:3031 tile grid, with the level, number of files and read time of each tile.

Follow LOD redraws boundaries for whatever level and overview the current zoom is reading, so they change as you zoom. Fixed keeps one level's boundaries on screen at every zoom, which is the way to see, for example, how the 50 km 2 m tiles sit inside the 100 km 10 m tiles, or how a single 512 px block of the 1 km file spans 512 km.

Compare

Pick a second layer and Swipe (drag the handle) or Overlay (opacity) it against the main view:

Globe

Globe drapes the current view, styled as it is on the map, on the WGS84 ellipsoid with heights along the ellipsoid normal. Because REMA heights are ellipsoidal, exaggeration 1 is geometrically exact; the slider goes from 1x to about 300x. Drag to orbit, right-drag to pan, scroll to zoom. Rebuild rereads from the map view, Continent builds the whole ice sheet, Pick view takes you back to the map to frame something else. Cutaway: after drawing a Profile, the Globe cuts the terrain along the line on the side facing the camera and puts up a wall: ice from the REMA surface down to the Bedmap2 bed, rock below. It opens at an exaggeration where the ice column is about a third of the profile length; try the line from Little Dome C (Beyond EPICA) to Dome C North (AAD Million Year Ice Core), both in the place list. Bedmap2 heights are relative to the GL04c geoid while REMA's are ellipsoidal, so thickness carries an offset of tens of metres for now. The texture is read at up to 3072 px across from whichever level matches, so a globe of one glacier comes from the fine levels and a globe of the continent from the 1 km file; the info line reports which level, how many files and what it cost.

Design

The page is a demonstration of one idea: a small descriptive file (a recipe) can make a dataset of thousands of files behave like one dataset at every scale, without copying or reprocessing the fine data. GDAL already does this. The explorer does the same thing in the browser, with no server, and keeps a ledger of what it costs.

The official streamable COGs

The Polar Geospatial Center publishes REMA v2 mosaics as Cloud Optimized GeoTIFFs in the pgc-opendata-dems bucket, organised as tiles:

ProductFilesEach file
2 m mosaic5800 COGs25100 x 25100 px, about 50 km, plus sibling browse, count, mad, mindate, maxdate, datamask and a meta.txt
10 m mosaic1523 COGsup to 10020 x 10020 px, 100 km
32 m mosaic1523 COGsup to 3131 x 3131 px, 100 km
100 m, 500 m, 1 kmwhole continentdistributed in tarballs, slow to reach

Each COG streams well within itself: it has internal overviews, so reading one 50 km tile at low resolution is cheap. But the internal overviews stop at the tile edge. A view of a 500 km glacier basin at 2 m-mosaic resolution touches hundreds of files, and a continent view touches all 5800, each needing at least a header read. There is no single file you can point a reader at and ask for "Antarctica at 1 km". This is the gap.

The overview VRT in this repo

REMA-2m_dem_ovr.vrt is a GDAL Virtual Raster that mosaics all 5800 2 m COGs in place (by URL) and declares five <Overview> elements. When a read asks for fewer pixels than the 2 m grid, GDAL uses the overview whose resolution fits, exactly as it would use the internal overviews of a single file:

REMA-2m_dem_ovr.vrt one DSN, 3 MB of XML 1 km COG (+4 internal ovr) 500 m COG (+5 internal ovr) 100 m COG (Int16, sparse) 32 m VRT → 1523 COGs 10 m VRT → 1523 COGs 2 m sources → 5800 COGs this repo (GitHub LFS for now) copied from PGC tarballs, or converted PGC bucket, untouched referenced by URL, never copied

The payload in the repo is only the coarse end of the pyramid: the 500 m and 1 km mosaics copied out of the PGC tarballs, and a 100 m version converted to Int16 with sparse tiles and no internal overviews so it fits on GitHub. Everything from 32 m down stays where PGC publishes it. The 10 m and 32 m VRTs are PGC's own.

gdalwarp -te 2301786 490292 2302786 491292 -tr 2 2 \
  /vsicurl/https://raw.githubusercontent.com/mdsumner/rema-ovr/main/REMA-2m_dem_ovr.vrt davis.tif
gdalwarp -tr 1000 1000 \
  /vsicurl/https://raw.githubusercontent.com/mdsumner/rema-ovr/main/REMA-2m_dem_ovr.vrt rema_1km.tif

Both commands read a bounded number of bytes, because the warper picks the level that matches the requested resolution.

What the browser does

  1. Reads the DSN. It fetches the VRT, parses its sources and <Overview> list, follows the overview VRTs and opens the headers of the overview COGs. /vsicurl/ and /vsis3/ paths are turned into HTTPS URLs; github.com/.../raw/... becomes raw.githubusercontent.com (XML) or media.githubusercontent.com (LFS TIFFs), which allow cross-origin reads.
  2. Picks a level per screen tile. The display grid is EPSG:3031 with 256 px tiles at 8192 / 2z m (z12 = 2 m), the same origin as GIBS polar tiles. For a tile at resolution R it takes the coarsest available level whose pixel is no larger than 1.5 R.
  3. Finds the files. For VRT levels, a bucket index over the DstRect footprints gives the files that intersect the tile; there is a cap of 12 files per tile, which Auto should not reach.
  4. Picks an image inside each file by the same rule (full resolution or internal overview), computes the pixel window, and asks geotiff.js for just those blocks. geotiff.js issues single-range HTTP requests, which browsers allow cross-origin without a preflight.
  5. Samples into the tile with nearest neighbour, or bilinear when upsampling, using each file's own georeferencing, and renders colour and hillshade on the CPU from a 1 px gutter.

Nothing is precomputed for the page: no tile cache, no server, no index other than the VRT itself. It is the same recipe GDAL reads.

Findings worth knowing

A continent costs megabytes, not gigabytes

A whole-continent view reads a few MB from the coarse internal overviews of the 1 km file; a 60 km coastal view reads about 1-2 MB of the 100 m file; a view at 2 m reads a few blocks of one or two 2 m COGs. The ledger in the side panel records requests, bytes and time for every settled view. The coarse overviews are Float32 with LZW compression, so they are larger than they need to be; an Int16 or better-compressed pyramid would make the cheap end cheaper.

GDAL ignores an overview's own georeferencing

GDAL treats an overview as covering exactly the extent of the base raster, scaled. The files here do not all share that extent:

LevelOrigin (x, y)SizeEffect in GDAL
2 m (base)-2700100, 33421002725100 x 2921100reference
32 m VRT-2700096, 3342080170318 x 182568up to about 20 m shift (0.6 px)
100 m-2700100, 334210054502 x 58422exact
500 m-2700500, 334250010902 x 11686up to about 400 m at the edges (0.8 px)
1 km-2701000, 33430005452 x 5844up to about 900 m at the edges (0.9 px)

The misregistration is sub-pixel at each level's own resolution, but it is real when you compare levels. The browser reader uses each file's own geotransform and does not have it. A fix on the GDAL side is to wrap the 500 m and 1 km files in VRTs whose extent matches the base grid.

The sibling products have no pyramid

count, mad, mindate and maxdate exist only per tile, at 2 m, 10 m and 32 m. The explorer can show them when zoomed in, and refuses at coarse zoom because a continent view would mean touching every tile. That refusal is the argument for building overviews for them too.

Smaller things

Next: bucket and index separation

GitHub is a stand-in. Git LFS counts every byte the explorer reads against the owner's bandwidth quota, and it is not meant for serving data. The design carries over unchanged to object storage: the coarse COGs move to a bucket, the VRT (the index, a few MB of text) stays wherever it is convenient to version it, and the browser and GDAL both keep reading the same recipe. The ledger is the measure of whether a change helps.

← back to the explorer · mdsumner/rema-ovr · REMA: Howat et al. 2019, Polar Geospatial Center