地图下载核心,如何使用Python下载高德地图瓦片并拼接成大图?

WMTS地图瓦片

  WMTS (Web Map Tile Service) 是一种提供基于瓦片的地图数据服务的网络协议。它通过将地图数据分割成相同大小的瓦片,并预先生成这些瓦片以供客户端请求,从而提高了地图数据呈现速度和效率。WMTS使用RESTful Web服务和基于HTTP/HTTPS协议进行数据交换,可通过Web浏览器、GIS软件和其他客户端应用程序访问和使用。WMTS广泛用于Web地图、桌面应用程序、移动应用程序等。 它是Open Geospatial Consortium(OGC)发布的开放标准之一。

Requst请求

  下面我们对高德地图瓦片进行分析,瓦片是通过X,Y,Z形式的行列号来请求一张图片的方式获取瓦片数据的,请求返回的数据为一个进制的图片数据,所以我们要建立一个图片请求,并将请求到的图片返回。

在这里插入图片描述

request请求并返回瓦片代码:

def request_tile(self, tile_url):
    headers = {
        "Connection": "keep-alive",
        "User-Agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
        "Cookie":
        "HWWAFSESTIME=1681889500446; HWWAFSESID=53a7d799024cfb3f627"
    }

    blank_img = Image.new(mode="RGB", size=(256, 256))
    try:
        r = requests.get(url=tile_url, headers=headers, timeout=20)
        if r.status_code == 200:
        	#将请求的内容转为图片
            img = Image.open(BytesIO(r.content))
            return img
        else:
            return blank_img
    except:
        print("failed")
        return blank_img

影像瓦片与道路瓦片合并

  在我们使用的示例下载地图数据(高德地图)中,影像数据包括:影像地图、标签地图,两者结合才是我们所看到的影像地图,标签地图与影像地图的请求方式一样,为一张256*256的透明背景PNG。

在这里插入图片描述

下载影像地图的时候,我们要将标签地图叠加在影像地图之上,下面代码中vec_tile_url为影像地图地址,cta_tile_url 为标签地图地址,xyz是瓦片的行列号,我们根据传入的行列号与地图级别对瓦片进行下载并重叠。

def get_tile(self, col, row, zoom):
    vec_tile_url = "https://webst01.is.autonavi.com/appmaptile?style=6&x={}&y={}&z={}".format(
        col, row, zoom)
    cta_tile_url = "https://wprd03.is.autonavi.com/appmaptile?x={}&y={}&z={}&lang=zh_cn&size=1&scl=1&style=8&ltype=11".format(
        col, row, zoom)

    vec_img = self.request_tile(vec_tile_url)
    cta_img = self.request_tile(cta_tile_url)

    new_vec_img = Image.new(mode="RGBA", size=(256, 256))
    new_vec_img.paste(vec_img, box=(0, 0))

    new_cta_img = Image.new(mode="RGBA", size=(256, 256))
    new_cta_img.paste(cta_img, box=(0, 0))

    merge_img = Image.alpha_composite(new_vec_img, new_cta_img)
    return merge_img

行列号的计算

  在下面计算瓦片行列号的时候我们传入的参数为经度(l)纬度(b)级别(zoom),由于我们要下载的瓦片数据为墨卡托投影坐标,所以在计算之前我们要先将经纬度坐标通过lonLat2Mercator方法转换为默卡托投影坐标,grithHalf:20037508.3427892(地球周长的一半)。

# 获取杆塔编号
def getTileNum(self, l, b, zoom):
    num = int(math.pow(2, zoom))
    cellsize = self.grithHalf * 2 / num
    np = self.lonLat2Mercator(l, b)
    x = math.floor((np.x() + self.grithHalf) / cellsize)
    x = max(0, x)
    x = min(x, int(num - 1))
    y = math.floor((self.grithHalf - np.y()) / cellsize)
    y = max(0, y)
    y = min(y, int(num - 1))
    return x, y
