Vue2基于天地图实现离线地图

3,185 阅读4分钟

应业务需求变更,需要在内网使用地图并实现打点等功能,由于无法直接使用外网天地图资源,基于项目现有代码做调整。需要将天地图瓦片资源下载到内网服务器,并且将天地图JavaScript API指向到内网服务器,实现在内网使用天地图相关功能。此文章仅作为前端开发、调试过程记录。

1.下载对应天地图api等资源文件并引入

将以上三个文件放置在项目对应的public文件夹下,并对api文件内容做相应的修改:

  1. 定义天地图瓦片资源地址
// 为了方便本地开发、调试,前期自行搭建瓦片资源服务(文章后续有记录瓦片下载及服务搭建)。
window.TMAP_URL = 'http://localhost:3000/tiles' // 此处地址暂时使用本地服务地址,后续修改为内网资源服务地址
  1. 格式化后约57行处,修改瓦片数据请求路径
R: T.Protocol.value + "api.tianditu." + T.Domain + "/apiserver/ajaxproxy?proxyReqUrl=",
r: function () {
  return window.TMAP_URL + '/vec_c/'
},
T: function () {
  return window.TMAP_URL + '/cva_c/'
},
t: function () {
  return window.TMAP_URL + '/vec_w/'
},
Y: function () {
  return window.TMAP_URL + '/cva_w/'
},
U: function () {
  return window.TMAP_URL + '/img_c/'
},
u: function () {
  return window.TMAP_URL + '/cia_c/'
},
I: function () {
  return window.TMAP_URL + '/img_w/'
},
i: function () {
  return window.TMAP_URL + '/cia_w/'
},
O: function () {
  return window.TMAP_URL + '/ter_c/'
},
o: function () {
  return window.TMAP_URL + '/cta_c/'
},
P: function () {
  return window.TMAP_URL + '/ter_w/'
},
p: function () {
  return window.TMAP_URL + '/cta_w/'
},
A: ["./components.js"], // 指向本地文件
a: ["./tianditu4.0.css"],
TMAP_DEFAULT_MAPTYPES: [],
  1. 修改请求瓦片入参格式(根据与后端协商后修改,此处为了前端本地调试方便简化写法)
// 格式化后约3165行
T.Map.M({
  LW: function (t) {
    var i = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
    i.getTileUrl = function (t) {
      return 0 == T.gq.EW
        ? T.w.t() + t.x + "/" + t.y + "/" + t.z
        : T.w.r() + t.x + "/" + t.y + "/" + t.z
    }
    var n = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
      ; (n.getTileUrl = function (t) {
        return 0 == T.gq.EW
          ? T.w.Y() + t.x + "/" + t.y + "/" + t.z
          : T.w.T() + t.x + "/" + t.y + "/" + t.z
      }),
        (TMAP_NORMAL_MAP = new T.MapType([i, n], "TMAP_NORMAL_MAP", { a: 1 }))
    var e = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
    e.getTileUrl = function (t) {
      return 0 == T.gq.EW
        ? T.w.I() + t.x + "/" + t.y + "/" + t.z
        : T.w.U() + t.x + "/" + t.y + "/" + t.z
    }
    var o = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
      ; (o.getTileUrl = function (t) {
        return 0 == T.gq.EW
          ? T.w.i() + t.x + "/" + t.y + "/" + t.z
          : T.w.u() + t.x + "/" + t.y + "/" + t.z
      }),
        (TMAP_SATELLITE_MAP = new T.MapType([e], "TMAP_SATELLITE_MAP")),
        (TMAP_HYBRID_MAP = new T.MapType([e, o], "TMAP_HYBRID_MAP"))
    var s = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
    s.getTileUrl = function (t) {
      return 0 == T.gq.EW
        ? T.w.P() + t.x + "/" + t.y + "/" + t.z
        : T.w.O() + t.x + "/" + t.y + "/" + t.z
    }
    var r = new T.TileLayer("", { minZoom: 1, maxZoom: 18 })
      ; (r.getTileUrl = function (t) {
        return 0 == T.gq.EW
          ? T.w.p() + t.x + "/" + t.y + "/" + t.z
          : T.w.o() + t.x + "/" + t.y + "/" + t.z
      }),
        (TMAP_TERRAIN_MAP = new T.MapType([s], "TMAP_TERRAIN_MAP")),
        (TMAP_TERRAIN_HYBRID_MAP = new T.MapType([s, r], "TMAP_TERRAIN_HYBRID_MAP"))
  },
}),

修改前后对比:

1.jpg

在index.html文件中引入

<script src="<%= BASE_URL %>api.js" type="text/javascript"></script>

2.下载天地图瓦片数据

为了方便前端开发、调试,使用地图瓦片下载工具下载部分瓦片数据作为示例。本次使用QGIS软件进行瓦片数据下载,QGIS软件打开天地图地图数据需要下载插件并配置。

  1. 安装TianDiTu Tools插件

2.jpg

3.jpg

  1. 配置插件 - 点击设置,在设置弹窗内添加已申请的天地图key

4.jpg

image-20240731112007586.png

  1. 选择需要下载的图层

