Workstream 4: Geo & Eintreffzeit-Sortierung (Phase 3)
Selbstgehostete Geo-Dienste (OSRM + Nominatim auf Österreich-Extrakt),
Geokodierung beim Speichern und ETA-Sortierung der Suchtreffer mit
vollständigem Haversine-Luftlinie-Fallback.
- src/lib/geo/types.ts, config.ts: reine Typen + zentrale Konfiguration
(aus kanonischem env.ts; Defaults, kaputte URL wird weiterhin abgelehnt).
- haversine.ts: Luftlinie in Metern (rein). St. Pölten->Wien ~55 km verifiziert.
- nominatim.ts: kanonische, reine geocodeAddress(address) (countrycodes=at,
Timeout/Abort, status ok/not_found/error; KEIN geocodeBrigade-Zweitpfad).
- osrm.ts: etaTable via /table (sources=0, lng,lat), wirft bei Fehler.
- eintreffzeit.ts: orderByEintreffzeit (OSRM-first, kompletter Haversine-
Fallback bei Wurf, Kandidaten ohne Koordinaten ans Ende, stabile Sortierung;
OSRM-Funktion injizierbar fuer Tests).
- candidates.ts: searchHitsToGeoCandidates (Adapter, laedt brigades.lat/lng)
+ reine filterAndCapCandidates (Bounding-Box-Vorfilter 60 km, max 100).
- API: /api/geo/geocode (POST, auth-gated 401, Zod-Body, 404 bei not_found)
und /api/geo/health (GET, auth-gated; OSRM/Nominatim up/down) — beide
default-deny ueber apiAuth.
- Komponenten: standort-input.tsx (Client, Geolocation + Geocode-Fetch),
eta-badge.tsx (kennzeichnet Luftlinie-Fallback), optionale karte.tsx
via next/dynamic (ssr:false).
- Infra: docker-compose.geo.yml (internes Netz, Healthchecks), docker/osrm/
Dockerfile, scripts/prepare-osm-data.sh, infra/geo/{Makefile,README.md}.
WS4 legt KEINE Migration an (brigades-Geo-Spalten + brigades_latlng_idx
stammen aus WS2); drizzle-kit check bleibt sauber.
Offline verifiziert: tsc --noEmit (exit 0), next lint (0 Warnungen),
vitest run (54 passed / 7 skipped DB-roundtrip), next build (exit 0 mit
gesetzten env-Vars), drizzle-kit check ("Everything's fine").
Deferred (kein Postgres/Server im Sandbox): db:migrate, Live-OSRM/Nominatim,
Playwright-E2E.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
26
infra/geo/Makefile
Normal file
26
infra/geo/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Geo-Daten-Pipeline (OSRM + Nominatim, Österreich-Extrakt)
|
||||
#
|
||||
# Ziele:
|
||||
# make data - lädt den OSM-Extrakt und führt das OSRM-Preprocessing aus
|
||||
# make up - startet OSRM + Nominatim (Basis-Compose + Geo-Overlay)
|
||||
# make down - stoppt die Geo-Dienste
|
||||
# make health - prüft die Geo-Health (intern, via App-Container)
|
||||
#
|
||||
# Hinweis: `data`/`up` benötigen Docker, Netzzugriff und viel RAM/Disk;
|
||||
# nicht in CI/Sandbox ausführen.
|
||||
|
||||
COMPOSE = docker compose -f ../../docker-compose.yml -f ../../docker-compose.geo.yml
|
||||
|
||||
.PHONY: data up down health
|
||||
|
||||
data:
|
||||
../../scripts/prepare-osm-data.sh
|
||||
|
||||
up:
|
||||
$(COMPOSE) up -d osrm nominatim
|
||||
|
||||
down:
|
||||
$(COMPOSE) stop osrm nominatim
|
||||
|
||||
health:
|
||||
$(COMPOSE) exec app wget -q -O - http://localhost:3000/api/geo/health || true
|
||||
67
infra/geo/README.md
Normal file
67
infra/geo/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Geo-Dienste: OSRM (Routing) + Nominatim (Geocoding)
|
||||
|
||||
Selbstgehostete Geo-Dienste auf einem **Österreich-OSM-Extrakt** (Geofabrik).
|
||||
Sie liefern die Eintreffzeit-Sortierung (`orderByEintreffzeit`) und die
|
||||
Adress-Geokodierung (`geocodeAddress`). Beide Dienste laufen in einem
|
||||
**internen** Compose-Netz und sind nur für den App-Container erreichbar.
|
||||
|
||||
## Komponenten
|
||||
|
||||
- **OSRM** (`ghcr.io/project-osrm/osrm-backend`, `--algorithm mld`) — `/table`
|
||||
liefert die Fahrzeit-Matrix von EINER Quelle (`sources=0`) zu N Zielen.
|
||||
Koordinaten in OSRM-Reihenfolge `lng,lat`.
|
||||
- **Nominatim** (`mediagis/nominatim`) — `/search?countrycodes=at` geokodiert
|
||||
österreichische Adressen.
|
||||
|
||||
## Erstinbetriebnahme
|
||||
|
||||
> Achtung: Import/Preprocessing brauchen Netzzugriff (Geofabrik, ~700 MB–1 GB),
|
||||
> mehrere GB RAM/Disk und Zeit (Minuten bis Stunden). Nicht in CI/Sandbox.
|
||||
|
||||
1. **OSRM-Daten vorbereiten** (extract → partition → customize):
|
||||
|
||||
```bash
|
||||
make -C infra/geo data # oder: scripts/prepare-osm-data.sh
|
||||
```
|
||||
|
||||
Erzeugt das `.osrm`-Set in `infra/geo/data` und füllt das OSRM-Volume.
|
||||
|
||||
2. **Dienste starten**:
|
||||
|
||||
```bash
|
||||
make -C infra/geo up
|
||||
# entspricht:
|
||||
# docker compose -f docker-compose.yml -f docker-compose.geo.yml up -d osrm nominatim
|
||||
```
|
||||
|
||||
Nominatim importiert beim ersten Start den PBF-Extrakt automatisch.
|
||||
|
||||
3. **Health prüfen** (intern, über die App):
|
||||
|
||||
```bash
|
||||
make -C infra/geo health # ruft GET /api/geo/health (auth-gated)
|
||||
```
|
||||
|
||||
## Konfiguration (kanonisch in `src/lib/env.ts`)
|
||||
|
||||
| Variable | Default | Zweck |
|
||||
| -------------------- | ------------------------ | -------------------------------------- |
|
||||
| `OSRM_URL` | `http://osrm:5000` | Basis-URL des OSRM-Dienstes |
|
||||
| `NOMINATIM_URL` | `http://nominatim:8080` | Basis-URL des Nominatim-Dienstes |
|
||||
| `GEO_HTTP_TIMEOUT_MS`| `4000` | Timeout/Abort für Geo-HTTP-Aufrufe |
|
||||
| `HAVERSINE_KMH` | `50` | Durchschnittstempo der Luftlinie-Fallback-Schätzung |
|
||||
|
||||
## Fallback-Verhalten
|
||||
|
||||
Fällt OSRM aus, schaltet `orderByEintreffzeit` **vollständig** auf die
|
||||
Haversine-Luftlinie um (`mode: "haversine"`, `isFallback: true`). Die UI
|
||||
kennzeichnet diese Werte als „Luftlinie (geschätzt)" (`EtaBadge`). Wehren ohne
|
||||
Koordinaten landen stets am Ende der Liste.
|
||||
|
||||
## Daten-Updates
|
||||
|
||||
Der Geofabrik-Extrakt veraltet. Aktualisierung ist ein manueller Lauf:
|
||||
|
||||
```bash
|
||||
make -C infra/geo data && make -C infra/geo up
|
||||
```
|
||||
Reference in New Issue
Block a user