The sections above state what a raster shares with the other tessellations. The operations below are the raster's own: a raster cell carries a value where an H3, QUADBIN or S2 cell carries only identity, so a trajectory read against a raster answers a tfloat rather than a temporal cell.
MobilityDB's raster sampling operator evaluates a raster band at the instants of a moving-point trajectory and returns a tfloat. Instants whose position falls outside the raster extent or on a nodata pixel are silently dropped. The sampling operators are double-valued whatever the pixel type of the band, so a pixel type belongs to the sampling surface when every value it can hold is exactly representable in a double precision number. That is what lets a band of any type be sampled into one tfloat, and a column holding tiles of several pixel types be read by a single query. A pixel type is also accepted under the name PostGIS raster gives it, so the band type an ST_BandPixelType call reports (8BUI, 16BSI, 32BF, …) passes into the tile constructors as it stands. PostGIS raster carries no 16-bit float and no 64-bit integer, so float16, int64 and uint64 take the spelling its grammar gives them, 16BF, 64BSI and 64BUI. Its 1BB, 2BUI and 4BUI types bound a pixel to less than a byte while PostGIS stores each of them a byte a pixel, so such a band is already the bytes of an unsigned 8-bit band and the constructors read it as uint8; the bound is the only thing the name adds, and a tile does not carry it. The name a tile reports is the one the RaQuet specification gives the type.
The types uint64 and int64 hold values beyond that domain: a tile of either is stored and read back exactly, while sampling a pixel whose value no double precision number names raises an error rather than answering with a neighbouring value. This enables queries such as what elevation profile did each vehicle follow? or which trips entered a temperature threshold band?
The typical query pattern joins a trajectory against a Raquet table in two steps:
-- Step 1: identify which tiles the trajectory overlaps SELECT DISTINCT q FROM unnest(quadbins(traj, 8)) AS q; -- Step 2: sample each matching tile SELECT rasterTileValueQuadbin(traj, band_data, width, height, quadbin, 'float32', nodata, true) FROM elevation_raquet WHERE quadbin = ANY(quadbins(traj, 8));
The restriction and predicate operators that close the section compose rasterValue with the standard MobilityDB temporal restriction and predicate functions. They accept an optional band argument (default 1) and call rasterValue exactly once per invocation.
Sample a raster band at each instant of a trajectory
rasterValue(tgeompoint,rast raster,band integer=1) → tfloat
Reads band band along traj, taking the value of the pixel each position falls in. A trajectory that states nothing between its instants is read at its instants and answers an instant-set tfloat. A trajectory that moves between them passes over the pixels between them, so it is walked at half a pixel, the step at which a hexagonal cell index is walked, and answers a step tfloat holding each value until the trip reaches a pixel holding another. A position outside the raster or over a nodata pixel carries no value and ends the run, so a trip that leaves and re-enters the raster answers one sequence per visit, and NULL when it never meets a pixel carrying data.
-- Build a 3x3 synthetic elevation raster (SRID 4326, 1 degree pixels)
WITH rast AS (
SELECT ST_SetValues(ST_AddBand( ST_MakeEmptyRaster(3, 3, 0.0, 3.0, 1.0, -1.0, 0.0, 0.0,
4326), '32BF'::text, 0.0::float8, NULL::float8), 1, 1, 1,
ARRAY[[10.0::float4, 20.0::float4, 30.0::float4],
[40.0::float4, 50.0::float4, 60.0::float4],
[70.0::float4, 80.0::float4, 90.0::float4]]) AS r )
SELECT rasterValue(tgeompoint 'SRID=4326;[POINT(0.5 2.5)@2001-01-01,
POINT(2.5 2.5)@2001-01-02, POINT(0.5 0.5)@2001-01-03]', r)::text FROM rast;
-- {10@2001-01-01, 30@2001-01-02, 70@2001-01-03}
Applied to a trajectory dataset with a terrain elevation raster:
-- Elevation profile per trip (requires a loaded elevation raster) SELECT tripid, rasterValue(trip, elev) AS elevation FROM trips, elevation_raster; -- Average elevation per trip SELECT tripid, avgValue(rasterValue(trip, elev)) AS mean_elev FROM trips, elevation_raster; -- Trips that crossed terrain above 200 m SELECT tripid FROM trips, elevation_raster WHERE atSpan(rasterValue(trip, elev), floatspan '[200, 9999]') IS NOT NULL;
Sample a raquet raster tile along a trajectory
rasterTileValue(tgeompoint,rast raquet) → tfloat
Evaluates the tile at each instant of traj (SRID 4326), returning the sampled pixel values as a tfloat. This is the typed equivalent of rasterTileValueQuadbin: the dimensions, QUADBIN cell, pixel type and nodata handling are taken from the raquet value instead of from separate arguments. Returns NULL when no instant falls inside the tile or survives nodata filtering.
-- Sample pre-packaged raquet tiles along ship trajectories SELECT t.tripid, rasterTileValue(t.trip, r.tile) FROM trips t JOIN elevation_tiles r ON r.quadbin = ANY(quadbins(t.trip, 8));
Sample an array of raquet raster tiles along a trajectory
rasterTileValue(tgeompoint,rast raquet[]) → tfloat
Evaluates every tile of rast at each instant of traj (SRID 4326) and returns the sampled pixel values as a single tfloat. A trajectory that leaves one tile is covered by several of them, each contributing the instants that fall inside it. Tiles of one zoom level partition the plane, so they contribute disjoint instants. Tiles of different zoom levels overlap, and where two of them sample the same instant the value of the tile of higher zoom is kept, that being the one carrying the finer resolution. Returns NULL when no instant falls inside a tile or survives nodata filtering.
-- Sample a trajectory across every tile it crosses, in one value SELECT t.tripid, rasterTileValue(t.trip, array_agg(r.tile)) FROM trips t JOIN elevation_tiles r ON r.quadbin = ANY(quadbins(t.trip, 8)) GROUP BY t.tripid, t.trip;
Sample a Raquet raster chip along a trajectory using a QUADBIN cell for georeferencing
rasterTileValueQuadbin(tgeompoint,pixels bytea,width integer,height integer, quadbin bigint,pixtype text,nodata float8,has_nodata boolean) → tfloat
Evaluates a row-major single-band pixel array at each instant of traj (SRID 4326). The tile georeferencing (bounding box, pixel-to-coordinate mapping) is derived from quadbin alone via the Web-Mercator slippy-tile transform. The pixtype argument must be one of uint8, int8, uint16, int16, uint32, int32, uint64, int64, float16, float32, or float64. When has_nodata is true, instants whose pixel equals nodata are silently dropped. Returns NULL when no instant survives.
-- Sample elevation tiles from a Raquet Parquet table for all ship trajectories SELECT t.tripid, rasterTileValueQuadbin(t.trip, r.band_data, r.width, r.height, r.quadbin, 'float32', r.nodata, true) FROM trips t JOIN elevation_raquet r ON r.quadbin = ANY(quadbins(t.trip, 8)); -- Average sea-surface temperature per trajectory SELECT t.tripid, avgValue(rasterTileValueQuadbin(t.trip, r.band_data, 256, 256, r.quadbin, 'int16', -9999, true)) AS mean_sst FROM trips t JOIN sst_raquet r ON r.quadbin = ANY(quadbins(t.trip, 6));
Return the instants of a trajectory where the sampled raster value falls inside a float range
atRasterValue(tgeompoint,rast raster,vspan floatspan,band integer=1) → tgeompoint
Evaluates rasterValue at every instant of traj and returns the sub-trajectory restricted to the times when the pixel value lies within vspan. Returns NULL when no instant satisfies the condition.
WITH rast AS (
SELECT ST_SetValues(ST_AddBand( ST_MakeEmptyRaster(3, 3, 0.0, 3.0, 1.0, -1.0, 0.0, 0.0,
4326), '32BF'::text, 0.0::float8, NULL::float8), 1, 1, 1,
ARRAY[[10.0::float4, 20.0::float4, 30.0::float4],
[40.0::float4, 50.0::float4, 60.0::float4],
[70.0::float4, 80.0::float4, 90.0::float4]]) AS r )
SELECT asText(atRasterValue(tgeompoint 'SRID=4326;
[POINT(0.5 2.5)@2001-01-01, POINT(1.5 1.5)@2001-01-02, POINT(0.5 0.5)@2001-01-03]', r,
floatspan '[40, 90]')) FROM rast;
-- {[POINT(1.5 1.5)@2001-01-02], [POINT(0.5 0.5)@2001-01-03]}
Return the instants of a trajectory where the sampled raster value falls outside a float range
minusRasterValue(tgeompoint,rast raster,vspan floatspan,band integer=1) → tgeompoint
The complement of atRasterValue: returns the sub-trajectory restricted to the times when the pixel value lies outside vspan. Returns NULL when every instant satisfies the condition.
-- Keep only the instant whose raster value (10) is below 40 SELECT asText(minusRasterValue(traj, elev_raster, floatspan '[40, 9999]')) FROM trips, elevation_raster;
Return true if the trajectory ever samples a raster pixel value inside a float range
eRasterValue(tgeompoint,rast raster,vspan floatspan,band integer=1) → boolean
Returns true when at least one in-extent instant of traj samples a pixel value within vspan, false otherwise. Returns NULL only when the input is NULL.
-- Trips that ever crossed terrain above 200 m SELECT tripid FROM trips, elevation_raster WHERE eRasterValue(trip, elev_raster, floatspan '[200, 9999]');
Return true if every in-raster-extent instant of the trajectory samples a pixel value inside a float range
aRasterValue(tgeompoint,rast raster,vspan floatspan,band integer=1) → boolean
Returns true when every in-extent instant of traj samples a pixel value within vspan, false when at least one does not. Returns NULL only when the input is NULL.
-- Trips that stayed entirely below 100 m elevation SELECT tripid FROM trips, elevation_raster WHERE aRasterValue(trip, elev_raster, floatspan '[0, 100]');
To use the operators described here, MobilityDB must be built with -DRASTER=ON. On such a build the generated mobilitydb.control declares requires = 'postgis, postgis_raster', so a single CASCADE creates the full stack:
CREATE EXTENSION mobilitydb CASCADE; -- NOTICE: installing required extension "postgis" -- NOTICE: installing required extension "postgis_raster"
PostGIS raster is part of the standard postgresql- package on Debian/Ubuntu; no extra package is required. Pass N-postgis-3-DRASTER=ON to CMake:
cmake -DRASTER=ON .. make -j$(nproc) sudo make install
To include raster in the CI coverage build alongside other optional families, add -DRASTER=ON to the cmake invocation in .github/workflows/pgversion.yml.