Wie finde ich die Anzahl aller Buchstaben auf allen Zeichen des Typs "Einreise in die Stadt X" im Land? Der genaue Weg, um solche Fragen zu beantworten

Vor kurzem musste ich im Rahmen eines Interviews ein Problem lösen, dessen Bedingung unten angegeben ist:

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
  • — ,
  • — , , ,




Ü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 Diktatur des belarussischen Internets und der Überlastung des OverPass-Servers. Um das zweite Problem zu lösen, müssen Sie eine Anfrage für alle Städte stellen, aber ich habe noch nicht herausgefunden, wie dies normal zu tun ist.



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:






All Articles