image-20240731140243755.png

  1. 打开工具箱,选择“生成XYZ瓦片(目录)”

    依次设置需要下载的范围及缩放级别,并选择需要输出的指定文件夹,点击运行等待下载完成。

5.jpg

image-20240731140548831.png

天地图接口请求的地图瓦片和标注瓦片是不同接口,重复步骤3、4依次下载:cia_w 影像地图标注、cva_w 矢量地图标注、img_w 影像地图、vec_w 矢量地图

3. 基于Express框架搭建服务器并访问瓦片数据

  1. 安装Express框架
npm init -y  # 初始化项目  
npm install express --save  # 安装Express
  1. 创建Express服务
const express = require("express")
const app = express()
const path = require("path")
const PORT = 3000

// 设置静态文件目录
// 假设瓦片数据存储在项目的`tiles`目录下
app.use("/tiles", express.static("tiles"))

// 假设瓦片存储在'tiles'目录下,目录结构为'tiles/{n}/{z}/{x}/{y}'
// 接口参数同api.js中配置
app.get("/tiles/:n/:z/:x/:y", (req, res) => {
  const n = req.params.n // 图层类型
  const z = req.params.z // 行
  const x = req.params.x // 列-图片名称
  const y = req.params.y // 缩放等级
  // 构造瓦片文件的完整路径
  const tilePath = path.join(__dirname, "tiles", n, y, z, `${x}.png`)

  // 检查文件是否存在
  app.use(express.static("tiles"))
  res.sendFile(tilePath, (err) => {
    if (err) {
      if (err.code === "ENOENT") {
        // 如果文件不存在,返回404
        res.status(404).send(tilePath + " not found")
      } else {
        // 其他错误
        res.status(500).send("服务器内部错误")
      }
    }
  })
})

// 启动服务器
app.listen(PORT, () => {
  console.log(`服务器正在监听端口 ${PORT}`)
})
  1. 将瓦片数据放入项目tiles文件夹下,运行服务器并访问瓦片数据
node server.js
http://localhost:3000/tiles/img_w/1/1/1.png // 访问示例

image-20240731144308991.png

4. 前端渲染地图并实现打点功能

<template>
  <div
    id="mapDiv"
    style="
      height: 800px;
      width: 800px;
      border: 1px solid black;
      border-radius: 10px;
      user-select: none;
    "
  />
</template>

<script>
export default {
  name: "largedatascreen",
  data() {
    return {
      map: null,
      autoStationData: [
        {
          id: 1,
          number: "001",
          name: "xxxxxxxxxx",
          city: "xx",
          lng: "81.2293213147951",
          lat: "40.5482562913776",
          stationType: "xx",
          stationLevel: "xx",
          creatime: "2022-09-21",
        },
        {
          id: 2,
          number: "002",
          name: "yyyyyyyyyyyyyyy",
          city: "yy",
          lng: "86.0602017389291",
          lat: "44.2363615256064",
          stationType: "yy",
          stationLevel: "yy",
          creatime: "2023-09-06",
        },
        {
          id: 3,
          number: "003",
          name: "zzzzzzzzzzzzzzzzzzz",
          city: "zz",
          lng: "79.0345031681691",
          lat: "39.8619961949699",
          stationType: "zz",
          stationLevel: "zz",
          creatime: "2022-07-21",
        },
      ],
    };
  },
  mounted() {
    this.initMap();
  },
  methods: {
    // 初始化地图
    initMap() {
      this.map = new T.Map("mapDiv", {
        center: new T.LngLat(87.6168, 43.8256), // 地图中心点
        minZoom: 1, // 最小缩放级别
        maxZoom: 6, // 最大缩放级别
        zoom: 5, // 初始缩放级别
      });
      this.map.setMapType(TMAP_HYBRID_MAP);
      this.autoStationData.map((i) => {
        this.onLoad(i);
      });
    },
    onLoad(obj) {
      let position = new T.LngLat(obj.lng, obj.lat);
      let icon = new T.Icon({
        iconUrl: require("@/assets/bigScreen/auto.svg"), //请求图标图片的URL
        iconSize: new T.Point(30, 30), //图标可视区域的大小。
        iconAnchor: new T.Point(30, 30), //图标的定位锚点
      });
      let marker = new T.Marker(position, {
        icon: icon,
      });
      this.map.addOverLay(marker);
      let infoWin = new T.InfoWindow();
      let sContent = `
          <div style="margin-top:5px;font-size:1.1rem;">
            详情:<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;名称:${obj.name}<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;经度:${obj.lng}<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;纬度:${obj.lat}<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;类型:${obj.stationType}<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;级别:${obj.stationLevel}<br/>
            &nbsp;&nbsp;&nbsp;&nbsp;时间:${obj.creatime}<br/>
          </div>
        `;
      infoWin.setContent(sContent);
      marker.addEventListener("click", (e) => {
        infoWin.setOffset(new T.Point(-15, 10));
        infoWin.setLngLat(e.lnglat);
        marker.openInfoWindow(infoWin);
      });
    },
  },
};
</script>

效果图:

image-20240731144119219.png

image-20240731144150444.png

地图服务代码及瓦片数据网盘地址:pan.baidu.com/s/1NgHM73VE…

瓦片数据层级只下载到6级,只做调试使用,数据层级没有那么高。