国の「都市Xへの入り口」タイプのすべての標識のすべての文字の数を見つける方法は?そのような質問に答える正確な方法

最近、あるインタビューの枠組みの中で、私は問題を解決する必要がありました。その条件は以下のとおりです。

世界最高のマネージャーであるPenultimoは、あなたが実現しなければならないもう1つの素晴らしいアイデアを持っています。彼は、島に長い碑文が書かれた素晴らしい道路標識がいくつあるかを全世界に伝えることができれば、イスラデエドゥカドスへの観光客の流れが増えると信じています。島の「X市への入り口」のすべての標識の文字の総数を計算し、得られた知識を適用してベラルーシ共和国の同様の指標を計算できるアルゴリズムを考え出すことをお勧めします。集落を指定するために使用される言語、および都市へのいくつかの入り口があるかもしれないという事実に注意してください。 Penultimoもイニシアチブを奨励しているため、特定の地域についてこの問題を調査し、その地域に住む人々の数と比較することができます。また、興味深いと思われるその他の調査も実施します。


カットの下で、私はこれと他の同様の問題に対する正確な解決策をあなたに示します、例えば:「モスクワ内にいくつのガソリンスタンドがありますか?」



一般的な解決方法



OpenStreetMapマップを見ると、次のアイデアがすぐに思い浮かびます。各都市の境界線と境界線内の道路を取得し、標識がある交差点を見つけましょう。交差点を探す方法:境界のセグメントを取得し、次に道路のセグメントを取得して、それらが交差するかどうかを確認します(典型的な幾何学的問題)。そして、すべてのセクションと都市が終わるまで続きます。



OSMデータアーキテクチャについて
, : , .

ID, .



  • — , ID
  • — ,
  • — , , ,




オーバーパス



OverPass-これはOpenStreetMapからデータを取得するためのAPIです。クエリを作成するための独自の言語がありますこの記事で詳細を読むことができます



クエリの作成をより簡単かつ便利にするために、ツールOverpass-turboがあります。このツールでは、クエリの結果を便利でインタラクティブな形式で表示できます。



PythonでのOverPassAPIの使用



PythonでOSMからのデータを処理するには、Overpyパッケージをラッパーとして使用できます。

リクエストを送信してデータを受信するには、次の手順を実行する必要があります。



import overpy

api = overpy.Overpass()
Data = api.query("""
* *
""")


ここで、変数(?)データには、サーバーから提供されたすべてのものが含まれています。



このデータを処理する方法は?ミンスクの境界線を取得するために次のリクエストを入力したとします。



relation["type"="boundary"]["boundary"="administrative"]["name:be"="і"];
//:      
>; out skel qt;


出力には、次の構造のXMLファイル(Jsonを選択できます)があります。



<* *>
<     >
  <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>


いくつかのデータを取得しましょう:



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)


pythonでOpenStreetMapを操作するという観点からは、データを取得するために必要なのはこれだけです。



問題に直接行きましょう



それを解決するために、コードはPythonで書かれ、スポイラーの下に表示されます。コードの品質について強く批判しないでください。これは、このような大規模なプロジェクトとしては初めてです。



スポイラーヘッダー
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) #  





コードノート



私は長い間、数えられないように、そして標識を見逃さないように、さまざまな種類の道路を選択するように要求しました。最後のクエリでは、住宅、サービス、歩道、線路など、標識のない道路を削除するだけです。



ウィキペディアから都市のリストを解析し、形式で保存しました



。コードに時間がかかり、一度も欲求がありました。 C ++で書き直しましたが、そのままにしておくことにしました。ベラルーシのインターネットの独裁とOverPassサーバーの過負荷の問題のために、私は2日かかりました2番目の問題を解決するには、すべての都市に対して1つの要求を行う必要がありますが、これを通常どおりに行う方法はまだわかりません。



問題に対する私の答え

18981





図の正しさについて言いたいのは、すべてがOSM自体のデータの品質にかかっているということです。つまり、たとえば、1つの道路が2つの境界線と交差する場所や、交差点のどこかで境界線が少し間違って描かれている場所があり、その結果、私たちが多すぎます。 /交差点がありません。しかし、これはこの特定のタスクの機能であり、実用的な意味はありません。それ以外の場合、OSMが強みです。



2番目のタスク



それでは、モスクワ内のガソリンスタンドの数を計算しましょう。

area[name=""];
(
  node["amenity"="fuel"](area);
  way["amenity"="fuel"](area);
  relation["amenity"="fuel"](area);
);
out body;
>;
out skel qt;


コード
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)) #   




結果-489フィリング:






All Articles