分享

【教你一招】基于高德API获取交通信息和数据之抓图篇

 zhou网摘 2017-09-05

一、为什么要截图?

最近有个需求,要截取一个城市的地图作为底图,衬在TransCAD中用于道路的校准和周围地物的显示。这经常是做交通规划之前,绘制TransCAD道路网络比较传统的做法。

最早网络地图还没有或者还不成熟的时候,通常的做法是直接扫描旅游地图,将其转化为TIF格式的图片,因为著名的TransCAD4.5版本只能识别无压缩的TIF格式图片。所以开始接触PS,就是源于对TIF格式转化的需求。


之后在2008年左右,手持GPS开始盛行。做图就比较简单了,拿着手持机,大街小巷跑一遍,一边跑、一边记录地物的名称。回来以后把轨迹点和标志点数据导出到EXCEL中,稍做处理,将经纬度的字段用英文命名好,用TransCAD打开,可以直接识别为点层。这样做的好处是道路比较准确,曾经和从规划部门拿过来的AutoCAD图做过比较,发现后者在某些地方偏差不是一般的大。坏处是太辛苦,而且信息记的不会很全。

大概在2013年左右,出现了一款叫做“稻歌Google Map截获器”的软件,能够非常完美的截取谷歌地图上任意范围和分辨率的地图,并且最后拼接起来成为一张完美的jpg格式地图图片。这两天在办公室再用这个软件,在确定地图选择范围时,总是弹出页面脚本错误,不知道是不是谷歌地图的API版本更新造成的错误,总之是不好用了。

目前,还有很多电子地图下载器,比如BIGEMAP、水经注、GOGOMAP等。但一般都需要授权或者购买,不然会有下载电子地图的大小限制或者增加水印。而现在网络地图,比如高德和百度,都做的已经挺好了,并且针对不同的平台,开放了很多种API,于是萌发了能否应用这些API去做一个截图且拼图功能的想法。

二、为什么用高德截图?

地图及上面地物信息最重要的就是是否准确。特意用百度搜索了一下,看看高德和百度地图差别在什么地方,网络上的信息比较繁杂,甚至有很多矛盾的评价。看到一个评论,说高德地图的更新会相对快一些。用自己居住城市附近的地图比较了一下:学校附近有一条宗泽路的延伸线,百度上赫然是可通行状态,高德上是修路状态。而实际上这条路已经修了快年许了。另外,在下图右半部分,江苏大学校内的各个单位称谓上,百度地图的名称明显比较陈旧,大概是几年前的版本,高德地图上的名称基本符合我们现在称呼的习惯。基本上一些细节的地方高德都相对比较正确。当然高德也不是都很准确,在江苏大学学生宿舍区出现了一个村庄叫“下仇家”,惊诧啊!最终选择高德地图作为开发对象。

高德API比较丰富,如下图所示。我们现在拟开发的功能不涉及手机,所以主要考虑Web端和Web服务两种。其中的Web是网页的意思,比如我们经常说的Web Site,网站。web系统的特点就是,不需要在电脑上安装任何的程序,只要通过浏览器,能够上网,就可以使用相应的功能。Web前端是泛指终端跟用户交互的一个媒介。

高德Web端提供的是基于Java API,百度地图在Web端也是提供了这种语言的API。Java(JS)是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常Java脚本是通过嵌入在HTML中来实现自身功能。

Web服务,个人理解,则是向服务器发送一个带参数的http网址,其实就是浏览器地址栏的地址,服务器会返回请求的数据。这两种API都尝试了一下。相对来说,Web服务集成性比较高,直接面向客户需求,清晰简洁,功能明确;Web端需要JS的语言知识,但更加灵活,能做出定制化的功能。

三、如何用Web服务API来截图?

