# Temporal Circular Buffers

A static circular buffer type `cbuffer` represents a 2D point and a radius greater than or equal to 0 around the point. The radius represent the “impact” of the point on its surroundings. This impact could be interpreted as noise, pollution, or similar aspects depending on application requirements.

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

tgeo_tcbuffer illustrates the use of the `tcbuffer` type for modelling the circular buffer around the wind swath of tropical storms in 2024. The data is obtained from the National Oceanic and Atmospherique Administration (NOAA). As illustrated in the figure, the types `tgeompoint` and `tcbuffer` allow linear interpolation, while as we have seen in tpoint_tgeo, the type `tgeometry` only allows step interpolation.

## Illustration of the use of the `tcbuffer` type for modelling the circular buffer around the wind swath of tropical storms in 2024.

## Static Circular Buffers

A `cbuffer` value is a couple of the form `(point,radius)` where `point` is a geometric point and `radius` is a `float` value greater than or equal to zero representing a radius around the point expressed using the units of the SRID of the point. Examples of input of circular buffer values are as follows:

    SELECT cbuffer 'Cbuffer(Point(1 1), 0.3)';
    SELECT cbuffer 'Cbuffer(Point(1 1), 10.0)';

The `cbuffer` type corresponds to the result of the PostGIS function ST_MinimumBoundingRadius.

Values of the circular buffer type must satisfy several constraints so that they are well defined. Examples of incorrect circular buffer type values are as follows.

    -- incorrect point value
    SELECT cbuffer 'Cbuffer(Linestring(1 1,2 2), 1.0)';
    -- incorrect 3D point
    SELECT cbuffer 'Cbuffer(Point Z(1 1 1), 1.0)';
    -- incorrect radius value
    SELECT cbuffer 'Cbuffer(Point(1 1), -2.0)';

We give next the functions and operators for the circular buffer type.

### Constructors

- Constructor for circular buffers

  `cbuffer(geompoint,float) → cbuffer`

      SELECT asText(cbuffer(ST_Point(1,1), 0.3));
      -- Cbuffer(POINT(1 1),0.3)

### Conversions

Values of the `cbuffer` type can be converted to the `geometry` type using an explicit `CAST` or using the `::` notation as shown below. Similarly, a `geometry` can be converted to a `cbuffer` value using the `ST_MinimumBoundingRadius` function in PostGIS.

- Convert between a circular buffer and a geometry

  `geometry::cbuffer`

  `cbuffer::geometry`

      SELECT ST_AsText(cbuffer(ST_Point(1,1), 1)::geometry);
      -- CURVEPOLYGON(CIRCULARSTRING(0 1,2 1,0 1))
      SELECT asText(geometry 'SRID=5676;CIRCULARSTRING(0 1,2 1,0 1)'::cbuffer);
      -- Cbuffer(POINT(1 1),1)
      SELECT asText(geometry 'SRID=5676;LINESTRING(0 0,2 2)'::cbuffer);
      -- Cbuffer(POINT(1 1),1.414213562373095)

- Convert a circular buffer and, optionally, a timestamp or a period, to a spatiotemporal box

  `stbox(cbuffer) → stbox`

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

      SELECT stbox(cbuffer 'SRID=5676;Cbuffer(Point(1 1),0.3)');
      -- SRID=5676;STBOX X((0.7,0.7),(1.3,1.3))
      SELECT stbox(cbuffer 'Cbuffer(Point(1 1),0.3)', timestamptz '2001-01-01');
      -- STBOX XT(((0.7,0.7),(1.3,1.3)),[2001-01-01, 2001-01-01])
      SELECT stbox(cbuffer 'Cbuffer(Point(1 1),0.3)', tstzspan '[2001-01-01,2001-01-02]');
      -- STBOX XT(((0.7,0.7),(1.3,1.3)),[2001-01-01, 2001-01-02])

### Accessors

- Return the point

  `point(cbuffer) → geompoint`

      SELECT ST_AsText(point(cbuffer 'Cbuffer(Point(1 1), 0.3)'));
      -- Point(1 1)

- Return the radius

  `radius(cbuffer) → float`

      SELECT radius(cbuffer 'Cbuffer(Point(1 1), 0.3)');
      -- 0.3

### Transformations

- Round the point and the radius of the circular buffer to the number of decimal places

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

      SELECT asText(round(cbuffer(ST_Point(1.123456789,1.123456789), 0.123456789), 6));
      -- Cbuffer(POINT(1.123457 1.123457),0.123457)

