谷歌卫星图瓦片介绍 1 2 http://mt{index}.google.com/vt/lyrs={style}&hl=zh-CN&gl=CN&src=app&x={x}&y={y}&z={z}"
参数解析
{index}为 0 到 3,分别为谷歌的 4 个服务器,随便取一个就行~
{style}为地图的类型,比如卫星图、路线图什么的.
m:路线图
t:地形图
p:带标签的地形图
s:卫星图
y:带标签的卫星图
h:标签层(路名、地名等)
其中 m 路线图和 s 卫星图是我们想要的。
{x}{y}{z} 则分别是瓦片坐标的 xy 和缩放级别 z,z 取 0-22。但是我测试发现卫星图最大只能取到 20。不过即使是路线图,到 20 级也就足够用了。
gl 参数决定了卫星图是否会偏移
假设不加这个参数,会按 WGS-84 来,会和路线图有偏移(因为路线图只有 GCJ-02 的版本)
加了这个参数(gl=CN)就会使卫星图也变成 GCJ-02。
也就是说,如果你使用 www.google.cn/maps 或者 ditu.google.cn 来获取坐标,那么最后你还按照这个坐标进行瓦片的获取,同时加上 gl 参数即可。如果你想按照 WGS-84 来获取坐标,那么就访问国际版的谷歌地图,获取瓦片时不加 gl 参数就好了。
对于 python3,可以用 urllib.request 库进行图片的下载,然后用 PIL.Image 库(pillow)进行图片的合并。
下载关键代码 伪墨卡托与经纬度互转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #------------------wgs84与web墨卡托互转------------------------- # WGS-84经纬度转Web墨卡托 def wgs_to_macator(x, y): y = 85.0511287798 if y > 85.0511287798 else y y = -85.0511287798 if y < -85.0511287798 else y x2 = x * 20037508.34 / 180 y2 = log(tan((90 + y) * pi / 360)) / (pi / 180) y2 = y2 * 20037508.34 / 180 return x2, y2 # Web墨卡托转经纬度 def mecator_to_wgs(x, y): x2 = x / 20037508.34 * 180 y2 = y / 20037508.34 * 180 y2 = 180 / pi * (2 * atan(exp(y2 * pi / 180)) - pi / 2) return x2, y2 #-------------------------------------------------------------
根据经纬度计算瓦片位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 # 根据WGS-84 的经纬度获取谷歌地图中的瓦片坐标 def wgs84_to_tile(j, w, z): ''' Get google-style tile cooridinate from geographical coordinate j : Longittude w : Latitude z : zoom ''' isnum = lambda x: isinstance(x, int) or isinstance(x, float) if not(isnum(j) and isnum(w)): raise TypeError("j and w must be int or float!") if not isinstance(z, int) or z < 0 or z > 22: raise TypeError("z must be int and between 0 to 22.") if j < 0: j = 180 + j else: j += 180 j /= 360 # make j to (0,1) w = 85.0511287798 if w > 85.0511287798 else w w = -85.0511287798 if w < -85.0511287798 else w w = log(tan((90 + w) * pi / 360)) / (pi / 180) w /= 180 # make w to (-1,1) w = 1 - (w + 1) / 2 # make w to (0,1) and left top is 0-point num = 2**z x = floor(j * num) y = floor(w * num) return x, y
经纬度度分秒与小数互转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def dms2dd(degrees, minutes, seconds, direction): dd = float(degrees) + float(minutes)/60 + float(seconds)/(60*60); if direction == 'E' or direction == 'N': dd *= -1 return dd; def dd2dms(deg): d = int(deg) md = abs(deg - d) * 60 m = int(md) sd = (md - m) * 60 return [d, m, sd] def parse_dms(dms): parts = re.split('[^\d\w]+', dms) lat = dms2dd(parts[0], parts[1], parts[2], parts[3]) return (lat)
下载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Downloader(Thread): # multiple threads downloader def __init__(self,index,count,urls,datas,update): # index 表示第几个线程,count 表示线程的总数,urls 代表需要下载url列表,datas代表要返回的数据列表。 # update 表示每下载一个成功就进行的回调函数。 super().__init__() self.urls=urls self.datas=datas self.index=index self.count=count self.update=update def download(self,url): HEADERS = {'User-Agent': random.choice(agents)} # print(url) err=0 while(err<4): full_url = url.replace('_index_',str(err)) # 如果下载失败,则切换服务器 header = ur.Request(full_url,headers=HEADERS) try: data = ur.urlopen(header).read() except: err+=1 else: return data # raise Exception("Bad network link.") print("Bad network link.",url) def run(self): for i,url in enumerate(self.urls): if i%self.count != self.index: continue self.datas[i]=self.download(url) if mutex.acquire(): self.update() mutex.release()
读取shapefile文件 读取shapefile文件的方式比较多,这里使用了两种方式,一种是使用shapefile模块读取,一种是使用gdal读取
shapefile读取 1 2 3 4 shp = shapefile.Reader(shapefilename) border_shapes = shp.shapes() area_infors = shp.records() return border_shapes, area_infors
gdal读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 def getShpFileByGDAL(shapefile, query_name): ''' 结果保存方式为b,c,h,w b为有多少个区域 c为区域轮廓个数,例如c=1,表示只有一个外轮廓,无空洞;c=2,表示下标为0的位置为外轮廓,下标为1的位置为外轮廓,除了0以外都是外轮廓 h为轮廓点的个数 w为2,表示经纬度 ''' # 支持中文路径 gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES") # 支持中文编码 gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8") # 注册所有的驱动 ogr.RegisterAll() # 打开数据 print('打开', shapefile) ds = ogr.Open(shapefile, 0) if ds == None: return "打开文件失败!" # 获取数据源中的图层个数,shp数据图层只有一个,gdb、dxf会有多个 iLayerCount = ds.GetLayerCount() print("\t图层个数 = ", iLayerCount) # 获取第一个图层 result = [] bbox = [10000, 10000, -10000, -10000] for layerIdx in range(iLayerCount): oLayer = ds.GetLayerByIndex(layerIdx) if oLayer == None: return "获取图层失败!" # 对图层进行初始化 oLayer.ResetReading() # 输出图层中的要素个数 num = oLayer.GetFeatureCount(0) print("\t要素个数 = ", num) result_list = [] # 获取要素 for i in range(0, num): ofeature = oLayer.GetFeature(i) # id = ofeature.GetFieldAsString("id") name = ofeature.GetFieldAsString('name') if query_name in name: count = ofeature.GetGeometryRef().GetGeometryCount() for gemIdx in range(count): gemo = ofeature.GetGeometryRef().GetGeometryRef(gemIdx) gemo_data = gemo.ExportToJson() gemo_json = json.loads(gemo_data) gemo_np = np.array(gemo_json['coordinates']) print(name, gemo_np.shape) min_x = min(np.min(gemo_np[..., 0]), bbox[0]) max_x = max(np.max(gemo_np[..., 0]), bbox[2]) min_y = min(np.min(gemo_np[..., 1]), bbox[1]) max_y = max(np.max(gemo_np[..., 1]), bbox[3]) bbox = [min_x, min_y, max_x, max_y] result.append(gemo_np) # print(dir(ofeature.GetGeometryRef()), # ofeature.GetGeometryRef().Boundary()) ds.Destroy() del ds return result, bbox
裁剪 最后的裁剪使用的是用一张mask对图片做掩码裁剪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 制作掩码 mask = np.zeros((height, width), np.uint8) for points_list in mercator_list: num = len(points_list) for idx in range(num): fill_value = 0 if idx == 0: fill_value = 1 else: fill_value = 0 mask = cv2.fillPoly(mask, [np.array(points_list[idx])], fill_value)
效果
完整代码
参考博客 https://blog.csdn.net/mygisforum/article/details/22997879 https://www.cnblogs.com/modyuan/p/12293843.html https://blog.csdn.net/u012866178/article/details/83188581 https://www.cnblogs.com/jyughynj/p/pythonGetMapTile.html https://www.cnblogs.com/xinwenpeng/p/9818953.html