Soporte de OGC GeoPose v1.0

El tipo pose implementa el modelo de datos que subyace al estándar OGC GeoPose v1.0: una position más una orientation en un marco de referencia conocido, donde la orientación es un cuaternión unitario en la convención de Hamilton o, de forma equivalente, una terna yaw / pitch / roll bajo la convención Tait-Bryan intrínseca ZYX. Esta sección agrupa la superficie SQL que expone esa interoperabilidad: E/S JSON para las clases de conformidad Basic y Advanced del estándar, una función auxiliar de renormalización explícita para quienes ejecutan composiciones largas, y los accesores de ángulos de Euler que esperan los consumidores Basic-YPR.

Entrada y salida JSON

  • Convierte hacia o desde la codificación JSON de OGC GeoPose v1.0 (clase de conformidad Basic-Quaternion, Basic-YPR o Advanced)

    asGeoPose(pose,conformance integer=0,maxdecimaldigits integer=-1) → text
    poseFromGeoPose(text) → pose
    

    El argumento conformance selecciona la clase de salida: 0 = Basic-Quaternion (predeterminado, sin pérdida), 1 = Basic-YPR (yaw, pitch, roll en grados, Tait-Bryan intrínseco ZYX), 2 = Advanced. El argumento maxdecimaldigits es el número de dígitos significativos que se conservan en los números JSON; -1 utiliza el valor predeterminado sin pérdida de json-c. La función de entrada detecta automáticamente la clase de conformidad a partir de las claves JSON, donde el miembro frameSpecification implica Advanced, el miembro quaternion implica Basic-Quaternion y el miembro angles implica Basic-YPR. Las clases Basic llevan la posición en un miembro position y dejan el marco implícito. La clase Advanced no tiene miembro de posición: nombra su marco exterior de forma explícita y la pose se sitúa en el origen de ese marco, de modo que las dos codificaciones de una misma pose se releen iguales. Todas las clases exigen un marco exterior geográfico, por lo que la pose debe ser geodésica; una pose planar se rechaza en el límite de la conversión, al igual que un SRID proyectado.

    SELECT asGeoPose(pose 'Geodpose(Point(8 47 1500), 0.707107, 0, 0, 0.707107)', 0, 6);
    /* {"position":{"lat":47,"lon":8,"h":1500},"quaternion":{"x":0,"y":0,"z":0.707107,
       "w":0.707107}} */
    SELECT asGeoPose(pose 'Geodpose(Point(8 47 1500), 0.707107, 0, 0, 0.707107)', 1, 6);
    -- {"position":{"lat":47,"lon":8,"h":1500},"angles":{"yaw":90,"pitch":0,"roll":0}}
    SELECT asGeoPose(pose 'Geodpose(Point(8 47 1500), 0.707107, 0, 0, 0.707107)', 2, 6);
    /* {"frameSpecification":{"authority":"/geopose/1.0","id":"LTP-ENU","parameters":
       "longitude=8&latitude=47&height=1500&crs=EPSG:4979"},
       "quaternion":{"x":0,"y":0,"z":0.707107,"w":0.707107}} */
    SELECT asEWKT(poseFromGeoPose( '{"position":{"lat":47,"lon":8,"h":1500},
      "angles":{"yaw":90,"pitch":0,"roll":0}}'), 6);
    -- SRID=4326;GeodPose(POINT Z (8 47 1500),0.707107,0,0,0.707107)
    

Renormalización de cuaterniones

  • Devuelve una pose cuyo cuaternión de orientación se ha llevado a una norma unitaria

    poseNormalize(pose) → pose
    

    Una pose 2D se devuelve sin cambios, ya que su orientación es un único ángulo. A una pose 3D se le divide el cuaternión por su norma euclidiana, de modo que composiciones largas de SLERP (o cualquier otra vía que acumule deriva de punto flotante en |q|) pueden restaurarse al invariante |q| = 1 que requiere el código de SLERP y de descomposición de Euler.

    SELECT asText(poseNormalize(pose 'Pose(Point(1 1 1), 0.5, 0.5, 0.5, 0.5)'));
    -- Pose(POINT Z (1 1 1),0.5,0.5,0.5,0.5)
    
  • Devuelve la inversa de una pose

    poseInverse(pose) → pose
    

    La inversa invierte la relación entre marcos: donde una pose lleva un valor del marco que nombra al marco en el que ella misma está expresada, la inversa lo lleva de vuelta. Es R_BA = R_AB^T y t_BA = −R_AB^T t_AB, de modo que componer una pose con su inversa da la identidad. Esto es lo que expresa una posición del mundo en el marco de un vehículo. Una pose sobre un marco geográfico no tiene inversa: el marco que nombraría no es un marco del elipsoide.

    SELECT asEWKT(round(poseInverse(pose(ST_Point(10,5), pi()/2)), 6));
    -- Pose(POINT(-5 10),-1.570796)
    SELECT asEWKT(round(applyPose(poseInverse(pose(ST_Point(10,5), pi()/2)),
      pose(ST_Point(10,5), pi()/2)), 6));
    -- Pose(POINT(0 0),0)
    SELECT asEWKT(round(poseInverse(pose 'Pose(Point(1 0 0), 0, 0, 0, 1)'), 6));
    -- Pose(POINT Z (1 0 0),0,0,0,-1)
    

Accesores de ángulos de Euler

  • Devuelve el ángulo yaw, pitch o roll de una pose, en radianes, bajo la convención Tait-Bryan intrínseca ZYX que exige la clase de conformidad Basic-YPR de OGC GeoPose

    yaw({pose,tpose}) → {float,tfloat}
    pitch({pose,tpose}) → {float,tfloat}
    roll({pose,tpose}) → {float,tfloat}
    

    Para una pose 2D yaw devuelve la rotación almacenada theta, por convención el yaw del marco del cuerpo, y pitch y roll devuelven 0. Para una pose 3D los tres valores provienen de la descomposición Tait-Bryan intrínseca ZYX del cuaternión de orientación. El término asin del pitch se acota a [-1, 1] para absorber la pequeña deriva numérica que pueden introducir las composiciones largas de cuaterniones. Sobre una pose temporal la interpolación del resultado sigue a la dimensión. Una pose 2D interpola su ángulo almacenado linealmente y ese ángulo es su yaw, de modo que un tpose 2D lineal se proyecta a un tfloat lineal. Una pose 3D interpola mediante SLERP, cuya descomposición Tait-Bryan no es lineal en el tiempo, de modo que un tpose 3D se proyecta a un tfloat de pasos: leerlo entre dos instantes responde el ángulo del instante de la izquierda en lugar de un ángulo que ninguna pose tiene.

    SELECT yaw(pose 'Pose(Point(1 1), 0.5)');
    -- 0.5
    SELECT pitch(pose 'Pose(Point(1 1), 0.5)');
    -- 0
    SELECT roll(pose 'Pose(Point(0 0 0), 0.7071067811865476, 0, 0, 0.7071067811865475)');
    -- 0
    SELECT yaw(pose 'Pose(Point(0 0 0), 0.7071067811865476, 0, 0, 0.7071067811865475)');
    -- 1.5707963267948966
    
    SELECT asText(yaw(tpose '[Pose(Point(0 0), 0.0)@2001-01-01,
      Pose(Point(1 1), 0.5)@2001-01-02]'));
    -- [0@2001-01-01, 0.5@2001-01-02]
    
  • Devuelve la orientación de una pose como una terna yaw / pitch / roll, en radianes

    ypr(pose) → ypr
    

    Esta es la codificación Basic-YPR de la orientación en OGC GeoPose, la contraparte de quaternion, y devuelve los tres ángulos que yaw, pitch y roll responden de uno en uno. Al igual que quaternion, está definida para ambas dimensiones: una pose 2D gira por su ángulo almacenado y no cabecea ni alabea. Los ángulos están en radianes, mientras que la codificación JSON de GeoPose los escribe en grados.

    SELECT ypr(pose 'Pose(Point(1 1), 0.5)');
    -- (0.5,0,0)
    SELECT ypr(pose 'Pose(Point Z(1 1 1), 1, 0, 0, 0)');
    -- (0,0,0)
    SELECT ypr(pose 'Pose(Point Z(1 1 1), 0.5, 0.5, 0.5, 0.5)');
    -- (1.5707963267948966,0,1.5707963267948966)
    
  • Eleva los accesores de ángulos de Euler sobre una pose temporal, devolviendo un flotante temporal en radianes

    yaw(tpose) → tfloat
    pitch(tpose) → tfloat
    roll(tpose) → tfloat
    

    El resultado lleva interpolación de pasos, ya que un ángulo leído de una rotación no varía linealmente entre dos poses.

    SELECT round(yaw(tpose '[Pose(Point(0 0),0.5)@2001-01-01,
      Pose(Point(1 1),1.5)@2001-01-02]'), 6);
    -- Interp=Step;[0.5@2001-01-01, 1.5@2001-01-02]
    SELECT round(pitch(tpose 'Pose(Point(0 0 0),1,0,0,0)@2001-01-01'), 6);
    -- 0@2001-01-01
    

Registro de metadatos de marcos

El estándar OGC GeoPose v1.0 distingue el marco exterior (la referencia global, por ejemplo, WGS-84 geográfico o ECEF) del marco interior (el marco del cuerpo cuya orientación es el cuaternión de la pose). Las clases de conformidad Basic exigen WGS-84 geográfico como marco exterior y un marco interior implícito de ejes de cuerpo dextrógiros; la clase Advanced nombra su marco exterior de forma explícita y sitúa la pose en el origen de ese marco.

En MobilityDB, el tipo pose codifica el marco exterior de forma implícita mediante su SRID y utiliza el marco interior convencional de ejes de cuerpo dextrógiros. La tabla geopose_frames registra esta correspondencia y declara todos los identificadores de marco que un documento puede nombrar: una serie de secuencia compuesta nombra LTP-ENU y RotateTranslate, y una cadena nombra esos mismos dos marcos como /Extrinsic/LTP-ENU e /Intrinsic/Translate-Rotate, todos bajo la autoridad /geopose/1.0. Las pilas de marcos de la clase Advanced no están admitidas.

SELECT frame_id, authority, code, name FROM geopose_frames ORDER BY frame_id;
-- 1 | EPSG | 4326 | WGS-84 geographic (lat/lon/h)
-- 2 | EPSG | 4978 | WGS-84 ECEF (Earth-Centred Earth-Fixed)
-- 3 | OGC | LTP | Local Tangent Plane (East-North-Up)
-- 4 | OGC | BODY | Right-handed body axes (default inner frame)
-- 5 | /geopose/1.0 | LTP-ENU | GeoPose outer frame of a Composite Sequence Series
-- 6 | /geopose/1.0 | RotateTranslate | GeoPose inner frame of a Composite Sequence Series
-- 7 | /geopose/1.0 | /Extrinsic/LTP-ENU | GeoPose outer frame of a Chain
-- 8 | /geopose/1.0 | /Intrinsic/Translate-Rotate | GeoPose inner frame of a Chain

Las filas anteriores provienen de MEOS, que las construye a partir de los identificadores que escriben los codificadores, de modo que un marco que esta compilación puede emitir nunca falta en el catálogo. Un usuario registra marcos adicionales insertándolos en la tabla. La columna is_geographic, omitida arriba, es verdadera únicamente para el marco geográfico WGS-84.

  • Devuelve todos los marcos que nombran el estándar y los codificadores

    geoPoseFrames() → setof record
    
    SELECT count(*) FROM geoPoseFrames();
    -- 8
    

Tres funciones auxiliares SQL proporcionan una interfaz de consulta estable que es independiente de la disposición de la tabla, y leen la tabla para que también se encuentre un marco que registre un usuario:

  • Devuelve el SRID de un marco, o NULL si el marco es paramétrico (LTP, BODY)

    geoPoseFrameSRID(int) → int
    
    SELECT geoPoseFrameSRID(1), geoPoseFrameSRID(2);
    -- 4326 | 4978
    SELECT geoPoseFrameSRID(3);
    -- NULL
    
  • Devuelve el nombre legible del marco

    geoPoseFrameName(int) → text
    
    SELECT geoPoseFrameName(1);
    -- WGS-84 geographic (lat/lon/h)
    SELECT geoPoseFrameName(2), geoPoseFrameName(3);
    -- WGS-84 ECEF (Earth-Centred Earth-Fixed) | Local Tangent Plane (East-North-Up)
    
  • Devuelve true para los marcos lat/lon/h, false para los cartesianos o proyectados

    geoPoseFrameIsGeographic(int) → boolean
    
    SELECT geoPoseFrameIsGeographic(1), geoPoseFrameIsGeographic(2);
    -- true | false
    

Los usuarios pueden registrar marcos personalizados insertándolos en geopose_frames; el catálogo está marcado como una tabla de configuración, por lo que pg_dump preserva las filas del usuario.

Transformación rígida cuerpo↔mundo

  • Aplica la transformación de cuerpo rígido codificada por una pose (la correspondencia cuerpo al mundo de OGC GeoPose) a una geometría en el marco del cuerpo, produciendo la geometría correspondiente en el marco del mundo

    applyPose(geometry,pose) → geometry
    applyPose(geometry,tpose) → tgeompoint
    applyPose(pose,pose) → pose
    

    La transformación es

    world = R(q) · body + p
    

    donde (p, q) son la posición y la orientación de la pose. La forma estática toma una única pose y una geometría estática; la forma temporal eleva la transformación rígida por instante de una tpose a lo largo de sus instantes, produciendo una trayectoria tgeompoint en el marco del mundo de la geometría del cuerpo. La forma estática admite una geometría de cuerpo de cualquier tipo y lleva su forma al marco de la pose; la forma temporal devuelve una tgeompoint, por lo que su geometría de cuerpo debe ser un punto. La pose y la geometría del cuerpo deben tener la misma dimensionalidad y deben coincidir en su SRID, adoptando el desconocido el del otro. La interpolación lineal de la trayectoria resultante es la cuerda de cada segmento, que aproxima la verdadera trayectoria de cuerpo rígido (un arco circular bajo SLERP), la misma concesión que MobilityDB ya adopta para las trayectorias espaciales.

    -- A body sensor offset 1 unit along the body X axis, traced through a tpose that ends 90
    -- degrees yawed and translated to (10, 20).
    SELECT asText(applyPose(ST_Point(1,0), tpose '[Pose(Point(0 0), 0)@2026-01-01,
      Pose(Point(10 20), 1.5707963267948966)@2026-01-02]'));
    -- [POINT(1 0)@2026-01-01, POINT(10 21)@2026-01-02]
    
    -- The shape of the body geometry is carried into the pose's frame: a hull 4 by 2 about
    -- the body origin, placed at (10, 20)
    SELECT ST_AsText(applyPose(ST_MakeEnvelope(-2,-1,2,1), pose 'Pose(Point(10 20), 0)'));
    -- POLYGON((8 19,8 21,12 21,12 19,8 19))
    

    Cualquiera de los dos marcos puede moverse. Aplicar una pose a una tpose lleva un cuerpo fijo a través de todo el movimiento de su padre, que es lo que pide un sensor montado sobre un vehículo en movimiento; aplicar una tpose a una pose lleva un cuerpo en movimiento a un marco que permanece quieto; y aplicar una tpose a una tpose compone dos marcos que ambos se mueven, sobre el tiempo que comparten. poseInverse se eleva de la misma manera, cambiando el punto de vista sobre un objeto en movimiento durante todo su movimiento.

    applyPose(pose,tpose) → tpose
    applyPose(tpose,pose) → tpose
    applyPose(tpose,tpose) → tpose
    poseInverse(tpose) → tpose
    
    -- A sensor one unit ahead of a body that ends yawed a quarter turn at (10, 20) ends one
    -- unit north of it
    SELECT asEWKT(round(applyPose(pose 'Pose(Point(1 0), 0)',
      tpose '[Pose(Point(0 0), 0)@2026-01-01,
      Pose(Point(10 20), 1.5707963267948966)@2026-01-02]'), 6));
    -- [Pose(POINT(1 0),0)@2026-01-01, Pose(POINT(10 21),1.570796)@2026-01-02]
    

    Una pose nombra un marco, por lo que ella misma es un valor en el marco del cuerpo: aplicar una pose a otra compone las dos relaciones entre marcos. Escribiendo P_WV para la pose de un vehículo en el mundo y P_VS para la pose de un sensor en el vehículo, la pose del sensor en el mundo es P_WS = P_WV ∘ P_VS. Esta es la operación que una cadena de poses pliega sobre sus eslabones, de modo que una cadena de dos eslabones se compone en lo que esto devuelve.

    -- A sensor one unit ahead of a vehicle turned a quarter turn is one unit North of it, and
    -- carries the vehicle's orientation
    SELECT asEWKT(round(applyPose(pose 'Pose(Point(1 0), 0)', pose(ST_Point(0,0), pi()/2)),
      6));
    -- Pose(POINT(0 1),1.570796)
    SELECT asEWKT(round(applyPose(pose 'Pose(Point(1 0), 0)', pose(ST_Point(10,5), pi()/2)),
      6));
    -- Pose(POINT(10 6),1.570796)
    SELECT asEWKT(round(applyPose(pose 'Pose(Point(1 0 0), 1, 0, 0, 0)',
      pose 'Pose(Point(0 0 5), 1, 0, 0, 0)'), 6));
    -- Pose(POINT Z (1 0 5),1,0,0,0)
    

E/S JSON temporal

  • Convierte una pose temporal hacia o desde su codificación OGC GeoPose v1.0

    asGeoPose(tpose,conformance integer=0,maxdecimaldigits integer=-1) → text
    tposeFromGeoPose(text) → tpose
    

    La clase de conformidad se deduce del valor. Un instante único es un documento Basic que lleva su validTime; una secuencia o un conjunto de secuencias es una serie de secuencia compuesta, de la clase Regular cuando los instantes están igualmente espaciados por un número entero de milisegundos y de la clase Irregular en caso contrario. El argumento conformance elige la codificación de la orientación de un documento Basic, como lo hace para una pose, y no tiene efecto sobre una serie, cuyos marcos interiores llevan un cuaternión y no ofrecen tal elección. Una serie declara su marco exterior una sola vez, como el marco Este-Norte-Arriba del plano tangente local en la posición de la primera pose, y da cada pose como un marco interior que contiene su traslación desde ese punto de tangencia, en metros, y su rotación relativa a la base de ese marco. Los tiempos son valores GeoPose_Instant, es decir, tiempo Unix en milisegundos enteros, que es lo que el estándar exige de toda posición temporal que define. Por lo tanto, el muestreo por debajo del milisegundo no se lleva; la codificación MF-JSON, que el estándar que la sustenta tipa como una cadena de fecha y hora, sí lo lleva. El modelo de transición informa de la interpolación. La interpolación lineal es el modelo interpolate del estándar y las interpolaciones de pasos y discreta son su modelo none; puesto que esos dos identificadores no distinguen la de pasos de la discreta, los parameters del modelo nombran la interpolación con las mismas palabras que usa la codificación MF-JSON.

    SELECT asGeoPose(tpose '[Pose(Point(0 0 0), 0.5, 0.5, 0.5, 0.5)@2026-01-01,
      Pose(Point(0 0 100), 0.5, 0.5, 0.5, 0.5)@2026-01-02,
      Pose(Point(0 0 300), 0.5, 0.5, 0.5, 0.5)@2026-01-05]', 0, 6);
    -- {
    --  "header": {
    --   "poseCount": 3,
    --   "startInstant": 1767254400000,
    --   "stopInstant": 1767600000000,
    --   "transitionModel": {
    --    "authority": "/geopose/1.0",
    --    "id": "interpolate",
    --    "parameters": "interpolation=Linear"
    --   }
    --  },
    --  "outerFrame": {
    --   "authority": "/geopose/1.0",
    --   "id": "LTP-ENU",
    --   "parameters": "longitude=0&latitude=0&height=0&crs=EPSG:4979"
    --  },
    --  "innerFrameAndTimeSeries": [
    --   {
    --    "frame": {
    --     "authority": "/geopose/1.0",
    --     "id": "RotateTranslate",
    --     "parameters": "translation=[0, 0, 0]&rotation=[0.5, 0.5, 0.5, 0.5]"
    --    },
    --    "validTime": 1767254400000
    --   },
    --   {
    --    "frame": {
    --     "authority": "/geopose/1.0",
    --     "id": "RotateTranslate",
    --     "parameters": "translation=[0, 0, 100]&rotation=[0.5, 0.5, 0.5, 0.5]"
    --    },
    --    "validTime": 1767340800000
    --   },
    --   {
    --    "frame": {
    --     "authority": "/geopose/1.0",
    --     "id": "RotateTranslate",
    --     "parameters": "translation=[0, 0, 300]&rotation=[0.5, 0.5, 0.5, 0.5]"
    --    },
    --    "validTime": 1767600000000
    --   }
    --  ],
    --  "trailer": {
    --   "poseCount": 3
    --  }
    -- }
    

    Una serie no tiene ni huecos ni límites abiertos, de modo que un conjunto de secuencias se aplana en una única secuencia cerrada y la inclusión de los límites de una secuencia no se conserva. La lectura acepta además la envoltura TemporalGeoPose que escribían versiones anteriores, un arreglo de objetos de la clase Basic cada uno con un validTime añadido, de modo que los datos ya almacenados en esa forma siguen cargándose.

    La forma de esa envoltura es

    {
      "type":          "TemporalGeoPose",
      "version":       "1.0",
      "conformance":   "Basic-Quaternion" | "Basic-YPR",
      "interpolation": "None" | "Discrete" | "Step" | "Linear",
      "instants":      [...]                  // for TInstant + TSequence
      "lower_inc":     true|false,            // for TSequence only
      "upper_inc":     true|false,            // for TSequence only
      "sequences":     [{...}, ...]           // for TSequenceSet only
    }
    
    SELECT asGeoPose(tpose '[Pose(Point(8 47), 0)@2026-01-01,
      Pose(Point(9 48), 0.5)@2026-01-02]', 1, 4);
    /* {"type":"TemporalGeoPose","version":"1.0","conformance":"Basic-YPR",
       "interpolation":"Linear","lower_inc":true,"upper_inc":true,
       "instants":[
       {"position":{"lat":47,"lon":8,"h":0},
       "angles":{"yaw":0,"pitch":0,"roll":0},
       "validTime":"2026-01-01"},
       {"position":{"lat":48,"lon":9,"h":0},
       "angles":{"yaw":28.65,"pitch":0,"roll":0},
       "validTime":"2026-01-02"}]} */
    
  • Escribe una pose temporal como un Stream de OGC GeoPose

    asGeoPoseStream(tpose,maxdecimaldigits integer=-1) → text
    

    Un stream lleva los marcos de una Serie Irregular sin declarar cuántas poses hay ni cuándo terminan, ya que pueden llegar más. El estándar lo escribe como un header, que contiene el modelo de transición y el marco exterior, y un arreglo streamElements con un elemento por pose. El marco exterior es el plano tangente local Este-Norte-Arriba en la posición de la primera pose, de modo que la cabecera y cada elemento hablan de un mismo punto de tangencia. La biblioteca C también escribe los dos documentos por partes, mediante tpose_as_geopose_stream_header y tpose_as_geopose_stream_element, para un productor que emite poses a medida que llegan. Una consulta devuelve un valor que ya posee entero, que es lo que esta función escribe.

    SELECT asGeoPoseStream(tpose 'Geodpose(Point(0 0 0), 1, 0, 0, 0)@2026-01-01', 6);
    /* {"header":{"transitionModel":{"authority":"/geopose/1.0","id":"none",
       "parameters":"interpolation=None"},"outerFrame":{"authority":"/geopose/1.0",
       "id":"LTP-ENU","parameters":"longitude=0&latitude=0&height=0&crs=EPSG:4979"}},
       "streamElements":[{"streamElement":{"frame":{"authority":"/geopose/1.0",
       "id":"RotateTranslate","parameters":"translation=[0, 0, 0]&rotation=[1, 0, 0, 0]"},
       "validTime":1767254400000}}]} */
    

Documentos compuestos de cadena y de grafo

El estándar declara una composición como un documento de cadena y un conjunto de marcos como un documento de grafo, que es lo que escribe una cadena de poses.

  • Escribe o lee un documento de cadena compuesta OGC GeoPose

    asGeoPose(tposechain,maxdecimaldigits integer=-1) → text
    tposechainFromGeoPose(text) → tposechain
    

    Un documento de cadena es un marco exterior y una secuencia de transformaciones que alcanza un marco interior final, que es lo que contiene una cadena de poses. El documento nombra el marco LTP-ENU tangente en la posición del eslabón exterior y una transformación por eslabón: la primera lleva ese marco tangente al marco propio del eslabón exterior, y cada una posterior es el eslabón tal como se almacena, leído en los ejes de su padre. El documento lleva un único tiempo de validez, por lo que se escribe a partir de un solo instante; atTime obtiene uno de un valor más largo. Su cadena de marcos contiene al menos dos marcos, por lo que una cadena de un solo eslabón no tiene documento de cadena. La clase de cadena nombra sus dos marcos /Extrinsic/LTP-ENU e /Intrinsic/Translate-Rotate, mientras que las clases Advanced, Series y Stream nombran esos mismos dos marcos LTP-ENU y RotateTranslate. Un documento se escribe con el par que usa su propia clase, y cualquiera de los dos pares se acepta al leer.

    SELECT asGeoPose(tposechain 'SRID=4326;
      PoseChain( GeodPose(Point Z(-122.3 47.7 11), 1, 0, 0, 0),
      Pose(Point Z(2 0 0), 1, 0, 0, 0))@2021-04-28 05:36:10.083+00', 6);
    /* {"validTime":1619588170083,"outerFrame":{"authority":"/geopose/1.0",
       "id":"/Extrinsic/LTP-ENU","parameters":"longitude=-122.3&latitude=47.7&
       height=11&crs=EPSG:4979"},"frameChain":[{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[0, 0, 0]&
       rotation=[1, 0, -1.38778e-17, 0]"},{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[2, 0, 0]&
       rotation=[1, 0, 0, 0]"}]} */
    
  • Escribe un documento de grafo compuesto OGC GeoPose

    asGeoPose(tposechain[],maxdecimaldigits integer=-1) → text
    

    Un documento de grafo es un conjunto de marcos y las transformaciones entre ellos, que es lo que contienen varias cadenas de poses que comparten su marco más exterior. La lista de marcos nombra el marco LTP-ENU tangente en ese eslabón más exterior seguido de los eslabones de cada cadena, y la lista de transformaciones nombra el padre y el hijo de cada arista por su posición en esa lista. Las aristas no llevan transformación propia; esta reside en los marcos que nombran. El documento lleva un único tiempo de validez, por lo que se escribe a partir de cadenas leídas en uno y el mismo instante; atTime las obtiene de valores más largos. Su lista de marcos contiene al menos dos marcos, que un solo eslabón de una sola cadena ya proporciona junto al marco tangente.

    SELECT asGeoPose(ARRAY[tposechain 'SRID=4326;
      PoseChain( GeodPose(Point Z(-122.3 47.7 11), 1, 0, 0, 0),
      Pose(Point Z(2 0 0), 1, 0, 0, 0))@2021-04-28 05:36:10.083+00',
      tposechain 'SRID=4326;
      PoseChain(GeodPose(Point Z(-122.3 47.7 11), 1, 0, 0, 0),
      Pose(Point Z(0 3 0), 1, 0, 0, 0))@2021-04-28 05:36:10.083+00'], 6);
    /* {"validTime":1619588170083,"frameList":[{"authority":"/geopose/1.0",
       "id":"/Extrinsic/LTP-ENU","parameters":"longitude=-122.3&latitude=47.7&
       height=11&crs=EPSG:4979"},{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[0, 0, 0]&
       rotation=[1, 0, -1.38778e-17, 0]"},{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[2, 0, 0]&
       rotation=[1, 0, 0, 0]"},{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[0, 0, 0]&
       rotation=[1, 0, -1.38778e-17, 0]"},{"authority":"/geopose/1.0",
       "id":"/Intrinsic/Translate-Rotate","parameters":"translation=[0, 3, 0]&
       rotation=[1, 0, 0, 0]"}],"transformList":[{"link":[0,1]},{"link":[1,2]},
       {"link":[0,3]},{"link":[3,4]}]} */