mirror of
https://github.com/zvx-echo6/central.git
synced 2026-05-21 18:14:44 +02:00
feat(3-K): real geocoder backends + producer-doc reframe + consumer-doc enrichment
Second of three PRs for v0.5.0 (J shipped the framework; this fills in real backends + documents the reframed design principle in-tree; L is the events tab + map fix, then tag). Backends (all satisfy GeocoderBackend; never raise, all-null on any failure): - NaviBackend — composed Navi /api/reverse/<lat>/<lon> (name/address + timezone + landclass + elevation in one call). Near-passthrough: response already matches the canonical 9-field shape. Best-effort warmup ping (Boise) on construction when a loop is running; config `headers` slot for a future Authorization: Bearer (config-only, no code change). Default base_url http://192.168.1.130:8440. - PhotonBackend — raw Photon /reverse?lat&lon&limit=1 (name/address only). Maps features[0].properties; postal_code <- postcode; timezone/landclass/ elevation_m null (Navi-composed-endpoint extras). - NominatimBackend — OSM Nominatim /reverse?format=jsonv2 (name/address only). Configurable rate limit (default 1/sec; 0 disables for self-hosted) + required User-Agent. Maps the address block; landclass/elevation_m/timezone null. Registered all three in supervisor _BACKEND_REGISTRY (resolved by EnrichmentConfig backend_class name). Docs — design pivot now in-tree: - PRODUCER §2 reframed: the verbatim Matt quote stays; the translation inverts. Central is the consumer's only data plane (consumers can't do follow-up lookups), so enrich deliberately and centrally, namespaced under _enriched, failing to null. "No enrichment" is gone. - PRODUCER §10.1 inverted: enrichment is expected; the anti-pattern is doing it OUTSIDE the framework (inline in poll(), bypassing cache + _enriched namespacing + the never-raise safety net). - PRODUCER new §13 Enrichment contract: Enricher / GeocoderEnricher / GeocoderBackend Protocols, NoOpBackend default, sqlite cache + TTL + cache-all-null + don't-cache-on-raise semantics, _enriched.<name> provenance, per-field coverage matrix (cross-checked against GEOCODER_FIELDS), and the landclass antimeridian known wrinkle. - CONSUMER FIRMS section: documents the data._enriched.geocoder bundle (9 fields), per-region coverage (US-full, non-US timezone+elevation), and the antimeridian landclass caveat. Tests: - test_navi/photon/nominatim_backend.py — happy-path field mapping, null handling, extra-key drop, network/timeout/non-200/malformed -> all-null (never raises), Nominatim rate-limit (disabled + spacing) + User-Agent. Env-gated live Navi smoke (NAVI_INTEGRATION_TEST=1; skipped by default — the 192.168.1.130 endpoint isn't reachable from CT104's segment). - test_producer_doc.py — +4: §2 verbatim quote present, §10.1 subsection exists, §13 names all four protocol types, §13 coverage matrix == GEOCODER_FIELDS (derived from code, not hardcoded). Verification: full pytest 525 passed, 1 skipped (was 495; +30 backend + 4 doc tests, -1 the env-gated skip). grep subject_for_event/_ADAPTER_REGISTRY clean. All three backends import + resolve via the registry. Flagged for later (NOT done here): adapters besides FIRMS that should declare enrichment_locations (nwis, eonet, gdacs, usgs_quake, wfigs_*) — that's PR L scope alongside the events tab. See PR description. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a477285b3f
commit
98b050b2af
11 changed files with 833 additions and 37 deletions
|
|
@ -296,6 +296,36 @@ ground-survey workflows.
|
|||
archive is at `https://firms.modaps.eosdis.nasa.gov/`.)
|
||||
- **Removal semantics:** none. FIRMS publishes detections; absence is the signal
|
||||
if a fire stops burning. Consumers should not expect explicit "removal" events.
|
||||
- **Enrichment (`data._enriched.geocoder`):** FIRMS is the enrichment pilot, so
|
||||
each event carries a Central-derived geocoder bundle under
|
||||
`data._enriched.geocoder`. It is *not* an upstream FIRMS field — Central
|
||||
reverse-geocodes the hotspot's `latitude`/`longitude` and attaches the result.
|
||||
The bundle always has these nine keys (any unresolved field is `null`, never
|
||||
missing):
|
||||
|
||||
| key | meaning |
|
||||
|---|---|
|
||||
| `name` | place / feature name |
|
||||
| `city` | city / town / village |
|
||||
| `county` | county (or equivalent) |
|
||||
| `state` | state / province |
|
||||
| `country` | country |
|
||||
| `postal_code` | postal / ZIP code |
|
||||
| `timezone` | IANA tz (e.g. `America/Boise`) |
|
||||
| `landclass` | land-management class (US PAD-US) |
|
||||
| `elevation_m` | ground elevation, metres |
|
||||
|
||||
**Coverage by region (v0.5.0):** US hotspots get the full bundle (with
|
||||
sparsity gaps in deep wilderness); non-US hotspots currently get only
|
||||
`timezone` and `elevation_m` populated (both planet-scale), the rest `null`,
|
||||
pending an upstream planet expansion. Treat `null` as "not resolved," not
|
||||
"does not exist."
|
||||
|
||||
**Known wrinkle — `landclass` antimeridian false-positive:** a non-US hotspot
|
||||
near 51–53°N can spuriously get a non-`null` `landclass` (it false-matches the
|
||||
Aleutian "Rat Islands" US land-management polygon across the dateline). If you
|
||||
consume `landclass`, treat a non-`null` value on a clearly non-US point as
|
||||
suspect. Fix is tracked upstream.
|
||||
- **Live example (verbatim from CT104):**
|
||||
|
||||
```json
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue