# Temporal Network Points

The temporal points that we have considered so far represent the movement of objects that can move freely on space since it is assumed that they can change their position from one location to the next one without any motion restriction. This is the case for animals and for flying objects such as planes or drones. However, in many cases, objects do not move freely in space but rather within spatially embedded networks such as routes or railways. In this case, it is necessary to take the embedded networks into account while describing the movements of these moving objects. Temporal network points account for these requirements.

Compared with the free-space temporal points, network-based points have the following advantages:

- Network points provide road constraints that reflect the real movements of moving objects.

- The geometric information is not stored with the moving point, but once and for all in the fixed networks. In this way, the location representations and interpolations are more precise.

- Network points are more efficient in terms of data storage, location update, formulation of query, as well as indexing. These are discussed later in this document.

Temporal network points are based on pgRouting, a PostgreSQL extension for developing network routing applications and doing graph analysis. Therefore, temporal network points asume that the underlying network is defined in a table named `ways`, which has at least three columns: `gid` containing the unique route identifier, `length` containing the route length, and `the_geom` containing the route geometry.

There are two static network types, `npoint` (short for network point) and `nsegment` (short for network segment), which represent, respectively, a point and a segment of a route. An `npoint` value is composed of a route identifier and a float number in the range [0,1] determining a relative position of the route, where 0 corresponds to the begining of the route and 1 to the end of the route. An `nsegment` value is composed of a route identifier and two float numbers in the range [0,1] determining the start and end relative positions. A `nsegment` value whose start and end positions are equal corresponds to an `npoint` value.

The `npoint` type serves as base type for defining the temporal network point type `tnpoint`. The `tnpoint` type has similar functionality as the temporal point type `tgeompoint` with the exception that it only considers two dimensions. Thus, all functions and operators described before for the `tgeompoint` type are also applicable for the `tnpoint` type. In addition, there are specific functions defined for the `tnpoint` type.

## Static Network Types

An `npoint` value is a couple of the form `(rid,position)` where `rid` is a `bigint` value representing a route identifier and `position` is a `float` value in the range [0,1] indicating its relative position. The values 0 and 1 of `position` denote, respectively, the starting and the ending position of the route. The road distance between an `npoint` value and the starting position of route with identifier `rid` is computed by multiplying `position` by length, where `length` is the route length. Examples of input of network point values are as follows:

    SELECT npoint 'Npoint(76, 0.3)';
    SELECT npoint 'Npoint(64, 1.0)';

The constructor function for network points has one argument for the route identifier and one argument for the relative position. An example of a network point value defined with the constructor function is as follows:

    SELECT npoint(76, 0.3);

An `nsegment` value is a triple of the form `(rid,startPosition,endPosition)` where `rid` is a `bigint` value representing a route identifier and `startPosition` and `endPosition` are `float` values in the range [0,1] such that `startPosition ≤ endPosition`. Semantically, a network segment represents a set of network points `(rid,position)` with `startPosition ≤ position ≤ endPosition`. If `startPosition=0` and `endPosition=1`, the network segment is equivalent to the entire route. If `startPosition=endPosition`, the network segment represents into a single network point. Examples of input of network point values are as follows:

    SELECT nsegment 'Nsegment(76, 0.3, 0.5)';
    SELECT nsegment 'Nsegment(64, 0.5, 0.5)';
    SELECT nsegment 'Nsegment(64, 0.0, 1.0)';
    SELECT nsegment 'Nsegment(64, 1.0, 0.0)';
    -- converted to nsegment 'Nsegment(64, 0.0, 1.0)';

As can be seen in the last example, the `startPosition` and `endPosition` values will be inverted to ensure that the condition `startPosition ≤ endPosition` is always satisfied. The constructor function for network segments has one argument for the route identifier and two optional arguments for the start and end positions. Examples of network segment values defined with the constructor function are as follows:

    SELECT nsegment(76, 0.3, 0.3);
    SELECT nsegment(76); -- start and end position assumed to be 0 and 1 respectively
    SELECT nsegment(76, 0.5); -- end position assumed to be 1

Values of the `npoint` type can be converted to the `nsegment` type using an explicit `CAST` or using the `::` notation as shown next.

    SELECT npoint(76, 0.33)::nsegment;

Values of static network types must satisfy several constraints so that they are well defined. These constraints are given next.

- The route identifier `rid` must be found in column `gid` of table `ways`.

- The `position`, `startPosition`, and `endPosition` values must be in the range [0,1]. An error is raised whenever one of these constraints are not satisfied.

