随着本世纪初Google Map的诞生,地图以一个全新形式进入人们的视野,大家发现原来地图不只是躺在课本里的彩页,还可以与我们互动。今天地图在生活中已经无处不在。导航地图,搜索附近美食,看一下我的外卖距离我还有多远。。。这些我们已经习以为常的功能充满了地图的奇思妙用,也蕴藏了大量时空数据技术的运用。这篇开始我们将揭秘一些地图原理,让大家了解更多地图背后的故事。
本文要介绍是地图地址搜索,专业名词为POI(Point Of Interest)检索。这个功能相信大多数人都使用过,比如,朋友约我到一个地点吃饭,我只记得这个饭馆的大致名称,于是会在地图搜索框里输入一些关键词,地图软件就很神奇的给为我推荐出了很多相近的地点,并且在地图上展示相应的位置。我们要做的就是结合地图上的位置和这些地点名字找出那个饭馆。

这个我们几乎每天都会用到的功能如果要实现,大致有三个步骤,1、地址数据清洗:去除包括经纬度异常点,重复点,空值等等;2、地址信息索引与搜索;3、经纬度地图打点。简单来讲就是,处理数据、存储与查询数据、展示数据。这三个步骤里最枯燥繁琐的就是数据的清洗,最有意思也是最有挑战性的的是地址信息索引与搜索,最简单的就是结果的展示。下面我们通过简单的实践,看看如何搭建一个基础的地址搜索服务来实现存储与搜索。
地址信息索引与搜索大致流程可以用下面简单的示意图表示。将带有位置信息的地名地址数据导入数据存储,并建立索引,一般需要建立全文倒排索引+空间索引。当客户端输入关键词时,会利用索引进行搜索,最终返回匹配的结果集。

这样看来和普通的业务系统也没有太大区别,关键是如何建立这个索引库以及怎样筛选结果。通常,在一个大型的地图引擎内部因为要服务的场景非常复杂,而且对准确率要求非常高,因此通常不会只建立一个索引库,不会只有一种匹配规则。本文,我们只关注主要的流程,建立一个最简单的框架,方便大家理解。
我们不难发现,地址信息的搜索最关键的步骤是地址文本的匹配。简单讲,就是如何用输入的词语检索出我们真正想要的地址文本。这和普通的关系数据库有些不同,因为我们的地址数据通常不会有很明确的结构,同时,用户输入信息也不一定规范。这种场景更类似于全文检索,通过对比查询词和数据库内的地址,返回最相似的文本结果。因此建立一个含有空间坐标信息的地址全文索引库才是实现这个功能的重点。
此次实验我们使用开源OSM数据,它是由全球用户共同协作的wiki地图数据,里面的数据种类非常全面,虽然准确性不是特别高,但是作为实验数据集完全够用。下面这幅图是在QGIS截取的北京经开区某个住宅区的地图,黄色圆点是OSM的POI数据,红字是POI的地址名称。

为了有更好的搜索效果,需要对数据进行一定的处理,这里我们需要把POI数据和行政区划数据进行叠加,使得POI的地址文本增加行政区域的信息。比如,地图上的“肯德基”,处理完之后就变成了“北京市经开区永华街道肯德基”,这样在我们搜索某个区域的肯德基时,就更有可能得到准确的结果。
全文索引有很多实现方案,比较成熟的产品有solr、elasticsearch、sphinx等等,还可以基于搜索框架Lucene直接扩展。这里我们方便起见直接用ES来搭建,它内部也支持空间信息的存储,并且分布式架构更方便后面扩展。
ES的安装非常简单,下载然后直接启动就能用,可以参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html。但是,这里要说明的是,光有原生的ES还不够,我们需要一个中文分词器,因为原生的分词器只适用于英文,对于中文信息完全不能用。关于中文分词器的安装可以参考:http://t.zoukankan.com/chenmc-p-9525163.html,我们使用比较流行的IK分词器。
IK分词器内部有两种分词方式,ik_max_word和ik_smart,以“中华人民共和国国歌”为例,
很难说哪个更好,这要取决于你的场景和你的词典,如果你没有足够丰富的词典,并且对准确性要求没有特别高,只要能返回沾边的信息就行,那就可以用ik_max_word。如果你有很强大的词典,而且不想要太傻的结果,那你可以尝试ik_smart,这还不够,因为你需要了解更多原理知识来调整匹配策略,这些内容会在后面文章中探讨。为了能匹配的更准确,这里建立了一个政区词典,把所有政区的名词全部导了进去,下面图一是词典内的一些行政区名称,图二是在ik配置文件内放置好的词典文件。(IK分词器扩展词典:https://github.com/medcl/elasticsearch-analysis-ik)


创建索引类似于mysql中创建一个表,我们要定义表结构,以及分词的方式。这里分词有两个方面,一个是构建索引词库的分词,一个是对搜索词语的分词,我们都设置为ik_smart。另外,将经纬度进行整合,构造一个geo_point字段,方便后面空间搜索使用。

地址信息的导入建议写一个脚本,方便后面重复使用。ES提供了相应的rest api,我们只需要读取数据映射到对应的property字段即可。可参考java版本:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-index.html
搜索地址可以直接调用ES的rest接口,利用match函数进行全文匹配。如下例子,搜索“通州区万达广场”,ES会返回最可能的几个结果。我们还可以把解释器打开(在match命令后面加上?explain=true),看一下返回记录的分数和计算方式。关于解释器和分数计算,会在后面文章介绍。

可以看到返回的结果基本符合我们的常识,只是排名第一的“建国路万达广场”有点突兀,查了一下它竟然位于朝阳区,而第二第三名里面有很明显的“通州区”字样,为啥反而排名很低呢?关于怎样让结果更符合我们的预期,后面文章再聊!
结果的展示非常简单,只需要将获取的location坐标通过前端组件展示到地图上即可,openlayers、mapbox、leaflet都是不错的开源框架,这里不再赘述。
在时空数据接入模块,选择本地shpfile数据导入到服务器上,这里选择了通州区的地址数据。

在时空数据查询模块,通过JUST-SQL检验数量是否一致




我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"
寻找有用的ruby的好网站是什么? 最佳答案 AgileWebDevelopment列出插件(虽然不是rubygems,我不确定为什么),并允许人们对它们进行评级。RubyToolbox按类别列出gem并比较它们的受欢迎程度。Rubygems有一个搜索框。StackOverflow对最有用的rails插件和rubygems有疑问。 关于ruby-如何搜索有用的ruby,我们在StackOverflow上找到一个类似的问题: https://stacko
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
我读了"BingSearchAPI-QuickStart"但我不知道如何在Ruby中发出这个http请求(Weary)如何在Ruby中翻译“Stream_context_create()”?这是什么意思?"BingSearchAPI-QuickStart"我想使用RubySDK,但我发现那些已被弃用前(Rbing)https://github.com/mikedemers/rbing您知道Bing搜索API的最新包装器(仅限Web的结果)吗? 最佳答案 好吧,经过一个小时的挫折,我想出了一个办法来做到这一点。这段代码很糟糕,因为它是
给定一个元素和一个数组,Ruby#index方法返回元素在数组中的位置。我使用二进制搜索实现了我自己的索引方法,期望我的方法会优于内置方法。令我惊讶的是,内置的在实验中的运行速度大约是我的三倍。有Rubyist知道原因吗? 最佳答案 内置#indexisnotabinarysearch,这只是一个简单的迭代搜索。但是,它是用C而不是Ruby实现的,因此自然可以快几个数量级。 关于Ruby#index方法VS二进制搜索,我们在StackOverflow上找到一个类似的问题:
我正在尝试复制此GETcurl请求:curl-D--XGET-H"Authorization:BasicdGVzdEB0YXByZXNlYXJjaC5jb206NGMzMTg2Mjg4YWUyM2ZkOTY2MWNiNWRmY2NlMTkzMGU="-H"Content-Type:application/json"http://staging.example.com/api/v1/campaigns在Ruby中,通过电子邮件+apikey生成身份验证:auth="Basic"+Base64::encode64("test@example.com:4c3186288ae23fd9661c
如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail