# Tipos temporales geométricos (Parte 1)

## Tipos espaciotemporales

MobilityDB proporciona un conjunto de tipos espaciotemporales, a saber, `tgeometry` (geometría temporal), `tgeography` (geografía temporal), `tgeompoint` (punto geométrico temporal), `tgeogpoint` (punto geográfico temporal), `tcbuffer` (buffer circular temporal), `tnpoint` (punto de red temporal), `tpose` (pose temporal) y `trgeometry` (geometría rígida temporal). A nivel conceptual, estos tipos espaciotemporales se organizan en la jerarquía que se muestra en la tspatial_fig.

### Jerarquía de tipos espaciotemporales en MobilityDB.

En este capítulo y el siguiente cubrimos el tipo `TGeo` y sus subtipos, es decir, los tipos espaciotemporales derivados de los tipos `geometry` y `geography` de PostGIS. En los capítulos siguientes, continuamos describiendo los tipos espaciotemporales restantes.

La tpoint_tgeo ilustra el uso de los tipos `tgeompoint` (arriba) y `tgeometry` (abajo) para modelizar la trayectoria y la franja de viento de las tormentas tropicales en 2024. Los datos provienen de National Oceanic and Atmospherique Administration (NOAA). Como se ilustra en la figura, el tipo `tgeompoint` permite la interpolación lineal, mientras que el tipo `tgeometry` solo permite la interpolación escalonada.

### Ilustración del uso de los tipos `tgeompoint` (arriba) y `tgeometry` (abajo) para modelizar la trayectoria y la franja de viento de las tormentas tropicales en 2024.

## Notación

Presentamos en la ttype_notation y en la talpha_notation la notación utilizada para definir la firma de las funciones y operadores para tipos temporales. A continuación, ampliamos estas notaciones para tipos espaciotemporales.

- `tspatial` representa un tipo espaciotemporal, por ejemplo, `tgeometry`, `tgeompoint`, `tpose`, o `tnpoint`,

- `tgeo` representa un tipo temporal geometría/geografía, es decir, `tgeometry`, `tgeography`, `tgeompoint`, o `tgeogpoint`,

- `tgeom` representa un tipo temporal geometría, es decir, `tgeometry` o `tgeompoint`,

- `tpoint` representa un tipo temporal punto, es decir, `tgeompoint` o `tgeogpoint`,

- `spatial` representa un tipo de base espacial, por ejemplo, `geometry`, `geography`, `pose`, o `npoint`,

- `geo` representa los tipos `geometry` o `geography`,

- `geompoint` representa el tipo `geometry` restringido a un punto.

- `point` representa los tipos `geometry` o `geography` restringidos a un punto.

- `lines` representa los tipos `geometry` o `geography` restringidos a una (multi)línea.

A continuación, se especifica con el símbolo &Z_support; que la función admite geometrías en 3D y con el símbolo &geography_support; que la función admite geografías.

## Entrada y salida

