Short answer

A vertical CRS defines what "zero elevation" means and which surface a height is measured from. The two heights people confuse are ellipsoidal height (h) — measured from the reference ellipsoid, and what raw GNSS outputs — and orthometric height (H) — measured from the geoid (close to mean sea level), and what maps, surveys, hydrology, and engineering use. They differ by the geoid undulation N, where H = h − N, and N ranges from about −106 m to +85 m worldwide. Getting elevation right means knowing which height your data carries, attaching the correct vertical datum, and converting with a geoid model rather than treating one as the other.

The two surfaces, and why they differ

The Earth's shape is approximated by two surfaces:

  • The ellipsoid (e.g. WGS84, GRS80) is a smooth mathematical figure. It is simple and is what satellite positioning naturally references.
  • The geoid is an equipotential surface of Earth's gravity field — the shape the oceans would take with no tides or currents, extended under the continents. It undulates by tens of metres because mass is unevenly distributed (mountains, dense rock, ocean trenches).

The vertical gap between them at any point is the geoid undulation N. Because water flows along the geoid, not the ellipsoid, orthometric height is the physically meaningful one for anything involving gravity: drainage, flooding, slope stability, contour lines, levelling.

So three numbers describe a point's elevation context: ellipsoidal height h, orthometric height H, and undulation N, linked by H = h − N.

Vertical datums you will actually meet

  • EGM2008 / EGM96 — global geoid models published by NGA. EGM2008 (~9 km resolution) is the common global default; the EPSG code for EGM2008 height is EPSG:3855.
  • NAVD88 (North American Vertical Datum of 1988, EPSG:5703) — the orthometric datum for the US, realised via the GEOID18 hybrid model that relates NAVD88 to NAD83 ellipsoidal heights.
  • EVRF / national datums — Europe and most countries maintain their own (e.g. EVRF2019, Ordnance Datum Newlyn, NAP). Each ties to a tide gauge or levelling network and is not interchangeable with the others.
  • MSL / Chart Datum / LAT — tidal datums for hydrography; bathymetry is often referenced to Lowest Astronomical Tide or chart datum, which is a different zero again from land orthometric height.

A height value with no stated vertical datum is ambiguous to tens of metres. "450 m" is not a measurement until you know 450 m above what.

Compound (3D) CRS

A full 3D position needs a horizontal CRS and a vertical CRS bound together — a compound CRS. EPSG provides ready compounds, e.g. WGS84 + EGM2008, or you compose them with the + syntax. In PROJ/WKT, a compound CRS names both components so software knows the height refers to the geoid, not the ellipsoid. Many GIS bugs come from a raster that has a horizontal CRS tag but no vertical CRS — the software then has no idea whether the Z values are ellipsoidal or orthometric and silently assumes.

Converting vertical datums with PROJ and GDAL

Modern PROJ (6+) handles vertical transformations using geoid grids, downloaded on demand if the network is enabled. To turn ellipsoidal GNSS heights into EGM2008 orthometric heights:

# enable PROJ to fetch the geoid grid
export PROJ_NETWORK=ON

# point cloud / XYZ: WGS84 ellipsoidal -> WGS84 + EGM2008 orthometric
echo "12.5 41.9 120.0" | cs2cs EPSG:4979 EPSG:4326+3855

EPSG:4979 is WGS84 3D (ellipsoidal height); EPSG:4326+3855 is WGS84 horizontal compounded with EGM2008 height. PROJ applies the geoid grid and adjusts the Z.

For a DEM, gdalwarp can transform both horizontal and vertical components when source and target are compound CRS:

PROJ_NETWORK=ON gdalwarp \
  -s_srs EPSG:4326+4979 \
  -t_srs "EPSG:32633+5703" \
  dem_ellipsoidal.tif dem_navd88_utm.tif

The crucial part is that both CRS carry a vertical component; if you only set horizontal CRS, GDAL reprojects X/Y and leaves Z untouched — a classic source of elevations that are tens of metres off. Inspect what a file actually declares with gdalsrsinfo dem.tif and gdalinfo dem.tif (look for the vertical CRS in the WKT).

In PostGIS, geometry SRIDs are essentially horizontal; vertical-datum transformation of Z is not done by ST_Transform in the same way, so do height conversions in PROJ/GDAL and store the resulting orthometric Z, recording the datum in a metadata column.

Worked example: GNSS survey vs the DEM

A field team logs RTK GNSS points at 120.0 m ellipsoidal height near 41.9° N, 12.5° E. The national 1:10,000 map shows ~73 m at that spot. Panic — until you check the vertical datums. The map is orthometric; the GNSS is ellipsoidal. The EGM2008 undulation there is roughly +47 m, so:

H = h − N = 120.0 − 47.0 = 73.0 m

The data agrees perfectly; the "error" was a missing geoid correction. The fix is to record GNSS heights with their datum and convert to the map's orthometric datum before comparison — never subtract a raw ellipsoidal height from a map elevation.

Why this matters for terrain work

  • Slope and contours are about gravity-relative height, so they must come from orthometric DEMs; using ellipsoidal Z biases values where N varies across the area.
  • Flood and inundation models reference water levels to a tidal or orthometric datum; a 47 m datum offset turns a flood map into fiction.
  • Merging DEMs from different sources frequently fails vertically because one is ellipsoidal (e.g. some lidar deliverables) and another orthometric. They look co-registered horizontally but step by tens of metres at the seam.
  • Cut/fill and volume calculations are differences of heights, so as long as both surfaces share the same vertical datum the result is fine — but mixing datums injects a constant (and spatially varying) error.

Common pitfalls and why they happen

  • Treating GNSS height as elevation. Raw GNSS is ellipsoidal; people assume it is "above sea level." Always apply a geoid model.
  • Missing vertical CRS on a DEM. Software assumes, you inherit a silent offset. Tag the vertical datum explicitly.
  • Reprojecting horizontally only. gdalwarp with horizontal-only CRS leaves Z in the old datum. Use compound CRS on both sides.
  • Mixing global and national geoids. EGM2008 and a national hybrid geoid (e.g. GEOID18) differ; converting with the wrong model leaves a residual. Use the datum the data was defined against.
  • Bathymetry vs land datum. Chart datum / LAT is not orthometric land zero; joining nearshore bathymetry to land DEMs needs an explicit datum bridge.

Validation checks

  • Confirm both the horizontal and vertical CRS with gdalsrsinfo / gdalinfo before any analysis; a height without a vertical datum is not usable.
  • Check a known benchmark with a published orthometric height against your converted data; agreement to a few centimetres (survey) or a metre (DEM) confirms the chain.
  • Where DEMs meet, profile across the seam; a constant vertical step signals a datum mismatch, not noise.
  • Record h, H, N, the geoid model used, and the source datum in project notes so the conversion is reproducible and auditable.

Bathyl perspective

We treat vertical datum as seriously as horizontal CRS, because a 47 m geoid offset will quietly corrupt a flood model or a merged terrain surface long after the map "looks right." Every elevation deliverable states its vertical datum and the geoid model used to get there.

Related reading

Sources