Der beste Manager der Welt namens Penultimo hat eine weitere brillante Idee, die Sie verwirklichen müssen. Er glaubt, dass der Touristenstrom zur Isla de Educados zunehmen wird, wenn er der ganzen Welt sagen kann, wie viele wundervolle Verkehrszeichen mit langen Inschriften sie auf der Insel haben. Sie werden gebeten, einen Algorithmus zu entwickeln, mit dem Sie die Gesamtzahl der Buchstaben auf allen Zeichen "Eingang zur Stadt X" auf der Insel berechnen und dann die gewonnenen Erkenntnisse anwenden können, um eine ähnliche Metrik für die Republik Belarus zu berechnen. Achten Sie auf die Sprache, in der Siedlungen ausgewiesen werden, sowie auf die Tatsache, dass es möglicherweise mehrere Eingänge zur Stadt gibt. Penultimo fördert auch die Initiative, sodass Sie dieses Problem für bestimmte Gebiete untersuchen und Vergleiche mit der Anzahl der in der Region lebenden Personen anstellen können.und andere Forschungen durchführen, die Sie interessant finden.
Unter dem Schnitt zeige ich Ihnen die genaue Lösung für dieses und ähnliche Probleme, zum Beispiel: "Wie viele Tankstellen befinden sich in Moskau?"
Allgemeine Lösungsmethode
Wenn Sie sich die OpenStreetMap-Karte ansehen, fällt Ihnen sofort die folgende Idee ein: Lassen Sie uns für jede Stadt ihre Grenzen und die Straßen innerhalb ihrer Grenzen ermitteln und dann ihre Kreuzungen finden, an denen es Schilder gibt! So suchen wir nach Kreuzungen: Wir nehmen einen Abschnitt der Grenze und dann einen Abschnitt der Straße und prüfen, ob sie sich schneiden (ein typisches geometrisches Problem). Und so weiter, bis alle Abschnitte und Städte vorbei sind.
Informationen zur OSM-Datenarchitektur
, : , .
ID, .
ID, .
- — , ID
- — ,
- — , , ,
Überführung
OverPass - Dies ist eine API zum Abrufen von Daten von OpenStreetMap. Es hat eine eigene Sprache zum Schreiben von Abfragen, die Sie in diesem Artikel ausführlich lesen können .
Um das Erstellen von Abfragen einfacher und bequemer zu machen, gibt es ein Tool Overpass-Turbo , mit dem das Ergebnis der Abfrage in einer bequemen und interaktiven Form angezeigt werden kann.
Verwenden der OverPass-API in Python
Um Daten aus OSM in Python zu verarbeiten, können Sie das Overpy-Paket als Wrapper verwenden.
Um Anforderungen zu senden und Daten zu empfangen, müssen Sie Folgendes tun:
import overpy
api = overpy.Overpass()
Data = api.query("""
* *
""")
wo die Variable (?) Daten alles enthält, was der Server uns gegeben hat.
Wie verarbeite ich diese Daten? Angenommen, wir haben die folgende Anfrage eingegeben, um die Grenzen von Minsk zu ermitteln:
relation["type"="boundary"]["boundary"="administrative"]["name:be"="і"];
//:
>; out skel qt;
Am Ausgang haben wir eine XML-Datei (Sie können Json auswählen) mit der folgenden Struktur:
<* *>
< >
<node id="277218521" lat="53.8605688" lon="27.3946601"/>
<node id="4623647835" lat="53.8603938" lon="27.3966685"/>
<node id="4713906615" lat="53.8605343" lon="27.3998220"/>
<node id="4713906616" lat="53.8605398" lon="27.3966820"/>
<node id="4713906617" lat="53.8605986" lon="27.3947987"/>
<node id="277050633" lat="53.8463790" lon="27.4431241"/>
<node id="277050634" lat="53.8455797" lon="27.4452681"/>
<node id="4713906607" lat="53.8460017" lon="27.4439797"/>
< ID , >
<way id="572768148">
<nd ref="5502433452"/>
<nd ref="277218520"/>
<nd ref="4713906620"/>
<nd ref="277218521"/>
<nd ref="4713906617"/>
<nd ref="4623647835"/>
<nd ref="4713906616"/>
</way>
<way id="29079842">
<nd ref="277212682"/>
<nd ref="277051005"/>
<nd ref="4739822889"/>
<nd ref="4739822888"/>
<nd ref="4739845423"/>
<nd ref="4739845422"/>
<nd ref="4739845421"/>
</way>
Lassen Sie uns einige Daten erhalten:
import overpy
api = overpy.Overpass()
Data = api.query("""
relation["type"="boundary"]["boundary"="administrative"]["name:be"="і"];
>; out skel qt;
""")
Xa=Data.ways[0].nodes[0].lon #
Ya=Data.ways[0].nodes[0].lat #
Xb=Data.ways[0].nodes[1].lon
Yb=Data.ways[0].nodes[1].lat
NodeID=Data.ways[0]._node_ids[0] # ID
print(len(Data.nodes)) #
print(NodeID)
print(Xa,Ya)
print(Xb,Yb)
Unter dem Gesichtspunkt der Arbeit mit OpenStreetMap in Python ist dies alles, was Sie benötigen, um die Daten abzurufen.
Gehen wir direkt zum Problem
Um das Problem zu lösen, wurde der Code in Python geschrieben. Sie können ihn unter dem Spoiler sehen. Bitte schimpfen Sie nicht zu viel für die Qualität des Codes, dies ist das erste so große Projekt darauf.
Spoiler Header
import overpy
###########################
def line_intersection(line1, line2): #
ax1 = line1[0][0]
ay1 = line1[0][1]
ax2 = line1[1][0]
ay2 = line1[1][1]
bx1 = line2[0][0]
by1 = line2[0][1]
bx2 = line2[1][0]
by2 = line2[1][1]
v1 = (bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1)
v2 = (bx2 - bx1) * (ay2 - by1) - (by2 - by1) * (ax2 - bx1)
v3 = (ax2 - ax1) * (by1 - ay1) - (ay2 - ay1) * (bx1 - ax1)
v4 = (ax2 - ax1) * (by2 - ay1) - (ay2 - ay1) * (bx2 - ax1)
return (v1 * v2 < 0) & (v3 * v4 < 0)
#######################################
citytmp = []
city = []
Borderway = []
Roadway = []
Total = 0
A = [0, 0]
B = [0, 0]
C = [0, 0]
D = [0, 0]
amount = 0
progressbar = 0
ReadyData = open(' .txt','w')
with open(" .txt", "r", encoding='utf8') as file:
for i in range(115):
citytmp.append(file.readline())
citytmp = [line.rstrip() for line in citytmp]
for i in range(115):
city.append('"' + citytmp[i] + '"')
city[0]='"і"'
api = overpy.Overpass()
for number in range(0,115):# ,
borderstring = """(
relation["type"="boundary"]["boundary"="administrative"]["name:be"=""" + city[number] + """][place=town];
relation["type"="boundary"]["boundary"="administrative"]["name:be"=""" + city[number] + """][place=city];
);
>; out skel qt;"""
roadstring = """(
area[place=town]["name:be"=""" + city[number] + """];
way["highway"][highway!=service]["highway"!="footway"]["highway"!="track"]["highway"!="path"]
["highway"!="cycleway"]["highway"!="pedestrian"]["highway"!="steps"]["highway"!="residential"](area);
area[place=city]["name:be"=""" + city[number] + """];
way["highway"][highway!=service]["highway"!="footway"]["highway"!="track"]["highway"!="path"]
["highway"!="cycleway"]["highway"!="pedestrian"]["highway"!="steps"]["highway"!="residential"](area);
);
out body; >; out skel qt;"""
print('Getting data about', city[number],'...')
road = api.query(roadstring)
border = api.query(borderstring)
print('got data!, city:', city[number]) #
for w in range(len(border.ways)): #
for i in range(len(border.ways[w]._node_ids)):#
progressbar = i / len(border.ways[w]._node_ids) * 100
print(progressbar, "%;", w, "of", len(border.ways), "parts ready; city-", city[number])
A[0] = border.ways[w].nodes[i].lon
A[1] = border.ways[w].nodes[i].lat
if i == len(border.ways[w]._node_ids) - 1:
break
B[0] = border.ways[w].nodes[i+1].lon
B[1] = border.ways[w].nodes[i+1].lat
for j in range(len(road.ways)):
for k in range(len(road.ways[j]._node_ids)):
C[0] = road.ways[j].nodes[k].lon
C[1] = road.ways[j].nodes[k].lat
if k == len(road.ways[j]._node_ids) - 1:
break
D[0] = road.ways[j].nodes[k+1].lon
D[1] = road.ways[j].nodes[k+1].lat
if line_intersection((A, B), (C, D)) == 1:
amount += 1
print(road.ways[j]._node_ids[k])
print(amount)
Total += amount * len(city[number])
ReadyData.write(str(city[number]))
ReadyData.write(str(amount))
ReadyData.write('\n')
amount = 0
print('Total', Total) #
Code-Hinweise
Ich habe lange nachgefragt und verschiedene Straßentypen ausgewählt, damit es weniger zählt und die Schilder nicht verpasst. Die letzte Abfrage entfernt einfach die Straßen, auf denen es keine Schilder gibt, z. B. Wohnen, Service, Fußweg, Gleis usw.
Ich habe die Liste der Städte aus Wikipedia analysiert und sie im Format gespeichert.
Der Code dauert lange, ich hatte sogar einmal einen Wunsch Schreiben Sie es in C ++ neu, aber lassen Sie es so, wie es ist. Ich habe zwei Tage gebraucht , alles wegen Problemen mit der
Meine Antwort auf das Problem
18981
Was ich zur Richtigkeit der Abbildung sagen möchte: Alles hängt von der Qualität der Daten des OSM selbst ab, dh es gibt Orte, an denen beispielsweise eine Straße zwei Grenzlinien kreuzt oder irgendwo an der Kreuzung die Grenze etwas falsch gezeichnet ist und wir daher zu viel haben / fehlende Kreuzung. Dies ist jedoch ein Merkmal dieser speziellen Aufgabe, das keine praktische Bedeutung hat, andernfalls ist OSM Stärke.
Zweite Aufgabe
Berechnen wir nun die Anzahl der Tankstellen in Moskau:
area[name=""];
(
node["amenity"="fuel"](area);
way["amenity"="fuel"](area);
relation["amenity"="fuel"](area);
);
out body;
>;
out skel qt;
Der Code
import overpy
api = overpy.Overpass()
Data = api.query("""
area[name=""];
(
node["amenity"="fuel"](area);
way["amenity"="fuel"](area);
relation["amenity"="fuel"](area);
);
out body;
>;
out skel qt;
""")
print(len(Data.nodes)) #
Ergebnis - 489 Füllungen:
