Files
Matthias Hochmeister e8bb75412b 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>
2026-06-09 09:37:39 +02:00
..

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 MB1 GB), mehrere GB RAM/Disk und Zeit (Minuten bis Stunden). Nicht in CI/Sandbox.

  1. OSRM-Daten vorbereiten (extract → partition → customize):

    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:

    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):

    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:

make -C infra/geo data && make -C infra/geo up