🧩 效果预览

👇 飞机从多个城市起飞并向其他城市飞行,动画流畅,地图可缩放拖拽:



📦 一、项目技术栈

技术用途
Vue 3现代前端框架
OpenLayers地图底图渲染
ECharts + ol-echarts飞机飞行动画渲染
ol-echarts将 ECharts 图层嵌入到 OpenLayers

⚙️ 二、环境准备

1. 创建项目(如果你已有 Vue3 项目可跳过)

npm init vue@latest vue-openlayers-echarts
cd vue-openlayers-echarts
npm install

选用 Vue 3 + TypeScriptVue 3 + JavaScript 皆可。

2. 安装必要依赖

npm install ol echarts ol-echarts

如果你使用的是 Vite 构建工具,也可以添加 ECharts 按需引入优化:


🌍 三、准备地图 JSON 数据

我们需要一个 GeoJSON 格式的中国地图数据 作为 ECharts 的底图。你有两种方式下载:

✅ 方法一:使用阿里官方提供的地图数据

  1. 打开:https://geo.datav.aliyun.com/areas/bound/100000_full.json

  2. 将其放入你的项目 public/map/china.json

✅ 方法二:也可以使用世界地图 world.json(视觉更国际化)

  • 下载链接:https://cdn.jsdelivr.net/npm/echarts@5/map/json/world.json

  • 保存路径同样为:public/map/world.json


🧱 四、完整代码实现(Composition API)

创建组件 OpenlayersPlane.vue,核心代码如下:

