地图数据的来源,不同分类的数据,其来源,采集方法都是有大不同的。地图的数据分类,必须先理解一个概念,就是地图图层的概念:
如上图,电子地图对我们实际空间的表达,事实上是通过不同的图层去描述,然后通过图层叠加显示来进行表达的过程。对于我们地图应用目标的不同,叠加的图层也是不同的,用以展示我们针对目标所需要信息内容。其次呢,我引入一下矢量模型和栅格模型的概念,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