全,
我一直在尝试建立一个网站(在Django),这是一个索引,在世界上所有的MTB路线。我是Python人,所以只要有可能,我都会尝试使用Python。
我已经成功地从OSM (Display relation (trail) in leaflet)中提取了数据,但发现对所有MTB跟踪(tag: route=mtb)执行此操作的数据量太大(处理时间非常长)。因此,我尝试在本地完成所有操作,方法是(从Latest Weekly Planet XML File)下载整个OpenStreetMap数据集的洪流,并使用osmfilter (Ubuntu20.04中的osmctools的一部分)过滤tag: route=mtb,如下所示:
osmfilter $unzipped_osm_planet_file --keep="route=mtb" -o=$osm_planet_dir/world_mtb_routes.osm这会产生一个大约1.2 GB的文件,仔细检查一下,它似乎包含了我需要的所有数据。我的目标是将文件转换为pandas.DataFrame(),以便在将相关方面推送到Django DB之前进行进一步的过滤和转换。我试图使用Python Pandas将该文件加载为常规的XML文件,但这导致Jupyter笔记本内核崩溃。我猜数据太大了。
我的第二种方法是这个解决方案:How to extract and visualize data from OSM file in Python。这对我来说很有效,至少,我可以获得一些信息,比如文件中关系的标签(以及其他指定的细节)。我遗漏的是关系成员(方式),然后是方式成员(节点)和它们的纬度/经度。我需要这些来实现我在这里所做的事情:Plotting OpenStreetMap relations does not generate continuous lines
我对许多解决方案持开放态度,例如,可以使用基于osmium的脚本将文件拆分成包含1个关系和每个文件的成员的多个不同文件。也许然后我可以继续使用pandas.read_xml()。这对于在填充数据库时进行批处理将是很好的。将整个OSM XML文件加载到pd.DataFrame中会很好,但我想这确实是大量的数据。也许这也可以在每个关系的基础上使用pyosmium来完成?
任何帮助都是非常感谢的。
发布于 2021-11-14 14:03:59
好吧,我想出了如何获取我想要的东西(以一种可访问的方式存储每个"route=mtb“类型的关系的所有信息),这是一个多步骤的过程,我将在这里描述它。
首先,我下载了world文件(转到wiki.openstreetmap.org/wiki/Planet.osm,opened the xml of the pbf file,并将world文件下载为.pbf ( Linux上的所有内容,下面将该文件称为$osm_planet_file )。
我使用osmconvert将此文件转换为o5m (在Ubuntu20.04中通过执行apt install osmctools,在Linux:
osmconvert --verbose --drop-version $osm_planet_file -o=$osm_planet_dir/planet.o5m下一步是从这个文件中过滤出所有感兴趣的关系(在我的例子中,我想要所有MTB路由:route=mtb),并将它们存储在一个新文件中,如下所示:
osmfilter $osm_planet_dir/planet.o5m --keep="route=mtb" -o=$osm_planet_dir/world_mtb_routes.o5m这将创建一个小得多的文件,其中包含作为MTB布线的关系的所有信息。
从那时起,我转而使用Jupyter笔记本,并使用Python3进一步将文件划分为有用的、可管理的块。我首先使用conda安装了osmium (在我首先创建的env中,可以跳过):
conda install -c conda-forge osmium然后我创建了一个推荐的osm.SimpleHandle类,这个类遍历大的o5m文件,在执行此操作时它可以执行操作。这是处理这些文件的方法,因为它们的内存太大了。我选择遍历该文件并将所需的所有内容存储到单独的json文件中。这确实会生成超过12.000个json文件,但它可以在我的笔记本电脑上用8 GB的内存完成。这是一个类:
import osmium as osm
import json
import os
data_dump_dir = '../data'
class OSMHandler(osm.SimpleHandler):
def __init__(self):
osm.SimpleHandler.__init__(self)
self.osm_data = []
def tag_inventory(self, elem, elem_type):
for tag in elem.tags:
data = dict()
data['version'] = elem.version,
data['members'] = [int(member.ref) for member in elem.members if member.type == 'w'], # filter nodes from waylist => could be a mistake
data['visible'] = elem.visible,
data['timestamp'] = str(elem.timestamp),
data['uid'] = elem.uid,
data['user'] = elem.user,
data['changeset'] = elem.changeset,
data['num_tags'] = len(elem.tags),
data['key'] = tag.k,
data['value'] = tag.v,
data['deleted'] = elem.deleted
with open(os.path.join(data_dump_dir, str(elem.id)+'.json'), 'w') as f:
json.dump(data, f)
def relation(self, r):
self.tag_inventory(r, "relation")像这样运行这个类:
osmhandler = OSMHandler()
osmhandler.apply_file("../data/world_mtb_routes.o5m")现在我们有了json文件,文件名是关系号,包含所有的元数据,还有一个方法列表。但是我们需要一个路线列表,然后是每条路线的所有节点,这样我们就可以绘制完整的关系( MTB路线)。为此,我们再次解析o5m文件(使用在osm.SimpleHandler类上构建的类),这一次我们提取所有的成员(节点),并创建一个字典:
class OSMHandler(osm.SimpleHandler):
def __init__(self):
osm.SimpleHandler.__init__(self)
self.osm_data = dict()
def tag_inventory(self, elem, elem_type):
for tag in elem.tags:
self.osm_data[int(elem.id)] = dict()
# self.osm_data[int(elem.id)]['is_closed'] = str(elem.is_closed)
self.osm_data[int(elem.id)]['nodes'] = [str(n) for n in elem.nodes]
def way(self, w):
self.tag_inventory(w, "way")执行类:
osmhandler = OSMHandler()
osmhandler.apply_file("../data/world_mtb_routes.o5m")
ways = osmhandler.osm_data这给出了所有方法的关键字和节点ID(!意思是我们需要更多步骤!)作为值。
len(ways.keys())
>>> 337597在下一步(也是几乎最后一步)中,我们将所有方法的节点In添加到我们的关系json中,以便它们成为文件的一部分:
all_data = dict()
for relation_file in [
os.path.join(data_dump_dir,file) for file in os.listdir(data_dump_dir) if file.endswith('.json')
]:
with open(relation_file, 'r') as f:
data = json.load(f)
if 'members' in data: # Make sure these steps are never performed twice
try:
data['ways'] = dict()
for way in data['members'][0]:
data['ways'][way] = ways[way]['nodes']
del data['members']
with open(relation_file, 'w') as f:
json.dump(data, f)
except KeyError as err:
print(err, relation_file) # Not sure why some relations give errors?因此,现在我们有了具有所有方法的关系json,并且所有方法都具有所有节点ID,最后要做的是用它们的值(纬度和经度)替换节点ID。我也是分两步完成的,首先我构建了一个nodeID:lat/lon字典,同样使用了一个基于osmium.SimpleHandler的类:
import osmium
class CounterHandler(osmium.SimpleHandler):
def __init__(self):
osmium.SimpleHandler.__init__(self)
self.osm_data = dict()
def node(self, n):
self.osm_data[int(n.id)] = [n.location.lat, n.location.lon]执行类:
h = CounterHandler()
h.apply_file("../data/world_mtb_routes.o5m")
nodes = h.osm_data我们可以在我们的json文件上使用它来填充坐标(现在仍然只有节点ID ),我在一个新的目录中创建了这些最终的json文件(在我的例子中是data/ with _coords),因为如果出现错误,我的原始(输入) json文件不会受到影响,我可以重试:
import os
relation_files = [file for file in os.listdir('../data/') if file.endswith('.json')]
for relation in relation_files:
relation_file = os.path.join('../data/',relation)
relation_file_with_coords = os.path.join('../data/with_coords',relation)
with open(relation_file, 'r') as f:
data = json.load(f)
try:
for way in data['ways']:
node_coords_per_way = []
for node in data['ways'][way]:
node_coords_per_way.append(nodes[int(node)])
data['ways'][way] = node_coords_per_way
with open(relation_file_with_coords, 'w') as f:
json.dump(data, f)
except KeyError:
print(relation)现在我有了我需要的东西,我可以开始将信息添加到我的Django数据库中,但这超出了这个问题的范围。
顺便说一句,有一些关系给出了错误,我怀疑对于一些关系方式被标记为节点,但我不确定。如果我发现了,我会在这里更新。我也必须定期进行这个过程(当world文件更新时,或者每隔一段时间),所以我可能会在以后写一些更简洁的东西,但现在这是有效的,步骤是可以理解的,至少对我来说是经过很多思考的。
所有的复杂性都来自于数据不足以容纳内存的事实,否则我会在第一步中创建一个pandas.DataFrame并使用它。也许我也可以一次性将数据加载到数据库中,但我对数据库还不是很在行。
https://stackoverflow.com/questions/68622198
复制相似问题