From bc9dde71ec3e95dd0e3603839184b59db7cc8085 Mon Sep 17 00:00:00 2001 From: Marco Schmid Date: Thu, 23 Apr 2026 18:11:43 +0200 Subject: [PATCH] Task_4: Adapter and Dataclass --- TASK.md | 13 ++-- __pycache__/overpass.cpython-313.pyc | Bin 0 -> 3655 bytes main.py | 111 ++++++++------------------- overpass.py | 90 ++++++++++++++++++++++ 4 files changed, 129 insertions(+), 85 deletions(-) create mode 100644 __pycache__/overpass.cpython-313.pyc create mode 100644 overpass.py diff --git a/TASK.md b/TASK.md index c24c073..6ac122f 100644 --- a/TASK.md +++ b/TASK.md @@ -1,7 +1,8 @@ -# TASK 3: +# TASK 4: -* verlagert als Nächstes die Logik von 'fetch_bergbahnen' in ein eigenes Modul `overpass.py` aus. -* nennt die Funktion allgemeiner `fetch_overpass` (anstelle `fetch_bergbahnen`) -> somit würde es Sinn - machen, wenn wir den Query der Funktion als Argument mitgeben könnten (ist genereller). -* Erstellt in diesem `main.py` eine eigene 'main-Funktion', welche nur die Hauptlogik beinhalten und somit `fetch_overpass` - importiert und aufruft. \ No newline at end of file +Wir arbeiten nun intern direkt mit den Daten von Overpass. Macht das Sinn? Warum vielleicht nicht? + +* Versucht einen Adapter zu bauen, wir wollen intern mit einer eigenen Dataclass `POI` arbeiten. Wir bauen also dazu eine + Funktion `load_pois`, welche einerseits die Daten fetched und andererseits auch parsed. Den Fetching-Teil haben wir + bereits (`fetch_overpass`), den Pasing-Teil haben wir noch nicht. + Schreibt bitte eine eigene Dataclass`Poi` in welche die gefetchten Daten 'abgefüllt' werden können. \ No newline at end of file diff --git a/__pycache__/overpass.cpython-313.pyc b/__pycache__/overpass.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..069e044b139714a1a7120dc06ff496ef4a46968b GIT binary patch literal 3655 zcma)9Piz}m8GmDs9mkpcS(>ul6tyo-A-0s**)(n&m$XfqY`SeXNj=E|ilfo9{W6}` zo-yx@o5ZD3E}*VBbcKXKVh=s+VYzW*4|{<2!ZB5mT845!NJ!;ml!6d@;d}EZaY73F z4)gwe?|pxN?|t9wc6Y}SwA|&t8Gls}`iqzZE$R<;e*lA@A{D994a7trx*=Vcn1qoU zW}y=(fmC?_sgXiB8}`mHS&bGVYDXce#tI!1QMGdb-IiIb(5c3A-$jV@Bcyh*xT>(O zLM(v_@j{o{JsDz3L4ui*l{`Ke>5?v`dR~d?LP~O?^*S?5yE$C3?K;n8GS*$n>V)%A zQa93Nnt?6@8(@)9_?A3A-q&Y}c0oEAR6tBp5eumj3#%a}>*18_^ey@fr|ZTXV-|ZA zgU!DL;SndMX(p*rO>^R!RtWXl*)C@JoZ4r4Z~3noveWZr27EL8PU8;HqE; z^s9bsVI&93D^k|y6GY4mt)RT%cc`s|)X=14hEJhYp|IQXF7|?%*C)^_;LvMocqKe0 zsj?bTqtJIu%BH*`=X(SCexLP+7yV?=hm=xNJ}yzz|O_y^l>& zs^A&Q^dhMMtgvC?GG&;UI@M{BilrH=VU`V3hruQ$#dKVyW|`WU3LN5V%galnON;9A zXr2NXxD99jTMTf(x>58L&}p3jj%ob5y0|bpZQ2_avvIS*o_tZdla9xyna*=@4DBGy zaFW|BmD@95^w!PMTLOQ$W&mee!%J0SH!GwH*Aw3pn7p5M`3PPI+YB{$-Jn%Z&Ow@( z4X;`aJ9nE~=Gv-l)To$c*Dk(+8%~;5nXV=XWd^Bkkgdd2!o8Z7nOIADnxrnK-gq3i z;}YCWP`hZAw=NF5a)bdmX*cRs3RWKsLzUx+(PcprA)BR5qeiMaBjuwEN^%fT8NnM6 zo)K(3R)r$7ZLQho9T=d_9wv`XDaFyX=%-!uS+_^xH*MfHQx#o}#x$EIc*7dNmR z3;aS@dCG;FQDK1mWukk88S5yDIl4!x9T*J z!wG@4#K@ujKsU-jeRurqME2YfRJa;IhUh^k5>)_Z4gz}Nh>bSb1;l%8Xa=DHoZ+y@ zN3bDro!qgCykapkasQAu_Yd<;r`uhF@mkw~`v3!CaO$HVh;oo7bND;d^xhQr#WG42 z8$fST1~&a%wlh|9IKEtGtc9@LkY|_t?Pel`K^akqxO~8~O+Ym=pm)rf%o} zhZ^RTLALg&2LKUp61^`B8#U~{Ln+V3HyHp3-g_e(+y!6_UQ0kWC_c{w#@1=2$^~3M z)!VV$E;QLp&!q9h_#|GoEc^j6w*szw1ZT%4@H_w!uo`H0I1&M`1@PDgWp{)~%0P8T z8c9h^BrZoT6PvsenZFrd0auJp>)@-Va&=b4bDL26K(ta}CeoAB;XjW~M}-owg*W>H z2aJk%`A%AB4jotyJR!tG_`qfYGE#~o-?j|X3D-#73B!BuMAt1=BeoM`UVh<@T(`LG zD2pG?<(H;aRa?p5bo!V;kOq6*nyA5@4qvca$vAz!yt&O1t=ooWf*g3zzGrnz zJh*V2IT5d{0g=3JnVdRdx>@3)YW2LwdW7XY>(nk)G|MmR*f1=L3WNU|KHI4Ep@jO5 zJ>2|g^ONu=S0DBNqUTxf@b81=UQ@CWKnu~?Bvg;elqn^ zIq~9aCw~6Tk59koKmOwQ;G;`B{o{Y{jP%DJ#99jKeCv5^@M&!D_pzZ?94Y+|r#_nc z@#P(P;9svhVfwu5XwqDEOf)}*CjI;QRnn%v6(Ifp66^t{00=)5uQyDi z)Tk2HmW$h&ex;qBru$oi_i$}BvCn@hu|@e|DSp~Noa0>$QnFrn`1l{m!u%C|-EBix z^nWH{tpO_#3TS7pq(b{BO?9(xz#irXX zrxitgliSMcs3diMy!9GE*NWw&laJ1}5OkkWxBFavEoQBzgk dict: +QUERY = """ + [out:json][timeout:2][maxsize:500000]; + ( + 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; """ - Fragt die Overpass API nach Bergbahnen in der angegebenen Bounding Box ab. - Sendet einen HTTP-POST-Request an die Overpass API und gibt die geparste - JSON-Antwort zurück. - - Args: - bbox (tuple): Bounding Box als 4-Tuple in Dezimalgrad: - (south, west, north, east) - Beispiel Davos: (46.72, 9.70, 46.92, 10.00) - Beispiel Schweiz: (45.8, 5.9, 47.8, 10.5) - - Returns: - dict: Geparste JSON-Antwort der Overpass API. Die Antwort enthält - unter dem Schlüssel "elements" eine Liste von OSM-Objekten - (nodes und ways) mit ihren Tags und Koordinaten. - Beispiel: - { - "elements": [ - { - "type": "node", - "id": 123456, - "lat": 46.8, "lon": 9.8, - "tags": {"aerialway": "station", "name": "Jakobshorn"} - }, - ... - ] - } - - Raises: - OverpassApiError: Wenn die API nicht innerhalb des gesetzten Timeouts - antwortet (clientseitig, unabhängig vom serverseitigen - Timeout im Query). - OverpassApiError: Wenn der Request aus einem anderen Grund fehlschlägt - (z.B. 429 Too Many Requests, 504 Gateway Timeout, - Netzwerkfehler). - """ - - bbox_str = ",".join(map(str, bbox)) - query = BERGBAHN_QUERY.format(bbox=bbox_str) - - try: - response = requests.post( - OVERPASS_URL, - data={"data": query}, - timeout=5, - headers={"User-Agent": "CDS Exercise"}, - ) - response.raise_for_status() # prüft den HTTP-Statuscode der Antwort und wirft eine Exception, wenn es ein Fehler war (requests.HTTPError) - except requests.Timeout as exc: - raise OverpassApiError("Overpass-API Timeout") from exc - except requests.RequestException as exc: - raise OverpassApiError("Overpass-API Request fehlgeschlagen") from exc - return response.json() +# --------------------------------------------------------------------------- +# Hauptlogik +# --------------------------------------------------------------------------- +def main() -> None: + for name, bbox in BBOXEN.items(): + try: + result = fetch_overpass(overpass_query=QUERY, bbox=bbox) + except OverpassApiError as exc: + print(f" Fehler : {exc}") + continue + + elements = result.get("elements", []) + print(elements) if __name__ == "__main__": - - # bbox = (46.72, 9.70, 46.92, 10.00) - bbox = (45.8, 5.9, 47.8, 10.5) - result = fetch_bergbahnen(bbox) - pprint(result) \ No newline at end of file + main() \ No newline at end of file diff --git a/overpass.py b/overpass.py new file mode 100644 index 0000000..803d0e2 --- /dev/null +++ b/overpass.py @@ -0,0 +1,90 @@ +import requests +from pprint import pprint + + +OVERPASS_URL = "https://overpass-api.de/api/interpreter" + + +class OverpassApiError(Exception): + pass + + +def fetch_overpass(overpass_query: str, bbox: tuple) -> dict: + """ + Fragt die Overpass API nach Bergbahnen in der angegebenen Bounding Box ab. + Sendet einen HTTP-POST-Request an die Overpass API und gibt die geparste + JSON-Antwort zurück. + + Args: + overpass_query (str): Overpass-QL-Query mit dem Platzhalter {bbox}. + Beispiel: + '[out:json][timeout:5]; + (node["aerialway"="station"]({bbox});); + out center body;' + bbox (tuple): Bounding Box als 4-Tuple in Dezimalgrad: + (south, west, north, east) + Beispiel Davos: (46.72, 9.70, 46.92, 10.00) + Beispiel Schweiz: (45.8, 5.9, 47.8, 10.5) + + Returns: + dict: Geparste JSON-Antwort der Overpass API. Die Antwort enthält + unter dem Schlüssel "elements" eine Liste von OSM-Objekten + (nodes und ways) mit ihren Tags und Koordinaten. + Beispiel: + { + "elements": [ + { + "type": "node", + "id": 123456, + "lat": 46.8, "lon": 9.8, + "tags": {"aerialway": "station", "name": "Jakobshorn"} + }, + ... + ] + } + + Raises: + OverpassApiError: Wenn die API nicht innerhalb des gesetzten Timeouts + antwortet (clientseitig, unabhängig vom serverseitigen + Timeout im Query). + OverpassApiError: Wenn der Request aus einem anderen Grund fehlschlägt + (z.B. 429 Too Many Requests, 504 Gateway Timeout, + Netzwerkfehler). + """ + + bbox_str = ",".join(map(str, bbox)) + query = overpass_query.format(bbox=bbox_str) + + try: + response = requests.post( + OVERPASS_URL, + data={"data": query}, + timeout=5, + headers={"User-Agent": "CDS Exercise"}, + ) + response.raise_for_status() # prüft den HTTP-Statuscode der Antwort und wirft eine Exception, wenn es ein Fehler war (requests.HTTPError) + except requests.Timeout as exc: + raise OverpassApiError("Overpass-API Timeout") from exc + except requests.RequestException as exc: + raise OverpassApiError("Overpass-API Request fehlgeschlagen") from exc + return response.json() + + +if __name__ == "__main__": + + BERGBAHN_QUERY = """ + [out:json][timeout:2][maxsize:500000]; + ( + 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; + """ + + bbox = (46.72, 9.70, 46.92, 10.00) + # bbox = (45.8, 5.9, 47.8, 10.5) + result = fetch_overpass(overpass_query=BERGBAHN_QUERY, bbox=bbox) + pprint(result) \ No newline at end of file