# Temporal Types: Spatial Operations

In the following, we specify with the symbol &Z_support; that the function supports 3D points and with the symbol &geography_support; that the function is available for geographies.

## Input and Output

- Return the Well-Known Text (WKT) representation &Z_support; &geography_support;

  `asText({tpoint,tpoint[],geo[]}) → {text,text[]}`

      SELECT asText(tgeompoint 'SRID=4326;[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02)');
      -- [POINT Z (0 0 0)@2001-01-01, POINT Z (1 1 1)@2001-01-02)
      SELECT asText(ARRAY[geometry 'Point(0 0)', 'Point(1 1)']);
      -- {"POINT(0 0)","POINT(1 1)"}

- Return the Extended Well-Known Text (EWKT) representation &Z_support; &geography_support;

  `asEWKT({tpoint,tpoint[],geo[]}) → {text,text[]}`

      SELECT asEWKT(tgeompoint 'SRID=4326;[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02)');
      -- SRID=4326;[POINT Z (0 0 0)@2001-01-01, POINT Z (1 1 1)@2001-01-02)
      SELECT asEWKT(ARRAY[geometry 'SRID=5676;Point(0 0)', 'SRID=5676;Point(1 1)']);
      -- {"SRID=5676;POINT(0 0)","SRID=5676;POINT(1 1)"}

