Chapter 13. Temporal Poses

Table of Contents

Static Poses
Input and Output
Constructors
Conversions
Accessors
Transformations
Spatial Reference System
Distance Operations
Comparisons
Temporal Poses
Validity of Temporal Poses
Input and Output
Constructors
Conversions
Accessors
Transformations
Modifications
Restrictions
Spatial Reference System
Bounding Box Operations
Distance Operations
Spatial Relationships
Comparisons
Aggregations
Indexing
OGC GeoPose v1.0 Support
JSON Input and Output
Quaternion Renormalization
Euler-Angle Accessors
Frame Metadata Registry
Body and World Rigid Transform
Temporal JSON I/O

The pose type is used to represent the location and orientation of geometric objects within coordinate systems anchored to the earth's surface or within other astronomical coordinate systems. The location is represented by a 2D or 3D point. For 2D poses, the orientation is defined by a rotation angle in (-π, π] expressed in radians. For 3D poses, the orientation is defined by four float values W, X, Y, Z, representing a unit quaternion Q = W , X , Y , Z where Q2 = W2 + X2 + Y2 + Z2 = 1 .

The pose constructor accepts quaternions whose norm is within 1e-3 of unit (a tolerance wide enough to absorb the integrator drift typical of sensor-fusion clients such as IMUs and AR/VR runtimes), then renormalizes to exactly unit norm before storing. Way-off norms (e.g., the obvious-bug case (1, 1, 1, 1) where |q| = 2), zero quaternions, and quaternions with NaN/Inf components are rejected up front. The on-disk representation is therefore independent of the caller's floating-point hygiene, and downstream comparison, hashing, SLERP, and Euler-decomposition code can rely on the unit-norm invariant.

The GeoPose Standards Working Group (SWG), working under the auspices of the Open Geospatial Consortium, has defined a standard for exchanging pose information across different users, devices, and platforms. More information about the standard can be found in the GitHub repository of the GeoPose SWG.

The pose type serves as base type for defining the temporal pose type tpose. The tpose type has similar functionality as the temporal point type tgeompoint. Thus, most functions and operators described before for the tgeompoint type are also applicable for the tpose type. In addition, there are specific functions defined for the tpose type.

We cover these functions in this chapter.

The tpose type is used for defining the type trgeometry (that is, temporal rigid geometry) defined in the next chapter. The implementation of the these types in MobilityDB has been studied in the following PhD thesis.

Static Poses

A 2D pose is a couple of the form (point2D,radius) where point2D is a 2D geometric point and radius is a float value representing a rotation angle in (-π, π] expressed in radians. A 3D pose is a tuple of the form (point3D,W,Y,X,Z) where point3D is a 3D geometric point, and W, and X, Y, and Z are four floats representing a unit quaternion. Examples of input of pose values are as follows:

SELECT pose 'Pose(Point(1 1), 0.5)';
SELECT pose 'Pose(Point Z(1 1 1), 0.5, 0.5, 0.5, 0.5)';

An SRID can be specified for a pose either at the begining of the pose literal or before the point literal as shown below.

SELECT pose 'SRID=3812;Pose(Point(1 1), 0.5)';
SELECT pose 'Pose(SRID=5676;Point Z(1 1 1), 0.5, 0.5, 0.5, 0.5)';

As for other spatial types, a pose can be either planar (geometric) or geodetic (geographic). A geodetic pose is denoted by the GeodPose keyword, in the same way that a geodetic stbox is denoted by GEODSTBOX.

SELECT pose 'GeodPose(Point(1 1), 0.5)';
SELECT pose 'SRID=4326;GeodPose(Point Z(1 1 1), 1, 0, 0, 0)';

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

-- Empty point
select pose 'Pose(Point empty, 0.5)';
-- Incorrect point value
SELECT pose 'Pose(Linestring(1 1,2 2), 1.0)';
-- Incorrect radius value
SELECT pose 'Pose(Point(1 1), -10.0)';
-- Incorrect 3D point
SELECT pose 'Pose(Point Z(1 1), 1.0)';
-- Incomplete 3D orientation
SELECT pose 'Pose(Point Z(1 1 1), 1.0)';

We give next the functions and operators for the pose type.

