10

干货|利用Python获取高德POI兴趣点数据

 3 years ago
source link: https://zhuanlan.zhihu.com/p/346554734
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

干货|利用Python获取高德POI兴趣点数据

上海市新能源汽车公共数据采集与监测研究中心 数据分析师

全国范围POI兴趣点数据可通过下面网址免费获取:

本文主要分享利用Python获取全量高德地图POI数据的方法及代码

POI数据,英文全称Point of Intersesting,中文的意思是兴趣点,指的是在地图上有意义的点:比如商店、酒吧、加油站、医院、车站等。POI数据能够赋能时空行为、城市规划、地理信息等研究,因此获取准而全的POI数据是开展科研工作的基础。

目前免费公开POI的主流地图平台有:

值得注意的是,开发者可以通过三个平台提供的接口免费获取POI数据,但是高德地图API以及腾讯地图API获取的POI数据是GCJ-02坐标系而百度地图API获取的POI数据是BAIDU-09坐标系。在使用之前需要注意POI数据与底图在空间地理坐标系上的一致性,否则会造成空间计算错误。

地理坐标系转换的原理及方法在下面这篇文章中有详细的讲述及代码实现:

如果不会写代码或者懒得动手代码,可使用下面这个工具进行批量地理坐标系转换:

本文选择高德地图API为例进行全量POI数据获取的演示,其他渠道在方法层面与高德地图API完全相同,只是在代码方面会有细微的差别。

利用高德地图API中的“搜索POI”接口可以批量获取POI数据,该接口提供关键字搜索、周边搜索、多边形搜索以及ID搜索四种方式。很明显,关键字搜索以及ID搜索并不适用于批量获取POI,而周边搜索会造成POI数据大量重复以及密钥限额浪费的问题。因此,本文采用多边形搜索来完成批量POI数据获取。

多边形搜索API的服务地址、请求参数以及返回参数可参考网址:搜索POI-API文档-开发指南-Web服务 API | 高德地图API

https://restapi.amap.com/v3/place/polygon=121.470251,31.23013|121.476474,31.230387|121.476946,31.235378|121.469179,31.234864|121.470251,31.23013&types=010000&&offset=20&page=2output=json&key=365ac412d6e22f49ce3d345270ecc643

点击上面的URL可以获取POI数据,其含义是:获取空间位置在输入多边形范围内且类型为“010000”的POI数据,数据传输格式为JSON,每页最多输出20个POI且当前是第2页。下面是利用Python来完成上述动作的代码,如果需要获取一个多边形内的所有POI需要遍历所有页码。

# 输入多边形边界
inputPolygon = "121.469579,31.226331|121.480994,31.226331|121.480994,31.235615|121.469579,31.235615|121.469579,31.226331"
# 输入的URL
inputUrl = "https://restapi.amap.com/v3/place/polygon?polygon=" + inputPolygon + "&offset=20&page=1&types=010000&output=json&key=365ac412d6e22f49ce3d345270ecc643"
response = requests.get(inputUrl)
# 返回结果,JSON格式
resultAnswer = response.json()
# 返回结果中的状态标签,1为成功,0为失败
resultStatus = resultAnswer['status']
if resultStatus == '1':  # 返回成功
    # 读取返回的POI列表
    resultList = resultAnswer['pois']
    if len(resultList) == 0:  # 返回的POI列表为空
        print("当前返回结果为空!")
    else:
        # 返回的POI列表不为空,则遍历读取所有的数据
        for j in range(0, len(resultList)):
            saveId = str(resultList[j]['id'])  # POI编号
            saveName = str(resultList[j]['name'])  # POI名称
            saveType = str(resultList[j]['type'])  # POI类别
            saveTypecode = str(resultList[j]['typecode'])  # POI类别编号
            saveAddress = str(resultList[j]['address'])  # POI地址
            saveLocation = str(resultList[j]['location'])  # POI坐标
            print([saveId, saveName, saveType, saveTypecode, saveAddress, saveLocation])
else:
    print("当前返回结果错误!")

值得注意的是,该接口规定了一个多边形最多返回1000个POI,因此想要获取全量POI数据,就必须将研究区域切分成若干个POI数量小于1000的矩形网格。

最容易想到的切分方法就是大小相同的矩形网格,如下图所示为500米*500米相同大小的矩形网格。这种切分方法虽然能勉强达到目的但是会出现以下两个问题:

  • 固定尺寸的网格会造成某些网格POI数量极少而某些网格POI数量大于1000,因此需要找到最大能够满足限制的网格尺寸。
  • 由于大城市的市中心十分繁华,因此最大能够满足限制的网格尺寸可能十分小,大量位于郊区的网格可能没有POI数据,这会造成密钥限额的浪费以及获取效率低下。
