KSvend Claude Happy commited on
Commit ·
504144a
1
Parent(s): c42be1f
docs: spec for AOI click-to-place redesign
Browse filesSingle-click placement with preset sizes (100/250/500 km²),
replacing MapboxDraw two-tap rectangle and GeoJSON upload.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
docs/superpowers/specs/2026-04-06-aoi-click-to-place-design.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AOI Click-to-Place Redesign
|
| 2 |
+
|
| 3 |
+
**Date:** 2026-04-06
|
| 4 |
+
**Status:** Approved
|
| 5 |
+
|
| 6 |
+
## Summary
|
| 7 |
+
|
| 8 |
+
Replace the current two-tap rectangle drawing + MapboxDraw interaction with a single-click placement model. User clicks the map → a fixed-size square AOI appears centered on that point. Three preset sizes (100, 250, 500 km²), 500 km² default. No resize handles, no corner dragging, no polygon draw mode. Geocoder search auto-places the AOI. GeoJSON upload removed.
|
| 9 |
+
|
| 10 |
+
## Motivation
|
| 11 |
+
|
| 12 |
+
The current AOI selection UX has unnecessary complexity for Aperture's use case. Users don't need arbitrary polygon shapes or resizable rectangles — they need to quickly define a square analysis area. The backend already constrains to 500 km² max and requires a bbox. A click-to-place model is faster, works better on mobile, and eliminates the MapboxDraw dependency.
|
| 13 |
+
|
| 14 |
+
## Interaction Model
|
| 15 |
+
|
| 16 |
+
### Click to Place
|
| 17 |
+
|
| 18 |
+
- Map is always in click-to-place mode (no activation button needed)
|
| 19 |
+
- Cursor shows as crosshair over the map
|
| 20 |
+
- Single click places a square bbox centered on the click point
|
| 21 |
+
- Clicking again replaces the previous AOI instantly (last click wins)
|
| 22 |
+
- No explicit "clear" action required
|
| 23 |
+
|
| 24 |
+
### Geocoder Search
|
| 25 |
+
|
| 26 |
+
- Typing a location name and pressing Enter geocodes via Nominatim
|
| 27 |
+
- On success: map flies to the location AND auto-places the AOI at the geocoded center
|
| 28 |
+
- Replaces any existing AOI (same as clicking)
|
| 29 |
+
|
| 30 |
+
### Size Presets
|
| 31 |
+
|
| 32 |
+
- Three toggle buttons in the sidebar: `[100 km²] [250 km²] [500 km²]`
|
| 33 |
+
- 500 km² selected by default
|
| 34 |
+
- Changing the size while an AOI is placed immediately resizes it around the same center
|
| 35 |
+
- All presets are within the backend's `MAX_AOI_KM2 = 500` limit
|
| 36 |
+
|
| 37 |
+
## UI Changes
|
| 38 |
+
|
| 39 |
+
### Sidebar Layout (Define Area page)
|
| 40 |
+
|
| 41 |
+
```
|
| 42 |
+
[Area name input]
|
| 43 |
+
[Search location input]
|
| 44 |
+
|
| 45 |
+
AOI size: [100 km²] [250 km²] [●500 km²]
|
| 46 |
+
|
| 47 |
+
[Area display: "487 km² — centered at 2.35°N, 36.82°E"]
|
| 48 |
+
|
| 49 |
+
[Analysis period inputs...]
|
| 50 |
+
[Season inputs...]
|
| 51 |
+
[Continue button]
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
### Removed Elements
|
| 55 |
+
|
| 56 |
+
- "Tap two corners" button (`#draw-rect-btn`)
|
| 57 |
+
- GeoJSON upload section (`#geojson-upload`, `#upload-label`)
|
| 58 |
+
- All MapboxDraw-related UI
|
| 59 |
+
|
| 60 |
+
## Technical Design
|
| 61 |
+
|
| 62 |
+
### Size Calculation
|
| 63 |
+
|
| 64 |
+
For a target area at a given latitude, compute the bbox as a square:
|
| 65 |
+
|
| 66 |
+
```javascript
|
| 67 |
+
function computeBbox(centerLng, centerLat, targetKm2) {
|
| 68 |
+
const sideKm = Math.sqrt(targetKm2);
|
| 69 |
+
const dLat = sideKm / 111.32;
|
| 70 |
+
const dLon = sideKm / (111.32 * Math.cos(centerLat * Math.PI / 180));
|
| 71 |
+
return [
|
| 72 |
+
centerLng - dLon / 2,
|
| 73 |
+
centerLat - dLat / 2,
|
| 74 |
+
centerLng + dLon / 2,
|
| 75 |
+
centerLat + dLat / 2,
|
| 76 |
+
];
|
| 77 |
+
}
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
### Map Rendering
|
| 81 |
+
|
| 82 |
+
Replace MapboxDraw with a plain GeoJSON source + layers:
|
| 83 |
+
|
| 84 |
+
- Add a `aoi-draw` GeoJSON source to the map
|
| 85 |
+
- Render with two layers: fill (semi-transparent) + outline (solid stroke)
|
| 86 |
+
- On click or geocode: update the source data with the new bbox polygon
|
| 87 |
+
- No vertex handles, no edit mode
|
| 88 |
+
|
| 89 |
+
### Dependencies Removed
|
| 90 |
+
|
| 91 |
+
- `@mapbox/mapbox-gl-draw` JS library (CDN link in index.html)
|
| 92 |
+
- `mapbox-gl-draw.css` (CDN link in index.html)
|
| 93 |
+
- All Draw-related code in `map.js`: `_draw`, `drawStyles()`, `activateDrawRect()`, `_onRectClick()`, `activateDrawPolygon()`, `loadGeoJSON()`, Draw event listeners
|
| 94 |
+
|
| 95 |
+
### Files Modified
|
| 96 |
+
|
| 97 |
+
| File | Changes |
|
| 98 |
+
|------|---------|
|
| 99 |
+
| `frontend/index.html` | Remove MapboxDraw CDN links. Remove GeoJSON upload section. Remove "Tap two corners" button. Add size toggle buttons. |
|
| 100 |
+
| `frontend/js/map.js` | Remove all MapboxDraw code. Add `computeBbox()`. Add click handler that places AOI. Add `setAoiSize()` for preset changes. Render AOI as plain GeoJSON source/layer. Update `geocode()` to auto-place AOI. |
|
| 101 |
+
| `frontend/js/app.js` | Remove GeoJSON upload handler. Remove draw button handler. Wire size toggle buttons. Update `_bboxAreaKm2` validation (presets are always valid, but still display area). Remove 10,000 km² limit logic. |
|
| 102 |
+
| `frontend/css/` | Remove Draw-related styles. Add size toggle button styles. |
|
| 103 |
+
|
| 104 |
+
### Geocode + Auto-Place Flow
|
| 105 |
+
|
| 106 |
+
```
|
| 107 |
+
User types "Turkana County" + Enter
|
| 108 |
+
→ geocode() calls Nominatim API
|
| 109 |
+
→ On success: extract center [lon, lat] from result
|
| 110 |
+
→ computeBbox(lon, lat, selectedSize)
|
| 111 |
+
→ Update map source with new bbox polygon
|
| 112 |
+
→ Fire onAoiChange(bbox) callback
|
| 113 |
+
→ Map flies to fit the bbox bounds
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
### Results Map
|
| 117 |
+
|
| 118 |
+
The results map (`initResultsMap`, `renderSpatialOverlay`, etc.) does NOT use MapboxDraw — it uses plain GeoJSON sources already. No changes needed there.
|
| 119 |
+
|
| 120 |
+
## Edge Cases
|
| 121 |
+
|
| 122 |
+
- **Click near East Africa bounds**: The computed bbox may extend outside the backend's valid region. The backend already validates this and returns an error. The frontend does not need to clamp.
|
| 123 |
+
- **Geocoder returns no results**: Show error message as today. No AOI placed.
|
| 124 |
+
- **Size change with no AOI placed**: Store the selected size, apply it on next click/geocode.
|
| 125 |
+
|
| 126 |
+
## What This Does NOT Change
|
| 127 |
+
|
| 128 |
+
- Backend AOI validation (bbox bounds, max area)
|
| 129 |
+
- Results map rendering
|
| 130 |
+
- API payload structure (`aoi.bbox` remains `[minLon, minLat, maxLon, maxLat]`)
|
| 131 |
+
- Analysis period, season, or indicator selection flows
|