Examples of incorrect static network type values are as follows.

    -- incorrect rid value
    SELECT npoint 'Npoint(87.5, 1.0)';
    -- incorrect position value
    SELECT npoint 'Npoint(87, 2.0)';
    -- rid value not found in the ways table
    SELECT npoint 'Npoint(99999999, 1.0)';

We give next the functions and operators for the static network types.

### Constructors

- Constructors for network points and network segments

  `npoint(bigint,float) → npoint`

  `nsegment(bigint,float,float) → nsegment`

      SELECT npoint(76, 0.3);
      SELECT nsegment(76, 0.3, 0.5);

### Conversions

Values of the `npoint` and `nsegment` types can be converted to the `geometry` type using an explicit `CAST` or using the `::` notation as shown below. Similarly, `geometry` values of subtype `point` or `linestring` (restricted to two points) can be converted, respectively, to `npoint` and `nsegment` values. For this, the route that intersects the given points must be found, where a tolerance of 0.00001 units (depending on the coordinate system) is assumed so a point and a route that are close are considered to intersect. If no such route is found, a null value is returned.

- Convert between a network point or segment and a geometry

  `{npoint,nsegment}::geometry`

  `geometry::{npoint,nsegment}`

      SELECT ST_AsText(npoint(76, 0.33)::geometry);
      -- POINT(21.6338731332283 50.0545869554067)
      SELECT ST_AsText(nsegment(76, 0.33, 0.66)::geometry);
      -- LINESTRING(21.6338731332283 50.0545869554067,30.7475989651999 53.9185062927473)
      SELECT ST_AsText(nsegment(76, 0.33, 0.33)::geometry);
      -- POINT(21.6338731332283 50.0545869554067)

      SELECT geometry 'SRID=5676;Point(279.269156511873 811.497076880187)'::npoint;
      -- NPoint(3,0.781413)
      SELECT geometry 'SRID=5676;LINESTRING(406.729536784738 702.58583437902,
        383.570801314823 845.137059419277)'::nsegment;
      -- NSegment(3,0.6,0.9)
      SELECT geometry 'SRID=5676;Point(279.3 811.5)'::npoint;
      -- NULL
      SELECT geometry 'SRID=5676;LINESTRING(406.7 702.6,383.6 845.1)'::nsegment;
      -- NULL

- Construct a spatiotemporal box from a network point and, optionally, a timestamp or a period

  `stbox(npoint) → stbox`

  `stbox(npoint,{timestamptz,tstzspan}) → stbox`

      SELECT stbox(npoint 'NPoint(1,0.3)');
      -- STBOX X((48.711754,20.92568),(48.711758,20.925682))
      SELECT stbox(npoint 'NPoint(1,0.3)', timestamptz '2001-01-01');
      -- STBOX XT(((62.786633,80.143555),(62.786636,80.143562)),[2001-01-01,2001-01-01])
      SELECT stbox(npoint 'NPoint(1,0.3)', tstzspan '[2001-01-01,2001-01-02]');
      -- STBOX XT(((62.786633,80.143555),(62.786636,80.143562)),[2001-01-01,2001-01-02])

### Accessors

- Return the route identifier

  `route({npoint,nsegment}) → bigint`

      SELECT route(npoint 'Npoint(63, 0.3)');
      -- 63
      SELECT route(nsegment 'Nsegment(76, 0.3, 0.3)');
      -- 76

- Return the position

  `getPosition(npoint) → float`

      SELECT getPosition(npoint 'Npoint(63, 0.3)');
      -- 0.3

- Return the start/end position

  `startPosition(nsegment) → float`

  `endPosition(nsegment) → float`

      SELECT startPosition(nsegment 'Nsegment(76, 0.3, 0.5)');
      -- 0.3
      SELECT endPosition(nsegment 'Nsegment(76, 0.3, 0.5)');
      -- 0.5

### Transformations

- Round the position(s) of the network point or the network segment to the number of decimal places

  `round({npoint,nsegment},integer=0) → {npoint,nsegment}`

      SELECT round(npoint(76, 0.123456789), 6);
      --  NPoint(76,0.123457)
      SELECT round(nsegment(76, 0.123456789, 0.223456789), 6);
      --  NSegment(76,0.123457,0.223457)

### Spatial Operations

- Return the spatial reference identifier

  `SRID({npoint,nsegment}) → integer`

      SELECT SRID(npoint 'Npoint(76, 0.3)');
      -- 5676
      SELECT SRID(nsegment 'Nsegment(76, 0.3, 0.5)');
      -- 5676

