Skip to content

Commit 8af0844

Browse files
committed
refactor(spatial): restructure geometry function modules into directories
Splits geo_functions.rs and the spatial scalar builtins into focused submodules, and replaces the ad-hoc spatial_ctor constant-folder with a geometry_expr resolver that surfaces malformed geometry as an error instead of silently folding to NULL. Also fixes ST_Contains argument order so the stored geometry, not the query geometry, is treated as the containing shape.
1 parent d544e3b commit 8af0844

36 files changed

Lines changed: 3079 additions & 1247 deletions

docs/spatial.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ Spatial is a **columnar profile**. Collections with a `SPATIAL_INDEX` column mod
1717
- **R\*-tree index** — Bulk loading, nearest neighbor, range queries
1818
- **Geohash** — Encode/decode, neighbor cells, area covering
1919
- **H3 hexagonal index** — Uber's H3 via h3o for uniform-area spatial binning
20-
- **OGC predicates**`ST_Contains`, `ST_Intersects`, `ST_Within`, `ST_DWithin`, `ST_Distance`, `ST_Intersection`, `ST_Buffer`, `ST_Envelope`, `ST_Union`
21-
- **Geometry constructors**`ST_MakePoint`, `ST_GeomFromText` (WKT), `ST_GeomFromWKB`, `ST_GeomFromGeoJSON` — usable directly in `INSERT VALUES`
20+
- **OGC predicates**`ST_Contains`, `ST_Intersects`, `ST_Within`, `ST_Disjoint`, `ST_DWithin`, `ST_IsValid`
21+
- **Geometry constructors**`ST_Point`, `ST_MakePoint`, `ST_GeomFromText` (WKT), `ST_GeomFromWKB`, `ST_GeomFromGeoJSON`, `ST_MakeLine`, `ST_MakePolygon`, `ST_MakeEnvelope`
22+
- **Accessors and measures**`ST_AsText`, `ST_AsGeoJSON`, `ST_X`, `ST_Y`, `ST_GeometryType`, `ST_NPoints`, `ST_SRID`, `ST_Distance`, `ST_Length`, `ST_Perimeter`, `ST_Area`
23+
- **Geometry operations**`ST_Buffer`, `ST_Envelope`, `ST_Centroid`, `ST_Union`, `ST_Intersection`
2224
- **Format support** — WKB, WKT, GeoJSON interchange. GeoParquet v1.1.0 + GeoArrow metadata.
2325
- **Hybrid spatial-vector** — Spatial R\*-tree narrows candidates by location, then HNSW ranks by semantic similarity in one query
2426
- **Spatial join** — R\*-tree probe join between two collections
@@ -82,6 +84,84 @@ FROM restaurants r, delivery_zones z
8284
WHERE ST_Contains(z.boundary, r.location);
8385
```
8486

87+
## Geometry Functions
88+
89+
Every geometry function works in every position a geometry expression may
90+
appear: an `INSERT` value, a `SELECT` projection, and a spatial predicate's
91+
query-geometry argument. Constructors nest, so `ST_Buffer(ST_Point(...), 500)`
92+
is a valid search area.
93+
94+
A string literal in geometry position is read as either WKT or GeoJSON, so
95+
`ST_DWithin(loc, 'POINT(1 2)', 500)` and
96+
`ST_DWithin(loc, '{"type":"Point","coordinates":[1,2]}', 500)` are equivalent.
97+
98+
### Constructors
99+
100+
| Function | Result |
101+
| --- | --- |
102+
| `ST_Point(lng, lat)` | Point |
103+
| `ST_MakePoint(x, y [, z])` | Point |
104+
| `ST_GeomFromText(wkt [, srid])` | Geometry parsed from WKT |
105+
| `ST_GeomFromGeoJSON(json)` | Geometry parsed from GeoJSON |
106+
| `ST_GeomFromWKB(bytes [, srid])` | Geometry parsed from WKB; accepts an `X'...'` literal or its hex string |
107+
| `ST_MakeLine(point, point, ...)` | LineString |
108+
| `ST_MakePolygon(ring, ...)` | Polygon, each ring an array of `[lng, lat]` pairs |
109+
| `ST_MakeEnvelope(min_lng, min_lat, max_lng, max_lat)` | Rectangular Polygon |
110+
111+
### Accessors
112+
113+
| Function | Result |
114+
| --- | --- |
115+
| `ST_AsText(geom)` | WKT rendering |
116+
| `ST_AsGeoJSON(geom)` | GeoJSON rendering |
117+
| `ST_X(geom)` / `ST_Y(geom)` | Ordinate of a Point; NULL for any other geometry |
118+
| `ST_GeometryType(geom)` | Type name, e.g. `Point` |
119+
| `ST_NPoints(geom)` | Total vertex count |
120+
| `ST_SRID(geom)` | Always `4326` — geometry is stored as GeoJSON, which RFC 7946 defines as WGS 84 |
121+
| `ST_IsValid(geom)` | Whether the geometry is well-formed |
122+
123+
### Measures
124+
125+
Every measure is geodesic: lengths in meters, areas in square meters.
126+
127+
| Function | Result |
128+
| --- | --- |
129+
| `ST_Distance(a, b)` | Distance between two geometries |
130+
| `ST_Length(geom)` | Total length of linear components; `0` for points and polygons |
131+
| `ST_Perimeter(geom)` | Boundary length of areal components, holes included |
132+
| `ST_Area(geom)` | Area of areal components, less holes; `0` for points and lines |
133+
134+
### Predicates and operations
135+
136+
| Function | Result |
137+
| --- | --- |
138+
| `ST_Contains(a, b)` | Whether `a` contains `b` |
139+
| `ST_Within(a, b)` | Whether `a` lies within `b` — the converse of `ST_Contains` |
140+
| `ST_Intersects(a, b)` / `ST_Disjoint(a, b)` | Whether the two geometries share any point |
141+
| `ST_DWithin(a, b, meters)` | Whether the two geometries lie within a geodesic distance |
142+
| `ST_Buffer(geom, meters [, segments])` | Geometry grown by a distance |
143+
| `ST_Envelope(geom)` | Bounding rectangle |
144+
| `ST_Centroid(geom)` | Centroid, weighted by the highest dimension present |
145+
| `ST_Union(a, b)` / `ST_Intersection(a, b)` | Combined / shared geometry |
146+
147+
`ST_Contains`, `ST_Within`, `ST_Intersects` and `ST_DWithin` are answered from
148+
the R\*-tree when the first argument is an indexed geometry column; the rest
149+
evaluate per row.
150+
151+
```sql
152+
-- Read stored geometry back in either interchange format
153+
SELECT name, ST_AsText(location), ST_X(location), ST_Y(location)
154+
FROM restaurants;
155+
156+
-- Measure and describe
157+
SELECT name, ST_GeometryType(boundary), ST_Area(boundary), ST_Perimeter(boundary)
158+
FROM delivery_zones;
159+
160+
-- A buffered point is a valid query geometry
161+
SELECT name FROM restaurants
162+
WHERE ST_Within(location, ST_Buffer(ST_Point(-73.990, 40.750), 1000));
163+
```
164+
85165
## Spatial as a Peer Engine
86166

87167
The `SPATIAL_INDEX` column modifier designates a geometry column for automatic R\*-tree indexing. Spatial is a peer engine alongside columnar, timeseries, and the rest:

0 commit comments

Comments
 (0)