四叉树获取地图上所有的poi

  地图数据的来源,不同分类的数据,其来源,采集方法都是有大不同的。地图的数据分类,必须先理解一个概念,就是地图图层的概念:
地图图层

  如上图,电子地图对我们实际空间的表达,事实上是通过不同的图层去描述,然后通过图层叠加显示来进行表达的过程。对于我们地图应用目标的不同,叠加的图层也是不同的,用以展示我们针对目标所需要信息内容。其次呢,我引入一下矢量模型和栅格模型的概念,GIS(电子地图)采用两种不同的数学模型来对现实世界进行模拟:
  矢量模型:同多X,Y(或者X,Y,Z)坐标,把自然界的地物通过点,线,面的方式进行表达


矢量模型

  栅格模型(瓦片模型):用方格来模拟实体


栅格模型

  我们目前在互联网公开服务中,或者绝大多数手机APP里看到的,都是基于栅格(瓦片)模型的地图服务,比如大家看到的百度地图或者谷歌地图,其实对于某一块地方的描述,都是通过10多层乃是20多层不同分辨率的图片所组成,当用户进行缩放时,根据缩放的级数,选择不同分辨率的瓦片图拼接成一幅完整的地图(由于一般公开服务,瓦片图都是从服务器上下载的,当网速慢的时候,用户其实能够亲眼看到这种不同分辨率图片的切换和拼接的过程)

  对于矢量模型的电子地图来说,由于所有的数据以矢量的方式存放管理,事实上图层是一个比较淡薄的概念,因为任何地图元素和数据都可以根据需要自由分类组成,或者划分成不同的图层。各种图层之间关系可以很复杂,例如可以将所有的道路数据做成一个图层,也可以将主干道做成一个图层,支路做成另外一个图层。图层中数据归类和组合比较自由。

  而对于栅格模型(瓦片图)来看,图层的概念就很重要的,由于图层是生成制作出来,每个图层内包含的元素相对是固化的,因此要引入一个底图的概念。也就是说,这是一个包含了最基本,最常用的地图数据元素的图层,例如:道路,河流,桥梁,绿地,甚至有些底图会包含建筑物或者其他地物的轮廓。在底图的基础上,可以叠加各种我们需要的图层,以满足应用的需要,例如:道路堵车状况的图层,卫星图,POI图层等等。

  POI数据:严格来说属于矢量数据,不过是最简单的矢量数据,换句话来说就是坐标点标注数据。也是电子地图上最常用的数据图层。

需求:获取全国所有汽车维修POI

  有个需求是获取全国所有的汽车维修POI,所以想通过矩形区域的地图poi接口获取汽车维修POI,但是每个矩形区域最多只能获得几百个poi,想利用这个接口来搞定全国汽车维修POI,需要先把全国分为若干矩形,保证每个矩形之内高德地图POI数量不超过地图返回POI上限个数(由于地图返回的上限不确定,所以保守取上限500)。

解决思路:

  将全国划分成x * x个栅格(x经纬度步长),每个栅格作为根节点获取POI,当POI不超过POI返回上限分页保存POI,超过500时,分成4个相等小栅格,这样创建出4叉树,直到把该栅格内的POI都找到,切换下一个栅格继续四叉树……

栅格图
算法实现
# Author:Sunshine丶天
import requests, random, traceback, math
from poi_get import settings
import numpy as np
import time, traceback

DECIMAL_POINT= 8 # 保留小数点后几位
LNG_START = 73   # 国内经度最小值
LNG_END = 136    # 国内经度最大值
LAT_START = 18   # 国内纬度最小值
LAT_END = 54     # 国内纬度最大值
REC_STEP = 1     # 步长

