??地圖數(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