KSvend Claude Happy commited on
Commit
504144a
·
1 Parent(s): c42be1f

docs: spec for AOI click-to-place redesign

Browse files

Single-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