由于之前探索过高德地图POI数据边界坐标的采集,后面接口太不稳定,很难能稳定成功采集到数据,该功能搁置了一段时间,最近在酷酷的@entropy同学的帮助下,完成了利用百度地图接口采集POI边界的功能。不过在此提前说明下,不论百度还是高德,各自都是利用POI的ID来完成边界坐标的采集的, 相同的POI数据,在高德和百度上的ID是不一样的。因此要使用百度采集边界的接口的话,必须保证已有的POI数据是通过百度POI接口采集到的,并且有ID字段。(总结来说:就是不要用高德接口采集到的POI数据来调用百度接口爬边界数据)。

确定POI边界数据采集的接口地址

  • 先说下POI的ID怎么来?百度地图提供的POI采集接口返回的就有ID字段,好像是叫uid,记下来就行。此处以UID为207119787bb3c5c95d17c334的POI为例。
  • 百度地图返回POI边界数据的接口:
https://map.baidu.com/?newmap=1&reqflag=pcmap&biz=1&from=webmap&da_par=direct&pcevaname=pc4.1&qt=ext&uid=207119787bb3c5c95d17c334&c=340&ext_ver=new&tn=B_NORMAL_MAP&nn=0&auth=fw9wVDQUyKS7%3DQ5eWeb5A21KZOG0NadNuxHNBxBBLBHtxjhNwzWWvy1uVt1GgvPUDZYOYIZuEt2gz4yYxGccZcuVtPWv3GuxNt%3DkVJ0IUvhgMZSguxzBEHLNRTVtlEeLZNz1%40Db17dDFC8zv7u%40ZPuxtfvSulnDjnCENTHEHH%40NXBvzXX3M%40J2mmiJ4Y&ie=utf-8&l=19&b=(12679382.095,2565580.38;12679884.095,2565907.38)&t=1573133634785

接口很简单,不需要密钥, GET请求就能调用,参数中主要就需要替换uid=后面的一串为你自己的POI ID就好了。因此在浏览器打开后就可以看到结果,数据结构如下:

{
    "content": {
        "geo": "4|12674567.8667,2556549.714;12674700.0816,2556667.07656|1-12674700.0816,2556615.59082,12674663.0912,2556549.714,12674567.8667,2556601.53877,12674605.8561,2556667.07656,12674700.0816,2556615.59082;",
        "uid": "207119787bb3c5c95d17c334"
    },
    "current_city": {
        "code": 340,
        "geo": "1|12697919.69,2560977.31;12697919.69,2560977.31|12697919.69,2560977.31;",
        "level": 12,
        "name": "深圳市",
        "sup": 1,
        "sup_bus": 1,
        "sup_business_area": 1,
        "sup_lukuang": 1,
        "sup_subway": 1,
        "type": 2,
        "up_province_name": "广东省"
    },
    "err_msg": "",
    "hot_city": [
        "北京市|131",
        "上海市|289",
        "广州市|257",
        "深圳市|340",
        "成都市|75",
        "天津市|332",
        "南京市|315",
        "杭州市|179",
        "武汉市|218",
        "重庆市|132"
    ],
    "result": {
        "data_security_filt_res": 0,
        "error": 0,
        "illegal": 0,
        "login_debug": 1,
        "qid": "",
        "region": "0",
        "type": 10,
        "uii_qt": "poi_profile",
        "uii_type": "china_main"
    },
    "uii_err": 0
}

contentgeo便是我们需要寻找的POI数据的边界坐标,剩下的就是怎么解析这个数据的问题了。不过需要注意的是这个坐标系是bd09mc(百度墨卡托米制坐标)。坐标系说明可以参考

http://lbs.baidu.com/index.php?title=android-yingyan/guide/coordtrans

因此后面需要将数据转换为百度经纬度坐标。

根据UID获取边界数据并做简单解析的代码参考:

