|
| 1 | + |
| 2 | +### 🧪 EXPERIMENTAL EXTENSIONS (this deployment only) |
| 3 | + |
| 4 | +This endpoint loads two community extensions beyond the stock set. They are **not** |
| 5 | +available on the default endpoint — do not assume them elsewhere. |
| 6 | + |
| 7 | +#### `raster` — read GeoTIFF/COG directly |
| 8 | + |
| 9 | +`RT_ReadCells(path)` returns **one row per pixel**: `(id, x, y, geometry, col, row, band_1, …)`. |
| 10 | +`RT_Read(path)` returns one row per tile with an `RT_DATACUBE` band, for band algebra |
| 11 | +(`RT_CubeStats`, `RT_CubeClip`, `RT_CubeBurn`). Paths may be S3/HTTP via GDAL's |
| 12 | +`/vsicurl/` or `/vsis3/` prefixes. |
| 13 | + |
| 14 | +⛔ **Size guard — `RT_ReadCells` materializes every pixel.** A 92×81 fire-severity |
| 15 | +raster is 7,452 rows and returns in seconds; a continental COG is billions and will |
| 16 | +exhaust the pod. Before reading an unfamiliar raster, check its dimensions with |
| 17 | +`SELECT cols, rows FROM RT_Read(path)` and stop if `cols * rows` exceeds ~10 million. |
| 18 | +For anything larger, use the dataset's `…/hex/h0=*/…` asset — every raster collection |
| 19 | +in the catalog is already aggregated to H3 by the ingest pipeline, and that path is |
| 20 | +partition-pruned and far cheaper. `RT_ReadCells` is for small rasters not yet ingested. |
| 21 | + |
| 22 | +⚠️ **CRS type mismatch.** `RT_ReadCells` emits `GEOMETRY('EPSG:4326')`; catalog |
| 23 | +GeoParquet is `GEOMETRY('OGC:CRS84')`. Both hold lon/lat, but DuckDB rejects the join |
| 24 | +on the type difference. Re-tag the raster side — no reprojection is involved: |
| 25 | + |
| 26 | +```sql |
| 27 | +WITH px AS ( |
| 28 | + SELECT band_1 AS value, ST_SetCRS(geometry, 'OGC:CRS84') AS g |
| 29 | + FROM RT_ReadCells('/vsicurl/https://example.org/severity.tif') |
| 30 | + WHERE band_1 > 0 -- drop nodata BEFORE the join |
| 31 | +) |
| 32 | +SELECT f.name, count(*) AS n_px, round(avg(px.value), 3) AS mean_value |
| 33 | +FROM px LEFT JOIN read_parquet('<geoparquet>') f ON f.geom.ST_Contains(px.g) |
| 34 | +GROUP BY 1; |
| 35 | +``` |
| 36 | + |
| 37 | +Filter the raster's nodata sentinel in the CTE (see §7) — it is usually a large |
| 38 | +negative value like `-9999` that will wreck any average. |
| 39 | + |
| 40 | +⚠️ **Area: take the pixel size from the raster's own transform.** Do not sum |
| 41 | +`h3_cell_area()` over pixel rows — that charges each pixel a whole H3 cell's area, |
| 42 | +which is a different quantity and overcounts badly (it inflated this fire by ~29%). |
| 43 | +H3 cell area is the right tool for *hex* rows, not raster rows. Do not use a |
| 44 | +hardcoded constant either: in a geographic CRS, pixel ground area varies with |
| 45 | +latitude. |
| 46 | + |
| 47 | +`RT_Read(path).metadata` is JSON; `$.transform` is a **6-element GDAL geotransform |
| 48 | +in the order `[originX, dx, rotX, originY, rotY, dy]`** — the pixel size lives at |
| 49 | +indices **1 and 5**, not 0 and 4: |
| 50 | + |
| 51 | +```sql |
| 52 | +WITH hdr AS ( |
| 53 | + SELECT abs(CAST(metadata->'$.transform'->>1 AS DOUBLE)) AS dlon, |
| 54 | + abs(CAST(metadata->'$.transform'->>5 AS DOUBLE)) AS dlat |
| 55 | + FROM RT_Read('<path>') |
| 56 | +), px AS ( |
| 57 | + SELECT band_1 AS value, |
| 58 | + ST_SetCRS(geometry, 'OGC:CRS84') AS g, |
| 59 | + h.dlon * 111320.0 * cos(radians(ST_Y(geometry))) -- metres per degree lon |
| 60 | + * h.dlat * 110574.0 -- metres per degree lat |
| 61 | + AS px_m2 |
| 62 | + FROM RT_ReadCells('<path>'), hdr h |
| 63 | + WHERE band_1 > 0 |
| 64 | +) |
| 65 | +SELECT round(sum(px_m2) / 10000, 2) AS hectares FROM px; |
| 66 | +``` |
| 67 | + |
| 68 | +Other useful `metadata` keys: `crs`, `bounds`, `width`, `height`, `bands`. Note |
| 69 | +`RT_Envelope` takes an `RT_DATACUBE`, not the `bbox` column — use `$.bounds`. |
| 70 | + |
| 71 | +#### `zarr` — read Zarr stores |
| 72 | + |
| 73 | +`read_zarr(path)`, plus `read_zarr_groups(path)` and `read_zarr_metadata(path)` for |
| 74 | +discovery. Call the metadata/groups functions first to learn array names, shapes and |
| 75 | +chunking; the same size discipline as `RT_ReadCells` applies — a Zarr array is |
| 76 | +typically a full datacube, so slice on its coordinate columns before aggregating. |
| 77 | + |
| 78 | +**Report which extension produced a number** when you use either one, so the user |
| 79 | +knows the result came from the experimental path. |
0 commit comments