Wohnort-Kompass ist ein Monorepo fuer einen datenbasierten Wohnortvergleich fuer Gemeinden in Deutschland. Das Projekt kombiniert:
- ein
FastAPI-Backend mitSQLModel,AlembicundPostGIS - eine
Nuxt 3-Web-App mit Finder, Ranking, Vergleich und Regionsdetailseiten - mehrere ETL-Jobs fuer BKG, Destatis, DWD, UBA, OSM, OSM-Postleitzahlen, OePNV und Unfallatlas
- Wohnort-Kompass
Die Anwendung berechnet Scores fuer sieben Kategorien:
climateairsafetydemographicsamenitieslanduseoepnv
Auf Basis dieser Teilscores entstehen:
- ein neutraler Gesamtscore pro Region
- ein personalisierter Profilscore auf Basis der Finder-Gewichte
- Top-100-Rankings je Kategorie und Bundesland
- Vergleichsansichten fuer bis zu drei Regionen
Der aktuelle Stand des Projekts umfasst unter anderem:
- Coverage-Felder je Kategorie statt Missing-Data-als-
0 - trust-/qualitaetsnahe UI-Hinweise fuer Coverage, Frische und Proxy-Flags
- serverseitigen Regions-Autocomplete fuer Gemeinde, PLZ oder AGS
- URL-state fuer Finder und Ranking
- ETL-Run-Auditing
- strukturierte JSON-Logs
- Schema-Drift-Checks beim App-Start
Das Backend liegt unter backend/ und stellt die API unter /api/v1 bereit.
Wichtige Schichten:
app/api/routesFastAPI-Endpunkteapp/servicesfachliche Logik fuer Scoring, Regionen und Erklaertexteapp/repositoriesDatenbankzugriffeapp/modelsSQLModel-Tabellenapp/schemasAPI-Response- und Request-Modelleapp/etlImport- und Materialisierungsjobs
Besonderheiten:
- Das Backend fuehrt keine Runtime-DDL mehr aus.
- Beim Start wird das DB-Schema geprueft.
- Wenn Migrationen fehlen, startet die App bewusst nicht.
Das Frontend liegt unter frontend/ und basiert auf Nuxt 3, Pinia, Tailwind CSS und Leaflet.
Zentrale Seiten:
/finderGewichtungen festlegen/resultspersonalisierte Empfehlungen/compareneutraler Vergleich von bis zu drei Regionen/region/[slug]Detailansicht mit Scores, Quellen, Datenbasis, Karten und Stationen/top-100/[stateCode]/[category]Top-100-Listen je Kategorie/methodikDatenquellen und fachliche Einordnung
Die lokale Entwicklungsumgebung nutzt postgis/postgis:16-3.4.
Wichtige Tabellen und Schemas:
regionregion_score_snapshotregion_indicator_valueindicator_definitiongeo.municipality_boundarypostal.region_postal_codepostal.postal_area_stageclimate.region_climate_stationetl_runetl_run_source
open-living-map/
├── backend/
│ ├── app/
│ │ ├── api/
│ │ ├── core/
│ │ ├── etl/
│ │ ├── models/
│ │ ├── repositories/
│ │ ├── schemas/
│ │ └── services/
│ ├── migrations/
│ ├── tests/
│ ├── pyproject.toml
│ └── .env.example
├── frontend/
│ ├── components/
│ ├── composables/
│ ├── pages/
│ ├── server/routes/
│ ├── stores/
│ ├── types/
│ ├── package.json
│ └── .env.example
├── infra/
│ └── docker-compose.yml
└── README.md
- Python
3.12 - Node.js
20+ pnpm- Docker + Docker Compose
ogr2ograus GDAL fuer den BKG-Import- optional
osm2pgsqlfuer den OSM-Rohimport
cd infra
docker compose up -dStandardmaessig laeuft PostGIS danach auf localhost:5433.
cd backend
python -m venv venv
source venv/bin/activate
pip install -e .
cp .env.example .env
./venv/bin/alembic upgrade head
./venv/bin/uvicorn app.main:app --reload --port 8000Backend:
- App:
http://localhost:8000 - API:
http://localhost:8000/api/v1
cd frontend
pnpm install
cp .env.example .env
pnpm devFrontend:
- App:
http://localhost:3000
Alle Backend-Settings werden aus backend/.env.example abgeleiteten Werten in backend/.env geladen.
Wesentliche Schalter:
DATABASE_URLDatenbankverbindungAPI_PREFIXStandard:/api/v1CORS_ALLOW_ORIGINSlokale Nuxt-Origin(s)RAW_DATA_DIRRohdatenverzeichnisSTAGING_DATA_DIRStaging-/ZwischendatenGENESIS_USERNAME,GENESIS_PASSWORDZugang fuer Destatis/RegionalstatistikDWD_BASE_URLDWD Daily KLDWD_MAX_STATIONSoptionales Stationslimit fuer TestlaeufeDWD_RECENT_CACHE_MAX_AGE_HOURSRefresh-Fenster fuer lokale DWD-recent-DateienUBA_API_BASEUmweltbundesamt-APIBKG_MUNICIPALITY_TABLElokale BKG-GemeindetabelleBKG_DISTRICT_TABLElokale BKG-KreistabelleBKG_GEOMETRY_COLUMNGeometriespalte der BKG-TabelleBKG_GEOMETRY_FLAVOURbevorzugter BKG-gf-WertOEPNV_GTFS_URLSkomma-separierte GTFS-QuellenOEPNV_HTTP_USERNAME,OEPNV_HTTP_PASSWORDoptional fuer geschuetzte GTFS-Endpunkte
Die wichtigsten Variablen stehen in frontend/.env.example.
Wesentliche Schalter:
NUXT_PUBLIC_API_BASEBasis-URL der APINUXT_PUBLIC_SITE_URLoeffentliche Basis-URLNUXT_PUBLIC_REPO_URLRepository-Link fuer Footer und SEONUXT_PUBLIC_LEGAL_*Inhalte fuer Impressum und Datenschutz
Die ETL-Jobs liegen unter backend/app/etl.
Typische Reihenfolge:
cd backend
./venv/bin/python -m app.etl.import_bkg
./venv/bin/python -m app.etl.import_destatis
./venv/bin/python -m app.etl.import_dwd
./venv/bin/python -m app.etl.import_uba
./venv/bin/python -m app.etl.import_unfallatlas
./venv/bin/python -m app.etl.import_osm
./venv/bin/python -m app.etl.import_postal_codes
./venv/bin/python -m app.etl.import_oepnv
./venv/bin/python -m app.etl.build_scoresimport_bkg erwartet eine lokal importierte Gemeindetabelle, standardmaessig public.vg25_gem.
Der Job:
- materialisiert Gemeinden nach
region - schreibt Gemeindegrenzen nach
geo.municipality_boundary - pflegt
district_name,slug,wikidata_*,wikipedia_url
Beispiel fuer einen lokalen Import mit GDAL:
ogr2ogr -f "PostgreSQL" \
PG:"dbname=wohnortkompass user=wohnortkompass port=5433 host=localhost" \
/pfad/zu/DE_VG25.gpkg vg25_gem \
-lco GEOMETRY_NAME=geom -lco SPATIAL_INDEX=GIST -lco PRECISION=NO \
-t_srs EPSG:4326 -nlt MULTIPOLYGON -overwrite -updateimport_destatis verarbeitet gemeindescharfe Demografie und verwandte Indikatoren aus der Regionalstatistik / GENESIS.
Aktuell genutzt werden unter anderem:
- Einwohner gesamt
- Frauenanteil
- Anteil unter 18 Jahren
- Anteil ab 65 Jahren
import_dwd verarbeitet aktuelle DWD-Tageswerte fuer das Daily-KL-Produkt.
Abgeleitete Indikatoren:
- Hitzetage
- Sommertage
- Niederschlag
Wichtig:
- es werden rollierende Kennzahlen auf Basis aktueller DWD-
recent-Dateien gebildet recent-ZIPs werden lokal zwischengespeichert- ein Re-Download erfolgt, wenn die Datei fehlt oder aelter als
DWD_RECENT_CACHE_MAX_AGE_HOURSist - fuer Regionen wird die naechstgelegene DWD-Station dokumentiert und in der Detailseite angezeigt
import_uba verarbeitet Luftqualitaetsdaten des Umweltbundesamts.
Aktuell genutzt werden:
NO2PM10PM2.5
Die Detailseite zeigt die zugeordnete Luftmessstation inklusive Stationsname und UBA-Seitenlink.
import_unfallatlas verarbeitet Unfallorte und daraus abgeleitete Sicherheitsindikatoren.
import_osm verarbeitet alltagsrelevante POIs und weitere OSM-basierte Informationen.
Genutzt werden unter anderem:
- Alltagsnaehe / Amenity-Dichten
- POI-Overlays fuer die Karten
- materialisierte
amenity_poi_stage-Tabellen fuer schnellere Regionsabfragen
import_postal_codes materialisiert OSM-Postleitzahlflaechen und mappt sie auf Gemeinden.
Ziel:
- Suche nach PLZ, Gemeindename oder AGS/ARS
- exakte 5-stellige PLZ werden auf eine primaere Gemeinde gemappt
- unscharfe PLZ- oder Namenssuchen koennen mehrere Treffer liefern
Technisch:
postal.postal_area_stageals vorgelagerte Stage fuer PLZ-Flaechenpostal.region_postal_codeals Zuordnungstabelle- GiST-Indizes fuer den raeumlichen Join
import_oepnv verarbeitet GTFS-Daten.
Aktuell genutzte OePNV-Indikatoren:
- Haltestellendichte
- Abfahrten je 10.000 Einwohner
- Angebotsmasse
- Abfahrtsregelmaessigkeit
Die OePNV-Kategorie ist fachlich intern gewichtet und nicht mehr nur ein einfacher Mittelwert.
build_scores berechnet aus den materialisierten Indikatoren:
- Kategoriescores
- neutralen Gesamtscore
- Coverage-Felder je Kategorie
Fehlende Kategorien werden dabei nicht mehr pauschal als 0 gewertet.
Wichtige Endpunkte:
GET /api/v1/healthGET /api/v1/regionsGET /api/v1/regions/search/autocompleteGET /api/v1/regions/state-boundariesGET /api/v1/regions/{ars}GET /api/v1/regions/{ars}/amenities/{category}GET /api/v1/regions/{ars}/accidents/{category}POST /api/v1/recommendationsGET /api/v1/compare?ars=...GET /api/v1/rankings/top/{category}GET /api/v1/rankings/top/{state_code}/{category}GET /api/v1/metadata/indicators
Beispiele:
curl 'http://localhost:8000/api/v1/regions/search/autocomplete?q=24937&limit=8'
curl 'http://localhost:8000/api/v1/regions/01001000'
curl 'http://localhost:8000/api/v1/compare?ars=01001000,01059113,08116053'Wichtige Frontend-Eigenschaften:
- Finder und Ranking-Zustand sind ueber Query-Parameter teilbar
- Compare nutzt Pinia und URL-State
- Regionsdetailseiten zeigen:
- Scores
- Datenbasis und Aktualitaet
- Qualitaets- und Proxy-Hinweise
- Luft- und Klimastationen
- thematische Kartenlayer
- Suche im Header arbeitet serverseitig ueber Autocomplete
Installieren:
cd backend
pip install -e .Nutzt:
ruffpytestalembic
Wichtige Checks:
cd backend
ruff check .
python -m py_compile $(find . -name '*.py' -type f)
pytest
alembic upgrade headVorhandene Tests:
backend/tests/etl/test_import_bkg.pybackend/tests/etl/test_import_destatis.pybackend/tests/etl/test_import_dwd.pybackend/tests/etl/test_import_flaechenatlas.py
Wichtige Checks:
cd frontend
pnpm install
pnpm run typecheck
pnpm run buildDie GitHub-Actions-Pipeline unter .github/workflows/ci.yml fuehrt aus:
- Backend-Lint
- Python-Syntaxcheck
pytest- Alembic-Check
- Import-Smoke-Checks
- Frontend-Typecheck
- Frontend-Build
Die Anwendung startet nur, wenn das DB-Schema zum Code passt.
Wenn die App mit SchemaDriftError abbricht:
cd backend
./venv/bin/alembic upgrade headWichtige Regel:
- Schema-Aenderungen ausschliesslich per Alembic
- keine Runtime-DDL
- bereits ausgefuehrte Migrationen nicht nachtraeglich erweitern
Wichtige ETLs schreiben Run-Metadaten nach:
etl_runetl_run_source
Damit sind Start, Ende, Status, Fehltext und grobe rows_written nachvollziehbar.
Backend und ETLs loggen strukturiert im JSON-Format.
API-Requests enthalten unter anderem:
request_idmethodpathstatus_codeduration_ms
Roh- und Staging-Daten werden ueber RAW_DATA_DIR und STAGING_DATA_DIR konfiguriert.
Standard in backend/.env.example:
RAW_DATA_DIR=../../data/rawSTAGING_DATA_DIR=../../data/staging
Wenn du diese Defaults uebernimmst, liegen die Daten ausserhalb von backend/ und frontend/.
Die Anwendung nutzt Leaflet und externe Kartentiles. Eine ausgearbeitete Tile-Provider-Strategie ist noch kein abgeschlossener Teil des Repos und sollte vor produktivem Betrieb sauber dokumentiert und abgesichert werden.
Dieses Repo vereint Daten aus verschiedenen Quellen mit jeweils eigenen Lizenz- und Nutzungsbedingungen. Vor oeffentlichem Betrieb solltest du die Bedingungen der verwendeten Quellen pruefen und korrekt ausweisen.
Wesentliche Quellen:
- OpenStreetMap
- BKG VG25
- Destatis / Regionalstatistik / GENESIS
- Deutscher Wetterdienst
- Umweltbundesamt
- Unfallatlas / Destatis
- GTFS-Feeds aus OpenData OePNV
Fuer OSM gilt insbesondere:
- Daten:
ODbL - Karten-/Tile-Nutzung: eigene Nutzungsbedingungen des gewaehlten Tile-Providers beachten
Wenn du lokal von null startest, ist das die stabilste Reihenfolge:
- PostGIS starten
- Backend-Dependencies installieren
alembic upgrade head- BKG-Gemeindetabelle lokal importieren
- ETLs in der empfohlenen Reihenfolge ausfuehren
build_scoreslaufen lassen- Backend starten
- Frontend starten
Damit hast du einen konsistenten Stand fuer Finder, Ranking, Compare, Top-100 und Regionsdetailseiten.