简介:本文详细介绍如何基于Vue.js与Leaflet库实现天地图离线访问,并集成飞线动画效果,涵盖离线地图配置、Vue集成方案及动态路径可视化技术。
在GIS(地理信息系统)开发中,天地图作为国内权威的电子地图服务,其在线API调用受限于网络环境与访问频次限制。对于需要离线运行或高并发访问的场景(如应急指挥、野外作业),构建本地化地图服务成为刚需。同时,飞线效果(动态路径动画)在物流轨迹、航班监控等场景中具有重要应用价值。
本文以Vue 3为前端框架,结合Leaflet(轻量级开源地图库)实现:
天地图采用XYZ瓦片坐标系,离线化核心步骤如下:
# 示例:使用wget下载指定区域的瓦片(需替换为实际URL)wget -r -np -nH --cut-dirs=3 -R index.html \"http://t0.tianditu.gov.cn/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&layer=vec&style=default&tilematrixset=w&format=tiles&tilematrix={z}&tilerow={y}&tilecol={x}&tk=您的密钥"
关键参数说明:
{z}/{x}/{y}:瓦片坐标(Zoom/Column/Row)vec_w:矢量地图图层(其他可选:img_w影像图、cva_w标注层)您的密钥为天地图开发者密钥存储结构建议:
/offline_tiles/├── vec/ # 矢量底图│ └── {z}/│ └── {x}/│ └── {y}.png├── cva/ # 标注层└── img/ # 影像图(可选)
安装依赖:
npm install leaflet @vue-leaflet/vue-leaflet
基础地图组件:
<template><l-map ref="map" :zoom="zoom" :center="center" style="height: 100vh"><l-tile-layer:url="offlineUrl":subdomains="subdomains"layer-type="base"name="Offline Tianditu"/></l-map></template><script setup>import { LMap, LTileLayer } from '@vue-leaflet/vue-leaflet';import { ref } from 'vue';const zoom = ref(10);const center = ref([39.9042, 116.4074]); // 北京中心坐标const offlineUrl = ref('http://localhost/vec/{z}/{x}/{y}.png'); // 本地瓦片服务const subdomains = ref(['a', 'b', 'c']); // 伪子域(离线环境可忽略)</script>
推荐使用Nginx配置静态资源服务:
server {listen 80;server_name localhost;location /vec/ {alias /path/to/offline_tiles/vec/;try_files $uri $uri/ =404;# 禁用缓存以确保开发环境实时更新add_header Cache-Control "no-store";}# 类似配置cva标注层}
飞线效果通过Canvas动态绘制贝塞尔曲线实现,关键步骤:
requestAnimationFrame实现动画循环
<template><l-map ref="map"><!-- 基础地图层省略 --><canvas ref="flyCanvas" class="fly-overlay" /></l-map></template><script setup>import { onMounted, ref } from 'vue';const flyCanvas = ref(null);const map = ref(null);// 飞线参数const flights = ref([{ start: [116.3, 39.9], end: [121.4, 31.2], color: '#ff0000' } // 北京到上海]);onMounted(() => {const canvas = flyCanvas.value;const ctx = canvas.getContext('2d');// 动态调整canvas尺寸const updateCanvasSize = () => {const mapContainer = map.value.$el.querySelector('.leaflet-pane');canvas.width = mapContainer.clientWidth;canvas.height = mapContainer.clientHeight;};// 动画主循环let animationId = null;const animate = (timestamp) => {ctx.clearRect(0, 0, canvas.width, canvas.height);flights.value.forEach(flight => {drawFlightLine(ctx, flight, timestamp);});animationId = requestAnimationFrame(animate);};// 启动动画updateCanvasSize();animate(0);// 监听地图事件调整canvas// map.value.on('moveend', updateCanvasSize);});// 贝塞尔曲线绘制函数const drawFlightLine = (ctx, { start, end, color }, timestamp) => {const progress = (timestamp % 3000) / 3000; // 3秒循环const [startX, startY] = mapToCanvas(start);const [endX, endY] = mapToCanvas(end);// 计算控制点(使曲线向上弯曲)const cpX1 = startX + (endX - startX) * 0.3;const cpY1 = startY - Math.abs(endY - startY) * 0.5;const cpX2 = endX - (endX - startX) * 0.3;const cpY2 = endY - Math.abs(endY - startY) * 0.5;// 绘制渐变线const gradient = ctx.createLinearGradient(startX, startY, endX, endY);gradient.addColorStop(0, color + '00'); // 起始透明gradient.addColorStop(0.5, color);gradient.addColorStop(1, color + '00'); // 结束透明ctx.strokeStyle = gradient;ctx.lineWidth = 2;ctx.beginPath();ctx.moveTo(startX, startY);// 根据进度绘制曲线片段if (progress < 0.5) {const t = progress * 2;const x = (1 - t) ** 2 * startX + 2 * (1 - t) * t * cpX1 + t ** 2 * cpX2;const y = (1 - t) ** 2 * startY + 2 * (1 - t) * t * cpY1 + t ** 2 * cpY2;ctx.lineTo(x, y);} else {const t = (progress - 0.5) * 2;const x = (1 - t) ** 2 * cpX1 + 2 * (1 - t) * t * cpX2 + t ** 2 * endX;const y = (1 - t) ** 2 * cpY1 + 2 * (1 - t) * t * cpY2 + t ** 2 * endY;ctx.quadraticCurveTo(cpX1, cpY1, x, y);ctx.lineTo(endX, endY);}ctx.stroke();};// 坐标转换(需根据实际项目实现)const mapToCanvas = ([lng, lat]) => {// 实际实现需将经纬度转换为canvas像素坐标// 示例伪代码:const point = map.value.latLngToContainerPoint([lat, lng]);return [point.x, point.y];};</script><style>.fly-overlay {position: absolute;top: 0;left: 0;pointer-events: none; /* 允许地图交互 */z-index: 400; /* 高于地图标注层 */}</style>
环境配置:
代码组织:
src/├── components/│ ├── OfflineMap.vue # 基础地图组件│ └── FlightLayer.vue # 飞线效果组件├── utils/│ └── tileUtils.js # 瓦片坐标计算工具└── assets/└── tiles/ # 本地瓦片目录(开发阶段)
部署方案:
FROM nginx:alpineCOPY ./dist /usr/share/nginx/htmlCOPY ./tiles /usr/share/nginx/tilesCOPY nginx.conf /etc/nginx/conf.d/default.conf
瓦片加载404错误:
飞线闪烁问题:
will-change: transform提升动画性能跨域问题:
add_header Access-Control-Allow-Origin *v-if控制矢量图/影像图显示通过本文方案,开发者可在离线环境下实现高性能的天地图可视化,并具备动态路径展示能力。实际项目需根据具体需求调整瓦片范围、动画参数及性能优化策略。