Python实现百度地图、高德地图地理编码及高德地图经纬度坐标转百度地图经纬度坐标

引言

近期博主在做地理数据可视化方面的研究,pyecharts提供了较好的工具,里面提供了很多图形,尤其是Map和Geo这两种图,一种是地图,一种是地理信息图。pyecharts的基础还是echart,echart是百度地图开源的一个数据可视化 JS 库,从我个人使用的情况来看,目前pyecharts(博主pyecharts版本是0.5.11)有这两个问题:

  • 地图精度不够。目前pyecharts提供的地图层级有世界地图、各个国家的地图、中国各省地图、中国各地市图、区县图,到区县后,就不再细分,如果我想看某条道路的具体信息,就难以实现。
  • 经纬度定位“不准确”。这不属于pyecharts是问题,应该属于百度地图在地理编码上的特点,当然,并不是说百度地图地理编码不准确,而是博主的目前在做的项目,地理位置都比较奇特,不管是BaiduMap还是AMap(高德地图)都很难定位到特别准确。由于pyecharts的底层地图是百度地图,所以看起来是pyecharts定位“不准确”。

要解决第一个问题,就是说想要更高层级的地图,这个就需要换可视化包了,也就是说,将pyecharts改成具有其他更高层级地图的可视化包。博主使用的是folium,这个包也很强大,pyecharts的强大之处在与echart的可视化功能,folium则体现在地图及地图的扩展应用上。folium目前支持OpenStreetMap、AMap等几种地图,但不支持百度地图。一度吐血,为啥我用的这两个包底层地图就不能通用下。但其实也好,为了用好这两个包,博主对百度、高德这两大地图大佬的开发文档进行了研究,现在将二者地理编码的方式记录下来。
开发环境:

  • Python3.7;
  • pyecharts0.5.11;
  • pycharm

百度地图地理编码

开发文档看这里,使用前需要去申请AK,同时最好申请开发者认证,能提升访问限制。开发文档其实写的很详细,无非是爬虫get和json解析那一套,我直接上代码。

import json
import pandas as pd
import numpy as np
from urllib.request import urlopen, quote
import requests

def Scene2CoordinateBaiduMap(filename):
    data = pd.read_csv(filename)
    result = []
    i = 1
    j = 1
    for k in data['地点']:
        address = quote(k)  # 1
        url = 'http://api.map.baidu.com/geocoder/v2/?address=' + address + '&output=json&ak=yourAK'  # 2
        req = urlopen(url)

        res = req.read().decode()
        temp = json.loads(res)
        try:  # 3
            result.append(temp['result']['location'])
            print('catch %d \n' % i)
            i = i + 1
        except:
            result.append({'lat': 'null', 'lng': 'null'})
            print('fail %d \n' % j)
            j = j + 1

    print('成功定位%d个,成功率%.2f%%' % (i - 1, (i - 1) * 100 / (i + j - 2)))

    df = pd.DataFrame(result)
    df.to_csv('经纬度原始数据-百度地图.csv', index=False)

if __name__ == '__main__':
    main()

先看导入的包,访问百度地图API返回的都是json格式的数据,要对数据进行解析,自然少不了json包,pandas和numpy常规包,基本上很难不用到,后面两个是网页数据获取的包。高德地图地理编码也是这几个包,后面不再重复写。
几点说明:
#1quote函数是为了得到GBK的url编码,国内的网站编码都是GBK编码的;
#2要把url里面的AK改成你自己申请的AK,而不是yourAK这个字符串;
#3为啥要用try来处理,因为不是所有的地理编码都能成功,如果不用try处理,容易报错,程序会中断。
这个函数写的很详细了,你提供一个CSV文件,其中一列命名为‘地点’,就能将你的所有填写的地点都能转换成经纬度,当然,也有不成功的,总体成功率在95%左右。总体来说,定位效果还是可以的,但是如果地点数据很多填写的不规范,地理编码虽然解析出经纬度,但很多都是有问题的,比如解析出来的点不在正确的地点,如下图。
Python实现百度地图、高德地图地理编码及高德地图经纬度坐标转百度地图经纬度坐标_第1张图片
这些点本来应该都出现在无锡市地图上,但还有不少点在无锡市外,也就是说,解析的不正确。问题出在哪里?博主又回过头去看文档,文档里说,请求的参数除了address外,还有city,也就是城市,这个参数不是必须要写的,如果是写了这个,会不会就正确了呢?于是,我找了一个落在无锡地图之外的点,将请求网址写成这样:

http://api.map.baidu.com/geocoding/v3/?address=南湖大道双庆桥公交站前&city=无锡市&output=json&ak=您的ak&callback=showLocation

返回的数据如下:

showLocation&&showLocation({"status":0,"result":{"location":{"lng":120.31700801956372,"lat":31.532789952709309},"precise":0,"confidence":50,"comprehension":24,"level":"桥"}})

根据经纬度去百度地图拾取坐标系统中看看,巧了,点还对了。。。
博主懵逼了,上午的时候,明明是不对的,所以才想到要写一篇博客来解决这个问题,回过头来一想,因为我上午写的请求网址是这样的:

http://api.map.baidu.com/geocoding/v3/?address=南湖大道双庆桥公交站前&city='无锡市'&output=json&ak=您的ak&callback=showLocation

无锡市我打了引号,其实是没必要打引号的。。。
Python实现百度地图、高德地图地理编码及高德地图经纬度坐标转百度地图经纬度坐标_第2张图片
所以,实际上,在请求网址里面加上城市名,就能保证经纬度转换的时候,所有点都落在你想要的城市里面。。。
持续吐血,这篇博客还要写吗,我上午已经按照我的解决思路,高德地图地理编码→高德地图转百度地图→pyecharts地点上图,这个套路,解析了3000+的地点。
都写了3000多字了,还是继续写完吧。。。

高德地图地理编码

开发文档看这里
,技术细节与百度地图基本一致,请求格式上我换了一种写法,让读者可以更灵活的使用爬虫的技巧。
上代码

def Scene2CoordinateAMap(filename):
    data = pd.read_csv(filename)
    sgdd = data['事故地点'].tolist()
    geo = []
    key = 'yourKEY'
    base = 'https://restapi.amap.com/v3/geocode/geo'
    j = 1
    k = 1
    for i in range(len(sgdd)):
        parameters = {'address': sgdd[i], 'key': key, 'city':'无锡'}
        response = requests.get(base, parameters)
        answer = response.json()
        try:
            pos = answer['geocodes'][0]['location']
            pos = pos.split(',')
            pos[0], pos[1] = pos[1], pos[0]
            geo.append(pos)
            print('catch %d \n'%j)
            j = j + 1
        except:
            geo.append([ 'null','null'])
            print('fail %d \n'%k)
            k = k + 1
    points = pd.DataFrame(geo)
    points.to_csv('经纬度填充-高德地图.csv', index=False, columns = ['lng', 'lat'])
    print('成功定位%d个,成功率%.2f%%' % (j - 1, (j - 1) * 100 / (j + k - 2)))

为啥我会写成‘无锡’,能理解了吧,我高德就是这么写的。。。
可以看出来,和百度地图请求方式有一点不同,我用的是requests.get去请求的,其实网上更多的推荐是用这种方式去请求,而不是用urlopen,不过我个人习惯用urlopen,两者都行吧。

高德地图经纬度转百度地图经纬度

其实已经没啥必要写了,因为二者都差不多。
文档看这里,上代码

def AMap2BaiduMap(filename):
    data = pd.read_csv(filename)
    base = 'http://api.map.baidu.com/geoconv/v1/?coords='
    tail = '&from=3&to=5&ak=yourAK'
    transpos = []
    j = 1
    k = 1
    for i in range(len(data)):
        url = base + str(data.iloc[i]['lng_Amap']) + ',' + str(data.iloc[i]['lat_Amap']) + tail
        req = urlopen(url)
        res = req.read().decode()
        temp = json.loads(res)
                     
        try:
            pos = list(temp['result'][0].values())  # 字典转列表
            transpos.append(pos)
            print('catch %d \n'%j)
            j = j + 1
        except:
            transpos.append(['null','null'])
            print('fail %d \n'%k)
            k = k + 1
    tran = pd.DataFrame(transpos)
    tran.to_csv('高德转百度.csv', index=False, columns = ['lng', 'lat'])
    print('成功转换%d个,成功率%.2f%%' % (j - 1, (j - 1) * 100 / (j + k - 2)))

注意下,链接里有这么句代码

from=3&to=5

官方文档解释如下:

源坐标类型:
1:GPS设备获取的角度坐标,WGS84坐标;
2:GPS获取的米制坐标、sogou地图所用坐标;
3:google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标,国测局(GCJ02)坐标;
4:3中列表地图坐标对应的米制坐标;
5:百度地图采用的经纬度坐标;
6:百度地图采用的米制坐标;
7:mapbar地图坐标;
8:51地图坐标
目标坐标类型:
5:bd09ll(百度经纬度坐标);
6:bd09mc(百度米制经纬度坐标)

我是高德经纬度转百度经纬度,自然是from=3to=5。
看看转换经纬度后的pyecharts-Geo图吧
Python实现百度地图、高德地图地理编码及高德地图经纬度坐标转百度地图经纬度坐标_第3张图片
全都在无锡市内了,大功告成,虽然没啥用了(╥╯^╰╥)

你可能感兴趣的:(笔记)