Two `npoint` values may be have different route identifiers but may represent the same spatial point at the intersection of the two routes. Function `same` is used for testing spatial simalarity of network points.

- Spatial similarity

  `equals(npoint, npoint)::Boolean`

      WITH inter(geom) AS (
        SELECT st_intersection(t1.the_geom, t2.the_geom)
        FROM ways t1, ways t2 WHERE t1.gid = 1 AND t2.gid = 2),
      fractions(f1, f2) AS (
        SELECT ST_LineLocatePoint(t1.the_geom, i.geom), ST_LineLocatePoint(t2.the_geom, i.geom)
        FROM ways t1, ways t2, inter i WHERE t1.gid = 1 AND t2.gid = 2)
      SELECT equals(npoint(1, f1), npoint(2, f2)) FROM fractions;
      -- true

### Comparisons

The comparison operators (=, <, and so on) for static network types require that the left and right arguments be of the same type. Excepted the equality and inequality, the other comparison operators are not useful in the real world but allow B-tree indexes to be constructed on static network types.

- Traditional comparisons

  `npoint {=, <>, <, >, <=, >=} npoint`

  `nsegment {=, <>, <, >, <=, >=} nsegment`

      SELECT npoint 'Npoint(3, 0.5)' = npoint 'Npoint(3, 0.5)';
      -- true
      SELECT nsegment 'Nsegment(3, 0.5, 0.5)' <> nsegment 'Nsegment(3, 0.5, 0.5)';
      -- false
      SELECT nsegment 'Nsegment(3, 0.5, 0.5)' < nsegment 'Nsegment(3, 0.5, 0.6)';
      -- true
      SELECT nsegment 'Nsegment(3, 0.5, 0.5)' > nsegment 'Nsegment(2, 0.5, 0.5)';
      -- true
      SELECT npoint 'Npoint(1, 0.5)' <= npoint 'Npoint(2, 0.5)';
      -- true
      SELECT npoint 'Npoint(1, 0.6)' >= npoint 'Npoint(1, 0.5)';
      -- true

## Temporal Network Points

The temporal network point type `tnpoint` allows to represent the movement of objects over a network. It corresponds to the temporal point type `tgeompoint` restricted to two-dimensional coordinates. As all the other temporal types it comes in three subtypes, namely, instant, sequence, and sequence set. Examples of `tnpoint` values in these subtypes are given next.

    SELECT tnpoint 'Npoint(1, 0.5)@2001-01-01';
    SELECT tnpoint '{Npoint(1, 0.3)@2001-01-01, Npoint(1, 0.5)@2001-01-02,
      Npoint(1, 0.5)@2001-01-03}';
    SELECT tnpoint '[Npoint(1, 0.2)@2001-01-01, Npoint(1, 0.4)@2001-01-02,
      Npoint(1, 0.5)@2001-01-03]';
    SELECT tnpoint '{[Npoint(1, 0.2)@2001-01-01, Npoint(1, 0.4)@2001-01-02,
      Npoint(1, 0.5)@2001-01-03], [Npoint(2, 0.6)@2001-01-04, Npoint(2, 0.6)@2001-01-05]}';

The temporal network point type accepts type modifiers (or `typmod` in PostgreSQL terminology). The possible values for the type modifier are `Instant`, `Sequence`, and `SequenceSet`. If no type modifier is specified for a column, values of any subtype are allowed.

    SELECT tnpoint(Sequence) '[Npoint(1, 0.2)@2001-01-01, Npoint(1, 0.4)@2001-01-02,
      Npoint(1, 0.5)@2001-01-03]';
    SELECT tnpoint(Sequence) 'Npoint(1, 0.2)@2001-01-01';
    -- ERROR: Temporal type (Instant) does not match column type (Sequence)

Temporal network point values of sequence subtype and linear or step interpolation must be defined on a single route. Therefore, a value of sequence set subtype is needed for representing the movement of an object that traverses several routes, even if there is no temporal gap. For example, in the following value

    SELECT tnpoint '{[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.5)@2001-01-03),
      [NPoint(2, 0.4)@2001-01-03, NPoint(2, 0.6)@2001-01-04)}';

the network point changes its route at 2001-01-03.

