Skip to content

Commit 2e31989

Browse files
authored
Merge pull request #212 from SnowEx/agent-files
add our agent files for documentation
2 parents c5adf19 + 2e27d4f commit 2e31989

8 files changed

Lines changed: 2290 additions & 3 deletions

.agents/POSTGIS_OPPORTUNITIES.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# PostGIS Optimization Opportunities
2+
3+
## Overview
4+
By leveraging PostGIS on the server side and sending WKT strings from the client, we can avoid heavy geospatial dependencies in Lambda while maintaining full spatial functionality.
5+
6+
## Current Implementation Status
7+
8+
### ✅ Implemented
9+
- **from_area() for Point/Layer Measurements**: Uses PostGIS `ST_Intersects`, `ST_Buffer`, `ST_Transform` for spatial filtering
10+
11+
## 🎯 High Priority Opportunities
12+
13+
### 1. **Raster Operations (RasterMeasurements.from_area)**
14+
**Current Issue**: Requires shapely's `from_shape()` in Lambda
15+
**Solution**: Accept WKT from client, use PostGIS functions
16+
17+
```python
18+
# Lambda Handler
19+
def _handle_raster_from_area_postgis(event, session):
20+
wkt = event.get('shp_wkt') or event.get('pt_wkt')
21+
crs = event.get('crs', 26912)
22+
buffer_dist = event.get('buffer')
23+
24+
if pt_wkt and buffer_dist:
25+
geom_sql = f"ST_Buffer(ST_Transform(ST_GeomFromText('{wkt}', {crs}), 4326)::geography, {buffer_dist})::geometry"
26+
else:
27+
geom_sql = f"ST_Transform(ST_GeomFromText('{wkt}', {crs}), 4326)"
28+
29+
query = text(f"""
30+
SELECT ST_AsTiff(
31+
ST_Clip(
32+
ST_Union(raster),
33+
({geom_sql}),
34+
TRUE
35+
)
36+
)
37+
FROM image_data
38+
WHERE ST_Intersects(raster, ({geom_sql}))
39+
""")
40+
# ... execute and return
41+
```
42+
43+
**Benefits**:
44+
- Raster clipping works in Lambda
45+
- No shapely/rasterio needed in Lambda
46+
- Fast server-side processing
47+
48+
### 2. **Distance-Based Queries**
49+
**New Feature**: Find measurements within X meters of a point
50+
51+
```python
52+
# Client API
53+
df = PointMeasurements.find_within_distance(
54+
pt=Point(x, y),
55+
distance=1000, # meters
56+
crs=26912,
57+
type='depth'
58+
)
59+
60+
# Lambda uses PostGIS ST_DWithin
61+
query = text(f"""
62+
SELECT *
63+
FROM point_data
64+
WHERE ST_DWithin(
65+
geom::geography,
66+
ST_Transform(ST_GeomFromText('{pt_wkt}', {crs}), 4326)::geography,
67+
{distance}
68+
)
69+
""")
70+
```
71+
72+
**Benefits**:
73+
- Uses PostGIS spatial index (extremely fast)
74+
- No need to buffer geometries
75+
- Geography type handles meters correctly
76+
77+
### 3. **Bounding Box Queries**
78+
**New Feature**: Query by bounding box (xmin, ymin, xmax, ymax)
79+
80+
```python
81+
# Client API
82+
df = PointMeasurements.from_bbox(
83+
bbox=(xmin, ymin, xmax, ymax),
84+
crs=26912,
85+
type='depth'
86+
)
87+
88+
# Lambda uses PostGIS ST_MakeEnvelope
89+
query = text(f"""
90+
SELECT *
91+
FROM point_data
92+
WHERE ST_Intersects(
93+
geom,
94+
ST_Transform(ST_MakeEnvelope({xmin}, {ymin}, {xmax}, {ymax}, {crs}), 4326)
95+
)
96+
""")
97+
```
98+
99+
**Benefits**:
100+
- Common use case for map viewers
101+
- Very efficient with spatial indexes
102+
- No client-side geometry construction needed
103+
104+
## 🔄 Medium Priority
105+
106+
### 4. **Nearest Neighbor Queries**
107+
**New Feature**: Find N nearest measurements to a point
108+
109+
```python
110+
df = PointMeasurements.find_nearest(
111+
pt=Point(x, y),
112+
n=10,
113+
crs=26912,
114+
type='depth'
115+
)
116+
117+
# Uses PostGIS <-> operator and ORDER BY distance
118+
query = text(f"""
119+
SELECT *, ST_Distance(geom::geography, point::geography) as distance
120+
FROM point_data
121+
CROSS JOIN (
122+
SELECT ST_Transform(ST_GeomFromText('{pt_wkt}', {crs}), 4326)::geography as point
123+
) pt
124+
WHERE type_id = (SELECT id FROM measurement_type WHERE name = :type)
125+
ORDER BY geom::geography <-> pt.point
126+
LIMIT :n
127+
""")
128+
```
129+
130+
### 5. **Spatial Aggregations**
131+
**New Feature**: Group measurements by proximity
132+
133+
```python
134+
# Find average depth within grid cells
135+
df = PointMeasurements.aggregate_by_grid(
136+
bbox=(xmin, ymin, xmax, ymax),
137+
cell_size=100, # meters
138+
type='depth',
139+
agg='mean'
140+
)
141+
142+
# Uses PostGIS ST_SnapToGrid
143+
```
144+
145+
## 🚀 Advanced Opportunities
146+
147+
### 6. **Line-of-Sight / Path Queries**
148+
Query measurements along a path (e.g., flight line, transect)
149+
150+
```python
151+
df = PointMeasurements.along_path(
152+
path=LineString([...]),
153+
buffer=50,
154+
type='depth'
155+
)
156+
```
157+
158+
### 7. **Temporal-Spatial Queries**
159+
Combine spatial and temporal proximity
160+
161+
```python
162+
# Find measurements near location X within 1 day of date Y
163+
df = PointMeasurements.find_nearby_in_time(
164+
pt=Point(x, y),
165+
date=datetime(2020, 2, 1),
166+
spatial_buffer=500,
167+
temporal_window=timedelta(days=1)
168+
)
169+
```
170+
171+
### 8. **Spatial Joins**
172+
Join different measurement types by proximity
173+
174+
```python
175+
# Find all SMP profiles within 10m of pits
176+
df = LayerMeasurements.join_nearby(
177+
reference_type='density', # pits
178+
join_type='smp',
179+
max_distance=10
180+
)
181+
```
182+
183+
## Implementation Pattern
184+
185+
For all PostGIS operations, follow this pattern:
186+
187+
1. **Client Side** (lambda_client.py):
188+
- Convert Shapely geometries to WKT
189+
- Send WKT + parameters to Lambda
190+
191+
2. **Lambda Handler** (lambda_handler.py):
192+
- Construct PostGIS SQL query
193+
- Use WKT with `ST_GeomFromText`
194+
- Let database do spatial operations
195+
196+
3. **Benefits**:
197+
- ✅ No heavy dependencies in Lambda
198+
- ✅ Fast database-side processing
199+
- ✅ Scales to millions of geometries
200+
- ✅ Uses PostGIS spatial indexes
201+
202+
## Performance Notes
203+
204+
PostGIS spatial indexes (`GIST`) make these operations extremely fast:
205+
- `ST_Intersects`: Uses index, very fast
206+
- `ST_DWithin`: Uses index, very fast
207+
- `ST_Distance` with ORDER BY: Uses index with KNN operator `<->`
208+
- Without spatial index: Linear scan, slow
209+
210+
Ensure all geometry columns have spatial indexes:
211+
```sql
212+
CREATE INDEX idx_point_data_geom ON point_data USING GIST(geom);
213+
CREATE INDEX idx_layer_data_site_geom ON site USING GIST(geom);
214+
```
215+
216+
## Summary
217+
218+
By systematically moving spatial operations to PostGIS:
219+
1. Lambda stays lightweight and fast
220+
2. Database does what it's optimized for
221+
3. Spatial queries scale efficiently
222+
4. No dependency hell in serverless environment
223+
224+
**Next Steps**:
225+
1. Implement raster WKT support
226+
2. Add distance-based queries
227+
3. Add bounding box queries
228+
4. Consider advanced features based on user needs

0 commit comments

Comments
 (0)