500米*500米的矩形网格

基于四叉树索引的概念,在POI数量较少的郊区用大尺寸的网格而在POI数量较多的市区用小尺寸的网格,保证每个网格中都至少有1个POI且小于1000个POI。下图为基于四叉树索引的概念对上海划分的矩形网格,点击四叉树演示可查看上述网格。

基于四叉树索引的矩形网格

下面是基于四叉树索引的概念实现矩形网格划分的Python代码。

# 利用递归建立四叉树
# polygonList代表当前需要计算的矩形网格列表
# count代表当前是第几层的四叉树
def executeQuadtree(polygonList, count):
    print("第", count, "层")
    print("+++++++++++++++++++++++++++++++++++++++++++")
    afterPolygonList = []  # 存放当前层不符合限制要求的矩形网格进行四叉树分裂后的网格的列表
    for i in range(0, len(polygonList)):
        currentMinLon = polygonList[i][0]  # 当前矩形网格的最小经度
        currentMaxLon = polygonList[i][1]  # 当前矩形网格的最大经度
        currentMinLat = polygonList[i][2]  # 当前矩形网格的最小纬度
        currentMaxLat = polygonList[i][3]  # 当前矩形网格的最大纬度
        # 判断当前矩形网格中的POI数量
        currentCount = getCountFromGaode([currentMinLon, currentMaxLon, currentMinLat, currentMaxLat], 1)
        if currentCount < currentBatch:
            # 当前矩形网格中的POI数量符合限制条件,输出
            print([currentMinLon, currentMaxLon, currentMinLat, currentMaxLat, currentCount])
        else:
            # 当前矩形网格中的POI数量符合限制条件,则对当前网格进行四叉树分裂
            dLon = (currentMaxLon - currentMinLon) / 2
            dLat = (currentMaxLat - currentMinLat) / 2
            # 左下角的网格
            afterPolygonList.append([currentMinLon, currentMinLon + dLon, currentMinLat, currentMinLat + dLat])
            # 右下角的网格
            afterPolygonList.append(
                [currentMinLon + dLon, currentMinLon + 2 * dLon, currentMinLat, currentMinLat + dLat])
            # 右上角的网格
            afterPolygonList.append(
                [currentMinLon + dLon, currentMinLon + 2 * dLon, currentMinLat + dLat, currentMinLat + 2 * dLat])
            # 左上角的网格
            afterPolygonList.append(
                [currentMinLon, currentMinLon + dLon, currentMinLat + dLat, currentMinLat + 2 * dLat])
    # 如果没有当前层没有不符合限制要求的网格,则退出递归
    if len(afterPolygonList) == 0:
        return True
    else:
        # 进入下一层四叉树
        count += 1
        executeQuadtree(afterPolygonList, count)

# 判断输入的矩形网格是否满足限制
# 符合的返回True,不符合的返回False
# polygon为输入的多边形
# batch为单个矩形网格能够获取到的POI上限,默认为1000
def getCountFromGaode(polygon, batch=1000):
    # 组成符合高德地图API要求的多边形边界格式
    currentPolygonStr = str(polygon[0]) + "," + str(polygon[2]) + "|" + str(polygon[1]) + "," + str(
        polygon[2]) + "|" + str(polygon[1]) + "," + str(polygon[3]) + "|" + str(polygon[0]) + "," + str(
        polygon[3]) + "|" + str(polygon[0]) + "," + str(polygon[2])
    # 输入的URL
    currentUrl = "https://restapi.amap.com/v3/place/polygon?polygon=" + currentPolygonStr + "&offset=20&page=1&types=" + bigCodeStr + "&output=json&key=" + "365ac412d6e22f49ce3d345270ecc643"
    response = requests.get(currentUrl)
    answer = response.json()
    # 获取
    resultCount = int(answer['count'])
    if resultCount < batch:
        return True
    else:
        return False

# 上海的矩形范围
inputPolygon = [[120.798865, 122.084266, 30.5918780, 31.918422]]
# 建立四叉树
executeQuadtree(inputPolygon, 1)

利用上述代码可以将上海划分成满足限制的且大小不一的矩形网格,接着利用前面获取POI的代码可以实现全量POI数据的获取。

以上是本文的全部内容,如果觉得有用请点赞、收藏并关注,谢谢!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK