一、概述
本文讲述利用Python获取高德地图POI数据的思路以及具体步骤。
此处以上海的瑞幸门店为例,目的是同时讲述多边形搜索、关键词搜索以及POI类型搜索的具体用法。
本文完整代码的获取方式在文末,有需求的小伙伴自取。
上海瑞幸门店地图可视化
上海瑞幸门店地图可视化
二、获取思路
通过调用高德地图API中的搜索POI(多边形搜索)接口来获取POI数据。
搜索POI接口
https://lbs.amap.com/api/webservice/guide/api/search#around
理论上来说,只需要将接口输入参数中的polygon设置为上海边界经纬度值,将keywords设置为瑞幸,将types设置为餐饮服务(编码:050000),就能够获取上海范围内的瑞幸咖啡门店数据。
GeoJSON格式的上海边界数据
但在多边形搜索接口中每个边界范围能够获取到的POI数量是有限制的,超过该限制的POI数据则不会返回。
因此,想要获取全量POI数据,需要将一个大多边形切割成若干个符合数量限制的小多边形。
此处,引入四叉树索引的概念,将大的多边形不断四分,直到所有小多边形均满足数量限制为止。
完成上述操作后,遍历所有小多边形并获取瑞幸POI,即可得到全量的上海瑞幸POI数据。
三、具体步骤
步骤1:申请高德地图API密钥
高德地图API密钥需要通过高德官网申请
高德地图API官网(https://lbs.amap.com/)
具体操作步骤可参考下面链接:https://kdocs.cn/l/coOAaCYwW0tg
步骤2:获取上海边界范围外接矩形的坐标值
# 通过读取GeoJSON格式的上海边界数据
# 获取上海边界外接矩形的最小经度、最大经度、最小纬度、最大纬度
import geopandas as gp
# 结果变量
resultMinLon = 99999.0 # 最小经度
resultMaxLon = 0.0 # 最大经度
resultMinLat = 99999.0 # 最小纬度
resultMaxLat = 0.0 # 最大纬度
# 读取上海边界数据
loadGeoData = gp.read_file("D:\\上海边界数据.geojson")
for i in range(0, len(loadGeoData)):
# 读取几何数据
loadGeometry = loadGeoData.loc[i, "geometry"]
# 上海边界的几何数据是由多个多边形组成,如崇明岛、长兴岛、上海市区等
# 所以需要遍历所有多边形
# 多边形的类型是Polygon或者MultiPolygon
for j in range(0, len(loadGeometry)):
loadPolygon = loadGeometry[j]
# 读取多边形的边界
# 边界的类型是LineString(单线)或MultiLineString(多线)
if loadPolygon.boundary.geom_type == 'LineString':
# 读取边界中的每个坐标点
for z in range(0, len(loadPolygon.boundary.coords)):
loadLon = loadPolygon.boundary.coords[z][0] # 经度
loadLat = loadPolygon.boundary.coords[z][1] # 纬度
# 进行大小判断,并替换结果
if loadLon <= resultMinLon:
resultMinLon = loadLon
if loadLon >= resultMaxLon:
resultMaxLon = loadLon
if loadLat <= resultMinLat:
resultMinLat = loadLat
if loadLat >= resultMaxLat:
resultMaxLat = loadLat
elif loadPolygon.boundary.geom_type == 'MultiLineString':
# 如果边界是MultiLineString,则需要遍历其中的每条LineString
for z in range(0, len(loadPolygon.boundary[0].coords)):
loadLon = loadPolygon.boundary[0].coords[z][0]
loadLat = loadPolygon.boundary[0].coords[z][1]
if loadLon <= resultMinLon:
resultMinLon = loadLon
if loadLon >= resultMaxLon:
resultMaxLon = loadLon
if loadLat <= resultMinLat:
resultMinLat = loadLat
if loadLat >= resultMaxLat:
resultMaxLat = loadLat
# 结果输出
print("上海最小经度:", resultMinLon)
print("上海最大经度:", resultMaxLon)
print("上海最小纬度:", resultMinLat)
print("上海最大纬度:", resultMaxLat)
步骤2输出结果
步骤3:以四叉树索引的方式不断四分矩形,直到所有矩形符合POI数量限制
import requests
# 上海边界经纬度值
minLon = 120.859465 # 最小经度
maxLon = 122.122756 # 最大经度
minLat = 30.677191 # 最小纬度
maxLat = 31.868458 # 最大纬度
# 存放符合POI数量限制多边形的列表
resultPolygonList = []
# 用来存放不符合POI数量限制多边形的列表
currentPolygonList = [[minLon, maxLon, minLat, maxLat]]
# 以四叉树的方式不断四分外接矩形
for currentLevel in range(1, 9999999):
# 用来存放不符合POI数量限制多边形的临时列表
tempPolygonList = []
for z in range(0, len(currentPolygonList)):
loadMinLon = currentPolygonList[z][0]
loadMaxLon = currentPolygonList[z][1]
loadMinLat = currentPolygonList[z][2]
loadMaxLat = currentPolygonList[z][3]
# 判断当前矩形是否符合POI数量限制
# 如果符合则放入resultPolygonList
# 如果不符合则进行四分,并将四分结果放入tempPolygonList
ifSatisfy = judgeIfSatisfy_GaoDe([loadMinLon, loadMaxLon, loadMinLat, loadMaxLat])
if ifSatisfy == True:
resultPolygonList.append([loadMinLon, loadMaxLon, loadMinLat, loadMaxLat])
else:
# 进行四叉树
tempQuadtree = executeQuadtree(loadMinLon, loadMaxLon, loadMinLat, loadMaxLat)
# 左下角
tempPolygonList.append(tempQuadtree[0])
# 右下角
tempPolygonList.append(tempQuadtree[1])
# 右上角
tempPolygonList.append(tempQuadtree[2])
# 左上角
tempPolygonList.append(tempQuadtree[3])
currentPolygonList = tempPolygonList.copy()
# 如果没有多边形进入下一层级,则跳出
print("第", currentLevel, "层", "......", "符合要求的矩形:", len(resultPolygonList), "......", "剩余矩形:",
len(currentPolygonList))
if len(currentPolygonList) == 0:
break
步骤3结果
步骤4:遍历所有矩形,获取上海瑞幸POI
# 遍历所有矩形,获取上海瑞幸POI数据
savePoiList = [] # 存放POI数据的结果列表
for i in range(0, len(resultPolygonList)):
# 读取每个矩形
loadPolygon = resultPolygonList[i]
# 获取矩形中的POI数据,并添加到结果列表中
savePoiList.extend(getPoiFromPolygon(loadPolygon, gaodeKey))
下面代码为获取单个矩形中POI数据的方法
# 从单个矩形获取瑞幸POI的方法
# 其中key为高德地图API密钥,需要自行申请
def getPoiFromPolygon(inputPolygon, key):
# 存放POI数据结果的列表
resultList = []
# 由于POI数量限制是100,每页返回POI数量是20
# 因此,最大的页数为5
for currentPage in range(1, 6):
# 组成Url
currentUrl = "https://restapi.amap.com/v3/place/polygon?polygon=" + str(inputPolygon[0]) + "," + str(
inputPolygon[2]) + "|" + str(inputPolygon[1]) + "," + str(
inputPolygon[2]) + "|" + str(inputPolygon[1]) + "," + str(inputPolygon[3]) + "|" + str(
inputPolygon[0]) + "," + str(inputPolygon[3]) + "|" + str(inputPolygon[0]) + "," + str(
inputPolygon[2]) + "&offset=20&page=" + str(
currentPage) + "&keywords=瑞幸&types=050000&output=json&key=" + key
# 发送Get请求,并接收返回内容
response = requests.get(currentUrl, stream=True, verify=False, timeout=60)
returnData = response.json()
returnPoiList = returnData['pois']
if len(returnPoiList) > 0:
for i in range(0, len(returnPoiList)):
saveName = returnPoiList[i]['name']
saveType = returnPoiList[i]['type']
saveAddress = returnPoiList[i]['address']
saveLocation = returnPoiList[i]['location']
saveProvince = returnPoiList[i]['pname']
saveCity = returnPoiList[i]['cityname']
saveArea = returnPoiList[i]['adname']
resultList.append(
[saveName, saveType, saveType, saveAddress, saveLocation, saveProvince, saveCity, saveArea])
print(saveName, saveType, saveType, saveAddress, saveLocation, saveProvince, saveCity, saveArea)
else:
# 如果当前页POI数量为0,则返回已获取的POI数据并跳出
return resultList
return resultList
上海瑞幸POI数据
四、代码获取方式
点击下面链接获取代码