#经纬度转墨卡托投影
def lonLat2Mercator(self, l, b):
    if (b > 85.0511287798066):
        b = 85.0511287798066
    if (b < -85.0511287798066):
        b = -85.0511287798066

    x = l * self.grithHalf / 180
    y = math.log(math.tan((90 + b) * math.pi / 360)) / (math.pi / 180)
    y = y * self.grithHalf / 180
    return QPointF(x, y)

根据给定坐标范围下载地图

  在下载地图时,我们要先给定一个坐标范围,这个范围为地图的左下角坐标及右上角坐标,如:[99,20,105,40];然后通过范围计算出我们瓦片在X方向上的最小瓦片编号与最大瓦片编号,以及在Y方向上的最小瓦片编号与最大瓦片编号,这样我们就可以得到整个范围的瓦片编号,然后对瓦片进行请求合并导出。

在这里插入图片描述

  由于瓦片编号在X方向是由左向右递增,在Y方向上是由上到下递增,所以我们在取瓦片X与Y的最大最小编号时要以左上角和右下角的坐标来进行瓦片编号计算。

def download_img(self,minx,miny,maxx,maxy,zoom):
	#左上角
    nx1, ny1 = self.getTileNum(minx, maxy, zoom)
    #右下角
    nx2, ny2 = self.getTileNum(maxx, miny, zoom)

    print(nx1, ny1, zoom)
    print(nx2, ny2, zoom)

    tile_w = int(qAbs(nx2 - nx1) * 256 + 256)
    tile_h = int(qAbs(ny2 - ny1) * 256 + 256)
    new_img = Image.new(mode="RGBA", size=(tile_w, tile_h))

    #瓦片总数
    total_count = (nx2 - nx1 + 1) * (ny2 - ny1 + 1)
    current_count = 0
    i = 0
    for col in range(nx1, nx2 + 1):
        j = 0
        for row in range(ny1, ny2 + 1):
            t_img = self.get_tile(col, row, zoom)
            new_img.paste(t_img, box=(i * 256, j * 256))
            current_count += 1
            progress = qAbs(current_count / total_count) * 100
            print("\r Downloading File Map [{:.0f}%] ".format(progress),
                    "\r",
                    end='')
            j += 1
        i += 1

    return new_img

导出图片

mapImage = download_img(103.563246,29.632548,103.686545,29.663265,16)
mapImage.save("D:\\crop_result.png", "PNG")

结果图片展示

在这里插入图片描述

完整代码:

import math
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import requests
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import numpy as np

