Task_1: Erkunden Overpass-Query

This commit is contained in:
Marco Schmid 2026-05-11 17:25:34 +02:00
commit 6413908a4b
5 changed files with 209 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
.idea
.venv
GIT_INSTRUCTOR.md
README_INSTRUCTOR.md
# Python
__pycache__/
*.pyc
*.pyo
.env

57
GIT_HINTS.md Normal file
View File

@ -0,0 +1,57 @@
# Git-Hints
Gesamtes Repo klonen:
```
git clone https://gitea.fhgr.ch/schmidmarco/overpass.git
```
Neuen Arbeitsbranch 'work' machen beim Stand 'Task 1' (alle Files):
```
git checkout -b work Task_1
```
Nur nächsten Task (nur Task.md) holen:
- holt nur die Aufgabe
- eigener Code bleibt unangetastet
```
git checkout Task_2 -- TASK.md
```
Soll der Code von `main` in den `work`-Branch gemerged werden (in einem normalen Projekt ist es ja umgekehrt!),
auf den `work`-Branch wechseln und:
```
git merge Task_7 -> ev. zu lösende Konflikte
```
Bedingt durch unseren Workflow wo sowohl auf `main` als auch `work` an den gleichen Files gearbeitet wird, führt das
ziemlich sicher zu einem merge-Konflikt, welcher manuell gelöst werden muss.
Den ganzen Code zu einem bestimmten Tag holen (Notfall):
```
# Vorher eigene Arbeit sichern:
git add .
git commit -m "mein Stand vor Notfall-Checkout"
# Dann eine einzelne Datei holen:
git checkout Task_3 -- main.py
# Oder alles auf Tag zurücksetzen (unwiderruflich!):
git reset --hard Task_3
```
Vor dem reset, besser:
```
git log work --oneline # nochmals anschauen was verloren geht
git commit -m "mein Stand, wird jetzt verworfen" # zumindest in History sichtbar
```
Immer vor dem Holen des nächsten Tasks einen eigenen `commit` machen!
```
git commit -m "Task xy: ..."
```

107
README.md Normal file
View File

