针对百度地图和高德地图POI数据限制,提出的正多边形搜索。

最近公司需要收集某个站点的POI数据,但是这些API默认有POI条数限制,针对高德和百度地图的开放平台周边搜索poi限制200条数据,搜索网上大部分是开通服务或者是多边形搜索的矩形搜索,我想要的是类似于圆形的周边搜索,于是提出正多边形的多边形搜索,建议正8边形起步(我这是12边形),可以达到类似圆形效果,本质是多个等腰三角形合在一起,然后两条腰的交点是公用的,也就是中心坐标,理论上说边数越多,越像圆形,就跟数学积分一样。

在这里展示的是高德地图POI数据多边形获取,其实百度地图开放平台也差不多。

1、api数据获取的类型尽量使用小类名,不要使用大类,例如餐饮属于大类、中餐馆属于中类、粤菜属于小类,如果你想把一个大类的全部爬取下来,建议是下载相对应的POI类别的excel表然后导入程序,然后然后把大类对应的全部小类导入到程序中,开发者文档里有下载链接,自己可以找找。

2、我这里其实算法不够完美,因为我这里搜索的POI数据少,用不上更深入的算法,理论上说,12-18边形足够满足圆形的需求,如果在12-18边形内,有一个三角形的POI数据超过200条(加一个判断,如果该三角形的poi数据大于170或者页码>17还存在poi数据的话(默认每页10条)则认为这个三角形的poi数据可能会超过200(不建议直接大于200,因为是不会到200条的,我亲测最高180几条)),则可以把这个等腰三角形分成两个全等三角形,这样子不会改变整体的边数,只是在一个三角形内部分成两个单独算,如果分开后仍然有上述POI总数大于170的情况,就再分开,类似于递归调用。

上代码展示。

from geopy.distance import geodesic
import pandas as pd
import json
import urllib.request
import urllib.parse
import math
import os


# 坐标字符串转换成数值坐标
def getLocation(locationStr):
    # 提取坐标值
    lon_str, lat_str = locationStr.split(',')
    # 转换为浮点数
    lon = float(lon_str)
    lat = float(lat_str)
    # 保留六位小数
    lon = round(lon, 6)
    lat = round(lat, 6)
    return lon, lat


# 计算两个坐标的距离
# 该作用的方法是计算站点内的数据点到中心点的直线距离,因为用的是多边形搜索,所以默认没有中心点
def haversine_distance(lat1, lon1, lat2, lon2):  # 四个参数分别是两个坐标的经纬度
    # 将经度和纬度转换为弧度
    lat1_rad = math.radians(lat1)
    lon1_rad = math.radians(lon1)
    lat2_rad = math.radians(lat2)
    lon2_rad = math.radians(lon2)

    # 应用 Haversine 公式计算距离
    dlon = lon2_rad - lon1_rad
    dlat = lat2_rad - lat1_rad
    a = math.sin(dlat / 2) ** 2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlon / 2) ** 2
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    radius = 6371  # 地球半径,单位为千米
    distance = radius * c * 1000  # 将距离转换为米
    return round(distance, 2)


# 参数1:大类中文名称;参数2:poi类别文件路径
# 该方法的作用是提取大类相对应的小类,并传入url作为参数
def littleTypeList(bigType, PoiTypeOfPath):
    df = pd.read_excel(PoiTypeOfPath, usecols=['大类', '小类'])
    littleTypeStr = ""
    for index, row in df.iterrows():
        if row['大类'] == bigType:
            littleTypeStr = row['小类'] + '|' + littleTypeStr
    littleTypeStr = littleTypeStr[:-1]
    return littleTypeStr


# 选择是否导出excel
def toExcel(df, stationName, centerLon, centerLat, radius):
    # 选择是否导入并保存文件
    while True:
        yes = input("选择是否将该数据导入文件,是请输入yes,否输入no:")  # 选择是否导入文件
        if yes == "yes":
            while True:
                path = input("请输入存放路径目录,注意如果路径最后没有斜杠 ,加上斜杠\:")
                if os.path.exists(path):  # 判断文件夹是否存在
                    df.to_excel(f"{path}{stationName}-{centerLon, centerLat}-{bigType}-{radius}米.xlsx")
                    break
                else:
                    print("路径不存在,请重新输入")
                    continue
            break
        elif yes == "no":
            print(df)
            break
        else:
            print("输入错误,请重新输入")
            continue


