Skill v0.44.1
currentAutomated scan100/100name: hotdata-geospatial description: Use this skill only when the user is working with geospatial data in Hotdata (PostGIS-style SQL like ST_* functions, geometry/WKB, bbox filtering, point-in-polygon, distance/area, lat/lon, spatial joins, “geospatial”, “GIS”, “PostGIS”). Do not load this skill for non-geospatial SQL or general Hotdata usage. version: 0.44.1
Hotdata Geospatial Skill
Hotdata supports a subset of PostGIS-style functions in PostgreSQL-dialect SQL. This skill is data-agnostic — apply it to any table with geometry columns.
Requires the core `hotdata` skill for auth, workspace, and table discovery. Related sub-skills (bundled alongside this one — Read on demand): `hotdata-analytics` (`../analytics/SKILL.md` — OLAP SQL), `hotdata-search` (`../search/SKILL.md` — BM25/vector).
Running these queries
All SQL below runs through the core CLI:
hotdata query "<sql>" [--workspace-id <id>] [--database <db>] [--output table|json|csv]
- Fully qualify tables as
<catalog>.<schema>.<table>, where the catalog is the instant database's--catalogalias (or the alias of an attached database) — every<table>placeholder below means a qualified name. - PostgreSQL dialect: double-quote any non-lowercase identifier (e.g.
"GeoID"). - Discover candidate tables with `hotdata databases tables list` (filter with
--schema/--table) and columns with `hotdata databases tables show <catalog.schema.table>` — see core skill.
Geometry columns
Most geospatial tables in Hotdata carry one or both of:
| Column | Type | Description | |
|---|---|---|---|
wkb_geometry | Binary | WKB-encoded geometry (polygon, point, multipolygon, etc.) | |
wkb_geometry_bbox | Struct | Precomputed bounding box: xmin, ymin, xmax, ymax (Float32) |
Parse `wkb_geometry` with `ST_GeomFromWKB()` before any spatial function:
ST_GeomFromWKB(wkb_geometry)
Access `wkb_geometry_bbox` fields with bracket notation (dot access is not supported):
wkb_geometry_bbox['xmin'] -- ✓ works(wkb_geometry_bbox).xmin -- ✗ not supported
Find these columns by their Binary / Struct types in hotdata databases tables list (or hotdata databases tables show <catalog.schema.table>).
Functions
Common building blocks (full catalog, the unsupported set + workarounds, and unit conversions: [references/functions.md](references/functions.md)):
- Construct:
ST_GeomFromWKB,ST_GeomFromText('POLYGON((...))'),ST_MakePoint(lon, lat) - Inspect:
ST_GeometryType,ST_IsValid,ST_X/ST_Y,ST_Centroid,ST_AsText - Relate (boolean):
ST_Within,ST_Contains,ST_Covers,ST_Intersects,ST_Overlaps,ST_Touches,ST_Disjoint,ST_Equals - Measure (in degrees):
ST_Distance,ST_Length,ST_Area - Process:
ST_ConvexHull,ST_Simplify(geom, tol),ST_OrientedEnvelope
Key limits (see reference for the full list + workarounds): no ::geography cast — measurements are in decimal degrees, convert with the factors in the reference (distance × 111000 ≈ meters at mid-latitudes; area is only order-of-magnitude). No ST_Buffer, ST_DWithin, ST_MakeEnvelope, ST_Union, ST_Transform, or GeoJSON I/O — use the documented substitutes.
Common patterns
Geometry types in a table
SELECT ST_GeometryType(ST_GeomFromWKB(wkb_geometry)) AS geom_type, COUNT(*)FROM <table>WHERE wkb_geometry IS NOT NULLGROUP BY 1
Bounding-box filter (replaces ST_MakeEnvelope / ST_DWithin)
Use ST_GeomFromText with a closed WKT polygon ring (repeat the first vertex):
WHERE ST_Within(ST_Centroid(ST_GeomFromWKB(wkb_geometry)),ST_GeomFromText('POLYGON((minLon minLat, maxLon minLat, maxLon maxLat, minLon maxLat, minLon minLat))'))
Faster on large tables — filter the precomputed bbox struct (no WKB parsing); use the ST_Within form when you need centroid-in-polygon precision:
WHERE wkb_geometry_bbox['xmin'] >= <minLon>AND wkb_geometry_bbox['xmax'] <= <maxLon>AND wkb_geometry_bbox['ymin'] >= <minLat>AND wkb_geometry_bbox['ymax'] <= <maxLat>
Point-in-polygon
SELECT *FROM <table>WHERE ST_Contains(ST_GeomFromWKB(wkb_geometry), ST_MakePoint(<lon>, <lat>))
Nearest neighbors (closest N to a point)
SELECT <id_col>,ST_Distance(ST_Centroid(ST_GeomFromWKB(wkb_geometry)), ST_MakePoint(<lon>, <lat>)) * 111000 AS dist_metersFROM <table>WHERE wkb_geometry IS NOT NULLORDER BY dist_metersLIMIT 10
Distance between two points
SELECTST_Distance(ST_MakePoint(<lon1>, <lat1>), ST_MakePoint(<lon2>, <lat2>)) * 111000 AS dist_meters,ST_Distance(ST_MakePoint(<lon1>, <lat1>), ST_MakePoint(<lon2>, <lat2>)) * 69.0 AS dist_miles
Area of polygons (order-of-magnitude — see reference caveat)
SELECT <id_col>,ST_Area(ST_GeomFromWKB(wkb_geometry)) * 111000 * 111000 AS area_sqm,ST_Area(ST_GeomFromWKB(wkb_geometry)) * 111000 * 111000 / 4047 AS area_acresFROM <table>WHERE wkb_geometry IS NOT NULL
Centroid coordinates
SELECT <id_col>,ST_X(ST_Centroid(ST_GeomFromWKB(wkb_geometry))) AS lon,ST_Y(ST_Centroid(ST_GeomFromWKB(wkb_geometry))) AS latFROM <table>WHERE wkb_geometry IS NOT NULL
Export / simplify as WKT
-- raw WKTSELECT <id_col>, ST_AsText(ST_GeomFromWKB(wkb_geometry)) AS wkt FROM <table> WHERE wkb_geometry IS NOT NULL LIMIT 10-- simplified (tolerance in degrees, ~11 m at mid-latitudes)SELECT <id_col>, ST_AsText(ST_Simplify(ST_GeomFromWKB(wkb_geometry), 0.0001)) AS wkt FROM <table> WHERE wkb_geometry IS NOT NULL
Workflow: explore a new geospatial table
- Find geometry columns —
hotdata databases tables list; look forBinary(WKB) /Struct(bbox) types. - Geometry types — run the "Geometry types in a table" pattern above.
- Coverage / extent — aggregate the bbox struct:
``sql SELECT MIN(wkb_geometry_bbox['xmin']) AS min_lon, MIN(wkb_geometry_bbox['ymin']) AS min_lat, MAX(wkb_geometry_bbox['xmax']) AS max_lon, MAX(wkb_geometry_bbox['ymax']) AS max_lat FROM <table> WHERE wkb_geometry_bbox IS NOT NULL ``
- Sample WKT — run the "Export as WKT" pattern with
LIMIT 3to see geometry structure.