如何在Hive中加载OpenStreetMap?

上一篇文章中,我研究了使用Spark进行反向地理编码。 现在想象一下,我们面临着直接对邮件地址进行地理编码的挑战。 也就是说,接收由某些地理坐标的文本记录的地址。

确定性的地址是俄语,而且最重要的是-它们的书写经常歪曲,即带有错误,歧义和其他令人愉悦的地方。 这些地址位于Hadoop集群上的Hive数据库中。


好吧,看来-我们采用了Google Maps Geocoding API(或者,如果您是导入替换的支持者,那么可以使用Yandex Maps API),然后我们开始工作。 但是在这里,以及反向地理编码,我们都在等待小小的埋伏。

或大,就像一个外观。 事实是,这一次我们需要处理大约500万个地址。 也许是50岁-目前尚不清楚。 如您所知,Google会在大约一万个地址后禁止您的IP,Yandex也会对您执行同样的操作,尽管可能稍后再进行(每天25,000个请求,例如)。 此外,两个API都是REST,这意味着它相对较慢。 即使您购买付费订阅,此订阅的速度也不会增加一分钱。

但是-我们用完了弹药轶事。

我忘记了最重要的事情-我们的Hadoop群集位于Intranet上,对于使用Yandex Maps的公司以及其他所有人,我们通常无法从该群集访问Google Maps。 也就是说,我们需要一个自治的解决方案。

我会马上说-您在这里找不到现成的解决方案。 我将只详细描述我们计划采用的方法,这是实现解决方案的很长一段路要走的步骤之一。

当然,我们还有一些储备。 我已经提到过内部的ArcGIS Server。 我们不允许操纵它们,但被允许使用其REST服务。

我们要做的第一件事就是将其固定在任务上。 他没有禁止我们,只是有时为了维护而关闭了。 很好-它具有批处理地理编码的方式,当您将地址包提交给输入时(配置服务器后,我们的包大小为1000件,默认情况下看起来小了一个数量级或两个)。 所有这些都不是一件容易的事,而且我们和ArcGIS支持人员长时间地致力于服务器的相扑,但这是另一回事了。

经过所有的花样和曲折,我们大约一天就可以处理500万个。 有必要继续前进,并尝试加速。

同时,很明显,任何具有REST的地理编码器都最不适合我们。 此外,我们查看了Nominatim,Pelias,Photon和giggraphy,总的来说,他们对此不满意。 质量和性能(或两者)都不理想。

例如,没人知道如何对软件包进行地理编码(这大大加快了ArcGIS的工作)。

或质量-转到gisgraphy.com演示服务器并尝试找到Moscow。 您将收到许多答案,包括:莫斯科(俄罗斯联邦的一个城市),堪萨斯城(美国的一个城市),希姆基,卡卢加州,维希诺-朱里比诺,以及许多其他我不希望在地址解析器的答案中看到的对象搜索莫斯科。

好吧,最后一个(但对我们而言并不重要)问题是,与所有地理编码器相比,API的思想和Google Maps一样深。 假设ArcGIS API已经很不方便了,其余的情况甚至更糟了。 如果您对用户界面的地址进行地址解析,那么通常人们会选择最佳选项。 而且他比程序做得更好。 就我们而言,就大规模地理编码而言,评估特定地址的结果质量是成功的重要组成部分之一。

结果,例如“扩展自己的提名人数”之类的选项也消失了。

怎么办


一个相当明显的解决方案是:由于地址不是从任何地方获取的,并且不会消失,房屋不是每天建造的,街道也不是建造的,因此您只需要向我们的流程中添加正式存在地址的数据库即可。 最好立即使用坐标,如果没有发生,则对其进行地理编码一次。 在这种情况下,对于我们而言,以不常出现的新房屋或街道出现频率来更新我们的基地就足够了。

现有地址基础的第一个也是主要候选者是FIAS 。 您说,等一下,但是FIAS仅有几百万个地址-您有多达5000万个地址? 是的,实际上只有几百万所房屋 。 我们的50个用户地址是5000万,也就是说,这是人们的地址,而他们突然在该地址中有一个公寓。 500万套1-100套住房中,每套公寓中都住着几个人……好吧,您了解一切。 第二种选择是办公室的地址,其中一个办公室中心拥有多达数百个场所,有时将其出租。

同时,我们显然不需要提供公寓(或办公室)编号的地址-首先,这是带有所有后果的个人数据,其次,我们仍然对公寓如何位于特定房屋中以及它们的坐标是什么不感兴趣。 只需要一所房子。 对于办公室而言,这并非完全正确,但是建筑物中按楼层划分的办公室位置仍然不是由坐标确定的。

最终,拥有一个(有条件的)500万个现有房屋作为基础,我们只需将公寓或办公室扔出地址并将其与基础相匹配,便可以解决50或1亿个地址的地理编码问题。

在哪里获得房屋的坐标? 只有一个明显的开放源代码-OpenStreetMap,那里有带有几何形状的房屋以及其他各种属性,例如层数甚至屋顶的颜色。

经过所有讨论,我们制定了拿破仑计划。 这是一个:

  • 将地图数据从OSM加载到Hadoop
  • 上传带有地址的FIAS数据
  • 建立带有门牌号的唯一完整地址列表
  • 我们通过在OSM中搜索地址对地址进行地理编码,但找不到的是通过ArcGIS


我们获得经纬度房屋的基础。 好好享受 收获收益。 喝奖金(笑话)。

在本文中,我将告诉您我们如何实施该计划的第一点。

什么是OpenStreetMap


如果从数据角度看OSM,那么可以想象这些卡以以下三个表的形式出现:

  • 点数
  • 线(方式)
  • 关系


该数据的实际方案将在下面给出。

只有点才具有坐标(纬度和经度,以度为单位)。 线是点的有序序列。 关系是一组点和线,每个点和线都有其作用

其他一切就是所谓的标签。 就是说,例如,一个ATM机,一个商店或地铁的入口-它可以是配备了标记amenity = atm的商店,或者shop =卖东西的东西。 有一个官方推荐标签的目录(对于每种适用的语言和国家,它们可以部分属于自己),以及发明非标准标签的做法。

除了标签之外,地图的每个元素还具有唯一的数字ID,以及与历史记录相关的一些属性-编辑时间,编辑编号等。

带有卡的数据库有几种格式:
-pbf是Google Protobuf,一种可移植的数据序列化格式。
-xml显然是XML。 数量更多。

您需要了解数据库每天都会更新。 因此,卸载是完整且增量的。

我们选择PBF更为紧凑。

要在Hadoop中阅读,有一个专门为OSM设计的Java API,称为“渗透项目”。 原则上,使用它很简单-您上传文件并浏览地图元素。 在一个位置添加点,在另一位置添加线,在第三位置添加关系。 原则上,渗透和例如Spark已经足以下载所有数据。

幸运的是,在实施我的自行车的过程中,我莫名其妙地搜索了Internet,将OSM转换为Hadoop接受的格式-Parquet(parquet)和Avro。 从某种意义上说,两者都是PBF的类似物,因此有机会找到一个转换器。 他被发现了,但没有一个。

认识OSM Parquetizer


看看我发现了什么!

对于懒惰的人-在项目的自述文件的第一行中说:Telenav每周将行星下载下载到该地址

对于非常懒惰的人:准备发货约700 GB;)好吧,如果您确实需要一个星球。 通常,您可以和欧洲打交道。

如果您不想加载,则过程如下所示:以PBF格式下载地图,例如从geofactory下载 。 如果需要俄罗斯,则为2.5 GB,如果需要欧洲,则为19 GB。 也不少,但您可以找到更多切碎的样品。 接下来,将文件放在磁盘上,然后运行程序:

java -jar ./osm-parquetizer.jar russia-latest.osm.pbf 

几分钟甚至几秒钟后,根据机器的性能,您将得到三个拼花形式的文件。 这是作者的样子(他来自罗马尼亚):

 -rw-r--r-- 1 adrianbona adrianbona 145M Apr 3 19:57 romania-latest.osm.pbf -rw-r--r-- 1 adrianbona adrianbona 372M Apr 3 19:58 romania-latest.osm.pbf.node.parquet -rw-r--r-- 1 adrianbona adrianbona 1.1M Apr 3 19:58 romania-latest.osm.pbf.relation.parquet -rw-r--r-- 1 adrianbona adrianbona 123M Apr 3 19:58 romania-latest.osm.pbf.way.parquet 

收到的.parquet文件的方案:

node
|-- id: long
|-- version: integer
|-- timestamp: long
|-- changeset: long
|-- uid: integer
|-- user_sid: string
|-- tags: array
| |-- element: struct
| | |-- key: string
| | |-- value: string
|-- latitude: double
|-- longitude: double

way
|-- id: long
|-- version: integer
|-- timestamp: long
|-- changeset: long
|-- uid: integer
|-- user_sid: string
|-- tags: array
| |-- element: struct
| | |-- key: string
| | |-- value: string
|-- nodes: array
| |-- element: struct
| | |-- index: integer
| | |-- nodeId: long

relation
|-- id: long
|-- version: integer
|-- timestamp: long
|-- changeset: long
|-- uid: integer
|-- user_sid: string
|-- tags: array
| |-- element: struct
| | |-- key: string
| | |-- value: string
|-- members: array
| |-- element: struct
| | |-- id: long
| | |-- role: string
| | |-- type: string


