vue+leaflet实现天地图离线访问,并完成飞线效果。

引言

有时候项目需要在内网中访问,这时候就需要离线地图。本文介绍了使用node下载天地图瓦片地图并在本地使用vue进行访问

技术选型

前端技术栈

  • Leaflet.js:轻量级地图库,提供基础地图功能
  • Vite:现代前端构建工具,提供快速的开发体验
  • 自定义插件:基于 Leaflet 开发的自定义图层和交互组件

后端技术栈

  • Node.js:高性能服务端运行环境
  • Axios:HTTP 客户端,用于下载地图瓦片
  • fs-extra:增强版文件系统操作库

核心实现

1. 地图瓦片下载器

js 复制代码
//创建项目
npm init -y
npm install
const 复制代码
const app = express();
const path = require("path");
const axios = require("axios");
const fs = require("fs-extra");
const port = 4000;

// 天地图API密钥
const TIANDITU_KEY = "替换为你的Key";

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

// 根路由
app.get("/", (req, res) => {
  res.send("欢迎访问地图服务");
});

// 瓦片请求处理
// 瓦片存储结构为'tiles/{n}/{z}/{x}/{y}.png'
app.get("/tile/: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, z, x, `${y}.png`);

  // 发送文件
  res.sendFile(tilePath, (err) => {
    if (err) {
      if (err.code === "ENOENT") {
        // 如果文件不存在,返回404
        res.status(404).send(`瓦片不存在: ${tilePath}`);
      } else {
        // 其他错误
        res.status(500).send("服务器内部错误");
      }
    }
  });
});

// 获取随机服务器编号(0-7)
function getRandomServer() {
  return Math.floor(Math.random() * 8);
}

// 天地图代理服务 - 从天地图服务器获取瓦片并缓存到本地
app.get("/tianditu/:type/:z/:x/:y", async (req, res) => {
  const { type, z, x, y } = req.params;

  // 确保type是有效的天地图类型
  const validTypes = ["vec_c", "cva_c", "img_c", "cia_c", "ter_c", "cta_c"];
  if (!validTypes.includes(type)) {
    return res.status(400).send(`无效的天地图类型: ${type}。有效类型: ${validTypes.join(", ")}`);
  }

  // 本地缓存路径
  const cachePath = path.join(__dirname, "tiles", "tianditu", type, z, x, `${y}.png`);

  try {
    // 检查本地是否已缓存
    if (await fs.pathExists(cachePath)) {
      return res.sendFile(cachePath);
    }

    // 随机选择一个服务器
    const serverNum = getRandomServer();

    // 构造天地图URL - 修改了URL格式和参数顺序
    const url = `https://t${serverNum}.tianditu.gov.cn/${type}/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${
      type.split("_")[0]
    }&STYLE=default&TILEMATRIXSET=c&FORMAT=tiles&TILECOL=${x}&TILEROW=${y}&TILEMATRIX=${z}&tk=${TIANDITU_KEY}`;

    // 请求天地图服务器
    const response = await axios.get(url, {
      responseType: "arraybuffer",
      headers: {
        Referer: "https://www.tianditu.gov.cn/",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
      },
      timeout: 10000
    });

    // 确保缓存目录存在
    await fs.ensureDir(path.dirname(cachePath));

    // 保存瓦片到本地
    await fs.writeFile(cachePath, response.data);

    // 返回瓦片数据
    res.set("Content-Type", "image/png");
    res.send(response.data);

    console.log(`天地图瓦片已缓存: ${type}/${z}/${x}/${y}`);
  } catch (error) {
    console.error(`获取天地图瓦片失败: ${type}/${z}/${x}/${y}`, error.message);
    res.status(500).send(`获取天地图瓦片失败: ${error.message}`);
  }
});

app.listen(port, () => {
  console.log(`服务器正在监听端口 ${port}`);
});

下载成功的话,就可以看到文件夹里面的地图瓦片数据

2使用vite创建vue项目

关键代码