<!--* @Author: 彭麒* @Date: 2025/7/8* @Email: 1062470959@qq.com* @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。-->
<template><div class="container"><div class="w-full flex justify-center flex-wrap"><div class="font-bold text-[24px]">在 Vue3 中使用 OpenLayers 地图上Echarts模拟飞机循环飞行</div></div><div id="vue-openlayers"></div></div>
</template><script setup>
import { onMounted } from 'vue'
import 'ol/ol.css'
import { Map, View } from 'ol'
import TileLayer from 'ol/layer/Tile'
import OSM from 'ol/source/OSM'
import EChartsLayer from 'ol-echarts'
import * as echarts from 'echarts/core'
// 引入世界地图数据
import { registerMap } from 'echarts/core'let map = nullonMounted(async () => {// 注册世界地图数据try {// 方法1:如果你已下载文件到项目中// import worldJson from '@/assets/geo/world.json'// 方法2:从公共目录获取const worldJson = await fetch('/map/china.json').then(res => res.json())//// // 注册世界地图数据registerMap('world', worldJson)initMap()} catch (error) {console.error('加载世界地图数据失败:', error)}
})function initMap() {const osmLayer = new TileLayer({source: new OSM()})map = new Map({target: 'vue-openlayers',layers: [osmLayer],view: new View({projection: 'EPSG:4326',center: [116.53, 39.44],zoom: 7})})// 正确初始化 EChartsLayerconst option = getOption()const echartslayer = new EChartsLayer(option, {hideOnMoving: false,hideOnZooming: false,forcedRerender: true, // 强制重新渲染coordinate: map.getView().getProjection().getCode()})echartslayer.appendTo(map)
}function getOption() {const geoCoordMap = {'北京': [116.4551, 40.2539],'上海': [121.4648, 31.2891],'广州': [113.5107, 23.2196],'大连': [122.2229, 39.4409],'南宁': [108.479, 23.1152],'南昌': [116.0046, 28.6633],'拉萨': [91.1865, 30.1465],'长春': [125.8154, 44.2584],'包头': [110.3467, 41.4899],'重庆': [107.7539, 30.1904],'常州': [119.4543, 31.5582],'昆明': [102.9199, 25.4663],'郑州': [113.4668, 34.6234],'长沙': [113.0823, 28.2568],'丹东': [124.541, 40.4242]}const BJData = [[{ name: '北京' }, { name: '上海', value: 95 }],[{ name: '北京' }, { name: '广州', value: 90 }],[{ name: '北京' }, { name: '大连', value: 80 }],[{ name: '北京' }, { name: '南宁', value: 70 }],[{ name: '北京' }, { name: '南昌', value: 60 }],[{ name: '北京' }, { name: '拉萨', value: 50 }],[{ name: '北京' }, { name: '长春', value: 40 }],[{ name: '北京' }, { name: '包头', value: 30 }],[{ name: '北京' }, { name: '重庆', value: 20 }],[{ name: '北京' }, { name: '常州', value: 10 }]]const SHData = [[{ name: '上海' }, { name: '包头', value: 95 }],[{ name: '上海' }, { name: '昆明', value: 90 }],[{ name: '上海' }, { name: '广州', value: 80 }],[{ name: '上海' }, { name: '郑州', value: 70 }],[{ name: '上海' }, { name: '长春', value: 60 }],[{ name: '上海' }, { name: '重庆', value: 50 }],[{ name: '上海' }, { name: '长沙', value: 40 }],[{ name: '上海' }, { name: '北京', value: 30 }],[{ name: '上海' }, { name: '丹东', value: 20 }],[{ name: '上海' }, { name: '大连', value: 10 }]]const planePath ='path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z';function convertData(data) {const res = []for (let i = 0; i < data.length; i++) {const fromCoord = geoCoordMap[data[i][0].name]const toCoord = geoCoordMap[data[i][1].name]if (fromCoord && toCoord) {res.push({fromName: data[i][0].name,toName: data[i][1].name,coords: [fromCoord, toCoord]})}}return res}const color = ['#f00', '#0000ff']const series = []// 修复数组格式和括号对齐问题const dataList = [['北京', BJData],['上海', SHData]]dataList.forEach((item, i) => {series.push({name: item[0] + ' Top10',type: 'lines',coordinateSystem: 'geo', // 添加坐标系统zlevel: 1,effect: {show: true,period: 6,trailLength: 0.7,color: '#fff',symbolSize: 3},lineStyle: {normal: {color: color[i],width: 0,curveness: 0.2}},data: convertData(item[1])},{name: item[0] + ' Top10',type: 'lines',coordinateSystem: 'geo', // 添加坐标系统zlevel: 2,effect: {show: true,period: 6,trailLength: 0,symbol: planePath,symbolSize: 15},lineStyle: {normal: {color: color[i],width: 1,opacity: 0.4,curveness: 0.2}},data: convertData(item[1])},{name: item[0] + ' Top10',type: 'effectScatter',coordinateSystem: 'geo',zlevel: 2,rippleEffect: {brushType: 'stroke'},label: {normal: {show: true,position: 'right',formatter: '{b}'}},symbolSize(val) {return val[2] / 8},itemStyle: {normal: {color: color[i]}},data: item[1].map(dataItem => ({name: dataItem[1].name,value: geoCoordMap[dataItem[1].name].concat([dataItem[1].value])}))})})return {tooltip: {trigger: 'item'},geo: {map: 'world',roam: true,silent: true,itemStyle: {normal: {borderColor: 'rgba(0, 0, 0, 0.2)'}}},series}
}
</script><style scoped>
.container {width: 840px;height: 570px;margin: 50px auto;border: 1px solid #42B983;position: relative;
}#vue-openlayers {width: 800px;height: 450px;margin: 0 auto;border: 1px solid #42B983;position: relative;
}
</style>

🔍 五、关键说明

✅ 为什么用 ol-echarts

  • 官方维护,轻量集成 ECharts 图层到 OpenLayers

  • 支持地图缩放拖拽不丢失动画

  • 自动将 ECharts 转换为地图坐标系

✅ 为什么要注册地图数据?

ECharts 默认不包含地图底图,注册 worldchina 是必须的。

✅ 常见问题:

  • 地图加载失败:检查 /map/china.json 路径是否正确

  • 飞机不动:确认 symbol 使用了合法的 SVG path

  • 加载慢:地图数据尽量缓存到本地,避免 CDN 延迟


🔚 六、结语与拓展建议

这只是地理可视化的一个小案例,你还可以尝试:

  • 🌟 动态航线实时更新(结合 WebSocket)

  • 🧭 飞机移动+转向效果(结合 Cesium)

  • 📡 与后端数据库联动(显示实时航班位置)

  • 🎯 鼠标交互事件,如点击城市展示信息面板


📁 七、项目源码 & 参考

  • OpenLayers 官网:https://openlayers.org/

  • ECharts 官网:https://echarts.apache.org/

  • ol-echarts 项目地址:https://github.com/sakitam-gis/ol-echarts

  • 阿里地图数据源:https://datav.aliyun.com/tools/atlas/


如果你觉得这篇文章对你有帮助,欢迎点赞 👍、收藏 ⭐、关注我获取更多前端 + 地图可视化实战教程!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88223.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88223.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88223.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OCR与PDF解析的区别

我们日常所接触的文档中&#xff0c;经常能碰到多语言混合的文档。比如论文试卷、财报研报、跨国票据都含有多种语言和文字。要将文档中的内容识别并提取务必需要使用到OCR技术&#xff0c;而传统的OCR工具在处理这类型文档的时候有局限性。早期的 OCR 系统识别精度有限&#x…

Java 单例类详解:从基础到高级,掌握线程安全与高效设计

作为一名Java开发工程师&#xff0c;你一定对**单例模式&#xff08;Singleton Pattern&#xff09;**不陌生。它是23种经典设计模式中最简单也是最常用的一种&#xff0c;用于确保一个类在整个应用程序中只有一个实例存在。单例广泛应用于系统配置、数据库连接池、日志管理器、…

面向对象设计

你列出的这些属于 C 高级开发中面向对象设计与架构设计的核心知识&#xff0c;也是面试高级工程师岗位必问的内容。下面我按顺序&#xff0c;深入讲解每一项概念、原理、用途&#xff0c;并穿插 C 示例。✅ 1. 设计原则&#xff08;SOLID&#xff09;SOLID 是面向对象设计的五大…

IntelliJ IDEA让我的开发效率翻倍:从新手到高效开发者的进阶之路

IntelliJ IDEA让我的开发效率翻倍&#xff1a;从新手到高效开发者的进阶之路 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用…

css sprites使用

CSS Sprites 是一种将多个小图标或背景图像合并到一个大图中的技术。通过减少HTTP请求次数&#xff0c;可以显著提高页面加载速度。其核心原理是&#xff1a;通过设置元素的背景图&#xff08;background-image&#xff09;为这个大图&#xff0c;然后调整背景位置&#xff08;…

分布式爬虫在电商平台商品数据大规模采集中的技术应用

在电商平台商品数据大规模采集场景中&#xff0c;分布式爬虫凭借其高效、可扩展、抗风险的特性&#xff0c;成为突破单节点爬虫性能瓶颈的核心技术方案。以下从技术架构、关键技术点、电商场景适配及挑战应对四个维度&#xff0c;解析其具体应用&#xff1a;一、分布式爬虫的核…

Linux的`if test`和`if [ ]中括号`的取反语法比较 笔记250709

Linux的if test和if 中括号的取反语法比较 笔记250709 Linux的 test命令&#xff08;或等价中括号写法 [空格expression空格]&#xff09;的用法详解. 笔记250709 四种取反语法: if ! test -e xxx ;then... 和 if test ! -e xxx ;then... 和 if ! [ -e xxx ] ;then... 和 if …

记录使用ubuntu16.04编译aosp(android8.1与10)遇到的问题

一、前言&#xff1a; 本来打算用wsl来编译AOSP&#xff0c;但是折腾了好几天&#xff0c;以失败告终。后来使用vmware反而成功了。 本篇同样会把wsl遇到的问题与尝试记录下来。 环境&#xff1a;vmware ubuntu16.04。 为什么会使用ubuntu16.04呢&#xff0c;因为在公司有一…

hiredis window之RFDMap

简介 RFDMap用于将socket分配映射成连续的文件描述符&#xff0c;同时管理回收的文件描述符&#xff0c;因为ae构架中管理fd与对应事件处理器使用的是数据&#xff0c;fd作为数组下标 结构 #mermaid-svg-zQz2LTrKRi0LQTII {font-family:"trebuchet ms",verdana,arial…

RustFS一款Rust 驱动的 高性能 分布式存储系统

演示地址&#xff1a;https://play.rustfs.com/browser 访问账号&#xff08;默认 rustfsadmin&#xff09;。 访问密钥&#xff08;默认 rustfsadmin&#xff09;。 下载mc https://dl.min.io/client/mc/release可以直接在 Linux 系统上安装 mc&#xff08;&#xff0c;然后访…