Input and Output

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

    asText({pose,pose[]}) → {text,text[]}

    asEWKT({pose,pose[]}) → {text,text[]}

    SELECT asText(pose 'SRID=4326;Pose(Point(0 0),1)');
    -- Pose(POINT(0 0),1)
    SELECT asText(ARRAY[pose 'Pose(Point(0 0),1)', 'Pose(Point(1 1),2)']);
    -- {"Pose(POINT(0 0),1)","Pose(POINT(1 1),2)"}
    SELECT asEWKT(pose 'SRID=4326;Pose(Point(0 0),1)');
    -- SRID=4326;Pose(Point(0 0),1)
    SELECT asEWKT(ARRAY[pose 'Pose(SRID=5676;Point(0 0),1)', 'Pose(SRID=5676;Point(1 1),2)']);
    -- {"Pose(SRID=5676;POINT(0 0),1)","Pose(SRID=5676;POINT(1 1),2))"}
    
  • Return the Well-Known Binary (WKB), the Extended Well-Known Binary (EWKB), or the Hexadecimal Extended Well-Known Binary (HexEWKB) representation

    asBinary(pose,endian text='') → bytea

    asEWKB(pose,endian text='') → bytea

    asHexEWKB(pose,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(pose 'Pose(Point(1 2),1)');
    -- \x0101000000000000f03f0000000000000040000000000000f03f
    SELECT asEWKB(pose 'SRID=7844;Pose(Point(1 2),1)');
    -- \x0141a41e0000000000000000f03f0000000000000040000000000000f03f
    SELECT asHexEWKB(pose 'SRID=3812;Pose(Point(1 2),1)');
    -- 0141E40E0000000000000000F03F0000000000000040000000000000F03F
    
  • Input from the Well-Known Text (WKT) or from the Extended Well-Known Text (EWKT) representation

    poseFromText(text) → pose

    poseFromEWKT(text) → pose

    SELECT asEWKT(poseFromText(text 'Pose(Point(1 2),1)'));
    -- Pose(POINT(1 2),1)
    SELECT asEWKT(poseFromEWKT(text 'SRID=3812;Pose(Point(1 2),1)'));
    -- SRID=3812;Pose(Point(1 2),1)
    
  • Input from the Well-Known Binary (WKB), from the Extended Well-Known Binary (EWKB), or from the Hexadecimal Extended Well-Known Binary (HexEWKB) representation

    poseFromBinary(bytea) → pose

    poseFromEWKB(bytea) → pose

    poseFromHexEWKB(text) → pose

    SELECT asEWKT(poseFromBinary(
      '\x0101000000000000f03f0000000000000040000000000000f03f'));
    -- Pose(POINT(1 2),1)
    SELECT asEWKT(poseFromEWKB(
      '\x0141a41e0000000000000000f03f0000000000000040000000000000f03f'));
    -- SRID=7844;Pose(Point(1 2),1)
    SELECT asEWKT(poseFromHexEWKB(
      '0141E40E0000000000000000F03F0000000000000040000000000000F03F'));
    -- SRID=3812;Pose(POINT(1 2),1)
    

Constructors

  • Constructor for poses

    pose(geompoint2D,float) → pose

    pose(geompoint3D,float,float,float,float) → pose

    SELECT asText(pose(ST_Point(1,1), radians(45)), 6);
    -- Pose(POINT(1 1),0.785398)
    SELECT asEWKT(pose(ST_Point(1,1,3812), radians(45)), 6);
    -- SRID=3812;Pose(POINT(1 1),0.785398)
    SELECT asText(pose(ST_PointZ(1,1,1), 1, 0, 0, 0));
    -- Pose(POINT Z (1 1 1),1,0,0,0)
    

Conversions

Values of the pose type can be converted to the geometry point type using an explicit CAST or using the :: notation as shown below.

  • Convert a pose and, optionally, a timestamp or a period, into a spatiotemporal box

    pose::stbox

    stbox(pose) → stbox

    stbox(pose,{timestamptz,tstzspan}) → stbox

    SELECT stbox(pose 'SRID=5676;Pose(Point(1 1),0.3)');
    -- SRID=5676;STBOX X((1,1),(1,1))
    SELECT stbox(pose 'Pose(Point(1 1),0.3)', timestamptz '2001-01-01');
    -- STBOX XT(((1,1),(1.3,1.3)),[2001-01-01, 2001-01-01])
    SELECT stbox(pose 'Pose(Point(1 1),0.3)', tstzspan '[2001-01-01,2001-01-02]');
    -- STBOX XT(((1,1),(1.3,1.3)),[2001-01-01, 2001-01-02])
    
  • Convert a pose into geometry point

    pose::geompoint

    SELECT ST_AsText(pose(ST_Point(1, 1), 1)::geometry);
    -- Point(1 1)
    SELECT ST_AsEWKT(pose(ST_PointZ(1, 1, 1, 5676), 1, 0, 0, 0)::geometry);
    -- SRID=5676;POINT(1 1 1)
    

Accessors

  • Return the point

    point(pose) → geompoint

    SELECT ST_AsText(point(pose 'Pose(Point(1 1), 0.3)'));
    -- Point(1 1)
    
  • Return the rotation

    rotation(pose2D) → float

    SELECT rotation(pose 'Pose(Point(1 1), 0.3)');
    -- 0.3
    
  • Return the orientation

    orientation(pose3D) → (X,W,Z,T)

    SELECT orientation(pose 'Pose(Point Z(1 1 1), 0, 0, 0, 1)');
    -- (0, 0, 0, 1)
    

Transformations

  • Round the point and the orientation of the pose to the number of decimal places

    round(pose,integer=0) → pose

    SELECT asText(round(pose(ST_Point(1.123456789,1.123456789), 0.123456789), 6));
    -- Pose(POINT(1.123457 1.123457),0.123457)
    

Spatial Reference System

  • Return or set the spatial reference identifier

    SRID(pose) → integer

    setSRID(pose) → pose

    SELECT SRID(pose 'Pose(SRID=5676;Point(1 1), 0.3)');
    -- 5676
    SELECT asEWKT(setSRID(pose 'Pose(Point(0 0),1)', 4326));
    -- SRID=4326;Pose(POINT(0 0),1)
    
  • Transform to a spatial reference identifier

    transform(pose,integer) → pose

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

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

    For a 3D pose the orientation is re-expressed in the basis of the target frame at the pose's point. The correction is defined for the canonical OGC GeoPose pair, WGS-84 geographic (EPSG:4326) ↔ WGS-84 ECEF (EPSG:4978), and takes the standard East-North-Up basis at the geographic point as the rotation pivot. For any other pair of SRIDs, transform emits a NOTICE and passes the orientation through unchanged. For a 2D pose the angle is intrinsic to the source projection and is passed through unchanged.

    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 pose is ignored, and the SRID of the output pose 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(pose 'SRID=4326;Pose(Point(4.35 50.85),1)', 3812), 6);
    -- SRID=3812;Pose(POINT(648679.018035 671067.055638),1)
    
    -- Round-tripping a 3D pose through ECEF and back lands at the input pose
    SELECT asEWKT(round(
      transform(transform(pose 'SRID=4326;Pose(Point(8 47 0), 1, 0, 0, 0)', 4978),
                4326), 6));
    -- SRID=4326;Pose(POINT Z (8 47 0),1,0,0,0)
    
    -- At the equator-meridian (lat=lon=0), the body identity quaternion in the
    -- ECEF basis is the canonical East-North-Up to ECEF rotation
    SELECT asEWKT(round(
      transform(pose 'SRID=4326;Pose(Point(0 0 0), 1, 0, 0, 0)', 4978), 6));
    -- SRID=4978;Pose(POINT Z (6378137 0 0),0.5,-0.5,-0.5,-0.5)
    
    WITH test(pose, pipeline) AS (
      SELECT pose 'Pose(SRID=4326;Point(4.3525 50.846667),1)',
        text 'urn:ogc:def:coordinateOperation:EPSG::16031' )
    SELECT asEWKT(transformPipeline(transformPipeline(pose, pipeline, 4326), pipeline,
      4326, false), 6)
    FROM test;
    -- SRID=4326;Pose(POINT(4.3525 50.846667),1)
    