class GET_POI(object):
    def __init__(self,type, offset):
        self.offset = offset
        self.pois_all = []
        self.type = type # gd: 高德地图  bd: 百度地图

    def run(self, lng, lat, area_count):

        self.getPolygonPoi(lng, lat, REC_STEP, area_count)

        if len(self.pois_all):
            out_path  = r'/Users/Sunshine/Desktop/gd_poi_lng%s-lat%s.txt'%(int(lng),int(lat)) if self.type == 'gd' else r'/Users/Sunshine/Desktop/bd_poi_%s-%s.txt'%(int(lng),int(lat))
            f_in = open(out_path, 'a+', encoding='utf-8')
            sets = set()
            for poi in self.pois_all:
                sets.add('%s\n' % poi)
            f_in.writelines(sets)
            f_in.close()

    def get_pois_gd(self, lng, lat, step, page):
        if self.type == 'gd':
            # 获取poi信息
            base_url = 'https://restapi.amap.com/v3/place/polygon'
            # 经纬度左上右下
            parameters = {'polygon': '%s,%s|%s,%s' % (
            round(lng + step / 2, DECIMAL_POINT), round((lat - step / 2), DECIMAL_POINT), lng, lat),
                          'key': random.choice(settings.MAPKEY_GD),
                          'types': settings.BIG_POI_CODE_GD,
                          'extensions': 'base',
                          'offset': self.offset,
                          'page': page}

            try:
                response = requests.get(base_url, parameters, timeout=5)
                contentDic = eval(response.content.decode().strip(''))
                count_poi = int(contentDic['count'])  # 该区域内的所有poi总数
                pois = contentDic['pois']  # poi数据数组
                return count_poi, pois
            except:
                traceback.print_exc()
        elif self.type == 'bd':
            # 获取poi信息
            base_url = 'http://api.map.baidu.com/place/v2/search?'

            # 经纬度左下右上
            parameters = {'query': settings.KEY_QUERY_BD,
                          'tag': settings.KEY_WORD_BD,
                          'bounds': '%s,%s,%s,%s' % (
                              lat, round((lng - step / 2), DECIMAL_POINT), round(lat + step / 2, DECIMAL_POINT), lng),
                          'output': 'json',
                          'coord_type': '2',
                          'page_size': 20,
                          'page_num': page,
                          'ak': random.choice(settings.MAPKEY_BD)}
            try:
                response = requests.get(base_url, parameters, timeout=5)
                contentDic = eval(response.content.decode().strip(''))
                print(contentDic)
                count_poi = contentDic['total']
                pois = contentDic['results']

                return count_poi, pois
            except:
                traceback.print_exc()

    def getPolygonPoi(self, lng, lat, step, area_count):
        # 从第一页开始获取
        count_poi, pois = self.get_pois_gd(lng, lat, 2 * step, 0)
        print('第%s个栅格开始获取poi数据,--->步长:%s---%s' % (area_count, step, count_poi))
        if len(pois):  # 有poi数据
            if count_poi >= 500:  # 该区域内poi数量超过500个 变换四叉树分裂下去
                # 左上
                lat_l_t = lat
                lng_l_t = lng
                self.getPolygonPoi(lng_l_t, lat_l_t, step / 2, area_count)
                # 右上
                lat_r_t = lat
                lng_r_t = lng + step / 2
                self.getPolygonPoi(lng_r_t, lat_r_t, step / 2, area_count)
                # 左下
                lat_l_d = lat - step / 2
                lng_l_d = lng
                self.getPolygonPoi(lng_l_d, lat_l_d, step / 2, area_count)
                # 右下
                lat_r_d = lat - step / 2
                lng_r_d = lng - step / 2
                self.getPolygonPoi(lng_r_d, lat_r_d, step / 2, area_count)
            else:
                self.pois_all += pois
                page_num = int(math.ceil(count_poi / self.offset))
                for index, page in enumerate(range(2, page_num + 1)):  # 获取每页中的poi数据
                    count_poi, pois = self.get_pois_gd(lng, lat, step * 2, page)
                    print(lng, lat, step, page,len(pois))
                    self.pois_all += pois
                return self.pois_all
        else:
            return self.pois_all

def get_pois(lng, lat, step, page):
    # 获取poi信息
    base_url = 'http://api.map.baidu.com/place/v2/search?'

    # 经纬度左下右上
    parameters = {'query': settings.KEY_QUERY_BD,
                  'tag': settings.KEY_WORD_BD,
                  'bounds': '%s,%s,%s,%s' % (
                  lat, round((lng - step / 2), DECIMAL_POINT), round(lat + step / 2, DECIMAL_POINT), lng),
                  'output': 'json',
                  'coord_type': '2',
                  'page_size': 20,
                  'page_num': page,
                  'ak': random.choice(settings.MAPKEY_BD)}

    try:
        response = requests.get(base_url, parameters, timeout=5)
        contentDic = eval(response.content.decode().strip(''))
        count_poi = contentDic['total']
        pois = contentDic['results']

        return count_poi, pois
    except:
        traceback.print_exc()


if __name__ == '__main__':
    count = 0

    def get_polygon(lng_l, lat_l):
        return round(lng_l, DECIMAL_POINT), round(lat_l, DECIMAL_POINT)

    lng_list = np.arange(LNG_START, LNG_END, REC_STEP)  # 生成lng范围
    lat_list = np.arange(LAT_START, LAT_END, REC_STEP)  # 生成lat范围
    rec_list = list()  # 全国矩形区域划分
    for lng in lng_list:
        for lat in lat_list:
            count += 1   # 第几个大栅格
            point =  get_polygon(lng, lat)
            # 高德每页最多 25条数据  百度每页 最多20条数据
            get_poi = GET_POI('gd', 25)
            get_poi.run(point[0], point[1], count)

补充:

多边形搜索api详情请参考百度开发支持api高德开发支持api
参考文章:
https://www.cnblogs.com/sparrowjack/p/5151477.html
https://www.zhihu.com/question/21530085/answer/18728706

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容