Tabla de contenidos
pcpoint y pcpatchpcpointset y pcpatchsettpcboxtpcpointtpcpatchreaders.tpcpatch y writers.tpcpatchLa extensión pgPointCloud almacena datos de nubes de puntos LIDAR en PostgreSQL mediante dos tipos estáticos: pcpoint, un único punto multidimensional cuyas dimensiones se describen mediante un esquema almacenado en la tabla de catálogo pointcloud_formats; y pcpatch, un lote comprimido de valores pcpoint que comparten el mismo pcid (identificador de esquema). Cada esquema fija las dimensiones presentes (X, Y, Z, Intensity, …), su tipo numérico, escala y desplazamiento.
MobilityDB eleva estos tipos al mundo temporal mediante tpcpoint (un sensor LIDAR/GPS en movimiento) y tpcpatch (una serie temporal de grupos de puntos comprimidos). También añade los tipos de conjunto pcpointset / pcpatchset y el tipo de caja delimitadora espaciotemporal tpcbox. Una referencia completa de los tipos estáticos pcpoint y pcpatch se ofrece en la documentación de pgPointCloud; este capítulo cubre las adiciones de MobilityDB sobre pgPointCloud.
Para usar los tipos descritos aquí, MobilityDB debe compilarse con la opción de CMake POINTCLOUD=ON. En tal compilación, el archivo mobilitydb.control generado declara requires = 'postgis, pointcloud', de modo que un único CASCADE crea la pila completa:
CREATE EXTENSION mobilitydb CASCADE; -- NOTICE: installing required extension "postgis" -- NOTICE: installing required extension "pointcloud"
Esta sección recorre un ejemplo mínimo de extremo a extremo: registrar un esquema, construir valores, elevar al tipo temporal, consultar con la superficie bbox e indexar. El objetivo es que cada sección posterior de este capítulo esté anclada a un uso concreto. Siga los pasos en orden sobre una base de datos nueva.
Cada valor pcpoint y pcpatch lleva un pcid que se resuelve a través de pointcloud_formats en un esquema XML. El esquema declara las dimensiones (X, Y, Z, …), su tipo numérico en disco y la escala por dimensión. Un esquema 3D mínimo con todas las dimensiones almacenadas como int32 con escala unitaria:
INSERT INTO pointcloud_formats (pcid, srid, schema) VALUES (1, 4326, '
<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1">
<pc:dimension><pc:position>1</pc:position><pc:size>4</pc:size>
<pc:name>X</pc:name><pc:interpretation>int32_t</pc:interpretation>
<pc:scale>1</pc:scale></pc:dimension>
<pc:dimension><pc:position>2</pc:position><pc:size>4</pc:size>
<pc:name>Y</pc:name><pc:interpretation>int32_t</pc:interpretation>
<pc:scale>1</pc:scale></pc:dimension>
<pc:dimension><pc:position>3</pc:position><pc:size>4</pc:size>
<pc:name>Z</pc:name><pc:interpretation>int32_t</pc:interpretation>
<pc:scale>1</pc:scale></pc:dimension>
</pc:PointCloudSchema>');
Los esquemas son globales a la base de datos; normalmente se registran una sola vez durante la carga inicial de datos. El SRID es por esquema y es heredado por cada valor con ese pcid.
MobilityDB incluye constructores ergonómicos sobre los PC_MakePoint / PC_Patch de pgPointCloud:
-- a single point at (1, 2, 3) under pcid=1
SELECT pcpoint(1, 1.0, 2.0, 3.0);
-- a patch holding two points
SELECT pcpatch(1, pcpoint(1, 1.0, 2.0, 3.0),
pcpoint(1, 4.0, 5.0, 6.0));
-- a single-instant tpcpoint at (10, 20, 30) at 2024-01-01
SELECT tpcpoint(pcpoint(1, 10, 20, 30), '2024-01-01'::timestamptz);
-- a 3-instant moving sensor track (step interpolation, the default for
-- pointcloud temporals because dimensions like Intensity don't interpolate
-- linearly the way coordinates do)
SELECT tpcpointSeq(ARRAY[
tpcpoint(pcpoint(1, 0, 0, 0), '2024-01-01'::timestamptz),
tpcpoint(pcpoint(1, 1, 1, 1), '2024-01-02'::timestamptz),
tpcpoint(pcpoint(1, 2, 2, 2), '2024-01-03'::timestamptz)]);
Para sesiones largas, declare la columna con un typmod para que el pcid de cada fila se imponga en el momento del INSERT:
CREATE TABLE scans (id int, traj tpcpoint(1), full_scan tpcpatch(1));
INSERT INTO scans VALUES (1,
tpcpointSeq(ARRAY[
tpcpoint(pcpoint(1, 0, 0, 0), '2024-01-01'::timestamptz),
tpcpoint(pcpoint(1, 1, 1, 1), '2024-01-02'::timestamptz)]),
tpcpatch(pcpatch(1, pcpoint(1,1,1,1), pcpoint(1,2,2,2)),
'2024-01-01'::timestamptz));
Insertar un valor cuyo pcid no coincide con el typmod de la columna genera ERROR: Pcid of tpcpoint value (N) does not match column typmod pcid (M); mezclar pcids en una columna sin restricciones también se rechaza en el momento de la agregación con extent.
Cada tpcpoint y tpcpatch lleva una tpcbox (espejo de stbox + pcid) calculada en el momento de la construcción. La superficie de operadores a nivel bbox — topológica (&&, @>, <@, ~=, -|-), direccional en X/Y/Z/tiempo y distancia KNN |=| — funciona entre cualquier par de (tpcbox, tpcpoint, tpcpatch):
-- rows whose track ever entered a 3D-temporal box SELECT id FROM scans WHERE traj && tpcbox_zt(0, 0, 0, 1, 1, 1, tstzspan '[2024-01-01, 2024-01-02]', 1, 4326); -- 5 nearest tracks to a query box (KNN, GiST-orderable) SELECT id, traj |=| my_box AS dist FROM scans ORDER BY traj |=| my_box LIMIT 5;
El filtrado a nivel de parche (descartar instantes completos cuyo parche no se superpone con una caja) se proporciona mediante atTpcbox / minusTpcbox. Véase Operaciones por punto para el ámbito de granularidad.
Las clases de operadores R-tree GiST y quadtree / kd-tree SP-GiST aceleran todos los predicados bbox. Elija GiST para cargas de trabajo de propósito general (y para ordenación KNN); SP-GiST kd-tree es competitivo en datos de solo puntos con muchas lecturas:
CREATE INDEX scans_traj_gist ON scans USING gist(traj); CREATE INDEX scans_full_spgist ON scans USING spgist(full_scan);
Las clases de operadores SP-GiST almacenan un stbox internamente y recuperan el filtro pcid en el momento de la reverificación, por lo que una columna con pcids mixtos devuelve un conjunto de candidatos ligeramente mayor para reverificar — no supone ningún problema cuando cada tabla contiene valores de un único esquema, que es lo que impone la receta typmod en Construir valores base y temporales.
-- spatiotemporal extent of every track (one tpcbox) SELECT extent(traj) FROM scans; -- count of active tracks at each timestamp (tint) SELECT tcount(traj) FROM scans; -- merge per-source instants into a single tpcpoint SELECT id, merge(traj) FROM scans GROUP BY id;
extent rechaza pcids mixtos (las dimensiones bbox serían ininterpretables entre esquemas); merge y tcount siguen las mismas reglas que sus equivalentes sin nube de puntos.
La función de retorno de conjuntos points expande el parche de cada instante en pcpoints individuales, lo que resulta útil para uniones con predicados por punto que la capa bbox no puede expresar:
-- explode every patch in the column into one row per (instant timestamp, point) SELECT id, t, point FROM scans, points(full_scan); -- total points across every instant of every track SELECT id, numPoints(full_scan) FROM scans;
El filtrado por punto — construir un nuevo parche a partir de un subconjunto de los puntos del original — se proporciona mediante atTpcboxFine / minusTpcboxFine (contra una tpcbox) y atGeometry / minusGeometry (contra una geometría 2D); véase Operaciones por punto para la matriz de granularidad.
El resto del capítulo profundiza en cada una de las superficies abordadas aquí: funciones de acceso de pcpoint / pcpatch, pcpointset / pcpatchset, la caja delimitadora tpcbox, tpcpoint, tpcpatch, índices y agregaciones.