原创:金小贝 QQ交流:675229288
实际业务中,特别是基于LBS的服务中,后台存储了很多经纬度数据,比如车辆行驶轨迹、快餐配送地址、上门按摩地址等,所以需要批量地理位置与经纬度的相互转换,一方面满足业务分布可视化的实现,另一方面满足实际地址的统计分析,两方面结合即可对整体业务热力区域进行把握,有针对性的指导线下运营细化到街道、小区。
对于单个或者少数地址位置的查询,线上一些免费工具可以使用,如果批量转换则需要特殊工具或者自行编写程序转换,本文重在在于介绍如何使用R进行批量的地理位置与经纬度相互转换(基于百度地图api的地址位置的经纬度解析与反解析,地理位置可精确到街道门牌号),第一部分会简单说明线上单个或少数地理位置的转换。
一、单个或少数地理位置转换
1、百度地图开放平台:
想到地址位置查询第一当然想到地图,在国内当然第一想到的是百度地图,具体路径如下描述,可实现单一位置与经纬度的相互转换,满足了少量位置的需求。路径:百度→地图→地图开放平台→地图拾取工具。通过鼠标移动点选,或者直接输入查询均可返回指定位置的经纬度。勾选“坐标反查”,输入经纬度数据(英文逗号隔开),可反查具体地址信息。具体操作说明页面上也有详细说明。
2、其他网站:
有很多在线查询网站,其中www.gpsspg.com/maps.htm可方便查询各种地图下的经纬度。
3、R软件实现城市经纬度查询:
加载REmap包,调用get_city_coord( )返回经纬度,get_geo_position( )返回批量城市的经纬度。
library(REmap)
get_city_coord(‘贵阳’)
[1] 106.63682 26.65275
get_geo_position(c(‘贵阳’,’益阳’,’成都’))
lon lat city
1 112.361677 28.559818 益阳
13 106.636816 26.652747 贵阳
5 104.071216 30.576279 成都
二、批量地理位置转换
基于百度地图的地理位置转换需要R加载RCurl包和rjson包,使用url转换函数及结果解析函数,大概了解Geocoding API说明文档(http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding)进行参数调整。步骤相对明确,代码具体如下。此方法的好处在于地理位置可以具体到街道、小区,甚至门牌单元。以下思路方法主要吸收知乎上一用户的回帖并加以改进。
操作步骤及方法:
第一步:申请百度地图ak(即获取密钥),若无百度账号则首先需要注册百度账号。
第二步,拼写发送http请求的url,注意需使用第一步申请的ak。
第三步,接收http请求返回的数据(支持json和xml格式)。
第四步,解析返回的数据(此处使用json格式)。
第五步,转换并保存所需数据。
实例1、地理位置→经纬度(如:山西省太原市小店区亲贤北街77号→112.5771 37.83013)
测试数据如下(输入了一些非正常值用作测试),可自行建立txt文件(注意最后一行多打个回车),存放在R的系统文件夹中:
address_list.txt
山西省太原市小店区亲贤北街77号
贵州省贵阳市云岩区书香门第B栋3单元
北京市通州区神树商业街168号
贵州省贵阳市南明区兴关路51
北京市东城区长巷二条乙5号
山西省太原市杏花岭区北肖墙12号
北京
北京市通州区
北京市的的的的的
天津市武清区
#导入地址列表
address <- read.table(‘address_list.txt’,header = F, col.names=c(‘address’),as.is = c(1))
address <- address$address #转化为向量格式,备for循环使用
head(address)
[1] “山西省太原市小店区亲贤北街77号” “贵州省贵阳市云岩区书香门第B栋3单元”
[3] “北京市通州区神树商业街168号” “贵州省贵阳市南明区兴关路51”
[5] “北京市东城区长巷二条乙5号” “山西省太原市杏花岭区北肖墙12号”
#建立备用向量,包括空向量及百度地图api秘钥
baidu_lng <- c()
baidu_lat <- c()
ak <- ‘HuwUWPmzvZmErI4Wnbe*************’ #百度地图api的秘钥,需自己申请
#加载包
library(rjson)
library(RCurl)
#循环解析过程
for (location in address) {
#生成规则的url地址(具体参数可参考Geocoding API文档)
url <- paste(‘http://api.map.baidu.com/geocoder/v2/?ak=’,ak,’&callback=renderOption&output=json&address=’,location,sep=”)
#利用URLencode()转换为可解析的URL地址
url_string <- URLencode(url)
#通过readLines读取URL地址,并解析JSON格式的结果
json<- readLines(url_string, warn=F)
geo <- fromJSON(substr(json,regexpr(‘\\(‘,json)+1,nchar(json)-1))
#在解析结果中提取经纬度
lng<-geo$result$location$lng
lat<-geo$result$location$lat
#存储到已经建好的字段中
baidu_lng <- c(baidu_lng,lng)
baidu_lat <- c(baidu_lat,lat)
}
#整理结果
result <- data.frame(address=address,longitude=baidu_lng,latitude=baidu_lat)
result
address longitude latitude
1 山西省太原市小店区亲贤北街77号 112.5771 37.83013
2 贵州省贵阳市云岩区书香门第B栋3单元 106.7254 26.59437
3 北京市通州区神树商业街168号 116.5607 39.82672
4 贵州省贵阳市南明区兴关路51 106.7200 26.56996
5 北京市东城区长巷二条乙5号 116.4076 39.90422
6 山西省太原市杏花岭区北肖墙12号 112.5753 37.89014
7 北京 116.3956 39.92999
8 北京市通州区 116.7401 39.80981
9 北京市的的的的的 116.3956 39.92999
10 天津市武清区 117.0346 39.45704
解读:地址列表必须转换成向量形式,因为数据框格式的直接参与for循环只取第一个值。经测试返回的json是带回调函数的(即不仅仅是纯json数据,可复制url_string的结果到浏览器打开观察数据格式),所以需要substr截取json格式内容,然后传递给fromJSON进行解析,解析的结果以列表形式存放(见geo),字段意义如下表。另外通过测试结果发现一些非正常格式数据会返回可识别内容的经纬度,但是如果输入的地址完全没有地名对应,则程序报错,无法执行,这点要求把握原始地址数据质量。根据百度文档说明API接口默认配额6000次/天,若需更高配额,需申请百度开发者认证(每天限额30万次)。
参考字段:
名称
|
类型
|
说明
|
status
|
Int
|
返回结果状态值, 成功返回0,其他值请查看下方返回码状态表。
|
location
|
object
|
经纬度坐标
|
|
lat
|
float
|
纬度值
|
lng
|
float
|
经度值
|
precise
|
Int
|
位置的附加信息,是否精确查找。1为精确查找,即准确打点;0为不精确,即模糊打点。
|
confidence
|
Int
|
可信度,描述打点准确度
|
level
|
string
|
地址类型
|
实例2、经纬度→地理位置(如:37.8051643371582,112.564300537109→山西省太原市小店区产业路21号)
经纬度到地理位置的转换为以上过程的逆过程,在于输入内容与输出内容的互逆,解析过程是一致的。
测试数据如下,可自行建立txt文件(注意最后一行多打个回车),存放在R的系统文件夹中:
lon_lat_list.txt
112.56430053710938 37.805164337158203
112.55702209472656 37.899299621582031
112.59318542480469 37.853942871093750
112.58712768554688 37.811378479003906
112.54068756103516 37.841072082519531
112.58361816406250 37.866237640380859
112.63124847412109 37.870849609375000
112.56057739257812 37.832489013671875
112.57408905029297 37.757740020751953
112.50973510742188 37.799381256103516
#导入经纬度列表,整理成纬度在前,经度在后,英文逗号隔开的格式
lat_lng <- read.table(‘lng_lat_list.txt’, header = F, as.is=c(1,2))
lat_lng <- paste(lat_lng[,2], lat_lng[,1], sep=’,’)
head(lat_lng)
[1] “37.8051643371582,112.564300537109” “37.8669395446777,112.617851257324”
[3] “37.8456039428711,112.51749420166” “37.8592185974121,112.572357177734”
[5] “37.8898696899414,112.575706481934” “37.8777236938477,112.576156616211”
#建立备用字段,包括空字段及百度地图api秘钥
baidu_address <- c()
ak <- ‘HuwUWPmzvZmErI4Wnbe*************’ #百度地图api的秘钥,需自己申请
#加载包
library(rjson)
library(RCurl)
#循环反解析过程
for (location in lat_lng) {
#生成规则的url地址(具体参数可参考Geocoding API文档)
url <- paste(‘http://api.map.baidu.com/geocoder/v2/?ak=’,ak,’&callback=renderReverse&location=’,location,’&output=json’,sep=”)
#利用URLencode()转换为可解析的URL地址
url_string <- URLencode(url)
#通过readLines读取转换后的URL地址,并解析JSON格式的结果
json<- readLines(url_string, warn=F, encoding = ‘UTF-8’)
geo <- fromJSON(substr(json,regexpr(‘\\(‘,json)+1,nchar(json)-1))
#结果中提取格式化位置
address<-geo$result$formatted_address
#存储到已经建好的字段中
baidu_address <-c(baidu_address,address)
}
#整理结果
result <- data.frame(lat_lng, address=baidu_address)
result
lat_lng address
1 37.8051643371582,112.564300537109 山西省太原市小店区产业路21号
2 37.899299621582,112.557022094727 山西省太原市杏花岭区新建路224号
3 37.8539428710938,112.593185424805 山西省太原市迎泽区建设南路66号
4 37.8113784790039,112.587127685547 山西省太原市小店区师范街23号
5 37.8410720825195,112.540687561035 山西省太原市万柏林区晋祠路1段-118
6 37.8662376403809,112.583618164062 山西省太原市迎泽区迎泽大街92号
7 37.870849609375,112.631248474121 山西省太原市杏花岭区五龙口街
8 37.8324890136719,112.560577392578 山西省太原市小店区北园街23-4
9 37.757740020752,112.574089050293 山西省太原市小店区电子街
10 37.7993812561035,112.509735107422 山西省太原市晋源区晋祠路
#数据导出到本地
write.table(result, ‘lat_lng_to_address.txt’)
解读:除了上面解读需要注意的,此处特殊强调几点:一是输入数据顺序为[纬度,经度],注意是纬度在前。二是生成规则的url地址时需要设置参数output=json,否则默认生成的是XML格式。三是上例中返回的是最全的结构化地址信息,也可以取区县或者街道(具体见下表参考字段),比如$result$addressComponent$district,对应到区县的后即可统计不同区的业务量,结合地图工具(比如与remapC函数)可呈现业务热力图,这也是应用之一。再次强调普通用户每天每个ak配额是6000次,超过6000程序会报错。