@ -0,0 +1,107 @@
# README
Ich habe euch den `GeoService` demonstriert. Das ist ein Docker-basierter Mikroservice, welcher Fragen wie
'Zeige mir basierend auf meinem Standort die 3 per Auto nächstgelegenen Restaurants' oder 'Wieviele Schwimmbäder
befinden sich im Umkreis von 3 Kilometer um meinen jetzigen Standort' etc.
Um den Service zu betreiben braucht es (valide) Daten. Diese kann man kaufen oder sich selbst bauen. Wir gehen hier
den zweiten Weg :-). Das Ziel für die nächsten paar Übungsstunden ist ein kleines Projekt (Miniprojekt), welches uns
diese Basisdaten mithilfe von OpenStreetMap (Overpass) für die ganze Schweiz baut.
Es geht weniger darum, ein super End-Produkt zu bauen (inhaltlich), als vielmehr darum viele in diesem Semester
gelernte Konzepte und Techniken zu integrieren und an einem praxisnahen Beispiel miteinander zu erarbeiten und zu
festigen.
Dies sind u.U.:
* Zugriff mit Python auf eine API (Overpass)
* Arbeiten mit git auf Gitea (commits, clone, checkout)
* Dataclasses
* sauberer Python-Code mit Docstrings, Typehints und Logging
* Speichern der Resultate als .json in einem File und in eine Postgres-Datenbank
* Parallelisierung (multithreading) von Abfragen auf Overpass-API
* Dekorator-Pattern (für Zeitmessung)
* Schreiben von Pytests mit und ohne Mocks
**Ziel:**
Valide Daten für ausgewählte POI's (Points Of Interests) zu produzieren, z.B.
```json
{
"generated_at": "2026-03-09T23:03:40.107307+00:00",
"count": 2511,
"pois": [
{
"id": "node/2068758717",
"type": "node",
"poi_type": "rail_station",
"name": "St. Urban Ziegelei",
"lat": 47.2271478,
"lon": 7.8361018,
"tags": {
"name": "St. Urban Ziegelei"
}
},
{
"id": "node/2068760081",
"type": "node",
"poi_type": "rail_station",
"name": "St. Urban",
"lat": 47.2317566,
"lon": 7.8359882,
"tags": {
"name": "St. Urban"
}
}
]
}
```
Für die POIs machen wir unseren eigenen Datatyp 'POI' (`dataclass`)
Ein einzelner POI:
``` {
"id": "node/2068758717",
"type": "node",
"poi_type": "rail_station",
"name": "St. Urban Ziegelei",
"lat": 47.2271478,
"lon": 7.8361018,
"tags": {
"name": "St. Urban Ziegelei"
}
```
Die Daten beziehen wir von `https://overpass-turbo.eu/`. Overpass bietet nebst diesem frontend-basierten Ansatz auch eine
API-Schnittstelle (POST- und GET-Requests) an, welche wir nutzen können (`OVERPASS_URL=https://overpass-api.de/api/interpreter`).
## Gitea
Das Miniprojekt liegt auf `Gitea` bereit. Ziel ist es auch den Umgang mit git ein wenig zu üben.
In `GIT_HINTS.md` habe ich die wichtigsten Befehle im Zusammenhang mit diesem Miniprojekt festgehalten.
**zu beachten**:
Der Workflow mit `git commit/pull` etc. hier ist nicht derselbe, wie in einem normalen Projekt.
Normalerweise machen wir von `main` einen Branch, lösen auf diesem einen Task, stellen dann einen `PR` und mergen zurück in
`main`. Dann beginnt das ganze Spiel wieder von vorne...
Hier im Miniprojekt verhält es sich ein wenig anders. Die (Muster)-Lösungen liegen schon zu Beginn auf dem `main`-Branch und
sind mit tags (z.B. TASK_1) versehen. Der main-Branch kann also zu unterschiedlichen Zeitpunkten ausgecheckt werden und
Zeit die Arbeit zu diesem Zeitpunkt. Der/die Studierende kann also zu einem bestimmten Task auschecken und dann in `Task.md`
nachlesen, was als Nächstes ansteht. Idee ist jedoch nicht, einfach die 'Lösungen' anzuschauen, sondern auf einem
paralellen Branch `work` die Übungen selbst zu programmieren. Wenn Du nicht mehr weiter weisst, kannst Du jederzeit auf
`main` zu einem Task-Tag wechseln und nachschauen. Programmiert (und commited) soll aber IMMER nur auf dem
`work`-Branch werden!
## Code
Aus u.a. didaktischen Gründen, beginnen wir nicht gerade mit allem möglichen in unserem Projekt, sondern arbeiten zuerst
z.B. einmal in `main.py` und nutzen `print`-Statements im Wissen, dass wir die später durch saubere `loggings` ersetzen.
Auch gibt es punktuell immer wieder Refaktorierungen des Codes (darauf werde ich jeweils in den Tasks hinweisen).
Ziel ist am Schluss ein verständliches Miniprojekt mit einer sauberen Struktur und verständlichem Code. Das Miniprojekt
erhebt aber eben keinen Anspruch auf absolute Performanz oder Stabilität, es ist ein und bleibt ein Lernprojekt!
Viel Spass im Projekt!

20
TASK.md Normal file
View File

@ -0,0 +1,20 @@
# TASK 1:
Als Erstes wollen wir die Struktur und Funktionen eines einfachen
Overpass-Queries verstehen und ein wenig interaktiv damit rumspielen.
```
[out:json][timeout:60];
(
node["aerialway"="station"]({{bbox}});
way["aerialway"="station"]({{bbox}});
node["railway"="funicular"]({{bbox}});
way["railway"="funicular"]({{bbox}});
node["railway"="station"]["funicular"="yes"]({{bbox}});
);
out center body;
```
* Kopiert diesen auf `https://overpass-turbo.eu/` und führt ihn mal aus.
* Spielt mit den Zoomstufen -> bbox
* Versucht mehr über bbox herauszufinden (z.B. https://wiki.openstreetmap.org/wiki/Overpass_API)

12
main.py Normal file
View File

@ -0,0 +1,12 @@
from pprint import pprint
def fetch_bergbahnen(bbox) -> dict:
return {}
if __name__ == "__main__":
bbox = (46.72, 9.70, 46.92, 10.00)
result = fetch_bergbahnen(bbox)
pprint(result)