### Spatial Operations

- Return or set the spatial reference identifier

  `SRID(cbuffer) → integer`

  `setSRID(cbuffer) → cbuffer`

      SELECT SRID(cbuffer 'Cbuffer(SRID=5676;Point(1 1), 0.3)');
      -- 5676
      SELECT asEWKT(setSRID(cbuffer 'Cbuffer(Point(0 0),1)', 4326));
      -- SRID=4326;Cbuffer(POINT(0 0),1)

- Transform to a spatial reference identifier

  `transform(cbuffer,integer) → cbuffer`

  `transformPipeline(cbuffer,pipeline text,to_srid integer,is_forward bool=true) →`

  `cbuffer`

  The `transform` function specifies the transformation with a target SRID. An error is raised when the input circular buffer 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 circular buffer is ignored, and the SRID of the output circular buffer 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(cbuffer 'SRID=4326;Cbuffer(Point(4.35 50.85),1)', 3812));
      -- SRID=3812;Cbuffer(POINT(648679.0180353033 671067.0556381135),1)

      WITH test(cbuffer, pipeline) AS (
        SELECT cbuffer 'Cbuffer(SRID=4326;Point(4.3525 50.846667),1)',
          text 'urn:ogc:def:coordinateOperation:EPSG::16031' )
      SELECT asEWKT(transformPipeline(transformPipeline(cbuffer, pipeline, 4326), pipeline,
        4326, false), 6)
      FROM test;
      -- SRID=4326;Cbuffer(POINT(4.3525 50.846667),1)

### Comparisons

The comparison operators (=, <, and so on) are available for circular buffers. They compare first the points and then the radius of the arguments. 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 circular buffers.

- Traditional comparisons

  `cbuffer {=, <>, <, >, <=, >=} cbuffer`

      SELECT cbuffer 'Cbuffer(Point(3 3), 0.5)' = cbuffer 'Cbuffer(Point(3 3), 0.5)';
      -- true
      SELECT cbuffer 'Cbuffer(Point(3 3), 0.5)' <> cbuffer 'Cbuffer(Point(3 3), 0.6)';
      -- true
      SELECT cbuffer 'Cbuffer(Point(3 3), 0.5)' < cbuffer 'Cbuffer(Point(3 3), 0.6)';
      -- true
      SELECT cbuffer 'Cbuffer(Point(3 3), 0.6)' > cbuffer 'Cbuffer(Point(2 2), 0.6)';
      -- true
      SELECT cbuffer 'Cbuffer(Point(1 1), 0.5)' <= cbuffer 'Cbuffer(Point(2 2), 0.5)';
      -- true
      SELECT cbuffer 'Cbuffer(Point(1 1), 0.6)' >= cbuffer 'Cbuffer(Point(1 1), 0.5)';
      -- true

## Temporal Circular Buffers

The temporal circular buffer type `tcbuffer` allows to represent the movement of objects together with a circular radius around them. As all temporal types, it comes in three subtypes, namely, instant, sequence, and sequence set. Examples of `tcbuffer` values in these subtypes are given next.

    SELECT tcbuffer 'Cbuffer(Point(1 1), 0.5)@2001-01-01';
    SELECT tcbuffer '{Cbuffer(Point(1 1), 0.3)@2001-01-01, Cbuffer(Point(1 1), 0.5)@2001-01-02,
      Cbuffer(Point(1 1), 0.5)@2001-01-03}';
    SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01, Cbuffer(Point(1 1), 0.4)@2001-01-02,
      Cbuffer(Point(1 1), 0.5)@2001-01-03]';
    SELECT tcbuffer '{[Cbuffer(Point(1 1), 1)@2001-01-01, Cbuffer(Point(2 2), 1)@2001-01-02],
      [Cbuffer(Point(2 2), 2)@2001-01-04, Cbuffer(Point(2 2), 3)@2001-01-05]}';

