最近导师要求在一个城市设计项目里应用街景分析,于是学习并梳理了一下爬取百度地图街景的流程,简单做个总结。
先附上百度官方文档。
简单分析一下全景API的请求构成:
"http://api.map.baidu.com/panorama/v2?ak=E4805d16520de693a3fe707cdc962045&width=512&height=256&location=116.313393,40.04778&fov=180&heading=0&pitch=0"
ak=密钥,width+height=图片尺寸,location经纬度坐标=本篇选用的全景图获取方式,fov、pitch和heading三个参数需要额外关注。fov=360时爬取整幅全景图,另外两个参数可省略;但如果希望爬取如下图所示的普通不变形的街景(fov=180),则需要考虑pitch和heading的值。经反复测试,pitch=20时视角比较正常,而heading取决于采样点所在道路的角度(见第4部分)。
路网数据的获取思路有很多,如:
(1)从OpenStreetMap上下载
OSM的国内数据毕竟不那么精确齐全,和百度地图的路网出入比较大(毕竟我们爬的是百度街景),建议作测试而非正式用。
(2)直接爬百度地图的路网
这显然是个最佳途径,但我搜了一圈并没能找到可以直接爬取的方法,猜测或许是保密数据。
(3)截取地图图像并矢量化提取路网
网上获取电子地图的软件不少,这里选用了百度地图截获器(可从软件原作者的公众号“让学术变得更简单的环哥的万事屋”处下载)。0.5Beta和1.0Beta我试了下都有问题,最终采用0.4Beta版本。
虽然需要安装火狐浏览器有点麻烦,但该版本有个独特的功能:可以直接下载街景导航轨迹(上图中的8),对街景爬取而言极为便利。下载后经PS调整得到下图,导入GIS。
处理过程分为矢量化、地理配准和简化三步,前两步可颠倒。
(1)将png图片的band 1拖入GIS,右键按照合适的阈值进行二值化分类(阈值影响道路识别精度,具体情况具体分析),再Reclassify重分类(非道路类的数值可设为NoData),得到纯路网栅格图。
(2)栅格转polygon,再转回栅格。这一步是为了提高精度,让栅格边缘更顺滑,不讲究的也可省略。
(3)使用ArcScan工具提取道路中心线,完成初步矢量化。具体操作步骤可参考这个教程。
下载的图片不含地理坐标信息,可能和实际位置差了十万八千里,比例也可能不同,需要手动配准调整。这里使用了OSM的路网作为参照。
(1)坐标系问题:涉及到投影坐标系和地理坐标系之间的设置和转化,直观来看投影坐标下的地图会稍“瘦”“窄”一些(毕竟地球是个球面),具体使用哪个依据自己的需求定,只要和参照路网统一即可,不影响后续采样点的坐标获取。
(2)比例问题:可调出Scale工具在编辑模式下调节,也可并在下一步。
(3)配准问题:使用空间校正工具与参照点对点Link一定数量后即可配准。当然,艺高人胆大可以选择直接Move。
经由栅格矢量化得到的路网比较复杂,有非常多不必要的转折和节点,可以使用Simplify Line(简化线)工具适当简化到近似直线的状态。如果道路断线太多也可以适当合并,具体方法暂时没研究。
*注:本步骤适用于普通视角街景图的爬取(fov=180),如果仅需要360°全景图(fov=360),则可跳过。
我们使用p1、p2两个变量分别存储同一条道路的两个相反方向对应的角度值,其中0≤p1≤180(代表自北向南和自西向东),180≤p2≤360(代表自南向北和自东向西)。可以根据实际需求(单向or双向街景)决定是否两者均要计算。
为得到p1、p2的值,需要先计算道路线段的绝对角度a,计算方法见一位师兄写的文档。经简单归纳,得:
p1 = a + 90 (0≤a≤90)
p1 = a - 90 (90≤a≤180)
p2 = p1 + 180
当然,肯定存在更简便直接的计算方法,本篇暂时不研究。
观测点的采样也有多种方式。我们使用Densify工具等距离增密道路的vertice,再通过Feature Vertices To Points转为采样点。经纬度坐标值可以使用Add XY Coordinates工具直接添加在属性表中。每个点对应的道路角度值和道路编号等信息用Spatial Join叠加获取。
完成后导出并整理采样点数据csv,举例如图(id=观测点编号):
爬取代码基于同门师兄的过往研究稍加修改得到,完整的代码网上有不少教程,这里就不赘述了。导入csv,使用数组读取并存储采样点信息,将相应数值填入API请求的url即可。
url = "http://api.map.baidu.com/panorama/v2?ak=" + baidumap_key + "&width=1024&height=512&location=" +
str(data[1]) + "," + str(data[2]) + "&coordtype=wgs84ll&fov=90&pitch=20&heading="
# 设置存储路径
url_a = os.path.join(outdir, str(data[0]) + "-" + str(data[3]) + ".bmp")
url_b = os.path.join(outdir, str(data[0]) + "-" + str(data[4]) + ".bmp")
# 设置heading的值,开始爬取
urllib.request.urlretrieve(url + str(data[3]), url_a)
urllib.request.urlretrieve(url + str(data[4]), url_b)
值得一提的是,百度全景图配额目前已降至100张/天,但实测可同时使用多个key,每个key可爬取约160-200张。然而,持续多天大量爬取有被监测到异常并封ip的可能,用ip修改工具(比如梯子?)或许可破。