Vue 离线地图实现

效果图:

一、获取市的地图数据

DataV.geoAtlas 获取市地图数据

点击地图缩放至想要的市区域,通过右侧的链接打开网址,复制json数据。

二、获取镇地图数据

下载软件bigemap

选择你想要的镇数据,点击下载

选择级别(清晰度)

三、合并市和镇的数据

通过 合并转化数据(geojson)geojson.io 网址合并json数据

首先将市json数据复制到这里

然后通过 open 打开镇文件夹中的 .kml 文件,即可实现镇和市的合并json操作

最后将合并好的json数据放到 map.json 文件夹中等待使用

将 json 数据复制放进Vue 项目的 /static/map.json 地址中

下载 echarts :

npm i echarts --save
npm i axios --save

main.js 注册

import { createApp } from 'vue'
import * as echarts from 'echarts';
import './style.css'
import App from './App.vue'

const app = createApp(App)
app.config.globalProperties.$echarts = echarts;

app.mount('#app')

组件内使用:

<template>
  <div class="map">
    <div class="map_chart" ref="map_chart"></div>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "Map",
  data() {
    return {
      chartInstance: null,
      allData: null,
      mapData: {}, // 所获取的省份的地图矢量数据
      colorArr: ['#4ab2e5', "#4fb6d2", "#52b9c7", "#5abead", "#f56321", "#f34e2b", "#f56f1c", "#f56f1c", "#f58414", "#c1bb1f", "#f5a305", "#b9be23"],
    };
  },
  created() { },
  mounted() {
    this.initChart();
    window.addEventListener("resize", this.screenAdapter);
    this.screenAdapter();
  },
  destroyed() {
    window.removeEventListener("resize", this.screenAdapter);
  },
  methods: {
    async initChart() {
      this.chartInstance = this.$echarts.init(this.$refs.map_chart);
      // 获取中国地图的矢量数据
      // http://localhost:8999/static/map/china.json
      // 由于我们现在获取的地图矢量数据并不是位于KOA2的后台, 所以咱们不能使用this.$http
      const ret = await axios.get("http://localhost:5173/static/map.json");
      console.log(ret, "22");
      this.$echarts.registerMap("shouguang", ret.data);
      const initOption = {
        // title: {
        //   text: "▎ 公墓分布",
        //   left: 20,
        //   top: 20,
        // },
        geo: {
          type: "map",
          map: "shouguang",
          top: "5%",
          bottom: "5%",
          label: {
            show: true,
            color: "#1DE9B6",
          },
          itemStyle: {
            areaColor: {
              type: "radial",
              x: 0.5,
              y: 0.5,
              r: 0.8,
              colorStops: [
                {
                  offset: 0,
                  color: "#09132c", // 0% 处的颜色
                },
                {
                  offset: 1,
                  color: "#274d68", // 100% 处的颜色
                },
              ],
              globalCoord: true, // 缺省为 false
            },
            shadowColor: "rgb(58,115,192)",
            shadowOffsetX: 1,
            shadowOffsetY: 1,
          },
          emphasis: {
            itemStyle: {
              areaColor: "rgb(46,229,206)",
              shadowColor: "rgb(12,25,50)",
              borderWidth: 0,
            },

            label: {
              show: true,
              color: "#fff",
            },
          },
        },
        legend: {
          left: "5%",
          bottom: "5%",
          orient: "vertical",
        },
        series: [
          // {
          //    type: 'map',
          //     map: 'shouguang' //使用
          // },
          {
            type: 'effectScatter',
            coordinateSystem: 'geo',
            showEffectOn: 'render',
            zlevel: 1,
            rippleEffect: {
              period: 10,
              scale: 3,
              brushType: 'fill'
            },
            silent: true,
            hoverAnimation: true,
            label: {
              // normal: {
              //     formatter:function(arg){
              //      return arg.data.title
              //     },
              //     position: 'top',
              //     offset: [0, -10],
              //     color: '#fff',
              //     show: true
              // },

            },
            emphasis: {
              //  label: {
              //    show:true,
              //   color: '#fff',
              //   formatter:function(arg){
              //      return arg.data.title
              //     },
              //     position: 'top',
              //     offset: [0, -20],
              //  }

            },
            itemStyle: {
              normal: {
                color: function () { //随机颜色
                  return "#f56321"
                },
                shadowBlur: 10,
                shadowColor: '#333'
              }
            },
            tooltip: {
              show: true,
              formatter: function (arg) {
                return arg.data.title
              }
            },
            symbolSize: 12,
            data: [
              {
                title: '田柳镇',
                name: "田柳镇",
                value: [118.7712820426, 37.0142500629]
              },
              {
                title: '羊口镇',
                name: "羊口镇",
                value: [118.8566660426, 37.2021022859]
              }
            ]
          }, //地图线的动画效果
          {
            type: 'lines',
            zlevel: 2,
            effect: {
              show: true,
              period: 4, //箭头指向速度,值越小速度越快
              trailLength: 0.4, //特效尾迹长度[0,1]值越大,尾迹越长重
              symbol: 'arrow', //箭头图标
              symbolSize: 7, //图标大小
            },
            lineStyle: {
              normal: {
                color: function () { //随机颜色
                  ['#f21347', '#f3243e', '#f33736', '#f34131', '#f34e2b',
                    '#f56321', '#f56f1c', '#f58414', '#f58f0e', '#f5a305',
                    '#e7ab0b', '#dfae10', '#d5b314', '#c1bb1f', '#b9be23',
                    '#a6c62c', '#96cc34', '#89d23b', '#7ed741', '#77d64c',
                    '#71d162', '#6bcc75', '#65c78b', '#5fc2a0', '#5abead',
                    '#52b9c7', '#4fb6d2', '#4ab2e5']
                  return "#" + ("00000" + ((Math.random() * 16777215 + 0.5) >> 0).toString(16)).slice(-6);
                },
                width: 1, //线条宽度
                opacity: 0.1, //尾迹线条透明度
                curveness: .3 //尾迹线条曲直度
              }
            },
            data: [
              {
                coords: [[118.7712820426, 37.0142500629], [118.8566660426, 37.2021022859]]
              }
            ]
          }
        ]
      };
      this.chartInstance.setOption(initOption);
      this.chartInstance.on("click", async (arg) => {
        // arg.name 得到所点击的省份, 这个省份他是中文
      });
    },
    getData(ret) {
      // 获取服务器的数据, 对this.allData进行赋值之后, 调用updateChart方法更新图表
      // const { data: ret } = await this.$http.get('map')
      this.allData = ret;
      console.log(this.allData);
      this.updateChart();
    },
    updateChart() {
      // 处理图表需要的数据
      // 图例的数据
      const legendArr = this.allData.map((item) => {
        return item.name;
      });
      const seriesArr = this.allData.map((item) => {
        // return的这个对象就代表的是一个类别下的所有散点数据
        // 如果想在地图中显示散点的数据, 我们需要给散点的图表增加一个配置, coordinateSystem:geo
        return {
          type: "effectScatter",
          rippleEffect: {
            scale: 5,
            brushType: "stroke",
          },
          name: item.name,
          data: item.children,
          coordinateSystem: "geo",
        };
      });
      const dataOption = {
        legend: {
          data: legendArr,
        },
        series: seriesArr,
      };
      this.chartInstance.setOption(dataOption);
    },
    screenAdapter() {
      const titleFontSize = (this.$refs.map_chart.offsetWidth / 100) * 3.6;
      const adapterOption = {
        title: {
          textStyle: {
            fontSize: titleFontSize,
          },
        },
        legend: {
          itemWidth: titleFontSize / 2,
          itemHeight: titleFontSize / 2,
          itemGap: titleFontSize / 2,
          textStyle: {
            fontSize: titleFontSize / 2,
          },
        },
      };
      this.chartInstance.setOption(adapterOption);
      this.chartInstance.resize();
    },
  },
};
</script>
<style scoped>
.map {
  width: 100%;
  height: 100%;
  background: rgb(22, 21, 34);
  color: #fff;
}

.map_chart {
  width: 1200px;
  height: 500px;
}
</style>

Vue 项目中实现 **离线地图 + 飞线图(Fly Line)** 是一个常见的 GIS 应用场景,尤其适用于内网环境或无网络访问的部署需求。你可以使用 [OpenLayers](https://openlayers.org/) 来构建地图,并通过本地资源加载地图瓦片和样式。 --- ## ✅ 实现目标 - 使用 Vue3 + OpenLayers 构建地图 - 离线加载地图瓦片(如 MBTiles、XYZ 本地瓦片) - 在地图上绘制飞线并实现动画效果 --- ## ✅ 技术栈 - Vue 3(组合式 API 或 Vue 2 也适用) - OpenLayers(用于地图渲染) - 本地瓦片服务(可选:Mapbox、Leaflet MBTiles 服务等) --- ## ✅ 步骤详解 ### 1. 创建 Vue 项目(Vue 3) ```bash npm create vue@latest ol-offline-demo cd ol-offline-demo npm install ``` ### 2. 安装 OpenLayers(虽然我们使用离线文件,但也可以安装备用) ```bash npm install ol ``` > 如果你完全离线开发,可以直接引入本地 `ol.js` 和 `ol.css` 文件(见步骤 5) --- ### 3. 准备离线地图瓦片(示例:XYZ 格式的本地瓦片) 假设你有如下结构的本地瓦片: ``` /public/tiles/{z}/{x}/{y}.png ``` 你可以使用 OpenLayers 的 `XYZ` 图层源来加载这些本地瓦片。 --- ### 4. 引入 OpenLayers 离线资源(可选) 将 OpenLayers 的 `ol.js` 和 `ol.css` 放到 `/public/ol/` 目录下: ``` /public/ol/ol.js /public/ol/ol.css ``` 然后在 `index.html` 中引用: ```html <!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vue + OpenLayers 离线地图</title> <link rel="stylesheet" href="/ol/ol.css"> </head> <body> <div id="app"></div> <script type="module" src="/main.js"></script> </body> </html> ``` --- ### 5.Vue 组件中创建地图并绘制飞线 #### 示例组件:`MapView.vue` ```vue <template> <div id="map" class="map"></div> </template> <script setup> import { onMounted } from 'vue' import Map from 'ol/Map' import View from 'ol/View' import TileLayer from 'ol/layer/Tile' import XYZ from 'ol/source/XYZ' import Feature from 'ol/Feature' import LineString from 'ol/geom/LineString' import VectorSource from 'ol/source/Vector' import { Style, Stroke } from 'ol/style' import VectorLayer from 'ol/layer/Vector' import { fromLonLat } from 'ol/proj' onMounted(() => { // 加载本地瓦片图层(XYZ 格式) const tileLayer = new TileLayer({ source: new XYZ({ url: '/tiles/{z}/{x}/{y}.png', maxZoom: 18, }), }) // 初始化地图视图 const view = new View({ center: fromLonLat([105, 36]), // 中国中心点 zoom: 4, }) // 创建地图 const map = new Map({ target: 'map', layers: [tileLayer], view, }) // 飞线数据:起点 → 终点 const flights = [ { start: [116.4, 39.9], end: [121.47, 31.23] }, // 北京 → 上海 { start: [113.4, 23.1], end: [114.3, 30.5] }, // 广州 → 武汉 { start: [121.47, 31.23], end: [108.94, 34.34] } // 上海 → 西安 ] // 创建矢量图层 const vectorSource = new VectorSource() const vectorLayer = new VectorLayer({ source: vectorSource }) map.addLayer(vectorLayer) // 存储动画状态 const animations = [] // 添加飞线并初始化动画状态 flights.forEach(flight => { const start = fromLonLat(flight.start) const end = fromLonLat(flight.end) const line = new LineString([start, end]) const feature = new Feature(line) vectorSource.addFeature(feature) animations.push({ feature, line, currentLength: 0, totalSteps: 100, color: '#' + Math.floor(Math.random() * 16777215).toString(16) }) }) // 动画函数 function animateLines() { let stillAnimating = false animations.forEach(animation => { if (animation.currentLength <= 1) { const pointA = animation.line.getCoordinateAt(animation.currentLength) const pointB = animation.line.getCoordinateAt(Math.min(animation.currentLength + 0.01, 1)) const animatedLine = new LineString([pointA, pointB]) animation.feature.setStyle(new Style({ geometry: animatedLine, stroke: new Stroke({ color: animation.color, width: 4 }) })) animation.currentLength += 1 / animation.totalSteps stillAnimating = true } }) if (stillAnimating) { requestAnimationFrame(animateLines) } } // 启动动画 requestAnimationFrame(animateLines) }) </script> <style scoped> .map { width: 100%; height: 100vh; } </style> ``` --- ## ✅ 效果说明 - 地图使用本地瓦片加载(无需联网) - 多条飞线从起点向终点“飞行” - 每条线使用不同颜色区分 - 使用 `requestAnimationFrame` 实现流畅动画 --- ## ✅ 离线地图资源准备建议 ### 方案一:使用 MBTiles + 瓦片服务器(适合开发时) - 使用工具如 [MapTiler](https://www.maptiler.com/) 将 GeoTIFF/TIF 切割为 MBTiles - 使用 Node.js 启动本地瓦片服务(如 `tileserver-gl`) ### 方案二:直接使用 XYZ 瓦片(适合部署) - 下载开源地图切片(如 OpenTopoMap、Stamen) - 按照 `{z}/{x}/{y}.png` 结构组织文件夹 - 放入 `/public/tiles/` 即可使用 --- ## ✅ 扩展功能建议 | 功能 | 描述 | |------|------| | 添加箭头图标 | 在飞线终点添加三角形或图片表示流向 | | 添加点击事件 | 实现飞线点击弹窗显示信息 | | 使用 GeoJSON 数据 | 替换硬编码数据为外部 JSON 数据源 | | 支持缩放与交互 | 增加鼠标滚轮缩放、拖拽等交互行为 | --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值