The temporal circular buffer type accepts type modifiers (or `typmod` in PostgreSQL terminology) to specify the subtype and/or the spatial reference identifier (SRID). The possible values for the subtype are `Instant`, `Sequence`, and `SequenceSet`. The two arguments are optional and if any of them is not specified for a column, values of any subtype and/or SRID are allowed.

    SELECT asEWKT(tcbuffer(Sequence,5676) 'SRID=5676;[Cbuffer(Point(1 1), 0.2)@2001-01-01,
      Cbuffer(Point(1 1), 0.5)@2001-01-03]');
    -- SRID=5676;[Cbuffer(POINT(1 1),0.2)@2001-01-01, Cbuffer(POINT(1 1),0.5)@2001-01-03]
    SELECT tcbuffer(Sequence) 'Cbuffer(Point(1 1), 0.2)@2001-01-01';
    -- ERROR: Temporal type (Instant) does not match column type (Sequence)
    SELECT tcbuffer(5676) 'Cbuffer(Point(1 1), 0.2)@2001-01-01';
    -- ERROR:  Temporal circular buffer SRID (0) does not match column SRID (5676)

Temporal circular buffer 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 asText(tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
      Cbuffer(Point(2 2), 0.4)@2001-01-02, Cbuffer(Point(3 3), 0.6)@2001-01-03)');
    -- [Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(3 3),0.6)@2001-01-03)
    SELECT asText(tcbuffer '{[Cbuffer(Point(1 1), 0.2)@2001-01-01,
      Cbuffer(Point(2 2), 0.3)@2001-01-02, Cbuffer(Point(2 2), 0.5)@2001-01-03),
      [Cbuffer(Point(2 2), 0.5)@2001-01-03, Cbuffer(Point(2 2), 0.7)@2001-01-04)}');
    /* {[Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(2 2),0.3)@2001-01-02,
       Cbuffer(Point(2 2),0.7)@2001-01-04)} */

## Validity of Temporal Circular Buffers

Temporal circular buffer 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 tcbuffer 'NULL@2001-01-01 08:05:00';
    SELECT tcbuffer 'Point(0 0)@NULL';
    -- Base type is not a circular buffer
    SELECT tcbuffer 'Point(0 0)@2001-01-01 08:05:00';

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

## Input and Output

- Return the Well-Known Text (WKT) or the Extended Well-Known Text (EWKT) representation

  `asText({tcbuffer,tcbuffer[],cbuffer[]}) → {text,text[]}`

  `asEWKT({tcbuffer,tcbuffer[],cbuffer[]}) → {text,text[]}`

      SELECT asText(tcbuffer 'SRID=4326;[Cbuffer(Point(0 0),1)@2001-01-01,
        Cbuffer(Point(1 1),2)@2001-01-02)');
      -- [Cbuffer(Point(0 0),1)@2001-01-01, Cbuffer(Point(1 1),2)@2001-01-02)
      SELECT asText(ARRAY[cbuffer 'Cbuffer(Point(0 0),1)', 'Cbuffer(Point(1 1),2)']);
      -- {"Cbuffer(POINT(0 0),1)","Cbuffer(POINT(1 1),2)"}
      SELECT asEWKT(tcbuffer 'SRID=4326;[Cbuffer(Point(0 0),1)@2001-01-01,
        Cbuffer(Point(1 1),2)@2001-01-02)');
      -- SRID=4326;[Cbuffer(Point(0 0),1)@2001-01-01, Cbuffer(Point(1 1),2)@2001-01-02)
      SELECT asEWKT(ARRAY[cbuffer 'Cbuffer(SRID=5676;Point(0 0),1)',
        'Cbuffer(SRID=5676;Point(1 1),2)']);
      -- {"Cbuffer(SRID=5676;POINT(0 0),1)","Cbuffer(SRID=5676;POINT(1 1),2))"}