Temporal network point values of sequence or sequence set subtype are converted into a normal form so that equivalent values have identical representations. For this, consecutive instant values are merged when possible. Three consecutive instant values can be merged into two if the linear functions defining the evolution of values are the same. Examples of transformation into a normal form are as follows.

    SELECT tnpoint '[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.4)@2001-01-02,
      NPoint(1, 0.6)@2001-01-03)';
    -- [NPoint(1,0.2)@2001-01-01, NPoint(1,0.6)@2001-01-03)
    SELECT tnpoint '{[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.3)@2001-01-02,
      NPoint(1, 0.5)@2001-01-03), [NPoint(1, 0.5)@2001-01-03, NPoint(1, 0.7)@2001-01-04)}';
    -- {[NPoint(1,0.2)@2001-01-01, NPoint(1,0.3)@2001-01-02, NPoint(1,0.7)@2001-01-04)}

## Validity of Temporal Network Points

Temporal network point values must satisfy the constraints specified in ttype_validity so that they are well defined. An error is raised whenever one of these constraints are not satisfied. Examples of incorrect values are as follows.

    -- Null values are not allowed
    SELECT tnpoint 'NULL@2001-01-01 08:05:00';
    SELECT tnpoint 'Point(0 0)@NULL';
    -- Base type is not a network point
    SELECT tnpoint 'Point(0 0)@2001-01-01 08:05:00';
    -- Multiple routes in a continuous sequence
    SELECT tnpoint '[Npoint(1, 0.2)@2001-01-01 09:00:00, Npoint(2, 0.2)@2001-01-01 09:05:00)';

We present next the operation for temporal network point types. Most functions for temporal types described in the previous chapters can be applied for temporal network point types. Therefore, in the signatures of the functions, the notation `base` also represents an `npoint` and the notations `ttype`, `tpoint`, and `tgeompoint` also represent a `tnpoint`. Furthermore, the functions that have an argument of type `geometry` accept in addition an argument of type `npoint`. To avoid redundancy, we only present next some examples of these functions and operators for temporal network points.

## Constructors

- Constructor for temporal network points having a constant value

  `tnpoint(npoint,timestamptz) → tnpointInst`

  `tnpoint(npoint,tstzset) → tnpointDiscSeq`

  `tnpoint(npoint,tstzspan,interp='linear') → tnpointContSeq`

  `tnpoint(npoint,tstzspanset,interp='linear') → tnpointSeqSet`

      SELECT tnpoint('Npoint(1, 0.5)', timestamptz '2001-01-01');
      -- NPoint(1,0.5)@2001-01-01
      SELECT tnpointSeq('Npoint(1, 0.3)', tstzset '{2001-01-01, 2001-01-03, 2001-01-05}');
      -- {NPoint(1,0.3)@2001-01-01, NPoint(1,0.3)@2001-01-03, NPoint(1,0.3)@2001-01-05}
      SELECT tnpointSeq('Npoint(1, 0.5)', tstzspan '[2001-01-01, 2001-01-02]');
      -- [NPoint(1,0.5)@2001-01-01, NPoint(1,0.5)@2001-01-02]
      SELECT tnpointSeqSet('Npoint(1, 0.2)', tstzspanset '{[2001-01-01, 2001-01-03]}', 'step');
      -- Interp=Step;{[NPoint(1,0.2)@2001-01-01, NPoint(1,0.2)@2001-01-03]}

- Constructor for temporal network points of sequence subtype

  `tnpointSeq(tnpointInst[],interp={'step','linear'},leftInc bool=true,`

  `rightInc bool=true) →tnpointSeq`

      SELECT tnpointSeq(ARRAY[tnpoint 'Npoint(1, 0.3)@2001-01-01',
        'Npoint(1, 0.5)@2001-01-02', 'Npoint(1, 0.5)@2001-01-03']);
      -- {NPoint(1,0.3)@2001-01-01, NPoint(1,0.5)@2001-01-02, NPoint(1,0.5)@2001-01-03}
      SELECT tnpointSeq(ARRAY[tnpoint 'Npoint(1, 0.2)@2001-01-01',
        'Npoint(1, 0.4)@2001-01-02', 'Npoint(1, 0.5)@2001-01-03']);
      -- [NPoint(1,0.2)@2001-01-01, NPoint(1,0.4)@2001-01-02, NPoint(1,0.5)@2001-01-03]

- Constructor for temporal network points of sequence set subtype

  `tnpointSeqset(tnpoint[]) → tnpointSeqSet`

  `tnpointSeqSetGaps(tnpointInst[],maxt=NULL,maxdist=NULL,interp='linear') →`

  `tnpointSeqSet`

      SELECT tnpointSeqSet(ARRAY[tnpoint '[Npoint(1,0.2)@2001-01-01, Npoint(1,0.4)@2001-01-02,
        Npoint(1,0.5)@2001-01-03]', '[Npoint(2,0.6)@2001-01-04, Npoint(2,0.6)@2001-01-05]']);
      /* {[NPoint(1,0.2)@2001-01-01, NPoint(1,0.4)@2001-01-02, NPoint(1,0.5)@2001-01-03],
         [NPoint(2,0.6)@2001-01-04, NPoint(2,0.6)@2001-01-05]} */
      SELECT tnpointSeqSetGaps(ARRAY[tnpoint 'NPoint(1,0.1)@2001-01-01',
        'NPoint(1,0.3)@2001-01-03', 'NPoint(1,0.5)@2001-01-05'], '1 day');
      -- {[NPoint(1,0.1)@2001-01-01], [NPoint(1,0.3)@2001-01-03], [NPoint(1,0.5)@2001-01-05]}

## Conversions

A temporal network point value can be converted to and from a temporal geometry point. This can be done using an explicit `CAST` or using the `::` notation. A null value is returned if any of the composing geometry point values cannot be converted into a `npoint` value.

- Convert between a temporal network point and a temporal geometry point

  `tnpoint::tgeompoint`

  `tgeompoint::tnpoint`

      SELECT asText((tnpoint '[NPoint(1, 0.2)@2001-01-01,
        NPoint(1, 0.3)@2001-01-02)')::tgeompoint);
      /* [POINT(23.057077727326 28.7666335767956)@2001-01-01,
         POINT(48.7117553116406 20.9256801894708)@2001-01-02) */
      SELECT tgeompoint '[POINT(23.057077727326 28.7666335767956)@2001-01-01,
        POINT(48.7117553116406 20.9256801894708)@2001-01-02)'::tnpoint
      -- [NPoint(1,0.2)@2001-01-01, NPoint(1,0.3)@2001-01-02)
      SELECT tgeompoint '[POINT(23.057077727326 28.7666335767956)@2001-01-01,
        POINT(48.7117553116406 20.9)@2001-01-02)'::tnpoint
      -- NULL

## Accessors

- Return the values

  `getValues(tnpoint) → npointset`

      SELECT getValues(tnpoint '{[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02)}');
      -- {"NPoint(1,0.3)","NPoint(1,0.5)"}
      SELECT getValues(tnpoint '{[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.3)@2001-01-02)}');
      -- {"NPoint(1,0.3)"}

- Return the road identifiers

  `routes(tnpoint) → bigintset`

      SELECT routes(tnpoint '{NPoint(3, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02}');
      -- {1, 3}

- Return the value at a timestamp

  `valueAtTimestamp(tnpoint,timestamptz) → npoint`

      SELECT valueAtTimestamp(tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03)',
        '2001-01-02');
      -- NPoint(1,0.4)

- Return the length traversed by the temporal network point

  `length(tnpoint) → float`

      SELECT length(tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02]');
      -- 54.3757408468784

- Return the cumulative length traversed by the temporal network point

  `cumulativeLength(tnpoint) → tfloat`

      SELECT cumulativeLength(tnpoint '{[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02,
        NPoint(1, 0.5)@2001-01-03], [NPoint(1, 0.6)@2001-01-04, NPoint(1, 0.7)@2001-01-05]}');
      /* {[0@2001-01-01, 54.3757408468784@2001-01-02, 54.3757408468784@2001-01-03],
         [54.3757408468784@2001-01-04, 81.5636112703177@2001-01-05]} */

- Return the speed of the temporal network point in units per second

  `speed({tnpointSeq, tnpointSeqSet}) → tfloatSeqSet`

      SELECT speed(tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.4)@2001-01-02,
        NPoint(1, 0.6)@2001-01-03]') * 3600 * 24;
      /* Interp=Step;[21.4016800272077@2001-01-01, 14.2677866848051@2001-01-02,
         14.2677866848051@2001-01-03] */

## Transformations

- Transform a temporal network point to another subtype

  `tnpointInst(tnpoint) → tnpointInst`

  `tnpointSeq(tnpoint) → tnpointSeq`

  `tnpointSeqSet(tnpoint) → tnpointSeqSet`

      SELECT tnpointSeq(tnpoint 'NPoint(1, 0.5)@2001-01-01', 'discrete');
      -- {NPoint(1,0.5)@2001-01-01}
      SELECT tnpointSeq(tnpoint 'NPoint(1, 0.5)@2001-01-01');
      -- [NPoint(1,0.5)@2001-01-01]
      SELECT tnpointSeqSet(tnpoint 'NPoint(1, 0.5)@2001-01-01');
      -- {[NPoint(1,0.5)@2001-01-01]}

- Transform a temporal network point to another interpolation

  `setInterp(tnpoint, interp) → tnpoint`

      SELECT setInterp(tnpoint 'NPoint(1,0.2)@2001-01-01','linear');
      -- [NPoint(1,0.2)@2001-01-01]
      SELECT setInterp(tnpoint '{[NPoint(1,0.1)@2001-01-01], [NPoint(1,0.2)@2001-01-02]}',
        'discrete');
      -- {NPoint(1,0.1)@2001-01-01, NPoint(1,0.2)@2001-01-02}

- Round the fraction of the temporal network point to the number of decimal places

  `round(tnpoint,integer) → tnpoint`

      SELECT round(tnpoint '{[NPoint(1,0.123456789)@2001-01-01, NPoint(1,0.5)@2001-01-02)}', 6);
      -- {[NPoint(1,0.123457)@2001-01-01 00:00:00+01, NPoint(1,0.5)@2001-01-02 00:00:00+01)}

## Restrictions

- Restrict to (the complement of) a set of values

  `atValues(tnpoint,values) → tnpoint`

  `minusValues(tnpoint,values) → tnpoint`

      SELECT atValues(tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-03]',
        'NPoint(2, 0.5)');
      -- {[NPoint(2,0.5)@2001-01-02]}
      SELECT minusValues(tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-03]',
        'NPoint(2, 0.5)');
      /* {[NPoint(2,0.3)@2001-01-01, NPoint(2,0.5)@2001-01-02),
         (NPoint(2,0.5)@2001-01-02, NPoint(2,0.7)@2001-01-03]} */

- Restrict to (the complement of) a geometry

  `atGeometry(tnpoint,geometry) → tnpoint`

  `minusGeometry(tnpoint,geometry) → tnpoint`

      SELECT atGeometry(tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-03]',
        'Polygon((40 40,40 50,50 50,50 40,40 40))');
      SELECT minusGeometry(tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-03]',
        'Polygon((40 40,40 50,50 50,50 40,40 40))');
      /* {(NPoint(2,0.342593)@2001-01-01 05:06:40.364673+01,
         NPoint(2,0.7)@2001-01-03 00:00:00+01]} */

## Distance Operations

- Return the smallest distance ever

  `{geo,npoint,tnpoint} |=| {geo,npoint,tnpoint} → float`

  The operator `|=|` can be used for doing nearest neightbor searches using a GiST or an SP-GiST index (see ttype_indexing).

      SELECT tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-02]' |=|
         geometry 'SRID=5676;Linestring(50 50,55 55)';
      -- 31.69220882252415
      SELECT tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-02]' |=|
        npoint 'NPoint(1, 0.5)';
      -- 19.49691305292373
      SELECT tnpoint '[NPoint(2, 0.3)@2001-01-01, NPoint(2, 0.7)@2001-01-02]' |=|
        tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.7)@2001-01-02]';
      -- 5.231180723735304

- Return the instant of the first temporal network point at which the two arguments are at the nearest distance

  `nearestApproachInstant({geo,npoint,tnpoint},{geo,npoint,tnpoint}) → tnpoint`

      SELECT nearestApproachInstant(tnpoint '[NPoint(2, 0.3)@2001-01-01,
        NPoint(2, 0.7)@2001-01-02]', geometry 'Linestring(50 50,55 55)');
      -- NPoint(2,0.349928)@2001-01-01 02:59:44.402905+01
      SELECT nearestApproachInstant(tnpoint '[NPoint(2, 0.3)@2001-01-01,
        NPoint(2, 0.7)@2001-01-02]', npoint 'NPoint(1, 0.5)');
      -- NPoint(2,0.592181)@2001-01-01 17:31:51.080405+01

- Return the line connecting the nearest approach point between the two arguments

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

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

      SELECT ST_AsText(shortestLine(tnpoint '[NPoint(2, 0.3)@2001-01-01,
        NPoint(2, 0.7)@2001-01-02]', geometry 'Linestring(50 50,55 55)'));
      -- LINESTRING(50.7960725266492 48.8266286733015,50 50)
      SELECT ST_AsText(shortestLine(tnpoint '[NPoint(2, 0.3)@2001-01-01,
        NPoint(2, 0.7)@2001-01-02]', npoint 'NPoint(1, 0.5)'));
      -- LINESTRING(77.0902838115125 66.6659083092593,90.8134936900394 46.4385792121146)

- Return the temporal distance

  `tnpoint <-> tnpoint → tfloat`

      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03]' <->
        npoint 'NPoint(1, 0.2)';
      -- [2.34988300875063@2001-01-02 00:00:00+01, 2.34988300875063@2001-01-03 00:00:00+01]
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03]' <->
        geometry 'Point(50 50)';
      -- [25.0496666945044@2001-01-01 00:00:00+01, 26.4085688426232@2001-01-03 00:00:00+01]
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03]' <->
        tnpoint '[NPoint(1, 0.3)@2001-01-02, NPoint(1, 0.5)@2001-01-04]'
      -- [2.34988300875063@2001-01-02 00:00:00+01, 2.34988300875063@2001-01-03 00:00:00+01]

## Spatial Operations

- Return the time-weighted centroid

  `twCentroid(tnpoint) → geometry(Point)`

      SELECT ST_AsText(twCentroid(tnpoint '{[NPoint(1, 0.3)@2001-01-01,
        NPoint(1, 0.5)@2001-01-02, NPoint(1, 0.5)@2001-01-03, NPoint(1, 0.7)@2001-01-04)}'));
      -- POINT(79.9787466444847 46.2385558051041)

## Bounding Box Operations

- Topological operators

  `{tstzspan,stbox,tnpoint} {&&, <@, @>, ~=, -|-} {tstzspan,stbox,tnpoint} → boolean`

      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02]' &&
        tstzspan '[2001-01-02,2001-01-03]';
      -- true
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02]' @>
        stbox(npoint 'NPoint(1, 0.5)');
      -- true
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03]' ~=
        tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.35)@2001-01-02,
        NPoint(1, 0.5)@2001-01-03]';
      -- true

- Position operators

  `{stbox,tnpoint} {<<, &<, >>, &>} {stbox,tnpoint} → boolean`

  `{stbox,tnpoint} {<<|, &<|, |>>, |&>} {stbox,tnpoint} → boolean`

  `{tstzspan,stbox,tnpoint} {<<#, &<#, #>>, #&>} {tstzspan,stbox,tnpoint} → boolean`

      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02]' <<
        stbox(npoint 'NPoint(1, 0.2)');
      -- true
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-02]' <<|
        stbox(npoint 'NPoint(1, 0.5)');
      -- false
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-03, NPoint(1, 0.5)@2001-01-05]' #&>
        tstzspan '[2001-01-01,2001-01-03]';
      -- true
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-03, NPoint(1, 0.3)@2001-01-05]' #>>
        tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.3)@2001-01-02]';
      -- true

## Spatial Relationships

The topological and distance relationships described in tgeo_spatial_rel such as `eIntersects`, `aDwithin`, or `tContains` are defined over the geographical space while the temporal network points are defined over the network space. To be able to apply these relationships to the temporal network points, they must be transformed to temporal geometry points. This can be easily performed using the functions `geometry` and `tgeompoint` or using an explicit casting `::` as shown in the examples below.

- Ever and always spatial relationships

      SELECT eContains(geometry 'SRID=5676;Polygon((0 0,0 50,50 50,50 0,0 0))',
        tgeompoint(tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'));
      -- false
      SELECT eDisjoint(geometry(npoint 'NPoint(2, 0.0)'),
        tgeompoint(tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'));
      -- true
      SELECT eIntersects(
        tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'::tgeompoint,
        tnpoint '[NPoint(2, 0.0)@2001-01-01, NPoint(2, 1)@2001-01-03)'::tgeompoint);
      -- false

- Spatiotemporal relationships

      SELECT tContains(geometry 'SRID=5676;Polygon((0 0,0 50,50 50,50 0,0 0))',
        tgeompoint(tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'));
      -- [f@2001-01-01 00:00:00+01, f@2001-01-03 00:00:00+01)
      SELECT tDisjoint(geometry(npoint 'NPoint(2, 0.0)'),
        tgeompoint(tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'));
      -- [t@2001-01-01 00:00:00+01, t@2001-01-03 00:00:00+01)
      SELECT tDwithin(
        tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03)'::tgeompoint,
        tnpoint '[NPoint(1, 0.5)@2001-01-01, NPoint(1, 0.3)@2001-01-03)'::tgeompoint, 1);
      /* {[t@2001-01-01 00:00:00+01, t@2001-01-01 22:35:55.379053+01],
         (f@2001-01-01 22:35:55.379053+01, t@2001-01-02 01:24:04.620946+01,
         t@2001-01-03 00:00:00+01)} */

## Comparisons

- Traditional comparisons

  `tnpoint {=, <>, <, >, <=, >=} tnpoint → boolean`

      SELECT tnpoint '{[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-02),
        [NPoint(1, 0.3)@2001-01-02, NPoint(1, 0.5)@2001-01-03]}' =
        tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.5)@2001-01-03]';
      -- true
      SELECT tnpoint '{[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.5)@2001-01-03]}' <>
        tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.5)@2001-01-03]';
      -- false
      SELECT tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.5)@2001-01-03]' <
        tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.6)@2001-01-03]';
      -- true

