# Coordinate epoch support

Added in version 3.4.

## Dynamic CRS and coordinate epoch

This document is intended to document the support for coordinate epoch, linked to dynamic CRS.

In a dynamic CRS, coordinates of a point on the surface of the Earth may change with time. To be unambiguous the coordinates must always be qualified with the epoch at which they are valid. The coordinate epoch is not necessarily the epoch at which the observation was collected.

Examples of dynamic CRS are `WGS 84 (G1762)`, `ITRF2014`, `ATRF2014`.

The generic EPSG:4326 WGS 84 CRS is also considered dynamic, although it is not recommended to use it due to being based on a datum ensemble whose positional accuracy is 2 meters, but prefer one of its realizations, such as WGS 84 (G1762)

The `OGRSpatialReference::IsDynamic()` method can be used to test if a CRS is a dynamic one.

The `OGRSpatialReference::SetCoordinateEpoch()` and `OGRSpatialReference::GetCoordinateEpoch()` methods can be used to set/retrieve a coordinate epoch associated with a CRS. The coordinate epoch is expressed as a decimal year (e.g. 2021.3 for April 21, 2021).

Formally, the coordinate epoch of an observation belongs to the observation. However, almost all formats do not allow for storing per-observation epoch, and typical usage is a set of observations with the same epoch. Therefore we store the epoch as property of the CRS, and assume that it is valid for every observation. This choice eases processing, storage and format complexity for most usage. For now, this means that a dataset containing observations or points with different epochs cannot be handled.

For vector formats, per-geometry coordinate epoch could also make sense, but as most formats only support a per-layer CRS, we also for now limit support of coordinate epoch at the layer level. The underlying coordinate transformation mechanics can support per-vertex coordinate epoch.

## Support in raster and vector formats

At time of writing, no formats handled by GDAL/OGR have a standardized way of encoding a coordinate epoch. We consequently have made choices how to encode it, with the aim of being as much as possible backward compatible with existing readers. Those encodings might change if corresponding official specifications evolve to take this concept into account. The coordinate epoch is only written when attached to the SRS of the layer/dataset that is created.

### FlatGeoBuf

The coordinate epoch is encoded as a WKT:2019 string using the `EPOCH` subnode of the COORDINATEMETADATA construct, set in the `Crs.wkt` header field of the FlatGeoBuf file.

    COORDINATEMETADATA[
        GEOGCRS["WGS 84 (G1762)",
            DYNAMIC[FRAMEEPOCH[2005.0]],
            DATUM["World Geodetic System 1984 (G1762)",
              ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1.0]]
            ],
            CS[ellipsoidal,3],
              AXIS["(lat)",north,ANGLEUNIT["degree",0.0174532925199433]],
              AXIS["(lon)",east,ANGLEUNIT["degree",0.0174532925199433]],
              AXIS["ellipsoidal height (h)",up,LENGTHUNIT["metre",1.0]]
        ],
        EPOCH[2016.47]
    ]

Note

Such construct will not be understood by GDAL < 3.4, but if the CRS has an associated EPSG code, this will not cause issues in those older GDAL versions.

### GeoPackage vector/raster

Each vector/raster table which has an associated coordinate epoch encodes it in the `epoch` column of the `gpkg_spatial_ref_sys` table, using an extended version of the CRS WKT extension (https://github.com/opengeospatial/geopackage/pull/600).

### GeoTIFF

The coordinate epoch is encoded as a new GeoTIFF GeoKey, `CoordinateEpochGeoKey` of code 5120 and type DOUBLE.

    Geotiff_Information:
       Version: 1
       Key_Revision: 1.0
       Tagged_Information:
          ModelTiepointTag (2,3):
             0                 0                 0
             440720            3751320           0
          ModelPixelScaleTag (1,3):
             60                60                0
          End_Of_Tags.
       Keyed_Information:
          GTModelTypeGeoKey (Short,1): ModelTypeProjected
          GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
          GTCitationGeoKey (Ascii,22): "WGS 84 / UTM zone 11N"
          GeogCitationGeoKey (Ascii,7): "WGS 84"
          GeogAngularUnitsGeoKey (Short,1): Angular_Degree
          ProjectedCSTypeGeoKey (Short,1): PCS_WGS84_UTM_zone_11N
          ProjLinearUnitsGeoKey (Short,1): Linear_Meter
          CoordinateEpochGeoKey (Double,1): 2021.3
          End_Of_Keys.
       End_Of_Geotiff.

### JPEG2000

GeoJP2 boxes use the above mentioned GeoTIFF encoding.

### Persistent Auxiliary Metadata (.aux.xml)

The coordinate epoch is encoded as `coordinateEpoch` attribute of the `SRS` element.

    <PAMDataset>
      <SRS dataAxisToSRSAxisMapping="1,2" coordinateEpoch="2021.3">PROJCS["WGS 84 / UTM zone 11N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32611"]]</SRS>
      <!-- snip -->
    </PAMDataset>

### GDAL VRT

The coordinate epoch is encoded as `coordinateEpoch` attribute of the `SRS` element.

    <VRTDataset rasterXSize="20" rasterYSize="20">
      <SRS dataAxisToSRSAxisMapping="1,2" coordinateEpoch="2021.3">PROJCS["WGS 84 / UTM zone 11N",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-117],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","32611"]]</SRS>
      <!-- snip -->
    </VRTDataset>

## Support in utilities

gdalinfo and ogrinfo report the coordinate epoch, when attached to a dataset/layer SRS.

gdal_edit.py has a `-a_coord_epoch` option to define the epoch of a dataset in place.

gdal_translate and ogr2ogr have a `-a_coord_epoch` option to be used together with `-a_srs`, and otherwise preserve the coordinate epoch in the output SRS from the source SRS when no SRS related options are specified.

gdalwarp and ogr2ogr have a `-s_coord_epoch` option to be used together with `-s_srs` (resp. `-t_coord_epoch` option to be used together with `-t_srs`) to override/set the coordinate epoch of the source (resp. target) CRS.

Before PROJ 9.4, `-s_coord_epoch` and `-t_coord_epoch` were mutually exclusive, due to lack of support for transformations between two dynamic CRS.

gdalwarp preserves the coordinate epoch in the output SRS when appropriate.

## Support in coordinate transformation

The `OGRCoordinateTransformation` class can perform time-dependent transformations between a static and dynamic CRS based on the coordinate epoch passed per vertex.

It can also take into account the coordinate epoch associated with a dynamic CRS, when doing time-dependent transformations between a static and dynamic CRS. The `OGR_CT_USE_SRS_COORDINATE_EPOCH` configuration option can be set to `NO` to disable using the coordinate epoch associated with the source or target CRS.

If a per-vertex time is specified, it overrides the one associated with the CRS.