世界上最好的经理Penultimo还有另一个绝妙的主意,您必须意识到。他认为,如果他能告诉世界上岛上有多少个带有长铭文的奇妙路标,到埃德卡多斯岛的游客就会增加。我们邀请您提出一种算法,该算法使您可以计算岛上所有“进入X州城市”标志上的字母总数,然后应用所获得的知识来计算白俄罗斯共和国的类似指标。注意用于指定定居点的语言,以及该城市可能有多个入口的事实。 Penultimo也鼓励采取主动行动,因此您可以针对特定地区研究此问题,并与该地区的人数进行比较,并进行其他您认为有趣的研究。
在削减部分,我将向您显示针对此问题和其他类似问题的确切解决方案,例如:“莫斯科内有多少个加油站?”
一般解决方法
如果您看一下OpenStreetMap地图,就会立即想到以下想法:让我们为每个城市确定其边界和边界内的道路,然后找到它们的交叉口,上面将有路标!我们如何寻找相交点:我们先取一段边界,然后取一段道路,看看它们是否相交(典型的几何问题)。依此类推,直到所有部分和城市都结束。
关于OSM数据架构
, : , .
ID, .
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 ++重写它,但决定保留它原样。我花了两天时间,都是因为白俄罗斯互联网
我对这个问题的回答
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馅料: