四叉樹獲取地圖上所有的poi

??地圖數(shù)據(jù)的來源了袁,不同分類的數(shù)據(jù),其來源湿颅,采集方法都是有大不同的载绿。地圖的數(shù)據(jù)分類,必須先理解一個概念油航,就是地圖圖層的概念:
地圖圖層

??如上圖崭庸,電子地圖對我們實際空間的表達(dá),事實上是通過不同的圖層去描述,然后通過圖層疊加顯示來進(jìn)行表達(dá)的過程怕享。對于我們地圖應(yīng)用目標(biāo)的不同执赡,疊加的圖層也是不同的,用以展示我們針對目標(biāo)所需要信息內(nèi)容函筋。其次呢沙合,我引入一下矢量模型和柵格模型的概念,GIS(電子地圖)采用兩種不同的數(shù)學(xué)模型來對現(xiàn)實世界進(jìn)行模擬:
??矢量模型:同多X,Y(或者X,Y,Z)坐標(biāo)跌帐,把自然界的地物通過點首懈,線,面的方式進(jìn)行表達(dá)


矢量模型

??柵格模型(瓦片模型):用方格來模擬實體


柵格模型

??我們目前在互聯(lián)網(wǎng)公開服務(wù)中谨敛,或者絕大多數(shù)手機(jī)APP里看到的究履,都是基于柵格(瓦片)模型的地圖服務(wù),比如大家看到的百度地圖或者谷歌地圖脸狸,其實對于某一塊地方的描述最仑,都是通過10多層乃是20多層不同分辨率的圖片所組成,當(dāng)用戶進(jìn)行縮放時炊甲,根據(jù)縮放的級數(shù)泥彤,選擇不同分辨率的瓦片圖拼接成一幅完整的地圖(由于一般公開服務(wù),瓦片圖都是從服務(wù)器上下載的蜜葱,當(dāng)網(wǎng)速慢的時候全景,用戶其實能夠親眼看到這種不同分辨率圖片的切換和拼接的過程)

??對于矢量模型的電子地圖來說耀石,由于所有的數(shù)據(jù)以矢量的方式存放管理牵囤,事實上圖層是一個比較淡薄的概念,因為任何地圖元素和數(shù)據(jù)都可以根據(jù)需要自由分類組成滞伟,或者劃分成不同的圖層揭鳞。各種圖層之間關(guān)系可以很復(fù)雜,例如可以將所有的道路數(shù)據(jù)做成一個圖層梆奈,也可以將主干道做成一個圖層野崇,支路做成另外一個圖層。圖層中數(shù)據(jù)歸類和組合比較自由亩钟。

??而對于柵格模型(瓦片圖)來看乓梨,圖層的概念就很重要的,由于圖層是生成制作出來清酥,每個圖層內(nèi)包含的元素相對是固化的扶镀,因此要引入一個底圖的概念。也就是說焰轻,這是一個包含了最基本臭觉,最常用的地圖數(shù)據(jù)元素的圖層,例如:道路,河流蝠筑,橋梁狞膘,綠地,甚至有些底圖會包含建筑物或者其他地物的輪廓什乙。在底圖的基礎(chǔ)上挽封,可以疊加各種我們需要的圖層,以滿足應(yīng)用的需要臣镣,例如:道路堵車狀況的圖層场仲,衛(wèi)星圖,POI圖層等等退疫。

??POI數(shù)據(jù):嚴(yán)格來說屬于矢量數(shù)據(jù)渠缕,不過是最簡單的矢量數(shù)據(jù),換句話來說就是坐標(biāo)點標(biāo)注數(shù)據(jù)褒繁。也是電子地圖上最常用的數(shù)據(jù)圖層亦鳞。

需求:獲取全國所有汽車維修POI

??有個需求是獲取全國所有的汽車維修POI,所以想通過矩形區(qū)域的地圖poi接口獲取汽車維修POI,但是每個矩形區(qū)域最多只能獲得幾百個poi棒坏,想利用這個接口來搞定全國汽車維修POI燕差,需要先把全國分為若干矩形,保證每個矩形之內(nèi)高德地圖POI數(shù)量不超過地圖返回POI上限個數(shù)(由于地圖返回的上限不確定坝冕,所以保守取上限500)徒探。

解決思路:

??將全國劃分成x * x個柵格(x經(jīng)緯度步長),每個柵格作為根節(jié)點獲取POI喂窟,當(dāng)POI不超過POI返回上限分頁保存POI测暗,超過500時,分成4個相等小柵格磨澡,這樣創(chuàng)建出4叉樹碗啄,直到把該柵格內(nèi)的POI都找到,切換下一個柵格繼續(xù)四叉樹……

柵格圖
算法實現(xiàn)
# Author:Sunshine丶天
import requests, random, traceback, math
from poi_get import settings
import numpy as np
import time, traceback

DECIMAL_POINT= 8 # 保留小數(shù)點后幾位
LNG_START = 73   # 國內(nèi)經(jīng)度最小值
LNG_END = 136    # 國內(nèi)經(jīng)度最大值
LAT_START = 18   # 國內(nèi)緯度最小值
LAT_END = 54     # 國內(nèi)緯度最大值
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'
            # 經(jīng)緯度左上右下
            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'])  # 該區(qū)域內(nèi)的所有poi總數(shù)
                pois = contentDic['pois']  # poi數(shù)據(jù)數(shù)組
                return count_poi, pois
            except:
                traceback.print_exc()
        elif self.type == 'bd':
            # 獲取poi信息
            base_url = 'http://api.map.baidu.com/place/v2/search?'

            # 經(jīng)緯度左下右上
            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數(shù)據(jù),--->步長:%s---%s' % (area_count, step, count_poi))
        if len(pois):  # 有poi數(shù)據(jù)
            if count_poi >= 500:  # 該區(qū)域內(nèi)poi數(shù)量超過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數(shù)據(jù)
                    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?'

    # 經(jīng)緯度左下右上
    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()  # 全國矩形區(qū)域劃分
    for lng in lng_list:
        for lat in lat_list:
            count += 1   # 第幾個大柵格
            point =  get_polygon(lng, lat)
            # 高德每頁最多 25條數(shù)據(jù)  百度每頁 最多20條數(shù)據(jù)
            get_poi = GET_POI('gd', 25)
            get_poi.run(point[0], point[1], count)

補(bǔ)充:

多邊形搜索api詳情請參考百度開發(fā)支持api稳摄、高德開發(fā)支持api
參考文章:
https://www.cnblogs.com/sparrowjack/p/5151477.html
https://www.zhihu.com/question/21530085/answer/18728706

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末稚字,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子厦酬,更是在濱河造成了極大的恐慌胆描,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仗阅,死亡現(xiàn)場離奇詭異昌讲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)霹菊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門剧蚣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來支竹,“玉大人,你說我怎么就攤上這事鸠按±窀椋” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵目尖,是天一觀的道長馒吴。 經(jīng)常有香客問我,道長瑟曲,這世上最難降的妖魔是什么饮戳? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮洞拨,結(jié)果婚禮上扯罐,老公的妹妹穿的比我還像新娘。我一直安慰自己烦衣,他們只是感情好歹河,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著花吟,像睡著了一般秸歧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衅澈,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天键菱,我揣著相機(jī)與錄音,去河邊找鬼今布。 笑死经备,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的险耀。 我是一名探鬼主播弄喘,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼玖喘,長吁一口氣:“原來是場噩夢啊……” “哼甩牺!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起累奈,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤贬派,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后澎媒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搞乏,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年戒努,在試婚紗的時候發(fā)現(xiàn)自己被綠了请敦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镐躲。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖侍筛,靈堂內(nèi)的尸體忽然破棺而出萤皂,到底是詐尸還是另有隱情,我是刑警寧澤匣椰,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布裆熙,位于F島的核電站,受9級特大地震影響禽笑,放射性物質(zhì)發(fā)生泄漏入录。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一佳镜、第九天 我趴在偏房一處隱蔽的房頂上張望僚稿。 院中可真熱鬧,春花似錦蟀伸、人聲如沸贫奠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唤崭。三九已至枉氮,卻和暖如春蹋艺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钦铺。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工小泉, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留芦疏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓微姊,卻偏偏與公主長得像酸茴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子兢交,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容