- Return the Well-Known Binary (WKB), the Extended Well-Known Binary (EWKB) representation, or the Hexadecimal Extended Well-Known Binary (HexEWKB) representation

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

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

  `asHexEWKB(tcbuffer,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 asBinary(tcbuffer 'Cbuffer(Point(1 2),1)@2001-01-01');
      -- \x013b0001000000000000f03f0000000000000040000000000000f03f009c57d3c11c0000
      SELECT asEWKB(tcbuffer 'SRID=7844;Cbuffer(Point(1 2),1)@2001-01-01');
      -- \x013b0041a41e0000000000000000f03f0000000000000040000000000000f03f009c57d3c11c0000
      SELECT asHexEWKB(tcbuffer 'SRID=3812;Cbuffer(Point(1 2),1)@2001-01-01');
      -- 013B0041E40E0000000000000000F03F0000000000000040000000000000F03F009C57D3C11C0000

- Input from the Well-Known Text (WKT) representation or from the Extended Well-Known Text (EWKT) representation

  `tcbufferFromText(text) → tcbuffer`

  `tcbufferFromEWKT(text) → tcbuffer`

      SELECT asEWKT(tcbufferFromText(text '[Cbuffer(Point(1 2),1)@2001-01-01,
        Cbuffer(Point(3 4),2)@2001-01-02]'));
      -- [Cbuffer(POINT(1 2),1)@2001-01-01, Cbuffer(POINT(3 4),2)@2001-01-02]
      SELECT asEWKT(tcbufferFromEWKT(text 'SRID=3812;[Cbuffer(Point(1 2),1)@2001-01-01,
        Cbuffer(Point(3 4),2)@2001-01-02]'));
      -- SRID=3812;[Cbuffer(Point(1 2),1)@2001-01-01, Cbuffer(Point(3 4),2)@2001-01-02]

- Input from the Well-Known Binary (WKB) representation, from the Extended Well-Known Binary (EWKB) representation, or from the Hexadecimal Extended Well-Known Binary (HexEWKB) representation

  `tcbufferFromBinary(bytea) → tcbuffer`

  `tcbufferFromEWKB(bytea) → tcbuffer`

  `tcbufferFromHexEWKB(text) → tcbuffer`

      SELECT asEWKT(tcbufferFromBinary(
        '\x013b0001000000000000f03f0000000000000040000000000000f03f009c57d3c11c0000'));
      -- Cbuffer(POINT(1 2),1)@2001-01-01
      SELECT asEWKT(tcbufferFromEWKB(
        '\x013b0041a41e0000000000000000f03f0000000000000040000000000000f03f009c57d3c11c0000'));
      -- SRID=7844;Cbuffer(Point(1 1),2)@2001-01-01
      SELECT asEWKT(tcbufferFromHexEWKB(
        '013B0041E40E0000000000000000F03F0000000000000040000000000000F03F009C57D3C11C0000'));
      -- SRID=3812;Cbuffer(POINT(1 2),1)@2001-01-01

## Constructors

- Constructor for temporal circular buffers having a constant value

  `tcbuffer(cbuffer,timestamptz) → tcbufferInst`

  `tcbuffer(cbuffer,tstzset) → tcbufferDiscSeq`

  `tcbuffer(cbuffer,tstzspan,interp='linear') → tcbufferContSeq`

  `tcbuffer(cbuffer,tstzspanset,interp='linear') → tcbufferSeqSet`

      SELECT asText(tcbuffer('Cbuffer(Point(1 1), 0.5)', timestamptz '2001-01-01'));
      -- Cbuffer(Point(1 1),0.5)@2001-01-01
      SELECT asText(tcbuffer('Cbuffer(Point(1 1), 0.3)',
        tstzset '{2001-01-01, 2001-01-03, 2001-01-05}'));
      /* {Cbuffer(Point(1 1),0.3)@2001-01-01, Cbuffer(Point(1 1),0.3)@2001-01-03,
         Cbuffer(Point(1 1),0.3)@2001-01-05} */
      SELECT asText(tcbuffer('Cbuffer(Point(1 1), 0.5)',
        tstzspan '[2001-01-01, 2001-01-02]'));
      -- [Cbuffer(Point(1 1),0.5)@2001-01-01, Cbuffer(Point(1 1),0.5)@2001-01-02]
      SELECT asText(tcbuffer('Cbuffer(Point(1 1), 0.2)',
        tstzspanset '{[2001-01-01, 2001-01-03]}', 'step'));
      -- Interp=Step;{[Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(1 1),0.2)@2001-01-03]}

- Constructor for temporal circular buffers of sequence subtype

  `tcbufferSeq(tcbufferInst[],interp='linear',leftInc bool=true,`

  `rightInc bool=true) →tcbufferSeq`

      SELECT asText(tcbufferSeq(ARRAY[tcbuffer 'Cbuffer(Point(1 1), 0.3)@2001-01-01',
        'Cbuffer(Point(2 2), 0.5)@2001-01-02', 'Cbuffer(Point(1 1), 0.5)@2001-01-03']));
      /* [Cbuffer(Point(1 1),0.3)@2001-01-01, Cbuffer(Point(2 2),0.5)@2001-01-02,
         Cbuffer(Point(1 1),0.5)@2001-01-03] */
      SELECT asText(tcbufferSeq(ARRAY[tcbuffer 'Cbuffer(Point(1 1), 0.3)@2001-01-01',
        'Cbuffer(Point(2 2), 0.5)@2001-01-02', 'Cbuffer(Point(1 1), 0.5)@2001-01-03'],
        'discrete'));
      /* {Cbuffer(POINT(1 1),0.3)@2001-01-01, Cbuffer(POINT(2 2),0.5)@2001-01-02,
         Cbuffer(POINT(1 1),0.5)@2001-01-03} */
      SELECT asText(tcbufferSeq(ARRAY[tcbuffer 'Cbuffer(Point(1 1), 0.2)@2001-01-01',
        'Cbuffer(Point(1 1), 0.4)@2001-01-02', 'Cbuffer(Point(1 1), 0.5)@2001-01-03'], 'step'));
      /* Interp=Step;[Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(1 1),0.4)@2001-01-02,
         Cbuffer(Point(1 1),0.5)@2001-01-03] */

- Constructor for temporal circular buffers of sequence set subtype

  `tcbufferSeqSet(tcbuffer[]) → tcbufferSeqSet`

  `tcbufferSeqSetGaps(tcbufferInst[],maxt=NULL,maxdist=NULL,interp='linear') →`

  `tcbufferSeqSet`

      SELECT asText(tcbufferSeqSet(ARRAY[tcbuffer
        '[Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(2 2),0.4)@2001-01-02]',
        '[Cbuffer(Point(2 2),0.6)@2001-01-03, Cbuffer(Point(2 2),0.8)@2001-01-04]']));
      /* {[Cbuffer(Point(1 1),0.2)@2001-01-01, Cbuffer(Point(2 2),0.4)@2001-01-02],
         [Cbuffer(Point(2 2),0.6)@2001-01-03, Cbuffer(Point(2 2),0.6)@2001-01-04]} */
      SELECT asText(tcbufferSeqSetGaps(ARRAY[tcbuffer 'Cbuffer(Point(1 1),0.1)@2001-01-01',
        'Cbuffer(Point(1 1),0.3)@2001-01-03', 'Cbuffer(Point(1 1),0.5)@2001-01-05'], '1 day'));
      /* {[Cbuffer(Point(1 1),0.1)@2001-01-01], [Cbuffer(Point(1 1),0.3)@2001-01-03],
         [Cbuffer(Point(1 1),0.5)@2001-01-05]} */

- Construct a temporal circular buffer from a temporal geometry point and a temporal float

  `tcbuffer(tgeompoint, tfloat) → tcbuffer`

  The time frame of the result is the intersection of the time frames of the temporal point and the temporal float. If they do not intersect, a null value is returned

      SELECT asText(tcbuffer(tgeompoint '[POINT(1 1)@2001-01-01, POINT(2 2)@2001-01-02)',
        tfloat '[1@2001-01-01, 2@2001-01-02)'));
      -- [Cbuffer(Point(1 1),1)@2001-01-01, Cbuffer(Point(1 1),2)@2001-01-02)
      SELECT asText(tcbuffer(tgeompoint '[POINT(1 1)@2001-01-01, POINT(2 2)@2001-01-02)',
        tfloat '[2@2001-01-03, 3@2001-01-04)'));
      -- NULL

## Conversions

A temporal circular buffer 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 `cbuffer` value.

- Convert a temporal circular buffer to a temporal geometry point

  `tcbuffer::tgeompoint`

      SELECT asText((tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-02)')::tgeompoint);
      -- [POINT(1 1)@2001-01-01, POINT(1 1)@2001-01-02)

- Convert a temporal circular buffer to a temporal float

  `tcbuffer::tfloat`

      SELECT (tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-02)')::tfloat;
      -- [0.2@2001-01-01, 0.3@2001-01-02)

## Accessors

- Return the values

  `getValues(tcbuffer) → cbufferset`

      SELECT asText(getValues(tcbuffer '{[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02)}'));
      -- {"Cbuffer(Point(1 1),0.3)","Cbuffer(Point(1 1),0.5)"}
      SELECT asEWKT(getValues(tcbuffer 'SRID=5676;{[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-02)}'));
      -- SRID=5676;{"Cbuffer(Point(1 1),0.3)"}

- Return the points or the radii

  `points(tcbuffer) → geomset`

  `radius(tcbuffer) → floatset`

      SELECT asEWKT(points(tcbuffer 'SRID=5676;{Cbuffer(Point(3 3), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02}'));
      -- SRID=5676;{Point(1 1), Point(3 3)}
      SELECT radius(tcbuffer 'SRID=5676;{Cbuffer(Point(3 3), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02}');
      -- {0.3, 0.5}

- Return the value at a timestamp

  `valueAtTimestamp(tcbuffer,timestamptz) → cbuffer`

      SELECT asText(valueAtTimestamp(tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(3 3), 0.5)@2001-01-03)', '2001-01-02'));
      -- Cbuffer(Point(2 2),0.4)

## Transformations

- Transform a temporal circular buffer to another subtype

  `tcbufferInst(tcbuffer) → tcbufferInst`

  `tcbufferSeq(tcbuffer) → tcbufferSeq`

  `tcbufferSeqSet(tcbuffer) → tcbufferSeqSet`

      SELECT asText(tcbufferSeq(tcbuffer 'Cbuffer(Point(1 1), 0.5)@2001-01-01', 'discrete'));
      -- {Cbuffer(Point(1 1),0.5)@2001-01-01}
      SELECT asText(tcbufferSeq(tcbuffer 'Cbuffer(Point(1 1), 0.5)@2001-01-01'));
      -- [Cbuffer(Point(1 1),0.5)@2001-01-01]
      SELECT asText(tcbufferSeqSet(tcbuffer 'Cbuffer(Point(1 1), 0.5)@2001-01-01'));
      -- {[Cbuffer(Point(1 1),0.5)@2001-01-01]}

- Transform a temporal circular buffer to another interpolation

  `setInterp(tcbuffer, interp) → tcbuffer`

      SELECT asText(setInterp(tcbuffer 'Cbuffer(Point(1 1),0.2)@2001-01-01','linear'));
      -- [Cbuffer(Point(1 1),0.2)@2001-01-01]
      SELECT asText(setInterp(tcbuffer '{[Cbuffer(Point(1 1),0.1)@2001-01-01],
        [Cbuffer(Point(1 1),0.2)@2001-01-02]}', 'discrete'));
      -- {Cbuffer(Point(1 1),0.1)@2001-01-01, Cbuffer(Point(1 1),0.2)@2001-01-02}

- Round the points and the radii of the temporal circular buffer to the number of decimal places

  `round(tcbuffer,integer) → tcbuffer`

      SELECT asText(round(tcbuffer '{[Cbuffer(Point(1 1.123456789),0.123456789)@2001-01-01,
        Cbuffer(Point(1 1),0.5)@2001-01-02)}', 3));
      -- {[Cbuffer(Point(1 1.123),0.123)@2001-01-01, Cbuffer(Point(1 1),0.5)@2001-01-02)}

## Spatial Operations

- Return or set the spatial reference identifier

  `SRID(tcbuffer) → integer`

  `setSRID(tcbuffer) → tcbuffer`

      SELECT SRID(tcbuffer 'SRID=5676;[Cbuffer(Point(0 0),1)@2001-01-01,
        Cbuffer(Point(1 1),2)@2001-01-02)');
      -- 5676
      SELECT asEWKT(setSRID(tcbuffer '[Cbuffer(Point(0 0),1)@2001-01-01,
        Cbuffer(Point(1 1),2)@2001-01-02)', 5676));
      -- SRID=5676;[Cbuffer(POINT(0 0),1)@2001-01-01, Cbuffer(POINT(1 1),2)@2001-01-02)

- Transform to a spatial reference identifier

  `transform(tcbuffer,integer) → tcbuffer`

  `transformPipeline(tcbuffer,pipeline text,to_srid integer,is_forward bool=true) →`

  `tcbuffer`

  The `transform` function specifies the transformation with a target SRID. An error is raised when the input temporal circular buffer 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 circular buffer is ignored, and the SRID of the output temporal circular buffer 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(tcbuffer 'SRID=4326;Cbuffer(Point(4.35 50.85),1)@2001-01-01',
        3812));
      -- SRID=3812;Cbuffer(POINT(648679.0180353033 671067.0556381135),1)@2001-01-01

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

- Return the area traversed by the temporal circular buffer

  `traversedArea(tcbuffer) → geometry`

      SELECT ST_AsText(traversedArea(tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]'));
      -- CURVEPOLYGON(CIRCULARSTRING(0.7 1,1.3 1,0.7 1))

## Distance Operations

- Return the smallest distance ever

  `{geo,cbuffer,tcbuffer} |=| {geo,cbuffer,tcbuffer} → float`

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

      SELECT tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-02]' |=| geometry 'Linestring(50 50,55 55)';
      -- 67.58225099390856
      SELECT tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-02]' |=| cbuffer 'Cbuffer(Point(1 1), 0.5)';
      -- 0.6142135623730951

- TODO Return the instant of the first temporal circular buffer at which the two arguments are at the nearest distance

  `nearestApproachInstant({geo,cbuffer,tcbuffer},{geo,cbuffer,tcbuffer}) → tcbuffer`

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

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

  `shortestLine({geo,cbuffer,tcbuffer},{geo,cbuffer,tcbuffer}) → geometry`

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

      SELECT ST_AsText(shortestLine(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-02]', geometry 'Linestring(50 50,55 55)'));
      -- LINESTRING(50 50,2.212132034355964 2.212132034355964)
      SELECT ST_AsText(shortestLine(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-02]', cbuffer 'Cbuffer(Point(1 1), 0.5)'));
      -- LINESTRING(1.353553390593274 1.353553390593274,1.787867965644036 1.787867965644036)

- TODO Return the temporal distance

  `{geo,cbuffer,tcbuffer} <-> {geo,cbuffer,tcbuffer} → tfloat`

      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-03]' <-> cbuffer 'Cbuffer(Point(1 1), 0.2)';
      -- [2.34988300875063@2001-01-02 00:00:00+01, 2.34988300875063@2001-01-03 00:00:00+01]
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 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 tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-03]' <->
        tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-02, Cbuffer(Point(1 1), 0.5)@2001-01-04]'
      -- [2.34988300875063@2001-01-02 00:00:00+01, 2.34988300875063@2001-01-03 00:00:00+01]

## Restrictions

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

  `atValues(tcbuffer,values) → tcbuffer`

  `minusValues(tcbuffer,values) → tcbuffer`

      SELECT asText(atValues(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-03]', cbuffer 'Cbuffer(Point(2 2), 0.5)'));
      -- {[Cbuffer(Point(2 2),0.5)@2001-01-02]}
      SELECT asText(minusValues(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-03]', cbuffer 'Cbuffer(Point(2 2), 0.5)'));
      /* {[Cbuffer(Point(2 2),0.3)@2001-01-01, Cbuffer(Point(2 2),0.5)@2001-01-02),
         (Cbuffer(Point(2 2),0.5)@2001-01-02, Cbuffer(Point(2 2),0.7)@2001-01-03]} */

- TODO Restrict to (the complement of) a geometry

  `atGeometry(tcbuffer,geometry) → tcbuffer`

  `minusGeometry(tcbuffer,geometry) → tcbuffer`

      SELECT asText(atGeometry(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-03]', 'Polygon((0 0,0 2,2 2,2 0,0 0))'));
      -- {[Cbuffer(POINT(2 2),0.3)@2001-01-01, Cbuffer(POINT(2 2),0.7)@2001-01-03]}
      SELECT asText(minusGeometry(tcbuffer '[Cbuffer(Point(2 2), 0.3)@2001-01-01,
        Cbuffer(Point(2 2), 0.7)@2001-01-03]', 'Polygon((0 0,0 2,2 2,2 0,0 0))'));
      /* {(Cbuffer(Point(2 2),0.342593)@2001-01-01 05:06:40.364673+01,
         Cbuffer(Point(2 2),0.7)@2001-01-03 00:00:00+01]} */

## Comparisons

- Traditional comparisons

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

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

- Ever and always comparisons

  `{tcbuffer,cbuffer} {?=, %=} {tcbuffer,cbuffer} → boolean`

      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.4)@2001-01-03)' ?= cbuffer 'Cbuffer(Point(1 1), 0.3)';
      -- true
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.4)@2001-01-03)' ?=
        tcbuffer '[Cbuffer(Point(1 1), 0.4)@2001-01-01, Cbuffer(Point(1 1), 0.2)@2001-01-03)';
      -- true
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.2)@2001-01-04)' %= cbuffer 'Cbuffer(Point(1 1), 0.2)';
      -- true

- Temporal comparisons

  `tcbuffer {#=, #<>} tcbuffer → tbool`

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

## Bounding Box Operations

- Topological operators

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

      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' && cbuffer 'Cbuffer(Point(1 1), 0.5)';
      -- true
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' @> stbox(cbuffer 'Cbuffer(Point(1 1), 0.5)');
      -- true
      SELECT cbuffer 'Cbuffer(Point(1 1), 0.5)'::geometry <@
        tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01, Cbuffer(Point(1 1), 0.5)@2001-01-02]';
      -- true
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-03]' ~= tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.35)@2001-01-02, Cbuffer(Point(1 1), 0.5)@2001-01-03]';
      -- true

- Position operators

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

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

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

      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' << cbuffer 'Cbuffer(Point(1 1), 0.2)'
      -- false
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' <<| stbox(cbuffer 'Cbuffer(Point(1 1), 0.5)')
      -- false
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' &> cbuffer 'Cbuffer(Point(1 1), 0.3)'::geometry
      -- true
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-02]' >>#
        tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-03, Cbuffer(Point(1 1), 0.5)@2001-01-05]'
      -- true

## Spatial Relationships

- TODO Ever and always relationships

  `eContains(geometry,tcbuffer) → boolean`

  `aContains(geometry,tcbuffer) → boolean`

  `eDisjoint({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `aDisjoint({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `eDwithin({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer},float) → boolean`

  `aDwithin({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer},float) → boolean`

  `eIntersects({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `aIntersects({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `eTouches({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `aTouches({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

      SELECT eContains(geometry 'Polygon((0 0,0 50,50 50,50 0,0 0))',
        tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01, Cbuffer(Point(1 1), 0.3)@2001-01-03)');
      -- false
      SELECT eDisjoint(cbuffer 'Cbuffer(Point(2 2), 0.0)',
        tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01, Cbuffer(Point(1 1), 0.3)@2001-01-03)');
      -- true
      SELECT eIntersects(tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-03)',
        tcbuffer '[Cbuffer(Point(2 2), 0.0)@2001-01-01, Cbuffer(Point(2 2), 1)@2001-01-03)');
      -- false

- TODO Spatiotemporal relationships

  `tContains(geometry,tcbuffer) → boolean`

  `tDisjoint({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `tDwithin({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer},float) → boolean`

  `tIntersects({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

  `tTouches({geometry,cbuffer,tcbuffer},{geometry,cbuffer,tcbuffer}) → boolean`

      SELECT tDisjoint(geometry 'Polygon((0 0,0 50,50 50,50 0,0 0))',
        tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01, Cbuffer(Point(1 1), 0.3)@2001-01-03)');
      -- {[t@2001-01-01 00:00:00+01, t@2001-01-03 00:00:00+01)}
      SELECT tDwithin(tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 1), 0.5)@2001-01-03)', tcbuffer '[Cbuffer(Point(1 1), 0.5)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-03)', 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)} */

## Aggregations

The three aggregate functions for temporal circular buffers are illustrated next.

- Temporal count

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

      WITH Temp(temp) AS (
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01,
          Cbuffer(Point(1 1), 0.3)@2001-01-03)' UNION
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-02,
          Cbuffer(Point(1 1), 0.4)@2001-01-04)' UNION
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-03,
          Cbuffer(Point(1 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(tcbuffer) → {tintSeq,tintSeqSet}`

      WITH Temp(temp) AS (
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01,
          Cbuffer(Point(1 1), 0.3)@2001-01-03)' UNION
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-02,
          Cbuffer(Point(1 1), 0.4)@2001-01-04)' UNION
        SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-03,
          Cbuffer(Point(1 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)} */

- TODO Temporal centroid

  `tCentroid(tcbuffer) → tgeompoint`

      WITH Temp(temp) AS (
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.1)@2001-01-01,
        Cbuffer(Point(1 1), 0.3)@2001-01-03)' UNION
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.2)@2001-01-01,
        Cbuffer(Point(1 1), 0.4)@2001-01-03)' UNION
      SELECT tcbuffer '[Cbuffer(Point(1 1), 0.3)@2001-01-01,
        Cbuffer(Point(1 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 circular buffers. 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 circular buffers, which is an `stbox`, as all spatiotemporal types.

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

- `<<`, `&<`, `&>`, `>>`, `<<|`, `&<|`, `|&>`, `|>>`, which only consider the spatial dimension in temporal circular buffers,

- `<<#`, `&<#`, `#&>`, `#>>`, which only consider the time dimension in temporal circular buffers,

- `&&`, `@>`, `<@`, `~=`, `-|-`, 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.