Chapter 18. Raster Sampling

Table of Contents

Sampling Operator
Raster Accessors
Raquet / QUADBIN Sampling
Raquet Serialization
Raquet Accessors and Comparison
Restriction and Predicate Operators
Build and Install
Production Roadmap

The PostGIS raster extension stores gridded coverage data (elevation, temperature, satellite imagery, …) in the raster type. Each raster has one or more bands; a band holds a 2D array of pixel values, a spatial extent, a pixel size, and an optional nodata sentinel value.

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?

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"

Sampling Operator

  • Sample a raster band at each instant of a trajectory

    rasterValue(tgeompoint,rast raster,band integer=1) → tfloat
    

    Evaluates pixel band at every instant of traj using bilinear-nearest sampling (ST_Value with resample=false). Returns an instant-set tfloat whose values are rounded to four decimal places. Returns NULL when no instant intersects the raster.

    -- 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;