Google 谷歌地图(高德地图)瓦片下载/缓存,使用Python实现、arcgis.js(三)

第一期 Google 谷歌地图(高德地图)瓦片下载/缓存,使用Python实现(一)_谷歌瓦片地图-CSDN博客

第二期 Google 谷歌地图(高德地图)瓦片下载/缓存,使用Python实现(二)_谷歌瓦片失效-CSDN博客

第四期 Google 谷歌地图(高德地图)瓦片下载/缓存,使用Python实现、自定义上传地图瓦片资源(四)-CSDN博客

后续可能还会出第四期,请持续关注。

1、项目流程

上期解释了地图瓦片的大致规则并且使用python实现了请求瓦片的工具。那么现在我们来说说结合前端显示具体实现流程。先请看下面的流程图:

地图请求流程图
地图请求流程图

地图参数设置流程图
地图参数设置流程图

2、使用技术与实现

2.1 技术选型:

前端使用:原生HTML,CSS,JavaScript,jQuery 3.6.0 地图控件:arcgis.js 4.22版本

后端使用 python 3.9.4 ,flask 3.0.3,requests 2.31.0 ,Flask-SocketIO 5.3.6

2.2 界面展示

界面图片

2.3 核心代码

前端html代码:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>地图缓存</title>
    <style>
        html,
        body,
        #viewDiv {
            padding: 0;
            margin: 0;
            height: 600px;
            width: 600px;
        }
    </style>

</head>

<body>
    <div id="viewDiv"></div>
    <div id="opt">
        <div id="mapURLDiv">
            <label for="mapUrl">地图源:</label>
            <select id="mapUrl" name="mapUrl">
                <option value="googleMap" >谷歌地图</option>
                <option value="AMap" selected>高德地图</option>
            </select>
        </div>
        <div id="mapURLChooseDiv">
            <label for="mapType">地图类型:</label>
            <select id="mapType" name="mapType">
                <option value="1" >卫星图</option>
                <option value="2" selected>路线图</option>
            </select>
            <br>
            <label for="parameterVal">更多参数:</label>
            <textarea id="parameterVal" name="parameterVal" rows="4" cols="50">
            </textarea>
            <br>
        </div>
        <input id="setMapProp" type="button" value="提交">
    </div>
    <!-- 导入arcgis -->
    <link rel="stylesheet" href="https://js.arcgis.com/4.22/esri/themes/light/main.css" />
    <script src="https://js.arcgis.com/4.22/"></script>
    <!-- 引入jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="/static/index.js"></script>
</body>

</html>

JavaScript 核心代码

//使用arcgis.js
require([
        "esri/Map",
        "esri/views/SceneView",
        "esri/views/MapView",
        "esri/layers/TileLayer",
        "esri/layers/BaseTileLayer",
        "esri/Color",
        "esri/request",
    ],
    (Map, SceneView, MapView,TileLayer,BaseTileLayer, Color, esriRequest) => {

    // *******************************************************
    // 自定义图层类代码
    // 创建一个BaseTileLayer的子类
    // *******************************************************

    const TintLayer = BaseTileLayer.createSubclass({
      properties: {
        urlTemplate: null,
        tint: {
          value: null,
          type: Color,
        },
      },

      //为LayerView提供的给定级别、行和列生成tile url
      getTileUrl: function (level, row, col) {
        return this.urlTemplate.replace("{z}", level).replace("{x}", col).replace("{y}", row);
      },

      // 此方法获取指定级别和大小的瓦片
      // 重写此方法以处理从服务器返回的数据
      fetchTile: function (level, row, col, options) {
        // 调用getTileUrl()方法来构造tiles的URL
        // 对于LayerView提供的给定级别、行和列
        const url = this.getTileUrl(level, row, col);
        // 基于生成的url请求平铺
        // the signal option 确保废弃的请求被中止
        return esriRequest(url, {
          responseType: "image",
          signal: options && options.signal,
        }).then(
          function (response) {
            // esri请求解析成功时
            // 从响应中获取图像
            const image = response.data;
            const width = this.tileInfo.size[0];
            const height = this.tileInfo.size[0];

            // 使用二维渲染上下文创建画布
            const canvas = document.createElement("canvas");
            const context = canvas.getContext("2d");
            canvas.width = width;
            canvas.height = height;

            // 将应用程序提供的色调应用于画布
            if (this.tint) {
              // 获取一个rgba形式的CSS颜色字符串,表示tint color实例.
              context.fillStyle = this.tint.toCss();
              context.fillRect(0, 0, width, height);

              // 在画布和steman平铺之间应用“差异”混合操作。差值混合操作从顶层(瓦片)中减去底层(画布),或者反过来总是得到一个正值
              context.globalCompositeOperation = "difference";
            }

            //将混合图像绘制到画布上。
            context.drawImage(image, 0, 0, width, height);

            return canvas;
          }.bind(this)
        );
      },
    });

    let centerPoint = [116.404177,39.909651]; //大概是北京天安门的经纬度

    // 创建一个瓦片图层实例
    myTileLayer = new TintLayer({
//      urlTemplate: "http://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
      urlTemplate: "/mapImg/{z}/{x}/{y}",
      title: "高德",
    });


    //底层
    map = new Map({
            layers : [myTileLayer]
    });
    //3D容器
     view = new MapView({
        container: "viewDiv", //与html元素绑定
        map: map, //设置底层
        zoom: 15, //初始地图距离大小
        center: centerPoint //中心点(可以取小数) ,这里设置的北京
    });
    // 刷新瓦片
    refreshMapTileFunction = function refreshMapTile(){
        map.remove(myTileLayer)
        myTileLayer = new TintLayer({
          urlTemplate: "/mapImg/{z}/{x}/{y}",
          title: "高德",
        });
        map.add(myTileLayer)
    }
})

