Den nächsten POI finden#
Aus Nutzer-Perspektive ist das ein ganz klassisches Problem:
Wo ist das nächste Café? Wo ist das nächste Klo? Wo ist die nächste Bahn-Haltestelle?
Aus einem geoinformatischen Blickwinkel stellt sich das ganze etwas komplizierter dar. Zunächst wollen wir die Frage in der Sprache der Geoinformatik formulieren:
Gegeben eine Koordinate und eine Menge an POIs einer bestimmten Kategorie, welcher ist fußläufig der nächste?
Die Koordinate ist unser Standort, also N 51° 57' 49.068 E 007° 36' 46.512"
. Die POIs einer bestimmten Kategorie, hier Cafés kann uns der openpoiservice
liefern.
Dieser wird allerdings einen Bereich um unseren Standort benötigen, um uns alle POIs, die in diesem Bereich liegen, geben zu können.
Angenommen dass wir maximal einen Kilometer, also ca. 15 Minuten, laufen wollen, können wir diesen Bereich einfach mit einem Kreis mit passendem Radius approximieren.
Wir können aber auch zunächst den tatsächlich erreichbaren Bereich mithilfe des openrouteservice
bestimmen:
Wir beginnen mit ein bisschen notwendigem Setup, das für den Rest des Beispiels gebraucht wird.
Show code cell source
import folium
import requests
# The key in here is not working anymore.
headers = {
'Accept': 'application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8',
'Authorization': '5b3ce3597851110001cf6248e2553546494b4efba514744ff48c2e59',
'Content-Type': 'application/json; charset=utf-8'
}
Als nächstes kann der /isochrones
-Endpunkt genutzt werden, um den Bereich zu finden, der fußläufig innerhalb von 15 Minuten (900 Sekunden) von unserem Standort aus erreichbar ist. Wir nutzen folium
, um das Ergebnis zu visualisieren.
# openrouteservice uses [lon, lat]
startpoint = [7.61282,51.96363] # [lon, lat]
startpoint_reverse = startpoint[::-1] # [lat, lon] for folium
body = {"locations":[startpoint],"range":[900]}
# We use the foot-walking profile here.
isochrone_response = requests.post('https://api.openrouteservice.org/v2/isochrones/foot-walking', json=body, headers=headers)
isochrone_geojson = isochrone_response.json()
# folium uses [lat, lon]
m = folium.Map(startpoint_reverse, zoom_start=14)
folium.GeoJson(isochrone_geojson).add_to(m)
m
Innerhalb dieses Bereichs können nun POIs der gewünschten Kategorie gesucht werden.
Dafür wird der /pois
-Endpunkt verwendet. Die entsprechende Kategorie findet sich in der Backend-Dokumentation des openrouteservice
# The POI endpoint needs a "plain" polygon, not a GeoJSON "Feature".
geojson = isochrone_geojson["features"][0]["geometry"]
body = {"request":"pois","geometry":{"geojson":geojson},"filters":{"category_ids":[564]},"sortby":"category"}
poi_response = requests.post('https://api.openrouteservice.org/pois', json=body, headers=headers)
poi_geojson = poi_response.json()
m = folium.Map(startpoint_reverse, zoom_start=14)
folium.GeoJson(poi_geojson).add_to(m)
m
Als Nächstes muss hieraus der nächste POI extrahiert werden. Dafür wird der /matrix
-Endpunkt genutzt.
Hierbei werden die POI-Koordinaten als destination
, der Startpunkt als source
übergeben.
Dadurch ist das Ergebnis einfach verarbeitbar.
pois = []
pois_info = []
for feature in poi_geojson["features"]:
pois.append(feature["geometry"]["coordinates"])
pois_info.append(feature)
# The final list of locations will include all POI locations and have the startpoint attached to the end.
destinations = list(range(len(pois)))
sources = [len(pois)]
pois.append(startpoint)
# now we have the POIs and can make a neat matrix call
body = {"locations": pois, "sources": sources, "destinations": destinations }
print(body)
matrix_response = requests.post('https://api.openrouteservice.org/v2/matrix/foot-walking', json=body, headers=headers)
print(matrix_response.status_code, matrix_response.reason)
print(matrix_response.text)
matrix_json = matrix_response.json()
print(matrix_json)
durations = matrix_json["durations"][0]
nearest_duration = min(durations)
nearest_index = durations.index(nearest_duration)
# The last element in the list is the startpoint.
nearest_poi = pois[nearest_index]
nearest_poi_reverse = nearest_poi[::-1]
nearest_poi_info = pois_info[nearest_index]
m = folium.Map(startpoint_reverse, zoom_start=14)
folium.map.Marker(location=nearest_poi_reverse).add_to(m)
m
{'locations': [[7.610429, 51.967839], [7.627296, 51.960045], [7.61323, 51.968324], [7.620167, 51.963336], [7.628404, 51.963373], [7.62639, 51.960486], [7.618128, 51.966363], [7.61678, 51.955436], [7.610157, 51.964569], [7.626256, 51.95945], [7.626413, 51.95943], [7.622595, 51.970044], [7.626235, 51.959383], [7.622607, 51.960341], [7.615033, 51.968788], [7.627175, 51.964004], [7.618026, 51.963168], [7.628389, 51.959613], [7.628547, 51.961154], [7.626372, 51.960522], [7.627238, 51.959161], [7.603077, 51.959505], [7.597829, 51.961154], [7.61055, 51.972014], [7.612293, 51.952544], [7.618595, 51.963004], [7.626887, 51.965795], [7.623465, 51.964594], [7.628791, 51.967291], [7.628029, 51.96306], [7.603274, 51.966042], [7.626422, 51.960006], [7.613973, 51.971225], [7.626536, 51.960224], [7.620144, 51.963368], [7.62603, 51.958297], [7.625461, 51.961987], [7.624915, 51.960543], [7.625832, 51.958746], [7.624067, 51.96627], [7.61812, 51.96232], [7.620895, 51.970133], [7.622075, 51.958793], [7.625878, 51.961425], [7.626765, 51.965017], [7.620929, 51.962295], [7.610729, 51.953073], [7.629119, 51.960431], [7.599961, 51.965219], [7.621319, 51.96368], [7.619153, 51.965286], [7.622336, 51.972149], [7.627463, 51.961908], [7.6257, 51.961969], [7.623672, 51.960466], [7.622219, 51.968612], [7.627632, 51.960853], [7.61282, 51.96363]], 'sources': [57], 'destinations': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56]}
200 OK
{"durations":[[694.68,898.18,576.89,437.54,932.15,841.57,480.02,895.72,165.85,918.12,925.99,905.94,917.62,854.84,617.94,868.43,353.34,978.13,984.38,838.67,986.11,766.48,1152.27,931.03,1207.65,390.23,910.78,662.47,965.32,952.92,775.47,883.76,826.47,863.05,437.03,1006.77,813.98,771.23,970.04,707.08,381.26,834.63,775.9,873.99,876.51,548.47,1208.87,1026.43,840.25,499.47,429.89,1061.13,912.27,825.86,727.15,750.22,927.44]],"destinations":[{"location":[7.610283,51.967753],"snapped_distance":13.85},{"location":[7.627326,51.960136],"snapped_distance":10.3},{"location":[7.613287,51.968282],"snapped_distance":6.08},{"location":[7.620129,51.963434],"snapped_distance":11.16},{"location":[7.628171,51.963316],"snapped_distance":17.14},{"location":[7.626464,51.960516],"snapped_distance":6.06},{"location":[7.618201,51.966395],"snapped_distance":6.12},{"location":[7.616714,51.955482],"snapped_distance":6.84},{"location":[7.610224,51.964556],"snapped_distance":4.81},{"location":[7.626263,51.959492],"snapped_distance":4.67},{"location":[7.626421,51.959482],"snapped_distance":5.82},{"location":[7.622588,51.970132],"snapped_distance":9.78},{"location":[7.626253,51.959492],"snapped_distance":12.23},{"location":[7.622557,51.960358],"snapped_distance":3.93},{"location":[7.615162,51.968708],"snapped_distance":12.51},{"location":[7.627213,51.963953],"snapped_distance":6.22},{"location":[7.61796,51.96317],"snapped_distance":4.51},{"location":[7.628276,51.959628],"snapped_distance":7.9},{"location":[7.628573,51.961148],"snapped_distance":1.89},{"location":[7.626432,51.960546],"snapped_distance":4.89},{"location":[7.627333,51.959153],"snapped_distance":6.58},{"location":[7.602917,51.959452],"snapped_distance":12.46},{"location":[7.598043,51.961508],"snapped_distance":41.96},{"location":[7.61064,51.972053],"snapped_distance":7.52},{"location":[7.612331,51.952518],"snapped_distance":3.94},{"location":[7.618565,51.962958],"snapped_distance":5.48},{"location":[7.627067,51.96577],"snapped_distance":12.64},{"location":[7.623369,51.964555],"snapped_distance":7.86},{"location":[7.628906,51.967312],"snapped_distance":8.21},{"location":[7.628019,51.963092],"snapped_distance":3.59},{"location":[7.603247,51.965996],"snapped_distance":5.47},{"location":[7.626293,51.959883],"snapped_distance":16.33},{"location":[7.613742,51.971188],"snapped_distance":16.32},{"location":[7.626703,51.960292],"snapped_distance":13.72},{"location":[7.620119,51.963432],"snapped_distance":7.33},{"location":[7.626151,51.958321],"snapped_distance":8.73},{"location":[7.625486,51.962106],"snapped_distance":13.36},{"location":[7.625054,51.960546],"snapped_distance":9.54},{"location":[7.625973,51.958766],"snapped_distance":9.88},{"location":[7.624015,51.9664],"snapped_distance":14.93},{"location":[7.61809,51.962237],"snapped_distance":9.47},{"location":[7.621015,51.970142],"snapped_distance":8.25},{"location":[7.621908,51.958871],"snapped_distance":14.36},{"location":[7.625971,51.96142],"snapped_distance":6.43},{"location":[7.626652,51.964937],"snapped_distance":11.77},{"location":[7.62085,51.962166],"snapped_distance":15.38},{"location":[7.610676,51.953119],"snapped_distance":6.27},{"location":[7.629164,51.960427],"snapped_distance":3.14},{"location":[7.599961,51.965167],"snapped_distance":5.82},{"location":[7.621265,51.963751],"snapped_distance":8.72},{"location":[7.619134,51.96517],"snapped_distance":12.93},{"location":[7.622479,51.972225],"snapped_distance":12.94},{"location":[7.627424,51.961838],"snapped_distance":8.22},{"location":[7.625725,51.962087],"snapped_distance":13.22},{"location":[7.623635,51.960549],"snapped_distance":9.55},{"location":[7.622043,51.96862],"snapped_distance":12.06},{"location":[7.627634,51.960853],"snapped_distance":0.16}],"sources":[{"location":[7.612824,51.963635],"snapped_distance":0.64}],"metadata":{"attribution":"openrouteservice.org | OpenStreetMap contributors","service":"matrix","timestamp":1743078653774,"query":{"locations":[[7.610429,51.967839],[7.627296,51.960045],[7.61323,51.968324],[7.620167,51.963336],[7.628404,51.963373],[7.62639,51.960486],[7.618128,51.966363],[7.61678,51.955436],[7.610157,51.964569],[7.626256,51.95945],[7.626413,51.95943],[7.622595,51.970044],[7.626235,51.959383],[7.622607,51.960341],[7.615033,51.968788],[7.627175,51.964004],[7.618026,51.963168],[7.628389,51.959613],[7.628547,51.961154],[7.626372,51.960522],[7.627238,51.959161],[7.603077,51.959505],[7.597829,51.961154],[7.61055,51.972014],[7.612293,51.952544],[7.618595,51.963004],[7.626887,51.965795],[7.623465,51.964594],[7.628791,51.967291],[7.628029,51.96306],[7.603274,51.966042],[7.626422,51.960006],[7.613973,51.971225],[7.626536,51.960224],[7.620144,51.963368],[7.62603,51.958297],[7.625461,51.961987],[7.624915,51.960543],[7.625832,51.958746],[7.624067,51.96627],[7.61812,51.96232],[7.620895,51.970133],[7.622075,51.958793],[7.625878,51.961425],[7.626765,51.965017],[7.620929,51.962295],[7.610729,51.953073],[7.629119,51.960431],[7.599961,51.965219],[7.621319,51.96368],[7.619153,51.965286],[7.622336,51.972149],[7.627463,51.961908],[7.6257,51.961969],[7.623672,51.960466],[7.622219,51.968612],[7.627632,51.960853],[7.61282,51.96363]],"profile":"foot-walking","profileName":"foot-walking","responseType":"json","sources":["57"],"destinations":["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56"]},"engine":{"version":"9.1.1","build_date":"2025-03-14T11:07:03Z","graph_date":"2025-03-19T23:54:28Z"}}}
{'durations': [[694.68, 898.18, 576.89, 437.54, 932.15, 841.57, 480.02, 895.72, 165.85, 918.12, 925.99, 905.94, 917.62, 854.84, 617.94, 868.43, 353.34, 978.13, 984.38, 838.67, 986.11, 766.48, 1152.27, 931.03, 1207.65, 390.23, 910.78, 662.47, 965.32, 952.92, 775.47, 883.76, 826.47, 863.05, 437.03, 1006.77, 813.98, 771.23, 970.04, 707.08, 381.26, 834.63, 775.9, 873.99, 876.51, 548.47, 1208.87, 1026.43, 840.25, 499.47, 429.89, 1061.13, 912.27, 825.86, 727.15, 750.22, 927.44]], 'destinations': [{'location': [7.610283, 51.967753], 'snapped_distance': 13.85}, {'location': [7.627326, 51.960136], 'snapped_distance': 10.3}, {'location': [7.613287, 51.968282], 'snapped_distance': 6.08}, {'location': [7.620129, 51.963434], 'snapped_distance': 11.16}, {'location': [7.628171, 51.963316], 'snapped_distance': 17.14}, {'location': [7.626464, 51.960516], 'snapped_distance': 6.06}, {'location': [7.618201, 51.966395], 'snapped_distance': 6.12}, {'location': [7.616714, 51.955482], 'snapped_distance': 6.84}, {'location': [7.610224, 51.964556], 'snapped_distance': 4.81}, {'location': [7.626263, 51.959492], 'snapped_distance': 4.67}, {'location': [7.626421, 51.959482], 'snapped_distance': 5.82}, {'location': [7.622588, 51.970132], 'snapped_distance': 9.78}, {'location': [7.626253, 51.959492], 'snapped_distance': 12.23}, {'location': [7.622557, 51.960358], 'snapped_distance': 3.93}, {'location': [7.615162, 51.968708], 'snapped_distance': 12.51}, {'location': [7.627213, 51.963953], 'snapped_distance': 6.22}, {'location': [7.61796, 51.96317], 'snapped_distance': 4.51}, {'location': [7.628276, 51.959628], 'snapped_distance': 7.9}, {'location': [7.628573, 51.961148], 'snapped_distance': 1.89}, {'location': [7.626432, 51.960546], 'snapped_distance': 4.89}, {'location': [7.627333, 51.959153], 'snapped_distance': 6.58}, {'location': [7.602917, 51.959452], 'snapped_distance': 12.46}, {'location': [7.598043, 51.961508], 'snapped_distance': 41.96}, {'location': [7.61064, 51.972053], 'snapped_distance': 7.52}, {'location': [7.612331, 51.952518], 'snapped_distance': 3.94}, {'location': [7.618565, 51.962958], 'snapped_distance': 5.48}, {'location': [7.627067, 51.96577], 'snapped_distance': 12.64}, {'location': [7.623369, 51.964555], 'snapped_distance': 7.86}, {'location': [7.628906, 51.967312], 'snapped_distance': 8.21}, {'location': [7.628019, 51.963092], 'snapped_distance': 3.59}, {'location': [7.603247, 51.965996], 'snapped_distance': 5.47}, {'location': [7.626293, 51.959883], 'snapped_distance': 16.33}, {'location': [7.613742, 51.971188], 'snapped_distance': 16.32}, {'location': [7.626703, 51.960292], 'snapped_distance': 13.72}, {'location': [7.620119, 51.963432], 'snapped_distance': 7.33}, {'location': [7.626151, 51.958321], 'snapped_distance': 8.73}, {'location': [7.625486, 51.962106], 'snapped_distance': 13.36}, {'location': [7.625054, 51.960546], 'snapped_distance': 9.54}, {'location': [7.625973, 51.958766], 'snapped_distance': 9.88}, {'location': [7.624015, 51.9664], 'snapped_distance': 14.93}, {'location': [7.61809, 51.962237], 'snapped_distance': 9.47}, {'location': [7.621015, 51.970142], 'snapped_distance': 8.25}, {'location': [7.621908, 51.958871], 'snapped_distance': 14.36}, {'location': [7.625971, 51.96142], 'snapped_distance': 6.43}, {'location': [7.626652, 51.964937], 'snapped_distance': 11.77}, {'location': [7.62085, 51.962166], 'snapped_distance': 15.38}, {'location': [7.610676, 51.953119], 'snapped_distance': 6.27}, {'location': [7.629164, 51.960427], 'snapped_distance': 3.14}, {'location': [7.599961, 51.965167], 'snapped_distance': 5.82}, {'location': [7.621265, 51.963751], 'snapped_distance': 8.72}, {'location': [7.619134, 51.96517], 'snapped_distance': 12.93}, {'location': [7.622479, 51.972225], 'snapped_distance': 12.94}, {'location': [7.627424, 51.961838], 'snapped_distance': 8.22}, {'location': [7.625725, 51.962087], 'snapped_distance': 13.22}, {'location': [7.623635, 51.960549], 'snapped_distance': 9.55}, {'location': [7.622043, 51.96862], 'snapped_distance': 12.06}, {'location': [7.627634, 51.960853], 'snapped_distance': 0.16}], 'sources': [{'location': [7.612824, 51.963635], 'snapped_distance': 0.64}], 'metadata': {'attribution': 'openrouteservice.org | OpenStreetMap contributors', 'service': 'matrix', 'timestamp': 1743078653774, 'query': {'locations': [[7.610429, 51.967839], [7.627296, 51.960045], [7.61323, 51.968324], [7.620167, 51.963336], [7.628404, 51.963373], [7.62639, 51.960486], [7.618128, 51.966363], [7.61678, 51.955436], [7.610157, 51.964569], [7.626256, 51.95945], [7.626413, 51.95943], [7.622595, 51.970044], [7.626235, 51.959383], [7.622607, 51.960341], [7.615033, 51.968788], [7.627175, 51.964004], [7.618026, 51.963168], [7.628389, 51.959613], [7.628547, 51.961154], [7.626372, 51.960522], [7.627238, 51.959161], [7.603077, 51.959505], [7.597829, 51.961154], [7.61055, 51.972014], [7.612293, 51.952544], [7.618595, 51.963004], [7.626887, 51.965795], [7.623465, 51.964594], [7.628791, 51.967291], [7.628029, 51.96306], [7.603274, 51.966042], [7.626422, 51.960006], [7.613973, 51.971225], [7.626536, 51.960224], [7.620144, 51.963368], [7.62603, 51.958297], [7.625461, 51.961987], [7.624915, 51.960543], [7.625832, 51.958746], [7.624067, 51.96627], [7.61812, 51.96232], [7.620895, 51.970133], [7.622075, 51.958793], [7.625878, 51.961425], [7.626765, 51.965017], [7.620929, 51.962295], [7.610729, 51.953073], [7.629119, 51.960431], [7.599961, 51.965219], [7.621319, 51.96368], [7.619153, 51.965286], [7.622336, 51.972149], [7.627463, 51.961908], [7.6257, 51.961969], [7.623672, 51.960466], [7.622219, 51.968612], [7.627632, 51.960853], [7.61282, 51.96363]], 'profile': 'foot-walking', 'profileName': 'foot-walking', 'responseType': 'json', 'sources': ['57'], 'destinations': ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56']}, 'engine': {'version': '9.1.1', 'build_date': '2025-03-14T11:07:03Z', 'graph_date': '2025-03-19T23:54:28Z'}}}
Jetzt kann als letzter Schritt eine Route zum nächsten POI berechnet werden.
body = {"coordinates":[startpoint,nearest_poi]}
directions_response = requests.post('https://api.openrouteservice.org/v2/directions/foot-walking/geojson', json=body, headers=headers)
directions_geojson = directions_response.json()
print("startpoint", startpoint)
print("nearest_poi", nearest_poi)
print("Directions Response", directions_geojson)
m = folium.Map(startpoint_reverse, zoom_start=14)
folium.map.Marker(location=nearest_poi_reverse).add_to(m)
folium.map.Marker(location=startpoint_reverse).add_to(m)
folium.GeoJson(directions_geojson).add_to(m)
m
startpoint [7.61282, 51.96363]
nearest_poi [7.610157, 51.964569]
Directions Response {'type': 'FeatureCollection', 'features': [{'bbox': [7.610224, 51.963635, 7.612824, 51.964735], 'type': 'Feature', 'properties': {'segments': [{'distance': 230.3, 'duration': 165.8, 'steps': [{'distance': 206.5, 'duration': 148.7, 'type': 11, 'instruction': 'Head northwest', 'name': '-', 'way_points': [0, 8]}, {'distance': 23.9, 'duration': 17.2, 'type': 0, 'instruction': 'Turn left', 'name': '-', 'way_points': [8, 9]}, {'distance': 0.0, 'duration': 0.0, 'type': 10, 'instruction': 'Arrive at your destination, on the right', 'name': '-', 'way_points': [9, 9]}]}], 'summary': {'distance': 230.3, 'duration': 165.8}, 'way_points': [0, 9]}, 'geometry': {'coordinates': [[7.612824, 51.963635], [7.612749, 51.963658], [7.612655, 51.963689], [7.611862, 51.963971], [7.61162, 51.964069], [7.61135, 51.964221], [7.611049, 51.96438], [7.610985, 51.964418], [7.610416, 51.964735], [7.610224, 51.964556]], 'type': 'LineString'}}], 'bbox': [7.610224, 51.963635, 7.612824, 51.964735], 'metadata': {'attribution': 'openrouteservice.org | OpenStreetMap contributors', 'service': 'routing', 'timestamp': 1743078653942, 'query': {'coordinates': [[7.61282, 51.96363], [7.610157, 51.964569]], 'profile': 'foot-walking', 'profileName': 'foot-walking', 'format': 'geojson'}, 'engine': {'version': '9.1.1', 'build_date': '2025-03-14T11:07:03Z', 'graph_date': '2025-03-19T23:54:28Z'}}}