- Ever and always comparisons

  `{npoint,tnpoint} {?=, %=} {npoint,tnpoint} → boolean`

      SELECT tnpoint '[Npoint(1, 0.2)@2001-01-01, Npoint(1, 0.4)@2001-01-04)' ?= Npoint(1, 0.3);
      -- true
      SELECT tnpoint '[Npoint(1, 0.2)@2001-01-01, Npoint(1, 0.2)@2001-01-04)' &= Npoint(1, 0.2);
      -- true

- Temporal comparisons

  `{npoint,tnpoint} {#=, #<>} {npoint,tnpoint} → tbool`

      SELECT tnpoint '[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.4)@2001-01-03)' #=
        npoint 'NPoint(1, 0.3)';
      -- {[f@2001-01-01, t@2001-01-02], (f@2001-01-02, f@2001-01-03)}
      SELECT tnpoint '[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.8)@2001-01-03)' #<>
        tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.7)@2001-01-03)';
      -- {[t@2001-01-01, f@2001-01-02], (t@2001-01-02, t@2001-01-03)}

## Aggregations

The three aggregate functions for temporal network points are illustrated next.

- Temporal count

  `tCount(tnpoint) → {tintSeq,tintSeqSet}`

      WITH Temp(temp) AS (
        SELECT tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)' UNION
        SELECT tnpoint '[NPoint(1, 0.2)@2001-01-02, NPoint(1, 0.4)@2001-01-04)' UNION
        SELECT tnpoint '[NPoint(1, 0.3)@2001-01-03, NPoint(1, 0.5)@2001-01-05)' )
      SELECT tCount(Temp)
      FROM Temp;
      -- {[1@2001-01-01, 2@2001-01-02, 1@2001-01-04, 1@2001-01-05)}

- Window count

  `wCount(tnpoint) → {tintSeq,tintSeqSet}`

      WITH Temp(temp) AS (
        SELECT tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)' UNION
        SELECT tnpoint '[NPoint(1, 0.2)@2001-01-02, NPoint(1, 0.4)@2001-01-04)' UNION
        SELECT tnpoint '[NPoint(1, 0.3)@2001-01-03, NPoint(1, 0.5)@2001-01-05)' )
      SELECT wCount(Temp, '1 day')
      FROM Temp;
      /* {[1@2001-01-01, 2@2001-01-02, 3@2001-01-03, 2@2001-01-04, 1@2001-01-05,
         1@2001-01-06)} */

- Temporal centroid

  `tCentroid(tnpoint) → tgeompoint`

      WITH Temp(temp) AS (
      SELECT tnpoint '[NPoint(1, 0.1)@2001-01-01, NPoint(1, 0.3)@2001-01-03)' UNION
      SELECT tnpoint '[NPoint(1, 0.2)@2001-01-01, NPoint(1, 0.4)@2001-01-03)' UNION
      SELECT tnpoint '[NPoint(1, 0.3)@2001-01-01, NPoint(1, 0.5)@2001-01-03)' )
      SELECT astext(tCentroid(Temp))
      FROM Temp;
      /* {[POINT(72.451531682218 76.5231414472853)@2001-01-01,
         POINT(55.7001249027598 72.9552602410653)@2001-01-03)} */

## Indexing

GiST and SP-GiST indexes can be created for table columns of temporal networks points. An example of index creation is follows:

    CREATE INDEX Trips_Trip_SPGist_Idx ON Trips USING SPGist(Trip);

The GiST and SP-GiST indexes store the bounding box for the temporal network points, which is an `stbox` and thus stores the absolute coordinates of the underlying space.

A GiST or SP-GiST index can accelerate queries involving the following operators:

- `<<`, `&<`, `&>`, `>>`, `<<|`, `&<|`, `|&>`, `|>>`, which only consider the spatial dimension in temporal network points,

- `<<#`, `&<#`, `#&>`, `#>>`, which only consider the time dimension in temporal network points,

- `&&`, `@>`, `<@`, `~=`, `-|-`, and `|=|` , which consider as many dimensions as they are shared by the indexed column and the query argument.

These operators work on bounding boxes, not the entire values.