- Devuelve la representación de texto conocido (Well-Known Text o WKT) o la representación extendida de texto conocido (Extended Well-Known Text o EWKT) &Z_support; &geography_support;

  `asText({tspatial,tspatial[],spatial[]}) → {text,text[]}`

  `asEWKT({tspatial,tspatial[],spatial[]}) → {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[tgeometry 'SRID=4326;[Point(0 0)@2001-01-01,
        Linestring(1 1,2 1)@2001-01-02]', 'Polygon((1 1,2 2,3 1,1 1))@2001-01-01']);
      /* {"[POINT(0 0)@2001-01-01, LINESTRING(1 1,2 1)@2001-01-02]",
          "POLYGON((1 1,2 2,3 1,1 1))@2001-01-01"} */
      SELECT asText(ARRAY[geometry 'Point(0 0)', 'Point(1 1)']);
      -- {"POINT(0 0)","POINT(1 1)"}

      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[tgeometry 'SRID=4326;[Point(0 0)@2001-01-01,
        Linestring(1 1,2 1)@2001-01-02]', 'Polygon((1 1,2 2,3 1,1 1))@2001-01-01']);
      -- {"SRID=4326;[POINT(0 0)@2001-01-01, LINESTRING(1 1,2 1)@2001-01-02]",
          "POLYGON((1 1,2 2,3 1,1 1))@2001-01-01"}
      SELECT asEWKT(ARRAY[geometry 'SRID=5676;Point(0 0)', 'SRID=5676;Point(1 1)']);
      -- {"SRID=5676;POINT(0 0)","SRID=5676;POINT(1 1)"}

- Devuelve la representación JSON de características móviles (Moving Features JSON o MF-JSON) &Z_support; &geography_support;

  `asMFJSON(tspatial,options=0,flags=0,maxdecdigits=15) → text`

  El argumento `options` puede usarse para agregar BBOX y/o CRS en la salida MFJSON:

  - 0: significa que no hay opción (valor por defecto)

  - 1: MFJSON BBOX

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

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

  El argumento `flags` puede usarse para personalizar la salida JSON, por ejemplo, para producir una salida JSON fácil de leer (para lectores humanos). Consulte la documentación de la biblioteca `json-c` para conocer los valores posibles. Los valores típicos son los siguientes:

  - 0: means no option (default value)

  - 1: JSON_C_TO_STRING_SPACED

  - 2: JSON_C_TO_STRING_PRETTY

  El argumento `maxdecdigits` puede usarse para establecer el número máximo de decimales en la salida de los valores en punto flotante (por defecto 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(tgeometry 'SRID=3812;Linestring(1 1,1 2)@2019-01-01 18:00:00.15+02');
      /* {"type":"MovingGeometry",
          "crs":{"type":"Name","properties":{"name":"EPSG:3812"}},
          "values":[{"type":"LineString","coordinates":[[1,1],[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, 0, 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"} */

- Devuelve la representación binaria conocida (Well-Known Binary o WKB), la representación extendida binaria conocida (Extended Well-Known Binary o EWKB), o la representación hexadecimal extendida binaria conocida (Extended Well-Known Binary o EWKB) &Z_support; &geography_support;

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

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

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

  El resultado se codifica utilizando la codificación little-endian (NDR) o big-endian (XDR). Si no se especifica ninguna codificación, se utiliza la codificación de la máquina.

      SELECT asBinary(tgeompoint 'Point(1 2 3)@2001-01-01');
      -- \x012e0011000000000000f03f00000000000000400000000000000840009c57d3c11c0000
      SELECT asEWKB(tgeogpoint 'SRID=7844;Point(1 2 3)@2001-01-01');
      -- \x012f0071a41e0000000000000000f03f00000000000000400000000000000840009c57d3c11c0000
      SELECT asHexEWKB(tgeompoint 'SRID=3812;Point(1 2 3)@2001-01-01');
      -- 012E0051E40E0000000000000000F03F00000000000000400000000000000840009C57D3C11C0000

- Entrar a partir de la representación de texto conocido (Well-Known Text o WKT) o de la representación extendida de texto conocido (Extended Well-Known Text o EWKT) &Z_support; &geography_support;

  `tspatialFromText(text) → tspatial`

  `tspatialFromEWKT(text) → tspatial`

  En las funciones anteriores, `tspatial` reemplaza cualquier tipo espacial, como `tgeompoint`, `tgeometry` o `tpose`

      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(tgeographyFromText(text
        '[Point(1 2)@2001-01-01, Linestring(1 2,3 4)@2001-01-02]'));
      -- SRID=4326;[POINT(1 2)@2001-01-01, LINESTRING(1 2,3 4)@2001-01-02]

      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, POINT(3 4)@2001-01-02]
      SELECT asEWKT(tgeographyFromEWKT(text 'SRID=7844;[Point(1 2)@2001-01-01,
        Linestring(1 2,3 4)@2001-01-02]'));
      -- SRID=7844;[POINT(1 2)@2001-01-01, LINESTRING(1 2,3 4)@2001-01-02]

- Entrar a partir de la representación JSON de características móviles (Moving Features JSON o MF-JSON) &Z_support; &geography_support;

  `tspatialFromMFJSON(text) → spatial`

  En la funcion anterior, `tspatial` reemplaza cualquier tipo espacial, como `tgeompoint`, `tgeometry` o `tpose`

      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
      SELECT asEWKT(tgeographyFromMFJSON(text '{"type":"MovingGeometry",
        "crs":{"type":"Name","properties":{"name":"EPSG:7844"}},
        "values":[{"type":"LineString","coordinates":[[1,1],[1,2]]}],
        "datetimes":["2019-01-01T17:00:00.15+01"],"interpolation":"None"}'));
      -- SRID=7844;LINESTRING(1 1,1 2)@2019-01-01 17:00:00.15+01

- Entrar a partir de su representación binaria conocida (WKB), de la representación extendida binaria conocida (EWKB), o de la representación hexadecimal extendida binaria conocida (HexEWKB) &Z_support; &geography_support;

  `tspatialBinary(bytea) → tspatial`

  `tspatialFromEWKB(bytea) → tspatial`

  `tspatialFromHexEWKB(text) → tspatial`

  En las funciones anteriores, `tspatial` reemplaza cualquier tipo espacial, como `tgeompoint`, `tgeometry` o `tpose`

      SELECT asEWKT(tgeompointFromBinary(
        '\x012e0011000000000000f03f00000000000000400000000000000840009c57d3c11c0000'));
      -- POINT Z (1 2 3)@2001-01-01
      SELECT asEWKT(tgeogpointFromEWKB(
        '\x012f0071a41e0000000000000000f03f00000000000000400000000000000840009c57d3c11c0000'));
      -- SRID=7844;POINT Z (1 1 1)@2001-01-01
      SELECT asEWKT(tgeompointFromHexEWKB(
        '012E0051E40E0000000000000000F03F00000000000000400000000000000840009C57D3C11C0000'));
      -- SRID=3812;POINT(1 2 3)@2001-01-01

## Conversión de tipos

- Convertir un valor espaciotemporal a un rango temporal o a un cuadro delimitador espaciotemporal

  `tspatial::stbox`

  `stbox(tspatial)`

      SELECT tgeompoint '[Point(1 1)@2001-01-01, Point(3 3)@2001-01-03]'::tstzspan;
      -- [2001-01-01, 2001-01-03]
      SELECT tgeompoint '[Point(1 1)@2001-01-01, Point(3 3)@2001-01-03]'::stbox;
      -- STBOX XT(((1,1),(3,3)),[2001-01-01, 2001-01-03])
      SELECT tgeography '[Point(1 1 1)@2001-01-01, Point(3 3 3)@2001-01-03]'::stbox;
      -- SRID=4326;GEODSTBOX ZT(((1,1,1),(3,3,3)),[2001-01-01, 2001-01-03])

- Convertir entre una geometría temporal y una geografía temporal

  `tgeometry::tgeography`

  `tgeography::tgeometry`

  `tgeompoint::tgeogpoint`

  `tgeogpoint::tgeompoint`

      SELECT asText((tgeompoint '[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02)')::tgeogpoint);
      -- [POINT(0 0)@2001-01-01, POINT(0 1)@2001-01-02)
      SELECT asText((tgeography 'Linestring(0 0,1 1)@2001-01-01')::tgeometry);
      -- LINESTRING(0 0,1 1)@2001-01-01

Una forma común de almacenar puntos temporales en PostGIS es representarlos como geometrías de tipo `LINESTRING M` y utilizar la dimensión M para codificar marcas de tiempo como segundos desde 1970-01-01 00:00:00. Estas geometrías aumentadas con tiempo, llamadas trayectorias, se pueden validar con la función `ST_IsValidTrajectory` para verificar quel el valor M está creciendo de cada vértice al siguiente. Las trayectorias se pueden manipular con las funciones `ST_ClosestPointOfApproach`, `ST_DistanceCPA` y `ST_CPAWithin`. Los valores de puntos temporales se pueden convertir a/desde trayectorias de PostGIS.

- Convertir entre un punto temporal y una trayectoria PostGIS

  `tpoint::geo`

  `geo::tpoint`

      SELECT ST_AsText((tgeompoint 'Point(0 0)@2001-01-01')::geometry);
      -- POINT M (0 0 978307200)
      SELECT ST_AsText((tgeompoint '{Point(0 0)@2001-01-01, Point(1 1)@2001-01-02,
        Point(1 1)@2001-01-03}')::geometry);
      -- MULTIPOINT M (0 0 978307200,1 1 978393600,1 1 978480000)
      SELECT ST_AsText((tgeompoint '[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02)')::geometry);
      -- LINESTRING M (0 0 978307200,1 1 978393600)
      SELECT ST_AsText((tgeompoint '{[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02),
        [Point(1 1)@2001-01-03, Point(1 1)@2001-01-04),
        [Point(1 1)@2001-01-05, Point(0 0)@2001-01-06)}')::geometry);
      /* MULTILINESTRING M ((0 0 978307200,1 1 978393600),(1 1 978480000,1 1 978566400),
         (1 1 978652800,0 0 978739200)) */
      SELECT ST_AsText((tgeompoint '{[Point(0 0)@2001-01-01, Point(1 1)@2001-01-02),
        [Point(1 1)@2001-01-03],
        [Point(1 1)@2001-01-05, Point(0 0)@2001-01-06)}')::geometry);
      /* GEOMETRYCOLLECTION M (LINESTRING M (0 0 978307200,1 1 978393600),
         POINT M (1 1 978480000),LINESTRING M (1 1 978652800,0 0 978739200)) */

      SELECT asText(geometry 'LINESTRING M (0 0 978307200,0 1 978393600,
        1 1 978480000)'::tgeompoint);
      -- [POINT(0 0)@2001-01-01, POINT(0 1)@2001-01-02, POINT(1 1)@2001-01-03]
      SELECT asText(geometry 'GEOMETRYCOLLECTION M (LINESTRING M (0 0 978307200,1 1 978393600),
        POINT M (1 1 978480000),LINESTRING M (1 1 978652800,0 0 978739200))'::tgeompoint);
      /* {[POINT(0 0)@2001-01-01, POINT(1 1)@2001-01-02], [POINT(1 1)@2001-01-03],
         [POINT(1 1)@2001-01-05, POINT(0 0)@2001-01-06]} */

## Accessores

- Obtenir la trayectoria o el área atravesada &Z_support; &geography_support;

  `trajectory(tpoint,unary_union=false) → geo`

  `traversedArea(tgeo,unary_union=false) → geo`

  Esta función es equivalente a `getValues` para los valores temporales alphanuméricos. El último argumento indica si se aplica la función PostGIS ST_UnaryUnion para eliminar geometrías redundantes en el resultado. Tenga en cuenta que establecer este argumento como verdadero implica un alto consumo computacional.

      SELECT ST_AsText(trajectory(tgeompoint '[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02,
        Point(0 0)@2001-01-03]'));
      -- LINESTRING(0 0,0 1,0 0)
      SELECT ST_AsText(trajectory(tgeompoint '[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02,
        Point(0 0)@2001-01-03]', true));
      -- LINESTRING(0 0,0 1)
      SELECT ST_AsText(trajectory(tgeompoint '{[Point(0 0)@2001-01-01, Point(0 1)@2001-01-02),
        [Point(0 1)@2001-01-03, Point(0 0)@2001-01-04)}'));
      -- LINESTRING(0 0,0 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),(1 1))

      SELECT ST_AsText(traversedArea(tgeometry '[Point(1 1)@2001-01-01,
        Linestring(1 1,2 2)@2001-01-02, Point(1 1)@2001-01-03]'));
      -- GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(1 1,2 2))
      SELECT ST_AsText(traversedArea(tgeometry '[Point(1 1)@2001-01-01,
        Linestring(1 1,2 2)@2001-01-02, Point(1 1)@2001-01-03]', true));
      -- LINESTRING(1 1,2 2)

- Devuelve el centroide como un punto temporal &geography_support;

  `centroid(tgeo) → tpoint`

      SELECT asText(centroid(tgeometry '[Point(1 1)@2000-01-01, Linestring(1 1,3 3)@2000-01-02,
        Polygon((1 1,4 4,7 1,1 1))@2000-01-03]'));
      -- Interp=Step;[POINT(1 1)@2000-01-01, POINT(2 2)@2000-01-02, POINT(4 2)@2000-01-03]
      SELECT asText(centroid(tgeography '[MultiPoint(1 1,4 4,7 1)@2000-01-01,
        Polygon((1 1,4 4,7 1,1 1))@2000-01-02]'),6);
      -- Interp=Step;[POINT(4 2.001727)@2000-01-01, POINT(4 2.001727)@2000-01-02]

- Devuelve los valores de las coordenadas X/Y/Z como un número flotante temporal &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]

- Devuelve verdadero si el punto temporal no se auto-intersecta espacialmente &Z_support;

  `isSimple(tpoint) → boolean`

  Nótese que un punto temporal de conjunto de secuencias es simple si cada una de las secuencias que lo componen es 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

- Devuelve la longitud atravesada por el punto temporal &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

- Devuelve la longitud acumulada atravesada por el punto temporal &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]

- Devuelve la velocidad del punto temporal en unidades por segundo &Z_support; &geography_support;

  `speed(tpoint) → tfloatSeqSet`

  El punto temporal debe tener interpolación linear

      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

- Devuelve la dirección, es decir, el acimut entre las ubicaciones inicial y final &Z_support; &geography_support;

  `direction(tpoint) → float`

  El resultado se expresa en radianes. Es NULL si solo hay una ubicación o si las ubicaciones inicial y final son iguales.

      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

- Devuelve el acimut temporal &Z_support; &geography_support;

  `azimuth(tpoint) → tfloat`

  El resultado se expresa en radianes. El azimut es indefinido cuando dos localizaciones sucesivas son iguales y en este caso se añade una brecha de tempo.

      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)}

- Devuelve la diferencia angular temporal &geography_support;

  `angularDifference(tpoint) → tfloat`

  El resultado se expresa en grados.

      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}

- Devuelve el rumbo temporal &Z_support; &geography_support;

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

  Nótese que esta función no acepta dos puntos geográficos temporales.

      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]

## Transformaciones

- Redondear los valores de las coordenadas a un número de decimales &Z_support; &geography_support;

  `round(tspatial,integer=0) → tgeo`

      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(tgeography 'Linestring(1.12345 1.12345,2.12345 2.12345)@2001-01-01', 2));
      -- LINESTRING(1.12 1.12,2.12 2.12)@2001-01-01

- Devuelve una matriz de fragmentos del punto temporal que son simples &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(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]}"} */

- Construir una geometría/geografía con medida M a partir de un punto temporal y un número flotante temporal &Z_support; &geography_support;

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

  El último argumento `segmentize` establece si el valor resultado ya sea es un `Linestring M` o un `MultiLinestring M` donde cada componente es un segmento de dos puntos.

      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))

  Una visualización típica de los datos de movilidad es mostrar en un mapa la trayectoria del objeto móvil utilizando diferentes colores según la velocidad. La figspeed muestra el resultado de la consulta a continuación usando una rampa de color en 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)) */

  La siguiente expresión se usa en QGIS para lograr esto. La función `scale_linear` transforma el valor M de cada segmento componente al rango [0, 1]. Este valor luego se pasa a la función `ramp_color`.

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

  ### Visualización de la velocidad de un objeto móvil usando una rampa de color en QGIS.

- Devuelve la transformación afín 3D de una geometría temporal para traducirla, rotarla y/o escalarla en un solo paso &Z_support;

  `affine(tgeo,float a,float b,float c,float d,float e,float f,float g,`

  `float h,float i,float xoff,float yoff,float zoff) → tgeo`

  `affine(tgeo,float a,float b,float d,float e,float xoff,float yoff) → tgeo`

      -- Rotate a 3D temporal point 180 degrees about the z axis
      SELECT asEWKT(affine(temp, cos(pi()), -sin(pi()), 0, sin(pi()), cos(pi()), 0, 0, 0, 1,
        0, 0, 0))
      FROM (SELECT tgeompoint '[POINT(1 2 3)@2001-01-01, POINT(1 4 3)@2001-01-02]' AS temp) t;
      -- [POINT Z (-1 -2 3)@2001-01-01, POINT Z (-1 -4 3)@2001-01-02]
      SELECT asEWKT(rotate(temp, pi()))
      FROM (SELECT tgeompoint '[POINT(1 2 3)@2001-01-01, POINT(1 4 3)@2001-01-02]' AS temp) t;
      -- [POINT Z (-1 -2 3)@2001-01-01, POINT Z (-1 -4 3)@2001-01-02]
      -- Rotate a 3D temporal point 180 degrees in both the x and z axis
      SELECT asEWKT(affine(temp, cos(pi()), -sin(pi()), 0, sin(pi()), cos(pi()), -sin(pi()),
         0, sin(pi()), cos(pi()), 0, 0, 0))
      FROM (SELECT tgeometry '[Point(1 1)@2001-01-01,
        Linestring(1 1,2 2)@2001-01-02]' AS temp) t;
      -- [POINT(-1 -1)@2001-01-01, LINESTRING(-1 -1,-2 -2)@2001-01-02]

- Devuelve el punto temporal rotado en sentido antihorario sobre el punto de origen

  `rotate(tgeo,float radians) → tgeo`

  `rotate(tgeo,float radians,float x0,float y0) → tgeo`

  `rotate(tgeo,float radians,geometry pointOrigin) → tgeo`

      -- Rotate a temporal point 180 degrees
      SELECT asEWKT(rotate(tgeompoint '[Point(5 10)@2001-01-01, Point(5 5)@2001-01-02,
        Point(10 5)@2001-01-03]', pi()), 6);
      -- [POINT(-5 -10)@2001-01-01, POINT(-5 -5)@2001-01-02, POINT(-10 -5)@2001-01-03]
      -- Rotate 30 degrees counter-clockwise at x=5, y=10
      SELECT asEWKT(rotate(tgeompoint '[Point(5 10)@2001-01-01, Point(5 5)@2001-01-02,
        Point(10 5)@2001-01-03]', pi()/6, 5, 10), 6);
      -- [POINT(5 10)@2001-01-01, POINT(7.5 5.67)@2001-01-02, POINT(11.83 8.17)@2001-01-03]
      -- Rotate 60 degrees clockwise from centroid
      SELECT asEWKT(rotate(temp, -pi()/3, ST_Centroid(traversedArea(temp))), 2)
      FROM (SELECT tgeometry '[Point(5 10)@2001-01-01, Point(5 5)@2001-01-02,
        Linestring(5 5,10 5)@2001-01-03]' AS temp) AS t;
      /* [POINT(10.58 9.67)@2001-01-01, POINT(6.25 7.17)@2001-01-02,
          LINESTRING(6.25 7.17,8.75 2.83)@2001-01-03] */

- Devuelve un punto temporal escalado por factores dados &Z_support;

  `scale(tgeo,float Xfactor,float Yfactor,float Zfactor) → tgeo`

  `scale(tgeo,float Xfactor,float Yfactor) → tgeo`

  `scale(tgeo,geometry factor) → tgeo`

  `scale(tgeo,geometry factor,geometry origin) → tgeo`

      SELECT asEWKT(scale(tgeompoint '[Point(1 2 3)@2001-01-01, Point(1 1 1)@2001-01-02]',
        0.5, 0.75, 0.8));
      --  [POINT Z (0.5 1.5 2.4)@2001-01-01, POINT Z (0.5 0.75 0.8)@2001-01-02]
      SELECT asEWKT(scale(tgeompoint '[Point(1 2 3)@2001-01-01, Point(1 1 1)@2001-01-02]',
        0.5, 0.75));
      -- [POINT Z (0.5 1.5 3)@2001-01-01, POINT Z (0.5 0.75 1)@2001-01-02]
      SELECT asEWKT(scale(tgeompoint '[Point(1 2 3)@2001-01-01, Point(1 1 1)@2001-01-02]',
        geometry 'Point(0.5 0.75 0.8)'));
      -- [POINT Z (0.5 1.5 2.4)@2001-01-01, POINT Z (0.5 0.75 0.8)@2001-01-02]
      SELECT asEWKT(scale(tgeometry '[Point(1 1)@2001-01-01, Linestring(1 1,2 2)@2001-01-02]',
        geometry 'Point(2 2)', geometry 'Point(1 1)'));
      -- [POINT(1 1)@2001-01-01, LINESTRING(1 1,3 3)@2001-01-02]

- Transformar un punto geométrico temporal en el espacio de coordenadas de un Mapbox Vector Tile &Z_support;

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

  El resultado es un par compuesto de un valor `geometry` y una matriz de valores de marca de tiempo asociados codificados como época de Unix. Los parámetros son los siguientes:

  - `tpoint` es el punto temporal para transformar

  - `bounds` es un `stbox` que define los límites geométricos del contenido del mosaico sin búfer

  - `extent` es la extensión del mosaico en el espacio de coordenadas del mosaico

  - `buffer` es la distancia del búfer en el espacio de coordenadas de mosaico

  - `clip` es un booleano que determina si las geometrías resultantes y las marcas de tiempo deben recortarse o no

      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}

- Extraer de un punto temporal con interpolación lineal las subsecuencias donde el punto permanece dentro de un área con un tamaño máximo especificado durante al menos la duración dada &Z_support; &geography_support;

  `stops(tpoint,maxDist=0.0,minDuration='0 minutes') → tpoint`

  El tamaño del área se calcula como la diagonal del rectángulo mínimo rotado de los puntos de la subsecuencia. Si no se especifica `maxDist`, se asume 0.0 y, por lo tanto, la función extrae los segmentos constantes del punto temporal dado. La distancia se calcula en las unidades del sistema de coordenadas. Tenga en cuenta que, aunque la función acepta geometrías 3D, el cálculo siempre se realiza en 2D.

      SELECT asText(stops(tgeompoint '[Point(1 1)@2001-01-01, Point(1 1)@2001-01-02,
        Point(2 2)@2001-01-03, Point(2 2)@2001-01-04]'));
      /* {[POINT(1 1)@2001-01-01, POINT(1 1)@2001-01-02), [POINT(2 2)@2001-01-03,
         POINT(2 2)@2001-01-04]} */
      SELECT asText(stops(tgeompoint '[Point(1 1 1)@2001-01-01, Point(1 1 1)@2001-01-02,
        Point(2 2 2)@2001-01-03, Point(2 2 2)@2001-01-04]', 1.75));
      /* {[POINT Z (1 1 1)@2001-01-01, POINT Z (1 1 1)@2001-01-02, POINT Z (2 2 2)@2001-01-03,
         POINT Z (2 2 2)@2001-01-04]} */