四十二、openlayers官网示例Flight Animation扩展——在地图上绘制飞机航线、飞机随航线飞行效果

 

上篇在地图上绘制了动态的飞机航线,于是我想着,能不能加个飞机的图标跟着航线飞行。

在iconfont上下载一个飞机的svg图形,放在public的data/icons下面 

因为图标需要随着航线的方向飞行,需要根据航线调整角度,因此在加载数据源的时候需要计算一下角度,绑在每个feature上。

//计算角度
const rotation = calculateRotation(from, to);features.push(new Feature({geometry: line,finished: false,rotation: rotation,}));function calculateRotation(from, to) {const dx = to[0] - from[0];const dy = to[1] - from[1];return Math.atan2(dy, dx);}

在航线绘制完成之后,添加一个飞机动画开始执行的标识flight,给feature设置一个初始的index值

if (elapsedPoints >= coords.length) {feature.set("finished", true);feature.set("flight", true);feature.set("index", 0);}

animateFlights会一直执行,所以我们利用这个特点来绘制循环的动画。绘制线的思路是取坐标数组的第0个到第n个,每毫秒绘制不同的线。绘制点的思路则是直接取第n个点,每毫秒绘制不同的点,并且在n大于等于坐标数组之后又让n重新等于0,以此来实现循环的动画。

 if (feature.get("flight")) {const frameState = event.frameState;const coords = feature.getGeometry().getCoordinates();let index = feature.get("index");index += step;if (index >= coords.length - 1) {index = 0;}if (index < 0) {index = 0;}feature.set("index", index);style.getImage().setRotation(feature.get("rotation"));vectorContext.setStyle(style);const currentPoint = new Point(coords[Math.floor(index)]);// 在当前和最近相邻的包裹世界中需要动画const worldWidth = getWidth(map.getView().getProjection().getExtent());const offset = Math.floor(map.getView().getCenter()[0] / worldWidth);//直接用矢量上下文绘制线条//在平铺地图上绘制线段时,需要考虑地图的无限水平滚动特性。通过平移和多次绘制线段,确保即使用户滚动地图,线段也能正确显示在地图的两端。这个方法处理了跨越地图边界的线段,避免了图形被截断的问题。currentPoint.translate(offset * worldWidth, 0);vectorContext.drawGeometry(currentPoint);currentPoint.translate(worldWidth, 0);vectorContext.drawGeometry(currentPoint);}

完整代码:

<template><div class="box"><h1>External map</h1><div id="map"></div></div>
</template><script>
import Feature from "ol/Feature.js";
import { LineString, Point, Polygon } from "ol/geom.js";
import Map from "ol/Map.js";
import StadiaMaps from "ol/source/StadiaMaps.js";
import VectorSource from "ol/source/Vector.js";
import View from "ol/View.js";
import { Stroke, Style, Icon, Circle as CircleStyle, Fill } from "ol/style.js";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer.js";
import { getVectorContext } from "ol/render.js";
import { getWidth } from "ol/extent.js";
var arc = require("arc");
export default {name: "",components: {},data() {return {map: null,extentData: "",};},computed: {},created() {},mounted() {const tileLayer = new TileLayer({source: new StadiaMaps({layer: "outdoors",}),});const map = new Map({layers: [tileLayer],target: "map",view: new View({center: [-11000000, 4600000],zoom: 2,}),});const style = new Style({stroke: new Stroke({color: "#EAE911",width: 2,}),image: new Icon({anchor: [0.5, 0.5],src: "data/icons/flight.svg",rotation: -0.19931501061749937,}),});const flightsSource = new VectorSource({attributions:"Flight data by " +'<a href="https://openflights.org/data.html">OpenFlights</a>,',loader: function () {const url ="https://openlayers.org/en/latest/examples/data/openflights/flights.json";fetch(url).then(function (response) {return response.json();}).then(function (json) {let flightsData = json.flights;flightsData = flightsData.splice(26, 100);for (let i = 0; i < flightsData.length; i++) {const flight = flightsData[i];const from = flight[0];const to = flight[1];// 在两个位置之间创建一个圆弧const arcGenerator = new arc.GreatCircle({ x: from[1], y: from[0] },{ x: to[1], y: to[0] });//生成100个点 offset是偏移量const arcLine = arcGenerator.Arc(100, { offset: 10 });//计算角度const rotation = calculateRotation(from, to);//穿过-180°/+180°子午线的路径是分开的//分成两个部分,按顺序设置动画const features = [];arcLine.geometries.forEach(function (geometry) {const line = new LineString(geometry.coords);//将 line 对象的坐标系从 WGS84(EPSG:4326)转换为 Web Mercator 投影(EPSG:3857)line.transform("EPSG:4326", "EPSG:3857");features.push(new Feature({geometry: line,finished: false,rotation: rotation,}));});// 动画延迟:使用i * 50来设置延迟是为了确保每条路径的动画不会同时启动,这样可以产生连续动画的效果。addLater(features, i * 50);}//tileLayer 图层每次完成渲染之后调用tileLayer.on("postrender", animateFlights);});},});let flag = false;const flightsLayer = new VectorLayer({source: flightsSource,style: function (feature) {//等动画完毕再现在最终的线样式if (feature.get("finished")) {return [new Style({stroke: new Stroke({color: "#EAE911",width: 2,}),}),new Style({image: new Icon({anchor: [0.5, 0.5],src: "data/icons/flight.svg",rotation: feature.get("rotation"),}),}),];}return null;},});map.addLayer(flightsLayer);const pointsPerMs = 0.05;let duration = 2000;let step = 0.5;function animateFlights(event) {const vectorContext = getVectorContext(event);const frameState = event.frameState;vectorContext.setStyle(style);const features = flightsSource.getFeatures();for (let i = 0; i < features.length; i++) {const feature = features[i];if (!feature.get("finished")) {// 只画动画尚未完成的线const coords = feature.getGeometry().getCoordinates();const elapsedTime = frameState.time - feature.get("start");if (elapsedTime >= 0) {const elapsedPoints = elapsedTime * pointsPerMs;if (elapsedPoints >= coords.length) {feature.set("finished", true);feature.set("flight", true);feature.set("index", 0);}const maxIndex = Math.min(elapsedPoints, coords.length);const currentLine = new LineString(coords.slice(0, maxIndex));// 在当前和最近相邻的包裹世界中需要动画const worldWidth = getWidth(map.getView().getProjection().getExtent());const offset = Math.floor(map.getView().getCenter()[0] / worldWidth);//直接用矢量上下文绘制线条//在平铺地图上绘制线段时,需要考虑地图的无限水平滚动特性。通过平移和多次绘制线段,确保即使用户滚动地图,线段也能正确显示在地图的两端。这个方法处理了跨越地图边界的线段,避免了图形被截断的问题。currentLine.translate(offset * worldWidth, 0);vectorContext.drawGeometry(currentLine);currentLine.translate(worldWidth, 0);vectorContext.drawGeometry(currentLine);}}if (feature.get("flight")) {const frameState = event.frameState;const coords = feature.getGeometry().getCoordinates();let index = feature.get("index");index += step;if (index >= coords.length - 1) {index = 0;}if (index < 0) {index = 0;}feature.set("index", index);style.getImage().setRotation(feature.get("rotation"));vectorContext.setStyle(style);const currentPoint = new Point(coords[Math.floor(index)]);// 在当前和最近相邻的包裹世界中需要动画const worldWidth = getWidth(map.getView().getProjection().getExtent());const offset = Math.floor(map.getView().getCenter()[0] / worldWidth);//直接用矢量上下文绘制线条//在平铺地图上绘制线段时,需要考虑地图的无限水平滚动特性。通过平移和多次绘制线段,确保即使用户滚动地图,线段也能正确显示在地图的两端。这个方法处理了跨越地图边界的线段,避免了图形被截断的问题。currentPoint.translate(offset * worldWidth, 0);vectorContext.drawGeometry(currentPoint);currentPoint.translate(worldWidth, 0);vectorContext.drawGeometry(currentPoint);}}//告诉OpenLayers继续动画map.render();}function addLater(features, timeout) {window.setTimeout(function () {let start = Date.now();features.forEach(function (feature) {feature.set("start", start);flightsSource.addFeature(feature);const duration =(feature.getGeometry().getCoordinates().length - 1) / pointsPerMs;start += duration;});}, timeout);}function calculateRotation(from, to) {const dx = to[0] - from[0];const dy = to[1] - from[1];return Math.atan2(dy, dx);}},methods: {},
};
</script><style lang="scss" scoped>
#map {width: 100%;height: 500px;
}
.box {height: 100%;
}</style>

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

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

相关文章

FPGA SPI采集ADC7606数据

一,SPI总线的构成及信号类型 SPI总线只需四条线(如图1所示)就可以完成MCU与各种外围器件的通讯: 1)MOSI – Master数据输出,Slave数据输入 2)MISO – Master数据输入,Slave数据输出 3)SCK – 时钟信号,由Master产生 4)/CS – Slave使能信号,由Master控制。 在一个SPI时…

递归【2】(组合回溯(生成括号)、子集回溯(背包问题))

括号对 &#xff08;组合型回溯&#xff09; 分解成子问题&#xff0c;每一次添加括号分两步&#xff1a; if左括号小于n&#xff0c;加左括号&#xff0c;然后k(index1), if左括号大于有括号&#xff0c;加右括号&#xff0c;k(index1),然后收尾括号单独考虑&#xff0c;到…

core dump核心转储

检查核心转储是否开启&#xff0c;否则无法生成core文件 ulimit -a 如果为0就需要修改 ulimit -c 10240 写一个会触发core命令的程序 以浮点数运算为例 #include <iostream>int main() {int i 1/0; } 在编译时使用-g选项 运行程序&#xff0c;生成core文件 gdb调试 g…

AI大模型在广告领域的应用

深度对谈&#xff1a;广告创意领域中AIGC的应用_生成式 AI_Tina_InfoQ精选文章

ChatGPT-4o, 腾讯元宝,通义千问对比测试中文文化

国内的大模型应用我选择了国内综合实力最强的两个&#xff0c;一个是腾讯元宝&#xff0c;一个是通义千问。其它的豆包&#xff0c;Kimi&#xff0c;文心一言等在某些领域也有强于竞品的表现。 问一个中文文化比较基础的问题,我满以为中文文化chatGPT不如国内的大模型。可事实…

【经典排序算法】堆排序(精简版)

什么是堆排序&#xff1a; 堆排序(Heapsort)是指利用堆&#xff08;完全二叉树&#xff09;这种数据结构所设计的一种排序算法&#xff0c;它是选择排序的一种。需要注意的是排升序要建大堆&#xff0c;排降序建小堆。 堆排序排序的特性总结&#xff1a; 1. 堆排序使用堆来选数…

vivado DIAGRAM、HW_AXI

图表 描述 块设计&#xff08;.bd&#xff09;是在IP中创建的互连IP核的复杂系统 Vivado设计套件的集成商。Vivado IP集成器可让您创建复杂的 通过实例化和互连Vivado IP目录中的IP进行系统设计。一块 设计是一种分层设计&#xff0c;可以写入磁盘上的文件&#xff08;.bd&…

软考架构-计算机网络考点

会超纲&#xff0c;3-5分 网络分类 按分布范围划分 局域网 LAN 10m-1000m左右 房间、楼宇、校园 传输速率高 城域网 MAN 10km 城市 广域网 WAN 100km以上 国家或全球&#xff08;英特网&#xff09; 按拓扑结构划分 总线型&#xff1a;利用率低、干…

(2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干

Vision-LSTM: xLSTM as Generic Vision Backbone 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2 方法 3 实验 3.1 分类设计 4 结论 0. 摘要 Transformer 被广泛用作计算…

基于深度学习的在线选修课程推荐系统

基于深度学习的在线选修课程推荐系统 1、效果图 点我查看Demo 2、功能 可联系我-微-信(1257309054) 登录注册、点赞收藏、评分评论&#xff0c;课程推荐&#xff0c;热门课程&#xff0c;个人中心&#xff0c;可视化&#xff0c;后台管理&#xff0c;课程选修3、核心推荐代…

Edge浏览器十大常见问题,一次性解决!

Edge曾被称为最好用的浏览器&#xff0c;拳打Chrome脚踢firefox, 可如今却隐藏着像是播放卡顿、下载缓慢、广告繁多等诸多问题&#xff0c;不知道各位还在用吗&#xff1f; 今天小编收集整理了Edge浏览器十大烦人问题&#xff0c;并提供简单有效的解决办法&#xff0c;让你的E…

277 基于MATLAB GUI火灾检测系统

基于MATLAB GUI火灾检测系统&#xff0c;可以实现图片和视频的火苗检测。火焰识别的三个特征&#xff1a;1个颜色特征&#xff0c;2个几何特征颜色特征&#xff1a;HSV颜色空间下&#xff0c;对三个通道值进行阈值滤波&#xff0c;几何特征1&#xff1a;长宽比&#xff0c;几何…

实战 | YOLOv10 自定义数据集训练实现车牌检测 (数据集+训练+预测 保姆级教程)

导读 本文主要介绍如何使用YOLOv10在自定义数据集训练实现车牌检测 (数据集训练预测 保姆级教程)。 YOLOv10简介 YOLOv10是清华大学研究人员在Ultralytics Python包的基础上&#xff0c;引入了一种新的实时目标检测方法&#xff0c;解决了YOLO以前版本在后处理和模型架构方面…

mac M1下安装PySide2

在M1下装不了PySide2, 是因为PySide2没有arm架构的包 1 先在M1上装qt5 安装qt主要是为了能用里面的Desinger, uic, rcc brew install qt5 我装完的路径在/opt/homebrew/opt/qt5 其中Designer就是用来设计界面的 rcc用resource compiler, 编绎rc资源文件的, 生成对应的py文件…

电子电气架构——车载诊断DTC一文通

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕的就是把别人的眼光当成自己生活的唯一标…

【模拟-BM99 顺时针旋转矩阵】

题目 BM99 顺时针旋转矩阵 描述 有一个NxN整数矩阵&#xff0c;请编写一个算法&#xff0c;将矩阵顺时针旋转90度。 给定一个NxN的矩阵&#xff0c;和矩阵的阶数N,请返回旋转后的NxN矩阵。 分析 模拟&#xff0c;写几个样例&#xff0c;分析一下新矩阵元素下标与原矩阵元素…

Windows系统问题

Windows系统问题 一、补丁更新提示&#xff1a;0x80070643问题&#xff1a;解决方法&#xff1a;1.以管理员权限运行【cmd】。2.禁用 【Windows RE】&#xff0c;请运行reagentc /disable。3.回收【Windows RE】恢复分区空间。4.准备新的【Windows RE】恢复分区空间。5.配置并启…

并查集进阶版

过关代码如下 #define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> #include<unordered_set> using namespace std;int n, m; vector<int> edg[400005]; int a[400005], be[400005]; // a的作用就是存放要摧毁 int k; int fa[400005]; int daan[400005]…

【保姆级图文教程】QT下载、安装、入门、配置VS Qt环境

【保姆级图文教程】QT下载、安装、入门、配置VS Qt环境-CSDN博客 0.QT介绍 QT 是一个跨平台的应用程序开发框架&#xff0c;它提供了丰富的工具和类库&#xff0c;用于开发图形用户界面&#xff08;GUI&#xff09;程序。Qt 提供了 C 编程语言接口&#xff0c;同时也支持其他…

Xcode设置cocoapods库的最低兼容版本

目录 前言 1.使用cocoapods遇到的问题 2.解决办法 1.用法解释 1. config.build_settings: 2.IPHONEOS_DEPLOYMENT_TARGET 2.使用实例 3.注意事项 1.一致性 2.pod版本 前言 这篇文章主要是介绍如何设置cocoapods三方库如何设置最低兼容的版本。 1.使用cocoapods遇到的…