如您所见,这里的一切都很简单。 然后,我们执行以下操作:

  • 我们使用hdfs dfs -put命令将文件放在Hadoop集群上
  • 让我们在Hue中说一下,根据上述数据创建一个架构/基础以及三个表
  • 从osm.nodes执行select *,并享受结果。

细微的差别:在我们的Hive版本中(可能还有您的版本),它无法基于Parquet的方案创建表。 您需要将上面的内容转换为CREATE TABLE(通常来说并不困难,我将其留给读者作为家庭练习),或者做一些棘手的工作:Spark可以从底层读取图表和数据,并基于它们创建临时表。 。 这样我们就可以像这样在Spark Shell中读取数据:

 val nodeDF = sqlContext.read.parquet("file:/tmp/osm/romania-latest.osm.pbf.node.parquet") nodeDF.createOrReplaceTempView("nodes") 

然后,您已经可以使用LIKE节点在Hive中创建表。

懒惰者的另一句话:作者了一个如此出色的例子 ,从这个例子中 ,所有事情通常都会变得很清楚(好吧,如果您拥有Spark)。 这当然不是Spark Shell,而是Databricks Notebook,但是我花了大约15分钟的时间才能敲击一个键盘将一个键盘翻译成另一个键盘。 在30-40分钟内,可以使用一些与spark稍有不同的类似物将其全部转换为Hive的查询。

真实请求示例


我们可以从该数据库的当前形式中得到什么? 总的来说,很多。 例如,如果您拥有Hive或Spark,Spatial Framework,Geometry API或备选方案之一,例如GeoSpark或GeoMesa,则可以在此基础上解决许多不同的问题。

让我们来看一个例子。 使用积分的最简单方法。 例如,查询以获取具有其坐标的ATM的列表,如下所示:

 select * from nodes where tags['amenity']='atm' 

如何构建这样的查询,您可以通过阅读Wiki上的页面进行猜测。 在这里,您可以找到其他标签,例如可以以标签['operator']的形式将其中的一些标签(而不是*)包含在您的请求中,例如,显示银行的名称。

从同一页面开始,可以以标签amenity = bank和atm = yes的形式进行ATM标记。 OS,OSM中到处都有这种歧义。

如果您是初学者,并且刚刚熟悉OSM,我强烈建议您精通Overpass-turbo (通过Wiki上的好例子)。 使用此工具,您可以使用几何条件和标签条件对地图数据执行各种搜索。

地址在哪里?


好问题。 OSM中的地址是随addr:*标记提供的映射元素,即 以addr开头。 描述,您将在这里找到。 原则上,了解了我上面已经说过的所有内容,您就可以编写一些工作请求:

 select * from nodes where tags['addr:housenumber']!=null 

这里有什么问题在等我们? 首先,地址既放置在点(例如建筑物入口)上,又放置在多边形上,即 在方式上。 因此,我们至少必须重复该请求。 其次,在上面提到的页面上,Wiki用纯文本编写,不建议放置指示城市,地区,地区和国家的标签,但是必须以几何方式进行计算。 怎么做? 通常,这实际上是对反向地理编码的任务,需要进行一些修改,并且在先前的文章中对此进行了描述。

也就是说,通常,您需要找到管理边界,并针对其中的所有地址将地址添加到该区域以及上面的所有内容。 这里描述如何划分行政实体的边界。

通常,此任务不是很简单,但是可以解决,并且不能通过地理编码解决,而可以通过在轻松的环境中将OSM更新下载到我们的数据库中来解决。

接下来要做的是什么


原则上,您已经可以使用我们拥有的节点,方式和关系表,但是最好稍微更改一下方案,使其更适合Hive和Spark。 事实是OSM方案已完全标准化,方式和关系完全不包含坐标。 要构建多边形,您需要与节点连接。 我建议立即进行此操作,将多边形保存为结构数组(Hive可以与复合类型数组,映射和结构一起使用),或者立即将其保存为例如Geometry类的序列化表示形式。 如何执行此操作以作者拼写器为例。

如果需要,可以在关系级别上重复类似的操作,但是这样做并不值得。 首先,您将不总是需要关系的所有元素,其次,OSM中的关系本身要小得多。

转换成Avro


是另一个转换器,这次是Avro格式。 此处描述了从何处获取完成的文件。 我没有测量大小,但是我认为每个行星大约15到20个文件应该可以与PBF相媲美。 也就是说,这些是千兆字节,而且很多。

一些结论


您问地理编码在哪里? 是的,下载地图和提取地址只是整个任务的一部分。 我希望这涉及到这一点。

Source: https://habr.com/ru/post/zh-CN438374/


All Articles