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 en un esquema, que declara las dimensiones que contiene el valor (X, Y, Z, …), el tipo numérico con el que se almacena cada una, y su escala y desplazamiento. El esquema se declara en SQL, en dos tablas: pointcloud_schemas contiene lo que declara un esquema en conjunto, y pointcloud_dimensions una fila por dimensión. Un esquema 3D mínimo, con cada dimensión almacenada como int32_t con escala unitaria:
INSERT INTO pointcloud_schemas (pcid, srid, compression, description) VALUES
(1, 4326, 'none', 'Esquema 3D mínimo');
INSERT INTO pointcloud_dimensions
(pcid, dim_no, dim_name, interpretation, dim_scale, dim_offset, active, description) VALUES
(1, 1, 'X', 'int32_t', 1, 0, true, 'Este'),
(1, 2, 'Y', 'int32_t', 1, 0, true, 'Norte'),
(1, 3, 'Z', 'int32_t', 1, 0, true, 'Elevación');
El tamaño de cada dimensión y su desplazamiento dentro de un punto no aparecen porque se derivan de la interpretación, y las dimensiones X, Y, Z y M tampoco porque se resuelven a partir de los nombres. La columna dim_no declara el orden en el que se almacenan las dimensiones, de modo que no depende del orden en el que se insertan las filas, y dim_name es el nombre por el que se resuelven. Ningún nombre de columna necesita comillas en ninguno de los hosts en los que se materializan estas tablas. Un esquema es global a la base de datos y suele registrarse una sola vez, al cargar los datos; el SRID es por esquema y lo heredan todos los valores que llevan ese pcid.
Una base de datos cuyos esquemas ya están registrados en el catálogo pointcloud_formats de pgPointCloud, como documento XML, sigue funcionando: ese catálogo se lee para un pcid que las dos tablas anteriores no declaren.
Tres funciones auxiliares responden lo que declara un pcid sin disponer de un valor de ese esquema y sin depender de la disposición de las tablas. Cada una responde NULL para un pcid que ningún esquema declara.
Devuelve el sistema de referencia espacial en el que se expresa todo valor de un esquema
pointCloudSchemaSRID(integer) → integer
SELECT pointCloudSchemaSRID(1); -- 4326
Devuelve cómo se almacena un parche de un esquema
pointCloudSchemaCompression(integer) → text
SELECT pointCloudSchemaCompression(1); -- none
Devuelve cuántas dimensiones de un esquema contienen valores
pointCloudSchemaNDims(integer) → integer
SELECT pointCloudSchemaNDims(1); -- 3
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(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(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.