@@ -84,7 +84,7 @@ PostGIS columns are exposed as GeoJSON objects in GraphQL. The `graphile-postgis
8484
8585### GeoJSON Output
8686
87- PostGIS fields return GeoJSON-structured data:
87+ PostGIS fields return GeoJSON-structured data with type-specific subfields :
8888
8989``` graphql
9090{
@@ -103,6 +103,52 @@ PostGIS fields return GeoJSON-structured data:
103103}
104104```
105105
106+ #### Type-Specific Fields
107+
108+ | Geometry Type | Available Fields |
109+ | ---------------| ------------------|
110+ | Point | ` x ` , ` y ` , ` z ` (if 3D), ` geojson ` , ` srid ` |
111+ | LineString | ` points ` (array of Points), ` geojson ` , ` srid ` |
112+ | Polygon | ` exterior ` (ring), ` interiors ` (holes), ` geojson ` , ` srid ` |
113+ | Multi* | ` geometries ` (array via union type), ` geojson ` , ` srid ` |
114+ | GeometryCollection | ` geometries ` (array via union type), ` geojson ` , ` srid ` |
115+
116+ For geography columns, Point fields use ` longitude ` /` latitude ` instead of ` x ` /` y ` .
117+
118+ #### Measurement Fields
119+
120+ Polygon and LineString types expose computed measurement fields (geodesic, in meters/sq meters):
121+
122+ | Field | Available On | Description |
123+ | -------| -------------| -------------|
124+ | ` area ` | Polygon | Geodesic area in square meters |
125+ | ` length ` | LineString | Geodesic length in meters |
126+ | ` perimeter ` | Polygon | Geodesic perimeter in meters |
127+
128+ ``` graphql
129+ {
130+ zones {
131+ nodes {
132+ boundary {
133+ area # sq meters
134+ perimeter # meters
135+ geojson
136+ }
137+ }
138+ }
139+ }
140+ ```
141+
142+ #### Transformation Fields
143+
144+ All geometry types expose computed transformation fields:
145+
146+ | Field | Returns | Description |
147+ | -------| ---------| -------------|
148+ | ` centroid ` | ` [x, y] ` | Mean of all coordinates |
149+ | ` bbox ` | ` [minX, minY, maxX, maxY] ` | Bounding box |
150+ | ` numPoints ` | ` Int ` | Total coordinate count |
151+
106152### Spatial Filter Operators
107153
108154The connection filter PostGIS plugin exposes these operators on geometry/geography columns:
@@ -122,8 +168,34 @@ The connection filter PostGIS plugin exposes these operators on geometry/geograp
122168| ` overlaps ` | geometry | Same dimension, share space, not fully contained |
123169| ` touches ` | geometry | At least one common point, interiors don't intersect |
124170| ` within ` | geometry | A is completely inside B |
171+ | ` orderingEquals ` | geometry | Same geometry and same point ordering |
125172| ` intersects3D ` | geometry | Share any portion of space in 3D |
126173
174+ #### Distance Operator
175+
176+ | Operator | Works On | Description |
177+ | ----------| ----------| -------------|
178+ | ` withinDistance ` | geometry, geography | Within a given distance (ST_DWithin) |
179+
180+ ` withinDistance ` is a compound input — it takes a ` point ` (GeoJSON geometry) and a ` distance ` (Float, meters for geography, SRID units for geometry):
181+
182+ ``` graphql
183+ {
184+ restaurants (
185+ where : {
186+ location : {
187+ withinDistance : {
188+ point : { type : " Point" , coordinates : [-73.99 , 40.73 ] }
189+ distance : 5000 # 5km for geography columns
190+ }
191+ }
192+ }
193+ ) {
194+ nodes { id name }
195+ }
196+ }
197+ ```
198+
127199#### Bounding Box Operators
128200
129201| Operator | Description |
@@ -218,6 +290,26 @@ const result = await db.location.findMany({
218290}).execute ();
219291```
220292
293+ ``` typescript
294+ // Find restaurants within 5km of a point
295+ const result = await db .restaurant .findMany ({
296+ where: {
297+ location: {
298+ withinDistance: {
299+ point: { type: ' Point' , coordinates: [- 73.99 , 40.73 ] },
300+ distance: 5000 , // meters for geography columns
301+ },
302+ },
303+ cuisine: { equalTo: ' italian' },
304+ },
305+ select: {
306+ id: true ,
307+ name: true ,
308+ location: { x: true , y: true },
309+ },
310+ }).execute ();
311+ ```
312+
221313``` typescript
222314// Find zones that contain a point
223315const result = await db .zone .findMany ({
@@ -238,6 +330,21 @@ const result = await db.zone.findMany({
238330
239331---
240332
333+ ## Aggregate Functions
334+
335+ The PostGIS plugin registers aggregate functions for geometry columns:
336+
337+ | Function | Description |
338+ | ----------| -------------|
339+ | ` ST_Extent ` | Bounding box of all geometries |
340+ | ` ST_Union ` | Union of all geometries into one |
341+ | ` ST_Collect ` | Collect all geometries into a GeometryCollection |
342+ | ` ST_ConvexHull ` | Convex hull of all geometries |
343+
344+ These are available through the Graphile aggregates system when enabled.
345+
346+ ---
347+
241348## Combining PostGIS with Text Search
242349
243350PostGIS spatial queries can be combined with text search filters for location-aware search:
@@ -268,9 +375,17 @@ const result = await db.restaurant.findMany({
268375
269376## When to Use PostGIS
270377
271- - Proximity search ("find restaurants within 5km")
272- - Geofencing ("is this point inside this boundary?")
273- - Spatial containment ("which zone contains this location?")
378+ - Proximity search ("find restaurants within 5km") — use ` withinDistance `
379+ - Geofencing ("is this point inside this boundary?") — use ` contains ` / ` coveredBy `
380+ - Spatial containment ("which zone contains this location?") — use ` within `
381+ - Area/length calculations — use ` area ` , ` length ` , ` perimeter ` fields
382+ - Bounding box queries — use ` bbox ` field or ` bboxContains ` filter
274383- Route and path queries
275384- Any query involving geographic or geometric relationships
276385- Combined with text search for location-aware search experiences
386+
387+ ## Cross-References
388+
389+ - For database setup and Docker: See [ pgpm skill] ( ../../pgpm/SKILL.md ) and [ pgpm Docker reference] ( ../../pgpm/references/docker.md )
390+ - For plugin implementation details: See ` graphile/graphile-postgis/ ` in ` constructive-io/constructive `
391+ - For codegen and ORM patterns: See [ codegen-orm-patterns.md] ( ./codegen-orm-patterns.md )
0 commit comments