首先,为了最高效的实现截图功能,下意识避开了不熟悉的JS API,转向只需要配置http地址就能获取结果的Web服务API。根据官网的介绍,Web服务API向开发者提供HTTP接口,开发者可通过这些接口使用各类型的地理数据服务,返回结果支持JSON和XML格式。但是在申请服务之前,必须要先注册高德的开发中心(http://lbs.amap.com)获取一个Key,这个Key是个人使用高德API的凭证。

在Web服务API中,有一个API称之为“静态地图”。这个API功能比较独特,并不返回JSON或XML格式的数据,而是返回一张地图图片。该API的服务地址为:http://restapi.amap.com/v3/staticmap?parameters,其中问号之前的字符串是“静态图片”API服务提供的地址,问号后面的parameters为用户配置的参数。

比如,已知经纬度119.51643,32.198474,想要获取一张地图缩放级别为17、大小为1024*1024的高清地图图片,parameters可以这样来配置。http://restapi.amap.com/v3/staticmap?location=119.51643,32.198474&zoom=17&size=1024*1024&scale=2&key=您自己申请的key。

在这些参数中,location是确定获取地图的中心点位置经纬度坐标;zoom是地图缩放级别,越大越清晰;size是确定显示地图图片的大小;scale是确定地图是否高清(1为普通,2为高清);最后key就是自己申请的key,填上即可。获得的图片如下所示。

挺简单吧,直接配置下HTTP地址,就能截取一张地图。但是高德限定了截取地图的范围和大小,根据Web服务的API说明文档,地图大小size最大为1024*1024,当缩放级别为17时,单位像素距离约为1米,那么1024*1024所覆盖的实际地图面积约为1平方公里。这对于动辄一个城市的交通规划来说,这么小的面积显然是不够的。此时自然能够想到,要是有很多个1平方公里拼接起来,不就能形成一张大图了吗?

根据获取一张图的Web服务API参数配置,我们知道确定地图最重要的两个参数就是位置和大小,即location和size。以拼接两张图为例,在知道了第一张图的位置和大小后,要能计算出第二张图的中心位置。稍微抽象点说,就是已知一个经纬度坐标和往某个方向的距离,求另一个经纬度坐标。如果是横过来拼图的话,距离显然就是图片的宽度。按照这样的思路,非常幸运的在Python中找到了GeoPy库,这是一个用于地理处理的工具包(http://geopy./en/latest/)。

在导入这个库的distance系列函数,可以很便利的计算任意两个坐标点之间距离、以及已知一个坐标点、角度和距离,去求另外一个坐标点。在得到一系列的地图图片后,可以再通过Python的PIL库中的paste函数,去实现图片的拼接。拼接之后,可以得到如下的图片:

仔细观察上面这张图片,可以发现这张图由9张图拼接起来。还存在这样几个问题:1. 每张小图的左下角都有一个高德的标识;2. 没有图例;3. 在每张小图的拼接处,会发现非常细微的错位。对于最后一个问题,当截图范围比较大的,比如超过大约60平方公里,错位就会比较明显。标识反映了高德的知识产权,还可以接受;图例主要用于标定地图的比例,没有就不太方便了;拼接的错位会导致地物位置的扭曲,是无法接受的。错位的原因是什么呢?思索了下估计有以下两个原因。

原因一,像素距离。以上图中的小图为例,宽度为1024个像素,也就是大约1024米。请注意,这里是大约,因为不同经纬度坐标上单位像素的距离是不一样的。根据JS API中的map.getResolution()函数,可以得到坐标119.51643,32.198474(江苏大学汽车与交通工程学院)处的单位像素距离是

1.0106496215946836米,而学校门口(119.518875,32.193371)的像素距离是1.010706298229816,如果是不同的城市,偏差会更大。要解决像素距离的偏差,必须要用到Web端的API——Java API函数。

原因二,坐标有偏。大家都知道,国内的网络电子,为了安全,都是有偏的。常用的坐标体系有WGS84、GCJ02和BD09。分别解释如下:

WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系,谷歌地图采用的是WGS84地理坐标系。

GCJ02坐标系:即火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。 谷歌中国地图、搜搜中国地图和高德地图采用的都是GCJ02地理坐标系。

BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系。搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。

利用加密之后的GCJ02坐标系坐标去计算距离,再使用不精确的像素距离,这大概就是图片拼接时有所错位的原因。所以结论是Web服务API虽然简便易用,但是不能满足完美拼图的需求,那么,就必须用到Web端的Java API了。

四、如何用Web端API来截图?

高德非常人性化的提供了应用各种场景的Java API的示例中心(http://lbs.amap.com/api/java-api/example/map/map-show)。如下图所示,示例中心每一个场景,都由两部分组成:左边是JS代码,右边是代码执行的地图效果展示。还可以直接在左边修改代码,执行后,在右边看代码的执行效果。把这个示例中心的各个场景代码和JS API的参考手册(http://lbs.amap.com/api/java-api/reference/core)配合起来看,基本能够实现对高德地图的各种功能性二次开发。

利用JS API来截图,就不需要像Web服务API中一样计算每张切片地图的中心坐标了。在JS API的map类中,有一个函数panBy(x:Number,y:Number),其官方解释为以像素为单位,沿x方向和y方向移动地图,x向右为正,y向下为正。这样我们只要以截图图片的宽度或高度像素为距离去移动地图,就可以抓取我们需要范围的地图。具体思路流程如下图:

1. 用鼠标确定取图范围。这里可以考虑参照示例中心的“事件绑定”场景的代码(http://lbs.amap.com/api/java-api/example/event/custom-event),给鼠标绑定点击事件,在事件中可以获取矩形范围的四个角坐标,也就是我们的取图范围。在确定取图范围之后,还要确定截图时地图的缩放级别,这可以使用HTML的select控件让用户来选择。

2. 在Python中确定图的大小。已知缩放级别和四个角坐标,就可以计算出整张图的宽度和高度像素值,用PIL库中的new函数生成图的底板。另外,很重要的一点是,因为这里是使用Python和JS API配合起来实现截图,就要实现Python和Web端之间的通讯,或者说,是Python和JS之间的通讯。

最开始我的思路是,在Python中模拟JS的运行环境,从而实现在Python中来运行高德的JS API,尝试了很多方法:1. windows的Control,运行简单的例子出错;2. 用Python的库PyV8,这个库在google上,无法下载,国内下的无法和Python3版本匹配上;3.Js2Py库,安装成功,但运行官网的例子,总出错;4. 用PyQt5的QtWebEngineWidgets组件来模拟浏览器运行Java,成功!

虽然第四种方法可用,并且也成功将计算距离、像素坐标和高德坐标转换等几个JS API函数在PYTHON中封装,但执行的效率很低。单个函数运行没有问题,但是如果循环使用这些封装的函数,就一定会报错。幸运的是找到了Python的selenium库,这本是python用于测试网站网页的工具,但是可以非常好的实现网页端和Python的交互。实际上,是Python通过selenium库来和浏览器中的HTML交互。但要实现这种交互,selenium还需要对应浏览器的驱动,其实就是一个exe可执行文件,不同浏览器对应的这个驱动文件都不一样。具体可到这里(https://seleniumhq./selenium/docs/api/py/index.html)查看每一种浏览器对应webdriver的下载地址。需要注意的是,下载了驱动文件后,其地址要放在环境变量path中。

3. 地图移动到截图矩形的左上角坐标。在监测到“开始截图”的按钮被点击事件后,利用JS API的map类的setZoomAndCenter()函数,将地图按照抓取时的缩放比例,移动到左上角坐标处,即开始抓取的初始位置。地图在移动后,会触发moveend事件,这时可以在该事件中,计算要截取的范围大小、需要截取行和列图块的个数、每个图块的高度和宽度(像素)。当然,也可以做一些截取进度条或进度百分比的初始化工作。将上述参数通过find_element_by_id().get_attribute()函数都传入Python中。

4. Python截图。用PIL库的new函数对截取的整张图片进行初始化,即建立整张截图的底板,并计算抓取图块(截屏)的像素坐标。更简单的说,就是截取电脑屏幕上特定的区域,因为只是要截取地图,所以截取的区域要避开浏览器的地址栏、菜单栏、工具栏等,并且让开电脑屏幕的任务栏。这里有个比较简便的方法,利用JS去获取电脑屏幕的可工作区域和整个屏幕的尺寸,再配合浏览器最大化的设置,就能计算出需要截取地图的范围。截图用PIL库的grab()函数。

5. 地图进行像素平移。利用从JS获取的图块行和列的个数,做循环。每一个循环,先截图,再利用map类的panBy()函数平移地图。每次平移后,会触发JS中的moveend事件,可以在该事件中设置截取进度显示。需要注意的是,每次截取的图块,粘贴到整个底板上的坐标要即时计算,根据循环的行列序号,以及平移地图的次序,动态的计算粘贴坐标。一直到循环完毕,利用PIL的save函数保存图片。可以自己选择图片格式。

五、哪一种图片格式比较适合作为TransCAD的底图?

测试不同格式图片作为TransCAD的底图时鼠标的拖动效率。测试范围为镇江市金山公园(119.414644,32.217909)至名都大饭店(119.514036,32.158782)之间,缩放级别17,实际面积61.63Km2,TransCAD版本7.0,导入TransCAD的坐标系统为Class: Aisa,Zone: China: China BeltⅠ,在TransCAD中以用经纬度来显示坐标,图片尺寸为10716*7840。截取结果为:

如上图,共截取了48个图块拼接在一起。生成的各种图片格式测评如下。

其中,optimize为True,则采用了图片压缩技术。另外,JPEG文件有两种保存方式:分别是Baseline JPEG(标准型)和Progressive JPEG(渐进式)。两种格式有相同尺寸以及图像数据,他们的扩展名也相同的,唯一的区别是二者显示的方式不同。比较了各种格式和参数配置后,可以考虑用JPEG文件格式或者加了optimize为True的参数配置文件。

六、截图系统功能描述

想了想,还是把已经实现的截图系统功能简单展示下。其实从以下的功能描述,再配合上面的思路,工科的同学们基本都能实现高德地图的截图了。

和用户的交互、截图界面都是在HTML中实现。这个HTML文件可单独打开,能展现高德地图,只是无法实现截图功能。从Python的程序中用selenium的get()函数打开该HTML文件,就可以执行截图操作。

打开HTML文件后,在地图的左下角,有这样几个控件。“点选范围”按钮是用来确定截图矩形范围的左上角坐标和右下角坐标。没有再多花时间去测试先右后左什么结果,可以用JS来检查。“取图时缩放级别”从3到18,18时大概每个电脑屏幕像素为0.5米,该值越大,截取后的图越细越大,从交通规划的角度,大概17或18皆可。“y偏移”是指每次屏幕截图时,在浏览器中HTML可见区域的下方舍去的高度,在这个高度内,会有进度条或进度百分比,如果不舍去,就无法实现无缝拼接。“开始取图”后,建议立刻把鼠标移动到任务栏上去,不然会被截取下来,最后出现在地图上哈!

在打开HTML后,地图的右上角会有截取范围的一些信息显示。缩放级别指当前可见的级别,用户可以通过鼠标滚轮来实现地图缩放,测试地图显示信息的详尽程度。因为选取范围限定为矩形,所以选点个数最多为2个。确定选取范围,就是给出了左上角和右下角坐标,同时最后给出选定范围的面积。

七、结语

做交通离不开地图,这里只是给出了一个自己动手从高德下载电子地图的思路。从最初有想法到最终实现,大概花费了两周时间。困难在于以前没有接触过JS和PYTHON语言,对他们不熟悉,很多功能要亲自测试过才敢使用;另外在实现思路上,也走了一些弯路。有兴趣可以按照上面的思路自己尝试下,还是蛮有意思的,欢迎交流。并且这个思路可以进一步扩展,不仅用来抓取图片,还可以用来抓取一些大量的地理兴趣点(POI),来突破高德对提取兴趣点个数的限制。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多