Distance Operations

  • Return the distance

    distance({geo,stbox,pose},pose) → float

    distance(pose,{geo,stbox,pose}) → float

    {geo,stbox,pose} <-> pose → float

    Only the point component of the pose is taken into account, the orientation is ignored. The distance is computed in two dimensions, even when the arguments are three-dimensional. The result is NULL when the geometry is empty.

    SELECT round(distance(geometry 'Point(1 0)', pose 'Pose(Point(4 0),0)'), 6);
    -- 3
    SELECT round(distance(stbox 'STBOX X((1,-1),(3,1))', pose 'Pose(Point(4 0),0)'), 6);
    -- 1
    SELECT round(pose 'Pose(Point(0 0),0)' <-> pose 'Pose(Point(3 4),0)', 6);
    -- 5
    SELECT round(pose 'Pose(Point(0 0 0), 1, 0, 0, 0)' <->
      pose 'Pose(Point(3 4 12), 1, 0, 0, 0)', 6);
    -- 5
    SELECT round(geometry 'Point empty' <-> pose 'Pose(Point(4 0),0)', 6);
    -- NULL
    

Comparisons

The comparison operators (=, <, and so on) are available for poses. 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 poses.

  • Traditional comparisons

    pose {=, <>, <, >, <=, >=} pose

    SELECT pose 'Pose(Point(3 3), 0.5)' = pose 'Pose(Point(3 3), 0.5)';
    -- true
    SELECT pose 'Pose(Point(3 3), 0.5)' <> pose 'Pose(Point(3 3), 0.6)';
    -- true
    SELECT pose 'Pose(Point(3 3), 0.5)' < pose 'Pose(Point(3 3), 0.6)';
    -- true
    SELECT pose 'Pose(Point(3 3), 0.6)' > pose 'Pose(Point(2 2), 0.6)';
    -- true
    SELECT pose 'Pose(Point Z(1 1 1), 0.5, 0.5, 0.5, 0.5)' <= pose 'Pose(Point Z(2 2 2), 0.5, 0.5, 0.5, 0.5)';
    -- true
    SELECT pose 'Pose(Point(1 1), 0.6)' >= pose 'Pose(Point(1 1), 0.5)';
    -- true
    
  • Are the poses approximately equal with respect to an epsilon value?

    pose ~= pose → boolean

    SELECT pose 'Pose(SRID=5676;Point(1 1), 0.3)' ~=
      pose 'Pose(SRID=5676;Point(1 1.0000001), 0.30000001)';
    -- true