- Return the Moving Features JSON representation &Z_support; &geography_support;

  `asMFJSON(tpoint,options=0,flags=0,maxdecdigits=15) → bytea`

  The `options` argument can be used to add BBOX and/or CRS in MFJSON output:

  - 0: means no option (default value)

  - 1: MFJSON BBOX

  - 2: MFJSON Short CRS (e.g., EPSG:4326)

  - 4: MFJSON Long CRS (e.g., urn:ogc:def:crs:EPSG::4326)

  The `flags` argument can be used to customize the JSON output, for example, to produce an easy-to-read (for human readers) JSON output. Refer to the documentation of the `json-c` library for the possible values. Typical values are as follows:

  - 0: means no option (default value)

  - 1: JSON_C_TO_STRING_SPACED

  - 2: JSON_C_TO_STRING_PRETTY

  The `maxdecdigits` argument can be used to set the maximum number of decimal places in the output of floating point values (default 15).

      SELECT asMFJSON(tgeompoint 'Point(1 2)@2019-01-01 18:00:00.15+02');
      /* {"type":"MovingPoint","coordinates":[[1,2]],"datetimes":["2019-01-01T17:00:00.15+01"],
         "interpolation":"None"} */
      SELECT asMFJSON(tgeompoint 'SRID=4326;
        Point(50.813810 4.384260)@2019-01-01 18:00:00.15+02', 3, 2);
      /* {"type":"MovingPoint","crs":{"type":"name","properties":{"name":"EPSG:4326"}},
         "stBoundedBy":{"bbox":[50.81,4.38,50.81,4.38],
         "period":{"begin":"2019-01-01 17:00:00.15+01","end":"2019-01-01 17:00:00.15+01"}},
         "coordinates":[[50.81,4.38]],"datetimes":["2019-01-01T17:00:00.15+01"],
         "interpolations":"None"} */

- Return the Well-Known Binary (WKB) representation &Z_support; &geography_support;

  `asBinary(tpoint,endian text='') → bytea`

  The result is encoded using either the little-endian (NDR) or the big-endian (XDR) encoding. If no encoding is specified, then the encoding of the machine is used.

      SELECT asBinary(tgeompoint 'Point(1 2 3)@2001-01-01');
      -- \x012e0011000000000000f03f00000000000000400000000000000840009c57d3c11c0000

- Return the Extended Well-Known Binary (EWKB) representation &Z_support; &geography_support;

  `asEWKB(tpoint,endian text='') → bytea`

  The result is encoded using either the little-endian (NDR) or the big-endian (XDR) encoding. If no encoding is specified, then the encoding of the machine is used.

      SELECT asEWKB(tgeogpoint 'SRID=7844;Point(1 2 3)@2001-01-01');
      -- \x012f0071a41e0000000000000000f03f00000000000000400000000000000840009c57d3c11c0000

- Return the Hexadecimal Extended Well-Known Binary (EWKB) representation as text &Z_support; &geography_support;

  `asHexEWKB(tpoint,endian text='') → text`

  The result is encoded using either the little-endian (NDR) or the big-endian (XDR) encoding. If no encoding is specified, then the encoding of the machine is used.

      SELECT asHexEWKB(tgeompoint 'SRID=3812;Point(1 2 3)@2001-01-01');
      -- 012E0051E40E0000000000000000F03F00000000000000400000000000000840009C57D3C11C0000

- Input a temporal point from its Well-Known Text (WKT) representation &Z_support; &geography_support;

  `tgeompointFromText(text) → tgeompoint`

  `tgeogpointFromText(text) → tgeogpoint`

      SELECT asEWKT(tgeompointFromText(text '[POINT(1 2)@2001-01-01, POINT(3 4)@2001-01-02]'));
      -- [POINT(1 2)@2001-01-01, POINT(3 4)@2001-01-02]
      SELECT asEWKT(tgeogpointFromText(text '[POINT(1 2)@2001-01-01, POINT(3 4)@2001-01-02]'));
      -- SRID=4326;[POINT(1 2)@2001-01-01, POINT(3 4)@2001-01-02]

- Input a temporal point from its Extended Well-Known Text (EWKT) representation &Z_support; &geography_support;

  `tgeompointFromEWKT(text) → tgeompoint`

  `tgeogpointFromEWKT(text) → tgeogpoint`

      SELECT asEWKT(tgeompointFromEWKT(text 'SRID=3812;[POINT(1 2)@2001-01-01,
        POINT(3 4)@2001-01-02]'));
      -- SRID=3812;[POINT(1 2)@2001-01-01 00:00:00+01, POINT(3 4)@2001-01-02 00:00:00+01]
      SELECT asEWKT(tgeogpointFromEWKT(text 'SRID=7844;[POINT(1 2)@2001-01-01,
        POINT(3 4)@2001-01-02]'));
      -- SRID=7844;[POINT(1 2)@2001-01-01, POINT(3 4)@2001-01-02]

- Input a temporal point from its Moving Features JSON representation &Z_support; &geography_support;

  `tgeompointFromMFJSON(text) → tgeompoint`

  `tgeogpointFromMFJSON(text) → tgeogpoint`

      SELECT asEWKT(tgeompointFromMFJSON(text '{"type":"MovingPoint","crs":{"type":"name",
        "properties":{"name":"EPSG:4326"}},"coordinates":[[50.81,4.38]],
        "datetimes":["2019-01-01T17:00:00.15+01"],"interpolation":"None"}'));
      -- SRID=4326;POINT(50.81 4.38)@2019-01-01 17:00:00.15+01
      SELECT asEWKT(tgeogpointFromMFJSON(text '{"type":"MovingPoint","crs":{"type":"name",
        "properties":{"name":"EPSG:4326"}},"coordinates":[[50.81,4.38]],
        "datetimes":["2019-01-01T17:00:00.15+01"],"interpolation":"None"}'));
      -- SRID=4326;POINT(50.81 4.38)@2019-01-01 17:00:00.15+01

- Input a temporal point from its Well-Known Binary (WKB) representation &Z_support; &geography_support;

  `tgeompointFromBinary(bytea) → tgeompoint`

  `tgeogpointFromBinary(bytea) → tgeogpoint`

      SELECT asEWKT(tgeompointFromBinary(
        '\x012e0011000000000000f03f00000000000000400000000000000840009c57d3c11c0000'));
      -- POINT Z (1 2 3)@2001-01-01

- Input a temporal point from its Extended Well-Known Binary (EWKB) representation &Z_support; &geography_support;

  `tgeompointFromEWKB(bytea) → tgeompoint`

  `tgeogpointFromEWKB(bytea) → tgeogpoint`

      SELECT asEWKT(tgeogpointFromEWKB(
        '\x012f0071a41e0000000000000000f03f00000000000000400000000000000840009c57d3c11c0000'));
      -- SRID=7844;POINT Z (1 1 1)@2001-01-01

- Input a temporal point from its Hexadecimal Extended Well-Known Binary (HexEWKB) representation &Z_support; &geography_support;

  `tgeompointFromHexEWKB(text) → tgeompoint`

  `tgeogpointFromHexEWKB(text) → tgeogpoint`

      SELECT asEWKT(tgeompointFromHexEWKB(
        '012E0051E40E0000000000000000F03F00000000000000400000000000000840009C57D3C11C0000'));
      -- SRID=3812;POINT(1 2 3)@2001-01-01

## Spatial Reference System

- Return the spatial reference identifier &Z_support; &geography_support;

  `SRID(tpoint) → integer`

      SELECT SRID(tgeompoint 'Point(0 0)@2001-01-01');
      -- 0

- Set the spatial reference identifier &Z_support; &geography_support;

  `setSRID(tpoint) → tpoint`

      SELECT asEWKT(setSRID(tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02)', 4326));
      -- SRID=4326;[POINT(0 0)@2001-01-01, POINT(1 1)@2001-01-02)

- Transform to a different spatial reference &Z_support; &geography_support;

  `transform(tpoint,integer) → tpoint`

  `transform(tpoint,pipeline text,to_srid integer,is_forward bool=true) → tpoint`

  The `transform` function specifies the transformation with a target SRID. An error is raised when the input temporal point has an unknown SRID (represented by 0).

  The `transformPipeline` function specifies the transformation with a defined coordinate transformation pipeline represented with the following string format: `urn:ogc:def:coordinateOperation:AUTHORITY::CODE`. The SRID of the input temporal point is ignored, and the SRID of the output temporal point will be set to zero unless a value is provided via the optional `to_srid` parameter. As stated by the last parameter, the pipeline is executed by default in a forward direction; by setting the parameter to false, the pipeline is executed in the inverse direction.

      SELECT asEWKT(transform(tgeompoint 'SRID=4326;Point(4.35 50.85)@2001-01-01', 3812));
      -- SRID=3812;POINT(648679.018035303 671067.055638114)@2001-01-01

      WITH test(tpoint, pipeline) AS (
        SELECT tgeogpoint 'SRID=4326;{Point(4.3525 50.846667 100.0)@2001-01-01,
          Point(-0.1275 51.507222 100.0)@2001-01-02}',
          text 'urn:ogc:def:coordinateOperation:EPSG::16031' )
      SELECT asEWKT(transformPipeline(transformPipeline(tpoint, pipeline, 4326),
        pipeline, 4326, false), 6)
      FROM test;
      /* SRID=4326;{POINT Z (4.3525 50.846667 100)@2001-01-01,
         POINT Z (-0.1275 51.507222 100)@2001-01-02} */

## Bounding Box Operations

- Return the spatiotemporal bounding box expanded in the spatial dimension by a float value &Z_support; &geography_support;

  `expandSpace({geo,tpoint},float) → stbox`

      SELECT expandSpace(geography 'Linestring(0 0,1 1)', 2);
      -- SRID=4326;GEODSTBOX X((-2,-2),(3,3))
      SELECT expandSpace(tgeompoint 'Point(0 0)@2001-01-01', 2);
      -- STBOX XT((-2,-2),(2,2),[2001-01-01,2001-01-01])

## Accessors

- Return the trajectory &Z_support; &geography_support;

  `trajectory(tpoint) → geo`

  This function is equivalent to `getValues` for temporal alphanumeric values

      SELECT ST_AsText(trajectory(tgeompoint '{[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02),
        [Point(0 1)@2001-01-03, Point(1 1)@2001-01-04)}'));
      -- MULTILINESTRING((0 0,0 1),(0 1,1 1))
      SELECT ST_AsText(trajectory(tgeompoint 'Interp=Step;{[Point(0 0)@2001-01-01,
        Point(0 1)@2001-01-02], [Point(0 1)@2001-01-03, Point(1 1)@2001-01-04]}'));
      -- MULTIPOINT((0 0),(0 1),(0 1),(1 1))
      SELECT ST_AsText(trajectory(tgeompoint '{Point(0 0)@2001-01-01, Point(0 1)@2001-01-02}'));
      -- MULTIPOINT((0 0),(0 1))
      SELECT ST_AsText(trajectory(tgeompoint '{[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02),
        [Point(1 1)@2001-01-03, Point(1 1)@2001-01-04),
        [Point(2 1)@2001-01-05, Point(2 2)@2001-01-06)}'));
      -- GEOMETRYCOLLECTION(POINT(1 1),MULTILINESTRING((0 0,0 1),(2 1,2 2)))

- Return the X/Y/Z coordinate values as a temporal float &Z_support; &geography_support;

  `getX(tpoint) → tfloat`

  `getY(tpoint) → tfloat`

  `getZ(tpoint) → tfloat`

      SELECT getX(tgeompoint '{Point(1 2)@2001-01-01, Point(3 4)@2001-01-02,
        Point(5 6)@2001-01-03}');
      -- {1@2001-01-01, 3@2001-01-02, 5@2001-01-03}
      SELECT getX(tgeogpoint 'Interp=Step;[Point(1 2 3)@2001-01-01, Point(4 5 6)@2001-01-02,
        Point(7 8 9)@2001-01-03]');
      -- Interp=Step;[1@2001-01-01, 4@2001-01-02, 7@2001-01-03]
      SELECT getY(tgeompoint '{Point(1 2)@2001-01-01, Point(3 4)@2001-01-02,
        Point(5 6)@2001-01-03}');
      -- {2@2001-01-01, 4@2001-01-02, 6@2001-01-03}
      SELECT getY(tgeogpoint 'Interp=Step;[Point(1 2 3)@2001-01-01, Point(4 5 6)@2001-01-02,
        Point(7 8 9)@2001-01-03]');
      -- Interp=Step;[2@2001-01-01, 5@2001-01-02, 8@2001-01-03]
      SELECT getZ(tgeompoint '{Point(1 2)@2001-01-01, Point(3 4)@2001-01-02,
        Point(5 6)@2001-01-03}');
      -- The temporal point must have Z dimension
      SELECT getZ(tgeogpoint 'Interp=Step;[Point(1 2 3)@2001-01-01, Point(4 5 6)@2001-01-02,
        Point(7 8 9)@2001-01-03]');
      -- Interp=Step;[3@2001-01-01, 6@2001-01-02, 9@2001-01-03]

- Return true if the temporal point does not spatially self-intersect &Z_support;

  `isSimple(tpoint) → boolean`

  Notice that a temporal sequence set point is simple if every composing sequence is simple.

      SELECT isSimple(tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(0 0)@2001-01-03]');
      -- false
      SELECT isSimple(tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02,
        Point(2 0 2)@2001-01-03, Point(0 0 0)@2001-01-04]');
      -- false
      SELECT isSimple(tgeompoint '{[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02],
        [Point(1 1 1)@2001-01-03, Point(0 0 0)@2001-01-04]}');
      -- true

- Return the length traversed by the temporal point &Z_support; &geography_support;

  `length(tpoint) → float`

      SELECT length(tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02]');
      -- 1.73205080756888
      SELECT length(tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02,
        Point(0 0 0)@2001-01-03]');
      -- 3.46410161513775
      SELECT length(tgeompoint 'Interp=Step;[Point(0 0 0)@2001-01-01,
        Point(1 1 1)@2001-01-02, Point(0 0 0)@2001-01-03]');
      -- 0

- Return the cumulative length traversed by the temporal point &Z_support; &geography_support;

  `cumulativeLength(tpoint) → tfloatSeq`

      SELECT round(cumulativeLength(tgeompoint '{[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(1 0)@2001-01-03], [Point(1 0)@2001-01-04, Point(0 0)@2001-01-05]}'), 6);
      -- {[0@2001-01-01, 1.414214@2001-01-02, 2.414214@2001-01-03],
        [2.414214@2001-01-04, 3.414214@2001-01-05]}
      SELECT cumulativeLength(tgeompoint 'Interp=Step;[Point(0 0 0)@2001-01-01,
        Point(1 1 1)@2001-01-02, Point(0 0 0)@2001-01-03]');
      -- Interp=Step;[0@2001-01-01, 0@2001-01-03]

- Return the speed of the temporal point in units per second &Z_support; &geography_support;

  `speed(tpoint) → tfloatSeqSet`

  The temporal point must have linear interpolation

      SELECT speed(tgeompoint '{[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(1 0)@2001-01-03], [Point(1 0)@2001-01-04, Point(0 0)@2001-01-05]}') * 3600 * 24;
      /* Interp=Step;{[1.4142135623731@2001-01-01, 1@2001-01-02, 1@2001-01-03],
         [1@2001-01-04, 1@2001-01-05]} */
      SELECT speed(tgeompoint 'Interp=Step;[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(1 0)@2001-01-03]');
      -- ERROR:  The temporal value must have linear interpolation

- Return the time-weighted centroid &Z_support;

  `twCentroid(tgeompoint) → point`

      SELECT ST_AsText(twCentroid(tgeompoint '{[Point(0 0 0)@2001-01-01,
        Point(0 1 1)@2001-01-02, Point(0 1 1)@2001-01-03, Point(0 0 0)@2001-01-04)}'));
      -- POINT Z (0 0.666666666666667 0.666666666666667)

- Return the direction, that is, the azimuth between the start and end locations &Z_support; &geography_support;

  `direction(tpoint) → float`

  The result is expressed in radians. It is NULL if there is only one location or if the start and end locations are equal.

      SELECT round(degrees(direction(tgeompoint '[Point(0 0)@2001-01-01,
        Point(-1 -1)@2001-01-02, Point(1 1)@2001-01-03]'))::numeric, 6);
      -- 45.000000
      SELECT direction(tgeompoint '{[Point(0 0 0)@2001-01-01,
        Point(0 1 1)@2001-01-02, Point(0 1 1)@2001-01-03, Point(0 0 0)@2001-01-04)}');
      -- NULL

- Return the temporal azimuth &Z_support; &geography_support;

  `azimuth(tpoint) → tfloat`

  The result is expressed in radians. The azimut is undefined when two succesive locations are equal and in this case a temporal gap is added.

      SELECT round(degrees(azimuth(tgeompoint '[Point(0 0 0)@2001-01-01,
        Point(1 1 1)@2001-01-02, Point(1 1 1)@2001-01-03, Point(0 0 0)@2001-01-04)')));
      -- Interp=Step;{[45@2001-01-01, 45@2001-01-02], [225@2001-01-03, 225@2001-01-04)}

- Return the temporal angular difference &geography_support;

  `angularDifference(tpoint) → tfloat`

  The result is expressed in degrees.

      SELECT round(angularDifference(tgeompoint '[Point(1 1)@2001-01-01, Point(2 2)@2001-01-02,
        Point(1 1)@2001-01-03]'), 3);
      -- {0@2001-01-01, 180@2001-01-02, 0@2001-01-03}
      SELECT round(degrees(angularDifference(tgeompoint '{[Point(1 1)@2001-01-01,
        Point(2 2)@2001-01-02], [Point(2 2)@2001-01-03, Point(1 1)@2001-01-04]}')), 3);
      -- {0@2001-01-01, 0@2001-01-02, 0@2001-01-03, 0@2001-01-04}

- Return the temporal bearing &Z_support; &geography_support;

  `bearing({tpoint, point}, {tpoint, point}) → tfloat`

  Notice that this function does not accept two temporal geographic points.

      SELECT degrees(bearing(tgeompoint '[Point(1 1)@2001-01-01, Point(3 3)@2001-01-03]',
        geometry 'Point(2 2)'));
      -- [45@2001-01-01, 0@2001-01-02, 225@2001-01-03]
      SELECT round(degrees(bearing(tgeompoint '[Point(0 0)@2001-01-01, Point(2 0)@2001-01-03]',
        tgeompoint '[Point(2 1)@2001-01-01, Point(0 1)@2001-01-03]')), 3);
      --  [63.435@2001-01-01, 0@2001-01-02, 296.565@2001-01-03]
      SELECT round(degrees(bearing(tgeompoint '[Point(2 1)@2001-01-01, Point(0 1)@2001-01-03]',
        tgeompoint '[Point(0 0)@2001-01-01, Point(2 0)@2001-01-03]')), 3);
      -- [243.435@2001-01-01, 116.565@2001-01-03]

## Transformations

- Round the coordinate values to a number of decimal places &Z_support; &geography_support;

  `round(tpoint,integer=0) → tpoint`

      SELECT asText(round(tgeompoint '{Point(1.12345 1.12345 1.12345)@2001-01-01,
        Point(2 2 2)@2001-01-02, Point(1.12345 1.12345 1.12345)@2001-01-03}', 2));
      /* {POINT Z (1.12 1.12 1.12)@2001-01-01, POINT Z (2 2 2)@2001-01-02,
         POINT Z (1.12 1.12 1.12)@2001-01-03} */
      SELECT asText(round(tgeogpoint 'Point(1.12345 1.12345)@2001-01-01', 2));
      -- POINT(1.12 1.12)@2001-01-01

- Return an array of fragments of the temporal point which are simple &Z_support;

  `makeSimple(tpoint) → tgeompoint[]`

      SELECT asText(makeSimple(tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(0 0)@2001-01-03]'));
      /* {"[POINT(0 0)@2001-01-01, POINT(1 1)@2001-01-02)",
         "[POINT(1 1)@2001-01-02, POINT(0 0)@2001-01-03]"} */
      SELECT asText(makeSimple(tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02,
        Point(2 0 2)@2001-01-03, Point(0 0 0)@2001-01-04]'));
      /* {"[POINT Z (0 0 0)@2001-01-01, POINT Z (1 1 1)@2001-01-02, POINT Z (2 0 2)@2001-01-03,
         POINT Z (0 0 0)@2001-01-04]"} */
      SELECT asText(makeSimple(tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(0 1)@2001-01-03, Point(1 0)@2001-01-04]'));
      /* {POINT Z (1.12 1.12 1. {"[POINT(0 0)@2001-01-01, POINT(1 1)@2001-01-02, POINT(0 1)@2001-01-03)",
        "[POINT(0 1)@2001-01-03, POINT(1 0)@2001-01-04]"} */
      SELECT asText(makeSimple(tgeompoint '{[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-02],
        [Point(1 1 1)@2001-01-03, Point(0 0 0)@2001-01-04]}'));
      /* {"{[POINT Z (0 0 0)@2001-01-01, POINT Z (1 1 1)@2001-01-02],
         [POINT Z (1 1 1)@2001-01-03, POINT Z (0 0 0)@2001-01-04]}"} */

- Construct a geometry/geography with M measure from a temporal point and a temporal float &Z_support; &geography_support;

  `geoMeasure(tpoint,tfloat,segmentize=false) → geo`

  The last `segmentize` argument states whether the resulting value is a either `Linestring M` or a `MultiLinestring M` where each component is a segment of two points.

      SELECT st_astext(geoMeasure(tgeompoint '{Point(1 1 1)@2001-01-01,
        Point(2 2 2)@2001-01-02}', '{5@2001-01-01, 5@2001-01-02}'));
      -- MULTIPOINT ZM (1 1 1 5,2 2 2 5)
      SELECT st_astext(geoMeasure(tgeogpoint '{[Point(1 1)@2001-01-01, Point(2 2)@2001-01-02],
        [Point(1 1)@2001-01-03, Point(1 1)@2001-01-04]}',
        '{[5@2001-01-01, 5@2001-01-02],[7@2001-01-03, 7@2001-01-04]}'));
      -- GEOMETRYCOLLECTION M (POINT M (1 1 7),LINESTRING M (1 1 5,2 2 5))
      SELECT st_astext(geoMeasure(tgeompoint '[Point(1 1)@2001-01-01,
        Point(2 2)@2001-01-02, Point(1 1)@2001-01-03]',
        '[5@2001-01-01, 7@2001-01-02, 5@2001-01-03]', true));
      -- MULTILINESTRING M ((1 1 5,2 2 5),(2 2 7,1 1 7))

  A typical visualization for mobility data is to show on a map the trajectory of the moving object using different colors according to the speed. figspeed shows the result of the query below using a color ramp in QGIS.

      WITH Temp(t) AS (
        SELECT tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-05,
          Point(2 0)@2001-01-08, Point(3 1)@2001-01-10, Point(4 0)@2001-01-11]'
      )
      SELECT ST_AsText(geoMeasure(t, round(speed(t) * 3600 * 24, 2), true))
      FROM Temp;
      -- MULTILINESTRING M ((0 0 0.35,1 1 0.35),(1 1 0.47,2 0 0.47),(2 0 0.71,3 1 0.71),
      -- (3 1 1.41,4 0 1.41))

  The following expression is used in QGIS to achieve this. The `scale_linear` function transforms the M value of each composing segment to the range [0, 1]. This value is then passed to the `ramp_color` function.

      ramp_color(
        'RdYlBu',
        scale_linear(
          m(start_point(geometry_n($geometry,@geometry_part_num))),
          0, 2, 0, 1)
      )

  ### Visualizing the speed of a moving object using a color ramp in QGIS.

- Transform a temporal geometric point into the coordinate space of a Mapbox Vector Tile &Z_support;

  `asMVTGeom(tpoint,bounds,extent=4096,buffer=256,clip=true) → (geom,times)`

  The result is a couple composed of a `geometry` value and an array of associated timestamp values encoded as Unix epoch. The parameters are as follows:

  - `tpoint` is the temporal point to transform

  - `bounds` is an `stbox` defining the geometric bounds of the tile contents without buffer

  - `extent` is the tile extent in tile coordinate space

  - `buffer` is the buffer distance in tile coordinate space

  - `clip` is a Boolean that determines if the resulting geometries and timestamps should be clipped or not

      SELECT ST_AsText((mvt).geom), (mvt).times
      FROM (SELECT asMVTGeom(tgeompoint '[Point(0 0)@2001-01-01, Point(100 100)@2001-01-02]',
        stbox 'STBOX X((40,40),(60,60))') AS mvt ) AS t;
      -- LINESTRING(-256 4352,4352 -256) | {946714680,946734120}
      SELECT ST_AsText((mvt).geom), (mvt).times
      FROM (SELECT asMVTGeom(tgeompoint '[Point(0 0)@2001-01-01, Point(100 100)@2001-01-02]',
        stbox 'STBOX X((40,40),(60,60))', clip:=false) AS mvt ) AS t;
      -- LINESTRING(-8192 12288,12288 -8192) | {946681200,946767600}

## Distance Operations

- Return the smallest distance ever &Z_support; &geography_support;

  `{geo,tpoint} |=| {geo,tpoint} → float`

      SELECT tgeompoint '[Point(0 0)@2001-01-02, Point(1 1)@2001-01-04, Point(0 0)@2001-01-06)'
        |=| geometry 'Linestring(2 2,2 1,3 1)';
      -- 1
      SELECT tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-03, Point(0 0)@2001-01-05)'
        |=| tgeompoint '[Point(2 0)@2001-01-02, Point(1 1)@2001-01-04, Point(2 2)@2001-01-06)';
      -- 0.5
      SELECT tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
        Point(0 0 0)@2001-01-05)' |=| tgeompoint '[Point(2 0 0)@2001-01-02,
        Point(1 1 1)@2001-01-04, Point(2 2 2)@2001-01-06)';
      -- 0.5
      SELECT tgeompoint 'Interp=Step;(Point(1 1)@2001-01-01, Point(3 1)@2001-01-03]' |=|
        geometry 'Linestring(1 3,2 2,3 3)';
      -- 1.4142135623731

  The operator `|=|` can be used for doing nearest neighbor searches using a GiST or an SP-GiST index (see temporal_types_indexing). This operator corresponds to the PostGIS function `ST_DistanceCPA`, altough the latter requires both arguments to be a trajectory.

      SELECT ST_DistanceCPA(
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
          Point(0 0 0)@2001-01-05)'::geometry,
        tgeompoint '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04,
          Point(2 2 2)@2001-01-06)'::geometry);
      -- 0.5

- Return the instant of the first temporal point at which the two arguments are at the nearest distance &Z_support; &geography_support;

  `nearestApproachInstant({geo,tpoint},{geo,tpoint}) → tpoint`

  The function will only return the first instant that it finds if there are more than one. The resulting instant may be at an exclusive bound.

      SELECT asText(NearestApproachInstant(tgeompoint '(Point(1 1)@2001-01-01,
        Point(3 1)@2001-01-03]', geometry 'Linestring(1 3,2 2,3 3)'));
      -- POINT(2 1)@2001-01-02
      SELECT asText(NearestApproachInstant(tgeompoint 'Interp=Step;(Point(1 1)@2001-01-01,
        Point(3 1)@2001-01-03]', geometry 'Linestring(1 3,2 2,3 3)'));
      -- POINT(1 1)@2001-01-01
      SELECT asText(NearestApproachInstant(tgeompoint '(Point(1 1)@2001-01-01,
        Point(2 2)@2001-01-03]', tgeompoint '(Point(1 1)@2001-01-01, Point(4 1)@2001-01-03]'));
      -- POINT(1 1)@2001-01-01
      SELECT asText(nearestApproachInstant(tgeompoint '[Point(0 0 0)@2001-01-01,
        Point(1 1 1)@2001-01-03, Point(0 0 0)@2001-01-05)', tgeompoint
        '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04, Point(2 2 2)@2001-01-06)'));
      -- POINT Z (0.75 0.75 0.75)@2001-01-03 12:00:00+00

  Function `nearestApproachInstant` generalizes the PostGIS function `ST_ClosestPointOfApproach`. First, the latter function requires both arguments to be trajectories. Second, function `nearestApproachInstant` returns both the point and the timestamp of the nearest point of approach while the PostGIS function only provides the timestamp as shown next.

      SELECT to_timestamp(ST_ClosestPointOfApproach(
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
          Point(0 0 0)@2001-01-05)'::geometry,
        tgeompoint '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04,
          Point(2 2 2)@2001-01-06)'::geometry));
      -- 2001-01-03 12:00:00+00

- Return the line connecting the nearest approach point &Z_support; &geography_support;

  `shortestLine({geo,tpoint},{geo,tpoint}) → geo`

  The function will only return the first line that it finds if there are more than one.

      SELECT ST_AsText(shortestLine(tgeompoint '(Point(1 1)@2001-01-01,
        Point(3 1)@2001-01-03]', geometry 'Linestring(1 3,2 2,3 3)'));
      -- LINESTRING(2 1,2 2)
      SELECT ST_AsText(shortestLine(tgeompoint 'Interp=Step;(Point(1 1)@2001-01-01,
        Point(3 1)@2001-01-03]', geometry 'Linestring(1 3,2 2,3 3)'));
      -- LINESTRING(1 1,2 2)
      SELECT ST_AsText(shortestLine(
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
          Point(0 0 0)@2001-01-05)',
        tgeompoint '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04,
          Point(2 2 2)@2001-01-06)'));
      -- LINESTRING Z (0.75 0.75 0.75,1.25 0.75 0.75)

  Function `shortestLine` can be used to obtain the result provided by the PostGIS function `ST_CPAWithin` when both arguments are trajectories as shown next.

      SELECT ST_Length(shortestLine(
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
          Point(0 0 0)@2001-01-05)',
        tgeompoint '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04,
          Point(2 2 2)@2001-01-06)')) <= 0.5;
      -- true
      SELECT ST_CPAWithin(
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 1)@2001-01-03,
          Point(0 0 0)@2001-01-05)'::geometry,
        tgeompoint '[Point(2 0 0)@2001-01-02, Point(1 1 1)@2001-01-04,
          Point(2 2 2)@2001-01-06)'::geometry, 0.5);
      -- true

The temporal distance operator, denoted `<->`, computes the distance at each instant of the intersection of the temporal extents of their arguments and results in a temporal float. Computing temporal distance is useful in many mobility applications. For example, a moving cluster (also known as convoy or flock) is defined as a set of objects that move close to each other for a long time interval. This requires to compute temporal distance between two moving objects.

The temporal distance operator accepts a geometry/geography restricted to a point or a temporal point as arguments. Notice that the temporal types only consider linear interpolation between values, while the distance is a root of a quadratic function. Therefore, the temporal distance operator gives a linear approximation of the actual distance value for temporal sequence points. In this case, the arguments are synchronized in the time dimension, and for each of the composing line segments of the arguments, the spatial distance between the start point, the end point, and the nearest point of approach is computed, as shown in the examples below.

- Return the temporal distance &Z_support; &geography_support;

  `{point,tpoint} <-> {point,tpoint} → tfloat`

      SELECT tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-03)' <->
        geometry 'Point(0 1)';
      -- [1@2001-01-01, 0.707106781186548@2001-01-02, 1@2001-01-03)
      SELECT tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-03)' <->
        tgeompoint '[Point(0 1)@2001-01-01, Point(1 0)@2001-01-03)';
      -- [1@2001-01-01, 0@2001-01-02, 1@2001-01-03)
      SELECT tgeompoint '[Point(0 1)@2001-01-01, Point(0 0)@2001-01-03)' <->
        tgeompoint '[Point(0 0)@2001-01-01, Point(1 0)@2001-01-03)';
      -- [1@2001-01-01, 0.707106781186548@2001-01-02, 1@2001-01-03)
      SELECT tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02)' <->
        tgeompoint '[Point(0 1)@2001-01-01, Point(1 2)@2001-01-02)';
      -- [1@2001-01-01,1@2001-01-02)

## Spatial Relationships

The topological relationships such as `ST_Intersects` and the distance relationships such as `ST_DWithin` can be generalized for temporal points. The arguments of these generalized functions are either a temporal point and a base type (that is, a `geometry` or a `geography`) or two temporal points. Furthermore, both arguments must be of the same base type, that is, these functions do not allow to mix a temporal geometry point (or a geometry) and a temporal geography point (or a geography).

There are three versions of the spatial relationships:

- The ever relationships determine whether the topological or distance relationship is ever satisfied (see ever_always_comparison) and returns a `boolean`. Examples are the `eIntersects` and `eDwithin` functions.

- The always relationships determine whether the topological or distance relationship is always satisfied (see ever_always_comparison) and returns a `boolean`. Examples are the `aIntersects` and `aDwithin` functions.

- The temporal relationships compute the topological or distance relationship at each instant and results in a `tbool`. Examples are the `tIntersects` and `tDwithin` functions.

For example, the following query

    SELECT eIntersects(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
      tgeompoint '[Point(0 2)@2001-01-01, Point(4 2)@2001-01-05)');
    -- t

tests whether the temporal point ever intersects the geometry. In this case, the query is equivalent to the following one

    SELECT ST_Intersects(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
      geometry 'Linestring(0 2,4 2)');

where the second geometry is obtained by applying the `trajectory` function to the temporal point.

In contrast, the query

    SELECT tIntersects(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
      tgeompoint '[Point(0 2)@2001-01-01, Point(4 2)@2001-01-05)');
    -- {[f@2001-01-01, t@2001-01-02, t@2001-01-04], (f@2001-01-04, f@2001-01-05)}

computes at each instant whether the temporal point intersects the geometry. Similarly, the following query

    SELECT eDwithin(tgeompoint '[Point(3 1)@2001-01-01, Point(5 1)@2001-01-03)',
      tgeompoint '[Point(3 1)@2001-01-01, Point(1 1)@2001-01-03)', 2);
    -- t

tests whether the distance between the temporal points was ever less than or equal to 2, while the following query

    SELECT tDwithin(tgeompoint '[Point(3 1)@2001-01-01, Point(5 1)@2001-01-03)',
      tgeompoint '[Point(3 1)@2001-01-01, Point(1 1)@2001-01-03)', 2);
    -- {[t@2001-01-01, t@2001-01-02], (f@2001-01-02, f@2001-01-03)}

computes at each instant whether the distance between the temporal points is less than or equal to 2.

The ever or always relationships are sometimes used in combination with a spatiotemporal index when computing the temporal relationships. For example, the following query

    SELECT T.TripId, R.RegionId, tIntersects(T.Trip, R.Geom)
    FROM Trips T, Regions R
    WHERE eIntersects(T.Trip, R.Geom)

which verifies whether a trip `T` (which is a temporal point) intersects a region `R` (which is a geometry), will benefit from a spatiotemporal index on the column `T.Trip` since the `eIntersects` function will automatically perform the bounding box comparison `T.Trip && R.Geom`. This is further explained later in this document.

Not all spatial relationships available in PostGIS have a meaningful generalization for temporal points. A generalized version of the following relationships are defined for temporal geometric points: `eIntersects`, `eDisjoint`, `eDwithin`, `eContains`, and `eTouches`, while for temporal geographic points only the three first ones are defined. Furthermore, not all combinations of parameters are meaningful for a given generalized function. For example, while `tContains(geometry, tpoint)` is meaningful, `tContains(tpoint, geometry)` is meaningful only when the geometry is a single point, and `tContains(tpoint, tpoint)` is equivalent to `tintersects(tpoint, geometry)`. For this reason, only the first combination of parameters is defined for `eContains`, `aContains`, and `tContains`.

Finally, it is worth noting that the temporal relationships allow to mix 2D/3D geometries but in that case, the computation is only performed on 2D.

### Ever and Always Spatial Relationships

We present next the ever and always spatial relationships. These relationships automatically include a bounding box comparison that makes use of any spatial indexes that are available on the arguments.

- Ever or always contains

  `eContains(geo,tgeompoint) → boolean`

  `aContains(geo,tgeompoint) → boolean`

  This function returns true if the temporal point is ever or always at the interior of the geometry. Recall that a geometry does not contain things in its boundary and thus, polygons and lines do not contain lines and points lying in their boundary. Please refer to the documentation of the ST_Contains function in PostGIS.

      SELECT eContains(geometry 'Linestring(1 1,3 3)',
        tgeompoint '[Point(4 2)@2001-01-01, Point(2 4)@2001-01-02]');
      -- false
      SELECT eContains(geometry 'Linestring(1 1,3 3,1 1)',
        tgeompoint '[Point(4 2)@2001-01-01, Point(2 4)@2001-01-03]');
      -- true
      SELECT eContains(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
       tgeompoint '[Point(0 1)@2001-01-01, Point(4 1)@2001-01-02]');
      -- false
      SELECT eContains(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
        tgeompoint '[Point(1 4)@2001-01-01, Point(4 1)@2001-01-04]');
      -- true

- Is ever or always disjoint &Z_support; &geography_support;

  `eDisjoint({geo,tgeompoint},{geo,tgeompoint}) → boolean`

  `aDisjoint({geo,tpoint},{geo,tpoint}) → boolean`

      SELECT eDisjoint(geometry 'Polygon((0 0,0 1,1 1,1 0,0 0))',
        tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-03)');
      -- false
      SELECT eDisjoint(geometry 'Polygon((0 0 0,0 1 1,1 1 1,1 0 0,0 0 0))',
        tgeompoint '[Point(0 0 1)@2001-01-01, Point(1 1 0)@2001-01-03)');
      -- true

- Is ever or always at distance within &Z_support; &geography_support;

  `eDwithin({geo,tpoint},{geo,tpoint},float) → boolean`

  `aDwithin({geo,tgeompoint},{geo,tgeompoint},float) → boolean`

      SELECT eDwithin(geometry 'Point(1 1 1)',
        tgeompoint '[Point(0 0 0)@2001-01-01, Point(1 1 0)@2001-01-02]', 1);
      -- true
      SELECT eDwithin(geometry 'Polygon((0 0 0,0 1 1,1 1 1,1 0 0,0 0 0))',
        tgeompoint '[Point(0 2 2)@2001-01-01,Point(2 2 2)@2001-01-02]', 1);
      -- false

- Ever or always intersects &Z_support; &geography_support;

  `eIntersects({geo,tpoint},{geo,tpoint}) → boolean`

  `aIntersects({geo,tgeompoint},{geo,tgeompoint}) → boolean`

      SELECT eIntersects(geometry 'Polygon((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0))',
        tgeompoint '[Point(0 0 1)@2001-01-01, Point(1 1 1)@2001-01-03)');
      -- false
      SELECT eIntersects(geometry 'Polygon((0 0 0,0 1 1,1 1 1,1 0 0,0 0 0))',
        tgeompoint '[Point(0 0 1)@2001-01-01, Point(1 1 1)@2001-01-03)');
      -- true

- Ever or always touches &Z_support;

  `eTouches({geo,tgeompoint},{geo,tgeompoint}) → boolean`

  `aTouches({geo,tgeompoint},{geo,tgeompoint}) → boolean`

      SELECT eTouches(geometry 'Polygon((0 0,0 1,1 1,1 0,0 0))',
        tgeompoint '[Point(0 0)@2001-01-01, Point(0 1)@2001-01-03)');
      -- true

### Temporal Spatial Relationships

We present next the temporal spatial relationships. A common requirement regarding them is to restrict the result of the relationship to the instants when the value of the result is true or false. As an example, the following query computes for each trip the time spent traveling in the Brussels municipality.

    SELECT TripId, duration(atValue(tintersects(T.trip, M.geom), True))
    FROM Trips T, Municipality M
    WHERE M.Name = "Brussels" AND atValue(tintersects(T.trip, M.geom), True) IS NOT NULL;

To simplify query writing, the temporal spatial relationships have an optional last parameter, which if given applies the `atValue` function (see temporal_types_restrictions) to the result of the relationship. In this way, the above query can be written as follows.

    SELECT TripId, duration(tintersects(T.trip, M.geom, True))
    FROM Trips T, Municipality M
    WHERE M.Name = "Brussels" AND tintersects(T.trip, M.geom, True) IS NOT NULL;

- Temporal contains

  `tContains(geometry,tgeompoint,atValue boolean=NULL) → tbool`

      SELECT tContains(geometry 'Linestring(1 1,3 3)',
        tgeompoint '[Point(4 2)@2001-01-01, Point(2 4)@2001-01-02]');
      -- {[f@2001-01-01, f@2001-01-02]}
      SELECT tContains(geometry 'Linestring(1 1,3 3,1 1)',
        tgeompoint '[Point(4 2)@2001-01-01, Point(2 4)@2001-01-03]');
      -- {[f@2001-01-01, t@2001-01-02], (f@2001-01-02, f@2001-01-03]}
      SELECT tContains(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
        tgeompoint '[Point(0 1)@2001-01-01, Point(4 1)@2001-01-02]');
      -- {[f@2001-01-01, f@2001-01-02]}
      SELECT tContains(geometry 'Polygon((1 1,1 3,3 3,3 1,1 1))',
        tgeompoint '[Point(1 4)@2001-01-01, Point(4 1)@2001-01-04]');
      -- {[f@2001-01-01, f@2001-01-02], (t@2001-01-02, f@2001-01-03, f@2001-01-04]}

- Temporal disjoint &Z_support; &geography_support;

  `tDisjoint({geo,tpoint},{geo,tpoint},atValue boolean=NULL) → tbool`

  The function only supports 3D or geographies for two temporal points

      SELECT tDisjoint(geometry 'Polygon((1 1,1 2,2 2,2 1,1 1))',
        tgeompoint '[Point(0 0)@2001-01-01, Point(3 3)@2001-01-04)');
      -- {[t@2001-01-01, f@2001-01-02, f@2001-01-03], (t@2001-01-03, t@2001-01-04]}
      SELECT tDisjoint(tgeompoint '[Point(0 3)@2001-01-01, Point(3 0)@2001-01-05)',
        tgeompoint '[Point(0 0)@2001-01-01, Point(3 3)@2001-01-05)');
      -- {[t@2001-01-01, f@2001-01-03], (t@2001-01-03, t@2001-01-05)}

- Temporal distance within &Z_support;

  `tDwithin({geompoint,tgeompoint},{geompoint,tgeompoint},float,atValue boolean=NULL)`

  `→ tbool`

      SELECT tDwithin(geometry 'Point(1 1)',
        tgeompoint '[Point(0 0)@2001-01-01, Point(2 2)@2001-01-03)', sqrt(2));
      --  {[t@2001-01-01, t@2001-01-03)}
      SELECT tDwithin(tgeompoint '[Point(1 0)@2001-01-01, Point(1 4)@2001-01-05]',
        tgeompoint 'Interp=Step;[Point(1 2)@2001-01-01, Point(1 3)@2001-01-05]', 1);
      -- {[f@2001-01-01, t@2001-01-02, t@2001-01-04], (f@2001-01-04, t@2001-01-05]}

- Temporal intersects &Z_support; &geography_support;

  `tIntersects({geo,tpoint},{geo,tpoint},atValue boolean=NULL) → tbool`

  The function only supports 3D or geographies for two temporal points

      SELECT tIntersects(geometry 'MultiPoint(1 1,2 2)',
        tgeompoint '[Point(0 0)@2001-01-01, Point(3 3)@2001-01-04)');
      /* {[f@2001-01-01, t@2001-01-02], (f@2001-01-02, t@2001-01-03],
         (f@2001-01-03, f@2001-01-04]} */
      SELECT tIntersects(tgeompoint '[Point(0 3)@2001-01-01, Point(3 0)@2001-01-05)',
        tgeompoint '[Point(0 0)@2001-01-01, Point(3 3)@2001-01-05)');
      -- {[f@2001-01-01, t@2001-01-03], (f@2001-01-03, f@2001-01-05)}

- Temporal touches

  `tTouches({geo,tgeompoint},{geo,tgeompoint},atValue boolean=NULL) → tbool`

      SELECT tTouches(geometry 'Polygon((1 0,1 2,2 2,2 0,1 0))',
        tgeompoint '[Point(0 0)@2001-01-01, Point(3 0)@2001-01-04)');
      -- {[f@2001-01-01, t@2001-01-02, t@2001-01-03], (f@2001-01-03, f@2001-01-04]}