# 参数1:站点名称;参数2:中心点经度;参数3:中心点维度;参数4:多边形边数;参数5:中心的范围,单位米;参数6:大类中文并称;参数7:
# 主方法,计算中心站点要修范围内的数据点及其相关信息
def calculate_location(stationName, centerLon, centerLat, num, radius, GaoDeKey, headers, bigType, PoiTypeOfPath):
    # 每次旋转的扫描的方位角:每一小部分根据中心点顺时针旋转,保留五位小数
    # 初始旋转方位角
    init_bearing = 0
    # 360/num 表示获取方位角并保留5位小数
    bearing = round(360 / num, 5)
    # 数据帧,用来存放已经处理好的数据,以便后期导出excel文件
    df = pd.DataFrame(
        columns=['parent', 'address', 'distance', 'pcode', 'adcode', 'pname', 'cityname', 'type', 'typecode',
                 'adname', 'citycode', 'name', 'location', 'id'])
    # 数据帧索引,从0开始
    dfIndex = 0
    for i in range(1, num + 1):
        # 三角形坐标
        # 计算偏移后的坐标
        destination1 = geodesic(kilometers=radius / 1000).destination((centerLat, centerLon), init_bearing)
        destination2 = geodesic(kilometers=radius / 1000).destination((centerLat, centerLon), init_bearing + bearing)
        # 计算偏移后的坐标保留6位小数
        lat2, lon2 = round(destination1.latitude, 6), round(destination1.longitude, 6)
        lat3, lon3 = round(destination2.latitude, 6), round(destination2.longitude, 6)
        init_bearing = init_bearing + bearing
        littleTypeStr = littleTypeList(bigType, PoiTypeOfPath)
        # 设置最大访问页数,因为我这边POI数据少,所以每个三角形一般不到两百条,可以根据自己情况设定
        for page_num in range(1, 11):
            # 高德地图开放平台API
            url = f'https://restapi.amap.com/v5/place/polygon?polygon={centerLon},{centerLat}|{lon2},{lat2}|{lon3},{lat3}|{centerLon},{centerLat}&types={littleTypeStr}&page_size=25&page_num={page_num}&output=json&key={GaoDeKey}'
            # 对 URL 进行编码
            url = urllib.parse.quote(url, safe=':/?&=|')
            req = urllib.request.Request(url=url, headers=headers, method='GET')
            response = urllib.request.urlopen(req)
            text = response.read().decode('utf-8')
            jsonStr = json.loads(text)
            pois = jsonStr["pois"]
            # 设置数据帧打印选项,不省略行和列
            pd.set_option('display.max_rows', 10)
            pd.set_option('display.max_columns', 15)
            # 判断爬取的内容当页有没有数据,
            if len(pois) == 0:
                # 如果该页没有数据,则表示该三角形内已经爬取完成,则开始爬到下一个三角形的poi数据,节约访问次数。
                print("该页没有数据,进入下一个三角形地区的数据获取")
                break
            else:
                print("该页有数据")
                for j in range(0, len(pois)):
                    df.loc[dfIndex] = pois[j]
                    rad_lon, rad_lat = getLocation(pois[j]["location"])
                    df.at[dfIndex, "distance"] = haversine_distance(centerLon, centerLat, rad_lon, rad_lat)
                    dfIndex = dfIndex + 1
    print(df)
    print("数据展示成功")
    toExcel(df, stationName, centerLon, centerLat, radius)


if __name__ == '__main__':
    # 高德地图分类文档下载,需要自行查看自己所要查询的分类,这个文件一定要存在
    # https://lbs.amap.com/api/webservice/download
    # 导入分类文件,选出大类相对应的小类当作类别
    # 中心点经度,保留六位小数
    centerLon =
    # 中心点维度,保留六位小数
    centerLat =
    # 多边形边数
    num = 12
    # POI类别文件路径,就是刚刚要求下载的文件,可以去官网搜索下载,然后每次直接导入
    PoiTypeOfPath = r"PoiTypeOf.xlsx"
    # 自己相对应的大类
    bigType = "poi大类中文名"
    # 请求头
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36'}
    # 搜索范围、单位米,最大取值5000000
    radius = 500
    # 高德地图开发Key:
    GaoDeKey = "你的高德地图开放平台key"
    # stationName,站点中文名称
    stationName = "站点中文名称"
    # 调用主方法
    # 参数1,2:中心坐标经度纬度;参数3:多边形边数;参数4:中心点范围,单位米,最大值位5000000;参数6:高德地图APIkey;参数7:请求头; 参数8:大类名称;参数9:
    calculate_location(stationName, centerLon, centerLat, num, radius, GaoDeKey, headers, bigType, PoiTypeOfPath)

POI文件内容文件内容(高德地图开放平台,百度地图的内容不一样)如下,去开发者文档里找,里面有下载链接

如果想要使用,首先要把库安装好,然后把下图的参数值改成自己的就行。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值