Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Copernicus DEM

This notebook illustrates a way to encode a 3D proj transformation into a GDAL VRT so that you can load elevation ellipsoid heights rather than geoid heights. This can happen “on-the-fly”, so there is no need to download and save local files.

We’ll use: https://portal.opentopography.org/raster?opentopoID=OTSDEM.032021.4326.3

Horizontal Coordinates: WGS84 [EPSG: 4326]
Vertical Coordinates: EGM2008 [EPSG: 3855]

In GDAL we can specify a full 3D CRS as a compound CRS EPSG:9055+3855


PROJ.4 : +proj=longlat +datum=WGS84 +no_defs

OGC WKT2:2019 :
GEOGCRS["WGS 84",
    ENSEMBLE["World Geodetic System 1984 ensemble",
        MEMBER["World Geodetic System 1984 (Transit)"],
        MEMBER["World Geodetic System 1984 (G730)"],
        MEMBER["World Geodetic System 1984 (G873)"],
        MEMBER["World Geodetic System 1984 (G1150)"],
        MEMBER["World Geodetic System 1984 (G1674)"],
        MEMBER["World Geodetic System 1984 (G1762)"],
        MEMBER["World Geodetic System 1984 (G2139)"],
        MEMBER["World Geodetic System 1984 (G2296)"],
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ENSEMBLEACCURACY[2.0]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    USAGE[
        SCOPE["Horizontal component of 3D system."],
        AREA["World."],
        BBOX[-90,-180,90,180]],
    ID["EPSG",4326]]

PROJ.4 string:
Cannot open https://cdn.proj.org/Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz: HTTP error 404: <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz</Key><RequestId>G5EQJJJ1G3SNACPW</RequestId><HostId>3mKkT5qXDZdI3B0lNvB9btUiJPPU200jcVJOcrk5Vxsf4iXklsUdHMyJwrQ3l1g4UswitgLAgvU=</HostId></Error>
Cannot open https://cdn.proj.org/Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz: HTTP error 404: <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message><Key>Und_min1x1_egm2008_isw=82_WGS84_TideFree.gz</Key><RequestId>G5EQJJJ1G3SNACPW</RequestId><HostId>3mKkT5qXDZdI3B0lNvB9btUiJPPU200jcVJOcrk5Vxsf4iXklsUdHMyJwrQ3l1g4UswitgLAgvU=</HostId></Error>
+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +geoidgrids=us_nga_egm08_25.tif +geoid_crs=WGS84 +vunits=m +no_defs +type=crs

WKT2:2019 string:
COMPOUNDCRS["WGS 84 (G1150) + EGM2008 height",
    GEOGCRS["WGS 84 (G1150)",
        DYNAMIC[
            FRAMEEPOCH[2001]],
        DATUM["World Geodetic System 1984 (G1150)",
            ELLIPSOID["WGS 84",6378137,298.257223563,
                LENGTHUNIT["metre",1]]],
        PRIMEM["Greenwich",0,
            ANGLEUNIT["degree",0.0174532925199433]],
        CS[ellipsoidal,2],
            AXIS["geodetic latitude (Lat)",north,
                ORDER[1],
                ANGLEUNIT["degree",0.0174532925199433]],
            AXIS["geodetic longitude (Lon)",east,
                ORDER[2],
                ANGLEUNIT["degree",0.0174532925199433]],
        USAGE[
            SCOPE["Geodesy. Navigation and positioning using GPS satellite system."],
            AREA["World."],
            BBOX[-90,-180,90,180]],
        ID["EPSG",9055]],
    VERTCRS["EGM2008 height",
        VDATUM["EGM2008 geoid"],
        CS[vertical,1],
            AXIS["gravity-related height (H)",up,
                LENGTHUNIT["metre",1]],
        USAGE[
            SCOPE["Geodesy."],
            AREA["World."],
            BBOX[-90,-180,90,180]],
        ID["EPSG",3855]]]

Convert to ellipsoid height

We are interested in comparing DEM elevations to altimeter elevations made by ICESAT-2. Which uses EPSG:7912 (ITRF2014 ellipsoid heights).

The simplest transform to ellipsoid height would be to keep the same datum realization and apply a vertical shift to account for the geoid offset:

Caution: use 3D CRS

Candidate operations found: 1
-------------------------------------
Operation No. 1:

unknown id, Null geographic offset from WGS 84 (G1150) to WGS 84 (G1150), 0 m, World.

PROJ string:
+proj=noop
Candidate operations found: 1
-------------------------------------
Operation No. 1:

unknown id, Inverse of WGS 84 to EGM2008 height (1) using WGS 84 to WGS 84 (G1150), 5 m, World.

PROJ string:
+proj=pipeline
  +step +proj=axisswap +order=2,1
  +step +proj=unitconvert +xy_in=deg +xy_out=rad
  +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1
  +step +proj=unitconvert +xy_in=rad +xy_out=deg
  +step +proj=axisswap +order=2,1
+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
Downloading from https://cdn.proj.org into /Users/scotthenderson/Library/Application Support/proj
https://cdn.proj.org/us_nga_egm08_25.tif already downloaded.
Driver: GTiff/GeoTIFF
Files: /vsicurl/https://cdn.proj.org/us_nga_egm08_25.tif
       /vsicurl/https://cdn.proj.org/us_nga_egm08_25.tif.aux.xml
Size is 8640, 4321
Coordinate System is:
GEOGCRS["WGS 84",
    ENSEMBLE["World Geodetic System 1984 ensemble",
        MEMBER["World Geodetic System 1984 (Transit)"],
        MEMBER["World Geodetic System 1984 (G730)"],
        MEMBER["World Geodetic System 1984 (G873)"],
        MEMBER["World Geodetic System 1984 (G1150)"],
        MEMBER["World Geodetic System 1984 (G1674)"],
        MEMBER["World Geodetic System 1984 (G1762)"],
        MEMBER["World Geodetic System 1984 (G2139)"],
        MEMBER["World Geodetic System 1984 (G2296)"],
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]],
        ENSEMBLEACCURACY[2.0]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,3],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["ellipsoidal height (h)",up,
            ORDER[3],
            LENGTHUNIT["metre",1]],
    USAGE[
        SCOPE["Geodesy. Navigation and positioning using GPS satellite system."],
        AREA["World."],
        BBOX[-90,-180,90,180]],
    ID["EPSG",4979]]
Data axis to CRS axis mapping: 2,1,3
Origin = (-180.020833333333343,90.020833333333329)
Pixel Size = (0.041666666666667,-0.041666666666667)
Metadata:
  TIFFTAG_IMAGEDESCRIPTION=WGS 84 (EPSG:4979) to EGM2008 height (EPSG:3855). Converted from egm08_25.gtx (last modified at 2018/10/08)
  TIFFTAG_DATETIME=2019:12:27 00:00:00
  TIFFTAG_COPYRIGHT=Derived from work by NGA. Public Domain
  area_of_use=World
  target_crs_epsg_code=3855
  TYPE=VERTICAL_OFFSET_GEOGRAPHIC_TO_VERTICAL
  AREA_OR_POINT=Point
Image Structure Metadata:
  COMPRESSION=DEFLATE
  INTERLEAVE=BAND
  PREDICTOR=3
Corner Coordinates:
Upper Left  (-180.0208333,  90.0208333) (180d 1'15.00"W, 90d 1'15.00"N)
Lower Left  (-180.0208333, -90.0208333) (180d 1'15.00"W, 90d 1'15.00"S)
Upper Right ( 179.9791667,  90.0208333) (179d58'45.00"E, 90d 1'15.00"N)
Lower Right ( 179.9791667, -90.0208333) (179d58'45.00"E, 90d 1'15.00"S)
Center      (  -0.0208333,   0.0000000) (  0d 1'15.00"W,  0d 0' 0.00"N)
Band 1 Block=256x256 Type=Float32, ColorInterp=Gray
  Description = geoid_undulation
  Min=-50.018 Max=85.824 
  Unit Type: metre
  Metadata:
    STATISTICS_APPROXIMATE=YES
    STATISTICS_MAXIMUM=85.823783874512
    STATISTICS_MEAN=1.0811106698347
    STATISTICS_MINIMUM=-50.018199920654
    STATISTICS_STDDEV=26.622038806097
    STATISTICS_VALID_PERCENT=100

Helmert transform to more recent WGS/ITRF Realization

The helmert transform accounts for updates to the datum realization and plate motion over time (so t_epoch is specified). This provides a matrix-transform to go from one WGS84/ITRF realization to another. These transforms are applied to ECEF coordinates. You can get parameters via online calculators (https://itrf.ign.fr/en/solutions/transformations) or look them up in the PROJ database:

Candidate operations found: 1
-------------------------------------
Operation No. 1:

unknown id, Conversion from WGS 84 (G1150) (geog3D) to WGS 84 (G1150) (geocentric) + WGS 84 (G1150) to WGS 84 (G1674) (1) + Conversion from WGS 84 (G1674) (geocentric) to WGS 84 (G1674) (geog3D), 0.02 m, World

PROJ string:
+proj=pipeline
  +step +proj=axisswap +order=2,1
  +step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m
  +step +proj=cart +ellps=WGS84
  +step +proj=helmert +x=-0.0024 +y=0.0016 +z=0.0232 +rx=-0.00027 +ry=0.00027
        +rz=-0.00038 +s=0.00208 +dx=-0.0001 +dy=-0.0001 +dz=0.0018 +drx=0 +dry=0
        +drz=0 +ds=-8e-05 +t_epoch=2005 +convention=coordinate_frame
  +step +inv +proj=cart +ellps=WGS84
  +step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m
  +step +proj=axisswap +order=2,1
+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m +step +proj=cart +ellps=WGS84 +step +proj=helmert +x=-0.0007 +y=-0.0012 +z=0.0261 +rx=0 +ry=0 +rz=0 +s=-0.00212 +dx=-0.0001 +dy=-0.0001 +dz=0.0019 +drx=0 +dry=0 +drz=0 +ds=-0.00011 +t_epoch=2010 +convention=position_vector +step +inv +proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m +step +proj=axisswap +order=2,1
    +proj=pipeline
      +step +proj=axisswap +order=2,1
      +step +proj=unitconvert +xy_in=deg +xy_out=rad
      +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1
      +step +proj=cart +ellps=WGS84
      +step +proj=helmert +x=-0.0024 +y=0.0016 +z=0.0232 +rx=-0.00027 +ry=0.00027
            +rz=-0.00038 +s=0.00208 +dx=-0.0001 +dy=-0.0001 +dz=0.0018 +drx=0 +dry=0
            +drz=0 +ds=-8e-05 +t_epoch=2005 +convention=coordinate_frame
      +step +inv +proj=cart +ellps=WGS84
      +step +proj=unitconvert +xy_in=rad +xy_out=deg
      +step +proj=axisswap +order=2,1
+proj=pipeline
  +step +proj=axisswap +order=2,1
  +step +proj=unitconvert +xy_in=deg +xy_out=rad
  +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1
  +step +proj=cart +ellps=WGS84
  +step +proj=helmert +x=-0.0007 +y=-0.0012 +z=0.0261 +rx=0 +ry=0 +rz=0
        +s=-0.00212 +dx=-0.0001 +dy=-0.0001 +dz=0.0019 +drx=0 +dry=0 +drz=0
        +ds=-0.00011 +t_epoch=2010 +convention=position_vector
  +step +inv +proj=cart +ellps=GRS80
  +step +proj=unitconvert +xy_in=rad +xy_out=deg
  +step +proj=axisswap +order=2,1

Encoding transform in a GDAL VRT

Driver: VRT/Virtual Raster
Size is 1296001, 626401
Coordinate System is:
GEOGCRS["WGS 84",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    ID["EPSG",4326]]
Data axis to CRS axis mapping: 2,1
Origin = (-180.000138899999996,84.000138899999996)
Pixel Size = (0.000277777777778,-0.000277777777778)
Metadata:
  AREA_OR_POINT=Point
Corner Coordinates:
Upper Left  (-180.0001389,  84.0001389) (180d 0' 0.50"W, 84d 0' 0.50"N)
Lower Left  (-180.0001389, -90.0001389) (180d 0' 0.50"W, 90d 0' 0.50"S)
Upper Right ( 180.0001389,  84.0001389) (180d 0' 0.50"E, 84d 0' 0.50"N)
Lower Right ( 180.0001389, -90.0001389) (180d 0' 0.50"E, 90d 0' 0.50"S)
Center      (  -0.0000000,  -3.0000000) (  0d 0' 0.00"W,  3d 0' 0.00"S)
Band 1 Block=128x128 Type=Float32, ColorInterp=Gray
Driver: VRT/Virtual Raster
Size is 1296001, 626399
Coordinate System is:
GEOGCRS["WGS 84",
    DATUM["World Geodetic System 1984",
        ELLIPSOID["WGS 84",6378137,298.257223563,
            LENGTHUNIT["metre",1]]],
    PRIMEM["Greenwich",0,
        ANGLEUNIT["degree",0.0174532925199433]],
    CS[ellipsoidal,2],
        AXIS["geodetic latitude (Lat)",north,
            ORDER[1],
            ANGLEUNIT["degree",0.0174532925199433]],
        AXIS["geodetic longitude (Lon)",east,
            ORDER[2],
            ANGLEUNIT["degree",0.0174532925199433]],
    ID["EPSG",4326]]
Data axis to CRS axis mapping: 2,1
Origin = (-180.000138899999996,84.000694455555546)
Pixel Size = (0.000277777777778,-0.000277777777778)
Metadata:
  AREA_OR_POINT=Point
Corner Coordinates:
Upper Left  (-180.0001389,  84.0006945) (180d 0' 0.50"W, 84d 0' 2.50"N)
Lower Left  (-180.0001389, -89.9990278) (180d 0' 0.50"W, 89d59'56.50"S)
Upper Right ( 180.0001389,  84.0006945) (180d 0' 0.50"E, 84d 0' 2.50"N)
Lower Right ( 180.0001389, -89.9990278) (180d 0' 0.50"E, 89d59'56.50"S)
Center      (  -0.0000000,  -2.9991667) (  0d 0' 0.00"W,  2d59'57.00"S)
Band 1 Block=128x128 Type=Float32, ColorInterp=Gray
+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
Creating output file that is 1296001P x 626401L.
Processing /vsicurl/https://opentopography.s3.sdsc.edu/raster/COP30/COP30_hh.vrt [1/1] : 0...10...20...30...40...50...60...70...80...90...100 - done.
+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1 +step +proj=cart +ellps=WGS84 +step +proj=helmert +x=-0.0007 +y=-0.0012 +z=0.0261 +rx=0 +ry=0 +rz=0 +s=-0.00212 +dx=-0.0001 +dy=-0.0001 +dz=0.0019 +drx=0 +dry=0 +drz=0 +ds=-0.00011 +t_epoch=2010 +convention=position_vector +step +inv +proj=cart +ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1
Creating output file that is 1296001P x 626401L.
Processing /vsicurl/https://opentopography.s3.sdsc.edu/raster/COP30/COP30_hh.vrt [1/1] : 0...10...20...30...40...50...60...70...80...90...100 - done.

Validation

  1. Query points & compare to transforming single points via PROJ

  2. Transform a single tiff from within the VRT and compare to on-the-fly transform of same subset region

Report:
  Location: (264600.50004P,163800.50004L)
  Band 1:
    <LocationInfo><File>/vsicurl/https://opentopography.s3.sdsc.edu/raster/COP30/COP30_hh/Copernicus_DSM_10_N38_00_W107_00_DEM.tif</File></LocationInfo>
    Value: 2758.54790051775
Report:
  Location: (1800.5P,1800.5L)
  Band 1:
    Value: 2758.5478515625
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
ERROR 1: PROJ: vgridshift: Invalid latitude
Report:
  Location: (264600.499999788P,163800.499999869L)
  Band 1:
    Value: 2744.04248051403
      38.50000      -106.50000    2744.04248           inf