class MapDownloader(object):
    def __init__(self):
        self.grithHalf = 20037508.3427892

    #经纬度转墨卡托投影
    def lonLat2Mercator(self, l, b):
        if (b > 85.0511287798066):
            b = 85.0511287798066
        if (b < -85.0511287798066):
            b = -85.0511287798066

        x = l * self.grithHalf / 180
        y = math.log(math.tan((90 + b) * math.pi / 360)) / (math.pi / 180)
        y = y * self.grithHalf / 180
        return QPointF(x, y)

    # 获取杆塔编号
    def getTileNum(self, l, b, zoom):
        num = int(math.pow(2, zoom))
        cellsize = self.grithHalf * 2 / num
        np = self.lonLat2Mercator(l, b)
        x = math.floor((np.x() + self.grithHalf) / cellsize)
        x = max(0, x)
        x = min(x, int(num - 1))
        y = math.floor((self.grithHalf - np.y()) / cellsize)
        y = max(0, y)
        y = min(y, int(num - 1))
        return x, y
    
    def download_img(self,minx,miny,maxx,maxy,zoom):
        nx1, ny1 = self.getTileNum(minx, maxy, zoom)
        nx2, ny2 = self.getTileNum(maxx, miny, zoom)

        print(nx1, ny1, zoom)
        print(nx2, ny2, zoom)

        tile_w = int(qAbs(nx2 - nx1) * 256 + 256)
        tile_h = int(qAbs(ny2 - ny1) * 256 + 256)
        new_img = Image.new(mode="RGBA", size=(tile_w, tile_h))

        #瓦片总数
        total_count = (nx2 - nx1 + 1) * (ny2 - ny1 + 1)
        current_count = 0
        i = 0
        for col in range(nx1, nx2 + 1):
            j = 0
            for row in range(ny1, ny2 + 1):
                t_img = self.get_tile(col, row, zoom)
                new_img.paste(t_img, box=(i * 256, j * 256))
                current_count += 1
                progress = qAbs(current_count / total_count) * 100
                print("\r Downloading File Map [{:.0f}%] ".format(progress),
                      "\r",
                      end='')
                j += 1
            i += 1

        return new_img

    def get_tile(self, col, row, zoom):
        vec_tile_url = "https://webst01.is.autonavi.com/appmaptile?style=6&x={}&y={}&z={}".format(
            col, row, zoom)
        cta_tile_url = "https://wprd03.is.autonavi.com/appmaptile?x={}&y={}&z={}&lang=zh_cn&size=1&scl=1&style=8&ltype=11".format(
            col, row, zoom)

        vec_img = self.request_tile(vec_tile_url)
        cta_img = self.request_tile(cta_tile_url)

        new_vec_img = Image.new(mode="RGBA", size=(256, 256))
        new_vec_img.paste(vec_img, box=(0, 0))

        new_cta_img = Image.new(mode="RGBA", size=(256, 256))
        new_cta_img.paste(cta_img, box=(0, 0))

        merge_img = Image.alpha_composite(new_vec_img, new_cta_img)
        return merge_img

    def request_tile(self, tile_url):
        headers = {
            "Connection": "keep-alive",
            "User-Agent":
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
            "Cookie":
            "HWWAFSESTIME=1681889500446; HWWAFSESID=53a7d799024cfb3f627"
        }

        blank_img = Image.new(mode="RGB", size=(256, 256))
        try:
            r = requests.get(url=tile_url, headers=headers, timeout=20)
            # print(r.status_code)
            if r.status_code == 200:
                img = Image.open(BytesIO(r.content))
                return img
            else:
                return blank_img
        except:
            print("failed")
            return blank_img
        
mapDownloader = MapDownloader()
mapImage = mapDownloader.download_img(103.563246,29.632548,103.686545,29.663265,16)
mapImage.save("D:\\crop_result.png", "PNG")

那么,这里一个可以按照地图范围与级别下载地图的方法就做好了;如果大家觉得我的方法还有用的话,也请大家能够点赞鼓励一下,您的鼓励是我分享的动力,谢谢各位!

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用Python爬取高德地图的XYZ瓦片,可以使用以下步骤进行操作: 1. 导入必要的库和模块,包括`requests`用于发送HTTP请求,`Pillow`用于处理图像数据。 2. 构建请求URL。高德地图的XYZ瓦片URL格式为:`http://wprd03.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}`,其中`{x}`表示瓦片的x坐标,`{y}`表示y坐标,`{z}`表示缩放级别。 3. 使用循环遍历需要下载瓦片的x和y坐标,并发送GET请求获取瓦片数据。可以根据具体情况设定循环范围和缩放级别。 4. 将获取到的瓦片数据保存为图片文件。可以使用`Pillow`库的`Image`模块将数据解码为图片,然后保存到本地文件夹中。 下面是一个简单的Python代码示例: ```python import requests from PIL import Image # 构建请求URL url_template = "http://wprd03.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}" # 设置需要下载瓦片的范围和缩放级别 min_x = 0 max_x = 10 min_y = 0 max_y = 10 zoom_level = 10 # 循环遍历需要下载瓦片 for x in range(min_x, max_x+1): for y in range(min_y, max_y+1): # 构建请求URL url = url_template.format(x=x, y=y, z=zoom_level) # 发送GET请求获取瓦片数据 response = requests.get(url) # 将获取到的瓦片数据保存为图片文件 file_name = f"tile_{zoom_level}_{x}_{y}.png" with open(file_name, "wb") as f: f.write(response.content) print("瓦片下载完成。") ``` 上述代码将会下载高德地图的XYZ瓦片,保存为相应的图片文件,图片文件的命名格式为`tile_{缩放级别}_{x坐标}_{y坐标}.png`。根据实际需求,可以调整代码中的循环范围、URL模板、保存文件的路径等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值