python获取高德地图POI——多边形搜索

本文主要内容是利用python获取高德地图上的感兴趣点(POIs)。

高德开放平台:https://lbs.amap.com/
下载POI分类编码和城市编码表
搜索POI相关文档:https://lbs.amap.com/api/webservice/guide/api/search

1.首先在高德开放平台上注册账号,然后进行开发者认证,认证成为个人开发者(当然如果商用也可以认证为企业开发者)
2.其次进入控制台,创建应用并申请key,然后就可以调用key啦。

在这里插入图片描述
3.利用python调用key结合多边形搜索法获取POI

完整的代码有三个文件:

1. Coordin_transformlat.py -- 坐标转换文件,用于对poi进行坐标系纠偏
2. GetPoi_keywords.py --  对高德地图发起数据请求并写入csv文件
3. RectanSearch.py --  四叉树划分法

三个文件缺一不可!!!

代码分别如下:

Coordin_transformlat.py文件:

# * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
# * 即 百度 转 谷歌、高德
# * @param bd_lon
# * @param bd_lat
# * @returns {*[]}
# */
import math
def bd09togcj02(bd_lon, bd_lat):
    x_pi = 3.14159265358979324 * 3000.0 / 180.0
    x = bd_lon - 0.0065
    y = bd_lat - 0.006
    z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
    theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
    gg_lng = z * math.cos(theta)
    gg_lat = z * math.sin(theta)
    return [gg_lng, gg_lat]


# * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
# * 即谷歌、高德 转 百度
# */
def gcj02tobd09(lng, lat):
    x_PI = 3.14159265358979324 * 3000.0 / 180.0
    z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_PI)
    theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_PI)
    bd_lng = z * math.cos(theta) + 0.0065
    bd_lat = z * math.sin(theta) + 0.006
    return [bd_lng, bd_lat]


# wgs84转高德
def wgs84togcj02(lng, lat):
    PI = 3.1415926535897932384626
    ee = 0.00669342162296594323
    a = 6378245.0
    dlat = transformlat(lng - 105.0, lat - 35.0)
    dlng = transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * PI
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
    mglat = lat + dlat
    mglng = lng + dlng
    return [mglng, mglat]


# GCJ02/谷歌、高德 转换为 WGS84 gcj02towgs84
def gcj02towgs84(localStr):
    lng = float(localStr.split(',')[0])
    lat = float(localStr.split(',')[1])
    PI = 3.1415926535897932384626
    ee = 0.00669342162296594323
    a = 6378245.0
    dlat = transformlat(lng - 105.0, lat - 35.0)
    dlng = transformlng(lng - 105.0, lat - 35.0)
    radlat = lat / 180.0 * PI
    magic = math.sin(radlat)
    magic = 1 - ee * magic * magic
    sqrtmagic = math.sqrt(magic)
    dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
    dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * PI)
    mglat = lat + dlat
    mglng = lng + dlng
    return [lng * 2 - mglng,lat * 2 - mglat]


def transformlat(lng, lat):
    PI = 3.1415926535897932384626
    ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * \
          lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
            math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lat * PI) + 40.0 *
            math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
    ret += (160.0 * math.sin(lat / 12.0 * PI) + 320 *
            math.sin(lat * PI / 30.0)) * 2.0 / 3.0
    return ret


def transformlng(lng, lat):
    PI = 3.1415926535897932384626
    ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
          0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
    ret += (20.0 * math.sin(6.0 * lng * PI) + 20.0 *
            math.sin(2.0 * lng * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(lng * PI) + 40.0 *
            math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
    ret += (150.0 * math.sin(lng / 12.0 * PI) + 300.0 *
            math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
    return ret

GetPoi_keywords.py文件:

import requests
import json
import csv
import re
from Coordin_transformlat import gcj02towgs84

def Get_poi_polygon(key,polygon,keywords,page):
    '''
    这是一个能够从高德地图获取poi数据的函数
    key:为用户申请的高德密钥
    polygon:目标城市四个点的坐标
    keywords:POI数据的类型
    page:当前页数
    '''
    #设置header
    header = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}

    #将输进来的矩形进行格式化
    Polygonstr = str(polygon[0]) + ',' + str(polygon[1]) + '|' + str(polygon[2]) + ',' + str(polygon[3])

    #构建url
    url = 'https://restapi.amap.com/v3/place/polygon?polygon={}&key={}&keywords={}&page={}'.format(Polygonstr, key, keywords, page)
    print(url)
    #用get函数请求数据
    r = requests.get(url, headers=header)

    #设置数据的编码为'utf-8'
    r.encoding = 'utf-8'

    # 将请求得到的数据按照'utf-8'编码成字符串
    data = r.text
    return data

def Get_times_polygon(key,polygon,keywords):
    '''
    这是一个控制Get_poi_polygon申请次数的函数
    '''
    page = 1
    # 执行以下代码,直到count为0的时候跳出循环
    while True:
        # 调用第一个函数来获取数据
        result = Get_poi_polygon(key,polygon, keywords, page)
        # json.loads可以对获取回来JSON格式的数据进行解码
        content = json.loads(result)
        pois = content['pois']
        count = content['count']
        print(count)
        #如果区域内poi的数量大于800,则认为超过上限,返回False请求对区域进行切割
        if int(count) > 800:
            return False
        else:
            for i in range(len(pois)):
                name = pois[i]['name']
                location = pois[i]['location']
                if 'address' not in pois[i].keys():
                    address = str(-1)
                else:
                    address = pois[i]['address']
                adname = pois[i]['adname']
                result = gcj02towgs84(location)
                lng = result[0]
                lat = result[1]
                row = [name, address, adname, lng, lat]
                #调用写入函数来保存数据
                writecsv(row, keywords)
            if count == '0':
                break
                # 递增page
            page = page + 1

def writecsv(poilist,keywords):
    """
    这是写入成csv文件的函数
    :param poilist:
    :param keywords:
    :return: 输出的结果存放在代码文件夹下
    """
    with open('{}.csv'.format(keywords),'a',newline='',encoding='utf-8') as csvfile:
        writer = csv.writer(csvfile)
        writer.writerow(poilist)

def get_city_scope(key, cityname):
    parameters = 'key={}&keywords={}&subdistrict={}&output=JSON&extensions=all'.format(key, cityname, 0)
    url = 'https://restapi.amap.com/v3/config/district?'
    # 设置header
    header = {'User-Agent': "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50"}
    res = requests.get(url,params=parameters)
    jsonData = res.json()
    if jsonData['status'] == '1':
        district = jsonData['districts'][0]['polyline']
        district_list = re.split(';|\|',district)
        xlist, ylist = [], []
        for d in district_list:
            xlist.append(float(d.split(',')[0]))
            ylist.append(float(d.split(',')[1]))
        xmax = max(xlist)
        xmin = min(xlist)
        ymax = max(ylist)
        ymin = min(ylist)
        return [xmin, ymax, xmax, ymin]
    else:
        print ('fail to acquire: {}'.format(jsonData['info']))


RectanSearch.py文件:

import GetPoi_keywords as gp

def Quadrangle(key,polygon,keywords):
    """
    :param key:高德地图密钥
    :param polygon: 矩形左上跟右下坐标的列表
    :param keywords: poi关键词
    :return:
    """
    #准备一个空列表,存放切割后的子区域
    PolygonList = []
    for i in range(len(polygon)):
        currentMinlat = round(polygon[i][0],6)#当前区域的最小经度
        currentMaxlat = round(polygon[i][2],6)#当前区域的最大经度
        currentMaxlon = round(polygon[i][1],6)#当前区域的最大纬度
        currentMinlon = round(polygon[i][3],6)#当前区域的最小纬度

        cerrnt_list = [currentMinlat, currentMaxlon, currentMaxlat, currentMinlon]
        #将多边形输入获取函数中,判断区域内poi的数量
        status = gp.Get_times_polygon(key,cerrnt_list,keywords)
        #如果数量大于800,那么返回False,对区域进行切分,否则返回区域的坐标对
        if status != False:
            print('该区域poi数量小于800,正在写入数据')
        else:
            #左上矩形
            PolygonList.append([
                currentMinlat, #左经
                currentMaxlon, #上纬
                (currentMaxlat+currentMinlat)/2, #右经
                (currentMaxlon+currentMinlon)/2]) #下纬
            #右上矩形
            PolygonList.append([
                (currentMaxlat+currentMinlat)/2,#左经
                currentMaxlon, #上纬
                currentMaxlat, #右经
                (currentMaxlon+currentMinlon)/2#下纬
            ])
            #左下矩形
            PolygonList.append([
                currentMinlat,#左经
                (currentMaxlon+currentMinlon)/2,#上纬
                (currentMaxlat+currentMinlat)/2,#右经
                currentMinlon#下纬
            ])
            #右下矩形
            PolygonList.append([
                (currentMaxlat+currentMinlat)/2,#左经
                (currentMaxlon+currentMinlon)/2,#上纬
                currentMaxlat,#右经
                currentMinlon#下纬
            ])
            print(len(PolygonList))
            if len(PolygonList) == 0:
                break
            else:
                Quadrangle(key,PolygonList,keywords)


#这里修改为自己的高德密钥
key ='************************'

#这里修改自己的poi类型
keywords = '公园'

#这里输入想要查询的城市(如城市中文、citycode、adcode)
city = '北京市'

#调用高德查询行政区的API接口来返回矩形坐标对
Retance = gp.get_city_scope(key,city)

#存储区域矩形的列表
input_polygon = []
input_polygon.append(Retance)

Quadrangle(key,input_polygon,keywords)

如果想要用poi的类型(types)来检索,可以将url里的keywords={}替换成types={},其他地方均不用再做修改!!!

文章参考:https://blog.csdn.net/abcbbbd/article/details/124162308

3.11更新:
有时候使用一个密钥爬取POI时,依然会出现达到数据限制但没爬取完整数据的情况,因此将多个密钥改为列表形式依次使用,直到所有密钥都被尝试完毕或者查询到结果为止。
代码如下:
只需要修改RectanSearch.py文件:

import GetPoi_keywords as gp
import time

def Quadrangle(key, polygon, keywords):
    """
    :param key: 高德地图密钥
    :param polygon: 矩形左上跟右下坐标的列表
    :param keywords: poi关键词
    :return:
    """
    # 准备一个空列表,存放切割后的子区域
    PolygonList = []
    for i in range(len(polygon)):
        currentMinlat = round(polygon[i][0], 6)  # 当前区域的最小经度
        currentMaxlat = round(polygon[i][2], 6)  # 当前区域的最大经度
        currentMaxlon = round(polygon[i][1], 6)  # 当前区域的最大纬度
        currentMinlon = round(polygon[i][3], 6)  # 当前区域的最小纬度

        cerrnt_list = [currentMinlat, currentMaxlon, currentMaxlat, currentMinlon]
        # 将多边形输入获取函数中,判断区域内poi的数量
        status = gp.Get_times_polygon(key, cerrnt_list, keywords)
        # 如果数量大于800,那么返回False,对区域进行切分,否则返回区域的坐标对
        if status != False:
            print("该区域内POI数量小于800,正在写入数据")
        else:
            # 左上矩形
            PolygonList.append([
                currentMinlat,  # 左经
                currentMaxlon,  # 上纬
                (currentMaxlat + currentMinlat) / 2,  # 右经
                (currentMaxlon + currentMinlon) / 2])  # 下纬
            # 右上矩形
            PolygonList.append([
                (currentMaxlat + currentMinlat) / 2,  # 左经
                currentMaxlon,  # 上纬
                currentMaxlat,  # 右经
                (currentMaxlon + currentMinlon) / 2  # 下纬
            ])
            # 左下矩形
            PolygonList.append([
                currentMinlat,  # 左经
                (currentMaxlon + currentMinlon) / 2,  # 上纬
                (currentMaxlat + currentMinlat) / 2,  # 右经
                currentMinlon  # 下纬
            ])
            # 右下矩形
            PolygonList.append([
                (currentMaxlat + currentMinlat) / 2,  # 左经
                (currentMaxlon + currentMinlon) / 2,  # 上纬
                currentMaxlat,  # 右经
                currentMinlon  # 下纬
            ])
            print(len(PolygonList))
            if len(PolygonList) == 0:
                break
            else:
                Quadrangle(key, PolygonList, keywords)
                time.sleep(1)


if __name__ == '__main__':
    # 定义多个高德地图密钥,存储在列表中
    key_list = ['key1', 'key2', 'key3']
    # 定义需要查询的poi类型
    keywords = '120000'
    # 定义需要查询的城市
    city = '北京市'
    # 调用高德查询行政区的API接口来返回矩形坐标对
    Retance = gp.get_city_scope(key_list[0], city)
    # 存储区域矩形的列表
    input_polygon = [Retance]
    # 利用多个密钥连续爬取POI
    for i in range(len(key_list)):
        print('使用第 %d 个密钥进行爬取' % (i + 1))
        try:
            Quadrangle(key_list[i], input_polygon, keywords)
        except KeyError:
            print('密钥 %d 查询失败,尝试使用下一个密钥' % (i + 1))
            continue
        else:
            break
    else:
        print('所有密钥查询失败,结束程序')

  • 4
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值