mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-10 17:04:45 +02:00
1 commit
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 31e543ca04 |
feat(v0.7-fire-tracker-3): spotting detection -- pixels beyond perimeter trigger immediate broadcast
Phase 3 of FIRMS+WFIGS fusion. v15.sql adds perimeter_geojson to fire_passes + last_spotting_broadcast_at to fires. FIRMS handler computes convex hull of each pass on pass-boundary close; attributed pixels >= 1.5 mi (configurable) from previous-pass perimeter emit wildfire_spotting broadcast. Cooldown 1h between spotting broadcasts per fire so rapid embers do not spam. wildfire_spotting category at immediate severity -- spotting is the highest-actionable fire signal (spread beyond perimeter). All thresholds GUI-editable. Phase 4 (LLM summaries + on-demand queries) deferred. Schema (v15.sql): - fire_passes gains perimeter_geojson TEXT (nullable; populated by _close_prev_perimeter at boundary). GeoJSON Polygon, single outer ring in (lon, lat) order per RFC 7946, closed (first == last). - fires gains last_spotting_broadcast_at REAL (per-fire cooldown latch). Index (irwin_id, last_spotting_broadcast_at) for the cooldown probe. adapter_config (defaults.py REGISTRY): - fires.spotting_distance_threshold_mi = 1.5 (float). Matches design doc Phase 3 spec; design doc open question #6 lists this as TBD pending real spotting observation data. - fires.spotting_cooldown_seconds = 3600 (int, 1h). Suppresses rapid-ember spam from a single satellite pass. ALERT_CATEGORIES (notifications/categories.py): - wildfire_spotting: immediate / fire. Highest fire severity -- spotting represents fire spread BEYOND the existing perimeter, the most actionable detection signal. FIRMS handler (central/firms_handler.py): - _handle_pass_boundary now closes the prior pass's perimeter (convex hull of fire_pixels via Andrew's monotone chain) on the first boundary; subsequent in-pass pixels reuse the stored hull. - _check_spotting runs for every attributed pixel: looks up the most recent CLOSED pass (perimeter_geojson NOT NULL AND pass_id != current), point-in-polygon test, vertex-distance approximation per design doc Q (sparse pixels make edge projection overkill at VIIRS 375 m resolution), per-fire cooldown gate. - Priority order: spotting (immediate) > growth (priority) > cluster (priority) > halt (routine). Spotting preempts growth at the same pixel because immediate > priority. - Helpers: _convex_hull (Andrew's monotone chain), _hull_to_geojson (RFC 7946 Polygon), _point_in_polygon (ray casting), _close_prev_perimeter, _check_spotting, _prev_has_perimeter. Wire string: - wildfire_spotting: "🔥 Possible spotting <dist:.1f> mi <dir> of <incident_name> perimeter" -- direction is 8-way bearing from the previous pass's centroid to the spotting pixel. Tests (tests/test_fire_tracker_phase3.py, 11 cases all green): - Pass close stamps perimeter_geojson as a closed Polygon (6 hex vertices -> 7-entry closed ring). - Pixel 2 mi NE of perimeter fires spotting with distance in the 1.0..2.5 mi band (vertex-distance approximation) and direction NE. - Pixel inside perimeter -> NO spotting wire. - Second spotting candidate within 1h cooldown -> suppressed. - Past-cooldown spotting fires again. - Convex hull / point-in-polygon / GeoJSON round-trip helper tests. - adapter_config seed for both new fires.* keys. - wildfire_spotting category registered with immediate severity. - 49 tests green across phase1/phase2/phase3/or-arch/include-roundtrip. Live verification on CT108 after rebuild: - v15 migration applied (schema_meta=15, no Traceback in 3 min). - Container healthy. Synthetic 25-pixel probe (PROBE-V07P3-*, cleaned up after): - Pass A: 20 pixels in a ~0.3 mi circle. Perimeter stored on boundary. - Pass B: 5 pixels at distances 0.5/1.0/2.0/5.0/7.0 mi from center. Observed wires: "🔥 Possible spotting 1.7 mi NE of Probe Spotting Fire perimeter" "🔥 Possible spotting 4.7 mi NW of Probe Spotting Fire perimeter" (Plus a Phase 2 growth wire on the first pass B pixel -- documented side effect: single-pixel pass B centroid shows 0.5 mi drift from pass A.) - 7.0 mi E pixel: outside 5 mi spread, no broadcast (cluster check found no co-located unattributed pixels). Cleanup confirmed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |