如何在该国家/地区中找到所有“进入城市X”类型标志上的所有字母的数量?回答此类问题的确切方法

最近,在一次采访的框架内,我需要解决一个问题,其条件如下:

世界上最好的经理Penultimo还有另一个绝妙的主意,您必须意识到。他认为,如果他能告诉世界上岛上有多少个带有长铭文的奇妙路标,到埃德卡多斯岛的游客就会增加。我们邀请您提出一种算法,该算法使您可以计算岛上所有“进入X州城市”标志上的字母总数,然后应用所获得的知识来计算白俄罗斯共和国的类似指标。注意用于指定定居点的语言,以及该城市可能有多个入口的事实。 Penultimo也鼓励采取主动行动,因此您可以针对特定地区研究此问题,并与该地区的人数进行比较,并进行其他您认为有趣的研究。


在削减部分,我将向您显示针对此问题和其他类似问题的确切解决方案,例如:“莫斯科内有多少个加油站?”



一般解决方法



如果您看一下OpenStreetMap地图,就会立即想到以下想法:让我们为每个城市确定其边界和边界内的道路,然后找到它们的交叉口,上面将有路标!我们如何寻找相交点:我们先取一段边界,然后取一段道路,看看它们是否相交(典型的几何问题)。依此类推,直到所有部分和城市都结束。



关于OSM数据架构
, : , .

ID, .



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




立交桥



OverPass-这是一个用于从OpenStreetMap获取数据的API。它有自己的语言来构成查询,您可以在本文中详细了解它



为了使编写查询更容易和更方便,有一个工具Overpass-turbo,可以在其中以方便且交互式的形式查看查询结果。



在Python中使用OverPass API



要使用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) #  





代码说明



我提出了很长一段时间的要求,选择了不同类型的道路,以减少计算次数,并且不会错过路标。最终查询只是删除那些没有标志的道路,例如住宅,服务,人行道,轨道等。



我从Wikipedia解析了城市列表,并将其保存为format.tht



代码执行了很长时间,我什至希望一次用C ++重写它,但决定保留它原样。我花了两天时间,都是因为白俄罗斯互联网独裁和OverPass服务器超载。要解决第二个问题,您需要向所有城市提出一个请求,但是我还没有弄清楚通常如何做。



我对这个问题的回答

18981





关于图形的正确性,我想说的是:一切都取决于OSM本身的数据质量,也就是说,在某些地方,例如,一条道路横过两条边界线,或者在某个交界处的某个地方,边界绘制有点不正确,结果我们拥有太多/缺少交集。但这是此特定任务的功能,没有实际意义,否则OSM就是优势。



第二项任务



现在,让我们计算莫斯科内的加油站数量:

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