Un raster de MobilityDB es el mismo tipo, con las mismas funciones, en todos los motores (“Operaciones específicas de ráster”). Dos motores tienen además un ráster propio: DuckDB mediante su extensión de rásteres, y Apache Spark mediante Apache Sedona; Apache Flink no tiene ninguno. Cada anfitrión nombra todo lo que añade con un prefijo, RT_ en DuckDB y RS_ en Sedona, y MobilityDB no nombra nada con ninguno de los dos, de modo que el nombre de una función indica sobre qué ráster trabaja: RT_Read y RS_FromGeoTiff son del anfitrión, summaryStats y rasterValue de MobilityDB. Una consulta calcula con el ráster de MobilityDB y convierte un valor en la frontera solo donde necesita al anfitrión: para leer un archivo ráster con el lector del anfitrión, o para pasar un ráster a una de las funciones del anfitrión. La conversión conserva cada valor de píxel y la georreferenciación, de modo que un ráster convertido al ráster del anfitrión y de vuelta es el ráster que era.
| Lo que hace la consulta | DuckDB con su extensión de rásteres | Spark con Apache Sedona |
|---|---|---|
| Disponer del ráster del anfitrión | LOAD raster | SedonaContext.create(spark), junto al registro de MobilitySpark |
| Leer un archivo ráster | RT_Read(file), y luego raster(databand_1, tile_x, tile_y, metadata) para cada tesela | RS_FromGeoTiff(content), y luego la conversión de “Spark y Apache Sedona” |
| Pasar un ráster a las funciones del anfitrión | CAST(rast AS RT_DATACUBE) para sus píxeles, rasterMetadata(rast) para su georreferenciación | RS_AddBandFromArray sobre RS_MakeEmptyRaster, a partir de sus accesores |
| Pasar las estadísticas de una banda al anfitrión | CAST(stats AS RT_STATS), y de vuelta CAST(stats AS summarystats) | ninguna |
Los dos anfitriones admiten tipos de píxel distintos. Cada tipo de píxel de MobilityDB se convierte en el tipo de cada anfitrión de su fila, y un ráster de un tipo para el que un anfitrión no tiene ninguno no tiene ráster en ese anfitrión.
| Tipo de píxel del ráster | Datacube de DuckDB | Tipo de píxel de Sedona |
|---|---|---|
| 1BB, 2BUI, 4BUI | UINT8 | ninguno |
| 8BUI | UINT8 | B |
| 8BSI | INT8 | ninguno |
| 16BUI | UINT16 | US |
| 16BSI | INT16 | S |
| 32BUI | UINT32 | ninguno |
| 32BSI | INT32 | I |
| 32BF | FLOAT | F |
| 64BF | DOUBLE | D |
La extensión de rásteres de DuckDB, una extensión de la comunidad, lee un archivo ráster con RT_Read como una tabla con una fila por tesela: los píxeles de cada banda en una columna databand_N de tipo RT_DATACUBE, la georreferenciación de todo el archivo en una columna JSON metadata, y la posición de la tesela en tile_x y tile_y. No tiene un valor ráster propio, solo estas columnas. Un ráster de MobilityDB se construye a partir de las cuatro columnas de una tesela, y devuelve un datacube y una columna de metadatos, las dos columnas a partir de las cuales las propias funciones de la extensión leen una tesela. Los metadatos que escribe rasterMetadata son los de un archivo que contiene el ráster como su única tesela, de modo que el ráster se reconstruye a partir de ellos en la tesela (0, 0).
El tipo RT_DATACUBE solo existe una vez cargada la extensión, de modo que una consulta que lo nombra se ejecuta después de LOAD raster; antes, DuckDB responde que no existe ningún tipo RT_DATACUBE. Las conversiones son conversiones de tipo y funciones explícitas: un ráster nunca se convierte por sí solo en un datacube al pasarlo a una función RT_, puesto que un datacube deja atrás la georreferenciación.
Un datacube tiene un solo tipo de píxel para todas sus bandas, y cada tipo de píxel de MobilityDB pasa a ser el tipo de datacube que GDAL le asigna, como ocurre con un ráster de PostGIS escrito mediante GDAL. Los tipos de 1, 2 y 4 bits no tienen un tipo más pequeño en un datacube: sus valores se conservan exactamente en 8 bits, y un ráster reconstruido a partir del datacube tiene el tipo 8BUI.
Una conversión conserva todos los valores o produce un error; nunca responde con una aproximación. Un datacube tiene un solo tipo de píxel y un solo valor nodata, de modo que un ráster cuyas bandas difieren en alguno de ellos no puede convertirse a RT_DATACUBE. Un datacube de tipo UINT64 o INT64 no tiene un tipo de píxel de MobilityDB al que convertirse. Un datacube o una columna de metadatos mal formados, truncados, con un tipo o un formato desconocido, o que declaran más píxeles de los que contienen, se rechazan en lugar de leerse. El sistema de referencia de coordenadas de los metadatos se lee como EPSG:n y da el SRID del ráster; un ráster combinado con un valor de otro SRID produce el error que produce cualquier mezcla de SRID. Un ráster convertido a datacube escribe sus píxeles sin comprimir; un datacube se lee sea cual sea la compresión con que la extensión lo escribió, RAW, SNAPPY, GZIP, ZSTD o LZ4.
LOAD raster;
-- The tiles of 2 x 2 pixels of a 4 x 4 GeoTIFF of elevations, as MobilityDB rasters
CREATE TABLE elevation AS
SELECT tile_x, tile_y, raster(databand_1, tile_x, tile_y, metadata) AS rast
FROM RT_Read('elevation.tif', blocksize_x := 2, blocksize_y := 2);
-- MobilityDB's raster functions over them
SELECT count(*), summarystatsMean(summaryStatsAgg(rast, true)) FROM elevation;
-- 4 | 57.06666666666667
-- A MobilityDB raster handed to a function of the extension
SELECT tile_x, tile_y, RT_CubeStats(CAST(rast AS RT_DATACUBE), 0).mean
FROM elevation ORDER BY tile_y, tile_x;
-- 0 | 0 | 53.5
-- 1 | 0 | 68.5
-- 0 | 1 | 46.0
-- 1 | 1 | 61.333333333333336
-- A raster converted to the extension's columns and back is the raster it was
SELECT bool_and(raster(CAST(rast AS RT_DATACUBE), 0, 0, rasterMetadata(rast)) = rast)
FROM elevation;
-- true
Apache Sedona registra su tipo ráster y sus funciones RS_ en una sesión de Spark mediante SedonaContext.create(spark), junto a las funciones de MobilitySpark; ningún nombre de una de ellas es un nombre de la otra. Un ráster de Sedona es un valor, que se lee de un archivo con RS_FromGeoTiff y se escribe con RS_AsGeoTiff, y se convierte desde y hacia un ráster de MobilityDB mediante las funciones que ambos ya tienen: la georreferenciación pasa por los accesores de cada uno, y los valores de una banda como un arreglo.
Un ráster de Sedona se construye a partir de un ráster r de MobilityDB mediante RS_MakeEmptyRaster con la georreferenciación de r y RS_AddBandFromArray con los valores de una banda, su tipo de píxel y su valor nodata. Un ráster de MobilityDB se construye a partir de un ráster s de Sedona mediante raster(pixels, width, height, upperleftx, upperlefty, scalex, scaley, skewx, skewy, srid, pixeltype[, nodataval]) sobre los accesores RS_ de s, siendo sus píxeles los bytes de los valores de RS_BandAsArray(s, 1) en su orden; cada valor se convierte en sus bytes como el píxel de una banda de un solo píxel, rasterPixels(addBand(makeEmptyRaster(1, 1, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 0), 1, pixeltype, v), 1). Ambas direcciones leen una banda; un ráster de varias bandas se convierte banda por banda.
Los seis tipos de píxel que admite Sedona se convierten en ambas direcciones, con o sin valor nodata, sin cambiar ningún valor, y un ráster convertido a Sedona y de vuelta es el ráster que era, byte a byte; los demás no tienen ráster de Sedona.
-- A Sedona raster from the georeferencing and the band of a MobilityDB raster r
SELECT RS_AddBandFromArray(
RS_MakeEmptyRaster(1, CASE rasterBandPixelType(r, 1) WHEN 'uint8' THEN 'B'
WHEN 'int16' THEN 'S' WHEN 'uint16' THEN 'US' WHEN 'int32' THEN 'I'
WHEN 'float32' THEN 'F' WHEN 'float64' THEN 'D' END,
rasterWidth(r), rasterHeight(r), upperLeftX(r), upperLeftY(r),
scaleX(r), scaleY(r), skewX(r), skewY(r), SRID(r)),
flatten(dumpValues(r, 1, false)), 1, rasterBandNoDataValue(r, 1)) AS s
FROM t;