微软 Bluetooth LE Explorer 实用工具的详细使用分析

微软 Bluetooth LE Explorer 实用工具的详细使用分析 文章目录 微软 **Bluetooth LE Explorer** 实用工具的详细使用分析1. **工具定位与核心功能**2. **关键特性与更新**3. **使用场景示例**4. **系统要求与依赖**5. **与专业工具对比**6. **局限性**7. **实践建议**结论以下是…

centos 7.6安装mysql8

在 CentOS 7.6 上安装 MySQL 8.0.42 的步骤如下&#xff0c;基于搜索结果中的最新信息&#xff1a; 下载 MySQL 8.0.42 安装包 https://dev.mysql.com/downloads/mysql/从 MySQL 官方网站下载 mysql-8.0.42-1.el7.x86_64.rpm-bundle.tar 文件&#xff1a; 官方下载地址&#xf…

CentOS7更换阿里云yum源

问题&#xff1a;刚刚在本地安装了CentOS7虚拟机&#xff0c;使用yum安装vim软件时&#xff08;最小化安装只有vi没有vim&#xff09;出现下面的报错原因 &#xff1a;CentOS7 已于2024-6-30停止维护&#xff0c;官方镜像源已不可用&#xff0c;可以更换为阿里云镜像源解决&…

UE5内置插件 AnimToTexture 简单入门

开启插件 首先安装插件&#xff0c;然后重启。打开显示插件内容我们就可以找到插件自带的转换内容将骨骼网格体转换为顶点动画有两种方式&#xff1a; 最简单的记录每个顶点的位置然后通过切换拾取颜色偏移实现记录骨骼的变换&#xff0c;然后通过贴图去修改骨骼位置计算 这两种…

如何搭建Appium环境?

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快1、安装Java Development Kit&#xff08;JDK&#xff09;前往Oracle官网下载JDK。在https://www.oracle.com/java/technologies/javase-jdk11-downloads.html 找到…

Android kotlin 协程的详细使用指南

Android Kotlin 协程的详细使用指南&#xff0c;结合核心概念、实战场景和最佳实践&#xff1a;一、协程基础概念‌协程本质‌协程是轻量级线程&#xff0c;通过挂起/恢复机制实现并发&#xff0c;相比线程节省90%以上的内存开销。其核心优势在于结构化并发和挂起函数的协作式调…

什么是 AMR 格式?简鹿音频转换器轻松批量转换 AMR 为 MP3

AMR 格式是一种比较特殊但又常见的音频格式&#xff0c;而MP3 格式则是大家耳熟能详的通用音频格式。那么&#xff0c;它们之间有什么区别&#xff1f;又该如何把 AMR 文件转换成更常用的 MP3 呢&#xff1f;下面我们就来通俗地了解一下。一、什么是 AMR 格式&#xff1f;AMR&a…

C++11 std::move与std::move_backward深度解析

文章目录移动语义的革命性意义std::move&#xff1a;正向范围移动函数原型与核心功能关键特性与实现原理适用场景与代码示例危险区域&#xff1a;重叠范围的未定义行为std::move_backward&#xff1a;反向安全移动函数原型与核心功能关键特性与实现原理适用场景与代码示例重叠范…

订单初版—2.生单链路中的技术问题说明文档

大纲1.生单链路的业务代码2.生单链路中可能会出现数据不一致的问题3.Seata AT模式下的分布式事务的原理4.Seata AT模式下的分布式事务的读写隔离原理5.Seata AT模式下的死锁问题以及超时机制6.Seata AT模式下的读写隔离机制的影响7.生单链路使用Seata AT模式的具体步骤8.生单链…

跨平台ROS2视觉数据流:服务器运行IsaacSim+Foxglove本地可视化全攻略

任务目标 本教程将完整实现&#xff1a; 在服务器无头模式下运行IsaacSim&#xff0c;并在本地显示GUI界面 通过IsaacSim的ROS2 Bridge发布图像数据 在本地Foxglove中实时可视化服务器端的ROS2数据流 实现步骤 1. 服务器无头运行IsaacSim 本地GUI显示 在服务器端执行&am…