def get_boundary_by_uid(uid):
    bmap_boundary_url = 'https://map.baidu.com/?newmap=1&reqflag=pcmap&biz=1&from=webmap&da_par=direct&pcevaname=pc4.1&qt=ext&uid=' + uid + '&c=340&ext_ver=new&tn=B_NORMAL_MAP&nn=0&auth=fw9wVDQUyKS7%3DQ5eWeb5A21KZOG0NadNuxHNBxBBLBHtxjhNwzWWvy1uVt1GgvPUDZYOYIZuEt2gz4yYxGccZcuVtPWv3GuxNt%3DkVJ0IUvhgMZSguxzBEHLNRTVtlEeLZNz1%40Db17dDFC8zv7u%40ZPuxtfvSulnDjnCENTHEHH%40NXBvzXX3M%40J2mmiJ4Y&ie=utf-8&l=19&b=(12679382.095,2565580.38;12679884.095,2565907.38)&t=1573133634785'

    s = requests.Session()
    s.mount('http://', HTTPAdapter(max_retries=3))
    s.mount('https://', HTTPAdapter(max_retries=3))

    data = s.get(url=bmap_boundary_url, timeout=5, headers={"Connection": "close"})
    data = data.text
    data = json.loads(data)
    content = data['content']
    if not 'geo' in content:
        return None
    geo = content['geo']
    i = 0
    strsss = ''
    for jj in str(geo).split('|')[2].split('-')[1].split(','):
        jj = str(jj).strip(';')
        if i % 2 == 0:
            strsss = strsss + str(jj) + ','
        else:
            strsss = strsss + str(jj) + ';'
        i = i + 1
    return strsss.strip(";")

调用百度地图API进行坐标转换

  • 目的:将百度米制坐标系转换为百度经纬度坐标系:
  • API参考:
http://lbsyun.baidu.com/index.php?title=webapi/guide/changeposition

需要注意的是该接口只能将其他坐标系的数据转换为百度的米制坐标系和百度经纬度坐标系两种,而不能逆转。

  • 坐标转换参考URL:
http://api.map.baidu.com/geoconv/v1/?coords=112.343,232.34&from=6&to=5&ak=百度密钥

其中from=6&to=5意思是从百度米制坐标系转换为百度经纬度坐标系。具体可以参见官方文档。

  • 坐标系转换参考代码:
def transform_coordinate_batch(coordinates):
    req_url = 'http://api.map.baidu.com/geoconv/v1/?coords='+coordinates+'&from=6&to=5&ak=' + bmap_key

    s = requests.Session()
    s.mount('http://', HTTPAdapter(max_retries=3))
    s.mount('https://', HTTPAdapter(max_retries=3))

    data = s.get(req_url, timeout=5, headers={"Connection": "close"})  # , proxies=proxies
    data = data.text
    data = json.loads(data)
    coords = ''
    if data['status'] == 0:
        result = data['result']
        if len(result) > 0:
            for res in result:
                lng = res['x']
                lat = res['y']
                coords = coords + ";" + str(lng) + "," + str(lat)
    return coords.strip(";")

最终数据

目前拿到的边界数据格式是这样的:

113.85752917167422,22.512113353880437;113.85719688487298,22.51156349239119;113.8563414779429,22.51199606423422;113.8566827388162,22.512543094177662;113.85752917167422,22.512113353880437

既然拿到了数据,剩下的就简单了,这个主要看自己的需求,如果需要在ARCGIS中展示为面状数据,则需要再进行处理下,结果示例:

在这里插入图片描述

解析下:uid就是POI 的ID, number是自增的,暂时没啥用,一个uid对应多个x,y对,一个x,y就是一个点坐标,多个点坐标连起来就形成了一个多边形面数据。

  • 代码参考:
file_name = 'data/boundary_result_wgs84 - polygon.csv'

csv_file = pd.read_csv(file_name, encoding='gbk')

a_col = []
data_csv = {}
numbers, xs, ys, uids = [], [], [], []
index = 1
for i in range(len(csv_file)):
    boundary = str(csv_file['boundary'][i])

    uid = str(uuid.uuid4()).replace('-', '')

    if boundary is not '':
        for point in boundary.split(";"):
            lng = point.split(",")[0]
            lat = point.split(",")[1]
            xs.append(lng)
            ys.append(lat)
            numbers.append(index)
            uids.append(uid)

            index = index + 1
data_csv['number'] = numbers
data_csv['x'] = xs
data_csv['y'] = ys
data_csv['uid'] = uids

df = pd.DataFrame(data_csv)
df.to_csv(os.getcwd() + os.sep + 'data/polygon-shape.csv', index=False, encoding='gbk')

一定要读

目前已经将采集POI边界坐标做成了一个在线工具,感兴趣的可以一试,地址:百度地图POI边界采集工具 上传需要采集的POI ID的CSV文件以及申请的百度地图密钥后,就可采集到对应的边界数据啦!需要注意的是,单次上传的数据最好不要太多哦。

原创文章,转载请注明出处:http://www.loveyuanwei.com/article/boundary/