python 瓦片请求接口代码

@app.route("/mapImg/<z>/<x>/<y>")
def mapImg(x, y, z):
    imgTypes = ["jpeg", "png", "jpg"]
    mapURLValue = setting.global_mapURLValue
    mapURLStyle = setting.global_mapURLStyle
    LOCAL_IMAGE_CACHE_DIR = "./map/" + mapURLValue + "/" + mapURLStyle + "/" + str(z) + "/" + str(x) + "/"
    # local_image_path = os.path.join(LOCAL_IMAGE_CACHE_DIR, str(y) + ".jpeg")

    for imgType in imgTypes:
        file_path = os.path.join(LOCAL_IMAGE_CACHE_DIR, str(y) + "." + imgType)
        if os.path.isfile(file_path) and os.path.exists(file_path):
            # 无论图片是从缓存读取还是刚下载完,都返回给客户端
            with open(file_path, 'rb') as img_file:
                img_data = img_file.read()

            # 设置正确的MIME类型
            mime_type = 'image/' + imgType  # 根据实际情况设置正确的图片MIME类型
            response = Response(img_data, mimetype=mime_type)
            # 返回图片数据
            return response

    savePath = None
    urlStr = None
    contentType = None

    mapUrl = DataCheckUtils.dataCheckNone(setting.global_mapURLValue)
    httpParameter = DataCheckUtils.dataCheckNone(setting.global_mapURLHttpParameter)
    httpProxies = setting.global_mapURLProxies
    httpHeaders = setting.global_mapURLHeaders
    # 下载地图前,参考用户设置的值
    if mapUrl.__eq__("googleMap"):
        lyrs = DataCheckUtils.dataCheckNone(setting.global_mapURLStyle,"s")
        hl = DataCheckUtils.dataCheckNone(setting.global_mapURLhl,"zh-CN")
        gl = DataCheckUtils.dataCheckNone(setting.global_mapURLgl,"cn")
        # httpParameter = None
        headers = httpHeaders
        proxies = httpProxies

        savePathGoogle, urlStrGoogle, contentTypeGoogle = MapDownloadUtils.googleMapDownload(x, y, z,lyrs=lyrs, hl=hl, gl=gl, httpParameter=httpParameter, headers=headers, proxies=proxies)
        savePath = savePathGoogle
        urlStr = urlStrGoogle
        contentType = contentTypeGoogle
    elif mapUrl.__eq__("AMap"):
        style = DataCheckUtils.dataCheckNone(setting.global_mapURLStyle,"6")
        headers = httpProxies
        proxies = httpHeaders
        savePathAMap, urlStrAMap, contentTypeAMap = MapDownloadUtils.AMapDownload(x, y, z,style=style, httpParameter=httpParameter, headers=headers, proxies=proxies)
        savePath = savePathAMap
        urlStr = urlStrAMap
        contentType = contentTypeAMap

    # 无论图片是从缓存读取还是刚下载完,都返回给客户端
    with open(savePath, 'rb') as img_file:
        img_data = img_file.read()

    # 设置正确的MIME类型
    mime_type = contentType  # 根据实际情况设置正确的图片MIME类型
    response = Response(img_data, mimetype=mime_type)

    # 返回图片数据
    return response

完整代码:

GitHub:

Crazy-GrowUp/flask-mapimg at v1.0.0 (github.com)

https://github.com/Crazy-GrowUp/flask-mapimg/tree/v1.0.0

Gitee:

flask-mapimg: 可自动缓存地图瓦片,保证项目下次加载更迅速 后端使用python flask 前端地图使用 arcgis.js - Gitee.com

https://gitee.com/jack_of_disco/flask-mapimg/tree/v1.0.0/

优化空间

1、地图中心位置可以提供

2、地图瓦片缓存可以更自动更深度下载

​ 如请求瓦片地址是 z/x/y ==> 3/5/7 ,

那么我们可以提前向深层下载(z暂时理解为深度):

a) z+1/x * 2/y * 2 ==> 4/10/14

b) z+1/x * 2+1/y * 2 ==> 4/11/14

c) z+1/x * 2/y * 2+1 ==> 4/10/15

d) z+1/x * 2+1/y * 2+1 ==> 4/11/15

然后再深层一点 ,可以设置默认深入几层下载,这样算是提前预判用户的下一步操作吧,也可以使得地图加载的更快

3、地图资源所占磁盘大小空间

如果要下载全球的地图瓦片资源需要的空间大概是6.59TB,那服务器无限制的缓存,早晚会把硬盘填满。那么可以再写一个方法,每次下载资源前或者每天一次检查资源的大小,如果超过了就删除很久没用到资源,每次删除100MB或者自定义。所以这里还要记录瓦片的最近一次的使用时间(建议持久化)。
地图瓦片大小

如果大家还有什么好的建议,可以打在评论区,欢迎大家来交流,后续可能还会更新此系列内容,也请大家多多关注、点赞、收藏。

3、参考链接

1、欢迎来到 Flask 的世界 — Flask中文文档(3.0.x) (dormousehole.readthedocs.io)

https://dormousehole.readthedocs.io/en/latest/

2、arcgis for javascript TileLayer 自定义高德地图图层_高德tilelayer-CSDN博客

https://blog.csdn.net/jieyucx/article/details/131083769

3、6.59TB 全球地名路网透明标签瓦片地图 (baidu.com)

https://baijiahao.baidu.com/s?id=1744453723688131827&wfr=spider&for=pc

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值