前端~地圖(openlayers)繪制車輛運動軌跡(仿高德)

  1. 繪制軌跡路線
  2. 軌跡路線描邊
  3. 增加起點終點圖標
  4. 繪制仿高德方向箭頭
  5. 模仿車輛動態運動動畫

車輛運行軌跡

車輛軌跡經緯度坐標
  const linePoints = [new Point([123.676031, 43.653421]),new Point([123.824347, 43.697124]),new Point([124.197882, 43.946811]),new Point([124.104498, 43.744764]),new Point([124.752692, 43.701096]),new Point([124.598883, 43.808225]),new Point([124.692267, 43.832006]),new Point([124.911993, 43.847854]),new Point([124.999884, 43.855777]),new Point([125.170172, 43.867658]),new Point([125.280035, 43.930989]),new Point([125.604132, 43.91912]),new Point([125.933722, 43.927033]),];
繪制軌跡路徑&增加描邊效果

就是繪制兩邊LineString,外邊框比內邊框寬一些,并通過控制zIndex控制在最下面即可

const drawDynamicCarLine = (linePoints: Array<Point>) => {// 內部軌跡線const innerLineFeature = new Feature(new LineString(linePoints.map((item) => item.getCoordinates())));innerLineFeature.setStyle(new Style({stroke: new Stroke({color: "rgb(237,73,23)",width: 3,}),zIndex: 3,}));// 外部邊框線const outLineFeature = new Feature(new LineString(linePoints.map((item) => item.getCoordinates())));outLineFeature.setStyle(new Style({stroke: new Stroke({color: "rgba(44,74,48,1)",width: 6,}),zIndex: 1,}));const lineSource = new VectorSource({features: [innerLineFeature, outLineFeature],});const lineLayer = new VectorLayer({source: lineSource,});debugger;map.addLayer(lineLayer);
};

效果如下
在這里插入圖片描述

增加起點終點圖標
const addStartEndPoint = (startPoint: Point, endPoint: Point) => {const startPointFeature = new Feature({geometry: startPoint,});startPointFeature.setStyle(new Style({image: new Icon({src: new URL("/src/assets/imgs/car/move_car_icon.png", import.meta.url).href,}),zIndex: 3,}));const endPointFeature = new Feature({geometry: endPoint,});endPointFeature.setStyle(new Style({image: new Icon({src: new URL("/src/assets/imgs/car/end_point_icon.png", import.meta.url).href,// 調整圖標中心點位置anchor: [0.4, 1],rotateWithView: true,rotation: 0,}),zIndex: 8,}));const startAndEndIconSource = new VectorSource({features: [startPointFeature, endPointFeature],});const startAndEndIconLayer = new VectorLayer({source: startAndEndIconSource,});map.addLayer(startAndEndIconLayer);
};

效果如下:
在這里插入圖片描述

繪制仿高德方向箭頭

參考了很多文章,算是把原理弄通了,簡單的描述一下

  1. 首先計算路徑長度,按照路徑長度除以固定寬度分割,計算需要顯示箭頭的位置
    舉個例子
    a. 當前坐標[0,0] 下一個坐標是[1,2]
    b. 計算這倆個坐標之間的距離,假設100米,每50米顯示一個箭頭,也就是需要三個箭頭
    c. Math.atan2 可以計算[0,0]和[1,2] 之間旋轉的角度,這三個箭頭旋轉都是這個角度

  2. 按照當前點位和下一個點位計算出箭頭的方向,主要通過js 提供的函數

  3. 那知道需要繪制箭頭的地方就簡單了,直接增加Point即可

// Math.atan2 是 JavaScript 中的一個數學函數,用于計算平面坐標中從原點 (0, 0) 到指定點 (x, y) 的有符號弧度角。它返回的角度范圍是從 -π 到 π(即 -180° 到 180°)
Math.atan2(y: number, x: number): number;

在這里插入圖片描述

const drawDynamicCarLine = (linePoints: Array<Point>) => {// 內部軌跡線lineFeature = new Feature(new LineString(linePoints.map((item) => item.getCoordinates())));lineFeature.setStyle(new Style({stroke: new Stroke({color: "rgb(237,73,23)",width: 3,}),zIndex: 4,}));// 外部邊框線const outLineFeature = new Feature(new LineString(linePoints.map((item) => item.getCoordinates())));outLineFeature.setStyle(new Style({stroke: new Stroke({color: "rgb(107,179,117)",width: 8,}),zIndex: 1,}));// 新增箭頭繪制邏輯const arrowPoints = [];const arrowSpacing = 0.1; // 箭頭間距,單位為線段長度的百分比const geometry = lineFeature.getGeometry() as LineString;const coordinates = geometry.getCoordinates();for (let i = 0; i < coordinates.length - 1; i++) {const start = coordinates[i];const end = coordinates[i + 1];const dx = end[0] - start[0];const dy = end[1] - start[1];const length = Math.sqrt(dx * dx + dy * dy); // 計算路徑總長度const numArrows = Math.ceil(length / arrowSpacing); // 這個長度需要幾個箭頭for (let j = 1; j < numArrows; j++) {const fraction = j / numArrows;const arrowX = start[0] + dx * fraction;const arrowY = start[1] + dy * fraction;const arrowAngle = Math.atan2(dy, dx); // 計算箭頭旋轉角度const arrowFeature = new Feature({geometry: new Point([arrowX, arrowY]),});arrowFeature.setStyle(new Style({image: new Icon({src: new URL("/src/assets/imgs/car/arrow-right-bold.png",import.meta.url).href,rotation: -arrowAngle, scale: 0.5, // 調整箭頭大小anchor: [0.5, 0.5], // 確保箭頭中心點對齊rotateWithView: true, // 確保箭頭隨地圖旋轉}),}));arrowPoints.push(arrowFeature);}}const arrowSource = new VectorSource({features: arrowPoints,});const arrowLayer = new VectorLayer({source: arrowSource,zIndex: 5, // 確保箭頭在軌跡線上方});map.addLayer(arrowLayer);const lineSource = new VectorSource({features: [lineFeature, outLineFeature],});lineLayer = new VectorLayer({source: lineSource,});map.addLayer(lineLayer);
};
模仿車輛動態運動動畫

其實原理是和繪制箭頭類似的,只不過需要一直觸發重復繪制,讓小車這個點位圖標看起來在運動
lineLayer.on(“postrender”, ()=>{});
昨天還在吐槽官網,對于我們后端來說,這個圖層可以綁定事件,那么事件的種類是不是可以列在后面,結果指向EventType,但是EventType 里面有什么都沒有,感覺這個文檔不是很友好,這個函數是通過別人的博客看來的,猜想是地圖繪制或者類似的,就是可以一直觸發,所學有限,但是感覺一直觸發是不是有點影響性能呀,先實現吧,后續了解更多了在補充。


let animating = false; // 動畫狀態
let distance = 0;
let lastTime: any;
let speedInput = 50; // 速度輸入
let lastPoint;
let currentPoint;const startMoveCartEvent = (event: RenderEvent) => {if (!animating) return; // 如果動畫未啟動,則不執行const speed = Number(speedInput);const time = (event as any).frameState.time;const elapsedTime = time - lastTime;distance = (distance + (speed * elapsedTime) / 1e6) % 2;lastTime = time;const currentCoordinate = lineFeature.getGeometry()?.getCoordinateAt(distance > 1 ? 2 - distance : distance);const position = startPointMarker.getGeometry()?.clone();lastPoint = position?.getCoordinates();currentPoint = currentCoordinate;// 計算車頭旋轉角度let dx = currentPoint[0] - lastPoint[0];let dy = currentPoint[1] - lastPoint[1];let rotation = Math.atan2(dy, dx); // 直接使用弧度值,無需轉換為角度position.setCoordinates(currentCoordinate);const vectorContext = getVectorContext(event);const carStyle = startPointMarker.getStyle();carStyle.getImage().setRotation(-rotation); // 設置車頭旋轉角度vectorContext.setStyle(carStyle);vectorContext.drawGeometry(position);// 判斷是否到達終點并停止動畫if (distance >= 1) {stopAnimation(); // 調用停止動畫函數}// 告訴 OpenLayers 繼續渲染動畫map.render();
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/80112.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80112.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/80112.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分布式之CAP原則:理解分布式系統的核心設計哲學

聲明&#xff1a;CAP中的P原則都是需要帶著的 在分布式系統的設計與實踐中&#xff0c;CAP原則&#xff08;又稱CAP定理&#xff09;是開發者必須掌握的核心理論之一。它揭示了分布式系統在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#x…

IF=40.8|腫瘤免疫:從免疫基因組學到單細胞分析和人工智能

一、寫在前面 今天分享的是發表在《Signal Transduction and Targeted Therapy》上題目為"Technological advances in cancer immunity: from immunogenomics to single-cell analysis and artificial intelligence"的文章。 IF&#xff1a;40.8 DOI:10.1038/s41392…

深入理解 Spring @Bean 注解

在 Spring 框架中,@Bean 注解是用于顯式地聲明一個或多個 Bean 實例,并將其注冊到 Spring 容器中的重要工具。與 @Component 系列注解不同的是,@Bean 是方法級別的注解,通常與 @Configuration 注解結合使用。本文將詳細介紹 @Bean 注解的功能、用法及其應用場景。 1. @Bean…

Pycharm 如何刪除某個 Python Interpreter

在PyCharm中&#xff0c;點擊右下角的“Interpreter Settings”按鈕&#xff0c;或者通過菜單欄選擇“File” > “Settings”&#xff08;macOS用戶選擇“PyCharm” > “Preferences”&#xff09;。在設置窗口中&#xff0c;導航到“Project: [Your Project Name]” >…

如何改電腦網絡ip地址完整教程

更改電腦的網絡IP地址以滿足特定的網絡需求&#xff0c;本文將為您提供一份詳細的步驟指南。其實&#xff0c;改變IP地址并不是一件復雜的事&#xff0c;能解決因為IP限制帶來的麻煩。以下是操作指南&#xff1a; 方法一&#xff1a;Windows 系統&#xff0c;通過圖形界面修改 …

Oracle--SQL性能優化與提升策略

前言&#xff1a;本博客僅作記錄學習使用&#xff0c;部分圖片出自網絡&#xff0c;如有侵犯您的權益&#xff0c;請聯系刪除 一、導致性能問題的內在原因 系統性能問題的底層原因主要有三個方面&#xff1a; CPU占用率過高導致資源爭用和等待內存使用率過高導致內存不足并需…

【go】什么是Go語言中的GC,作用是什么?調優,sync.Pool優化,逃逸分析演示

Go 語言中的 GC 簡介與調優建議 Go語言GC工作原理 對于 Go 而言&#xff0c;Go 的 GC 目前使用的是無分代&#xff08;對象沒有代際之分&#xff09;、不整理&#xff08;回收過程中不對對象進行移動與整理&#xff09;、并發&#xff08;與用戶代碼并發執行&#xff09;的三…

【unity實戰】Animator啟用root motion根運動動畫,實現完美的動畫動作匹配

文章目錄 前言1、動畫分類2、如何使用根位移動畫&#xff1f; 一、根位移動畫的具體使用1、導入人形模型2、導入動畫3、配置動畫參數4、配置角色Animator動畫狀態機5、使用代碼控制人物前進后退 二、問題分析三、Humanoid動畫中的Root Motion機制及相關配置1、Humanoid動畫中的…

中間件--ClickHouse-10--海量數據存儲如何抉擇ClickHouse和ES?

在Mysql數據存儲或性能瓶頸時&#xff0c;采用冷熱數據分離的方式通常是一種選擇。ClickHouse和Elasticsearch&#xff08;ES&#xff09;是兩個常用的組件&#xff0c;但具體使用哪種組件取決于冷數據的存儲目的、查詢模式和業務需求等方面。 1、核心對比 &#xff08;1&…

服務器運維:服務器流量的二八法則是什么意思?

文章目錄 用戶行為角度時間分布角度應用場景角度 服務器流量的二八法則&#xff0c;又稱 80/20 法則&#xff0c;源自意大利經濟學家帕累托提出的帕累托法則&#xff0c;該法則指出在很多情況下&#xff0c;80% 的結果是由 20% 的因素所決定的。在服務器流量領域&#xff0c;二…

springboot對接豆包大模型

文檔地址: 豆包大模型-火山引擎 模型廣場地址: 賬號登錄-火山引擎 首先來到模型廣場&#xff0c;選取你需要的模型,我這邊要做圖片理解的應用&#xff0c;所以選用了Doubao-1.5.vision-pro. 點立即體驗&#xff0c;進入一個新的頁面&#xff0c;可以上傳圖片&#xff0c;然后…

數據通信學習筆記之OSPF其他內容3

對發送的 LSA 進行過濾 當兩臺路由器之間存在多條鏈路時&#xff0c;可以在某些鏈路上通過對發送的 LSA 進行過濾&#xff0c;減少不必要的重傳&#xff0c;節省帶寬資源。 通過對 OSPF 接口出方向的 LSA 進行過濾可以不向鄰居發送無用的 LSA&#xff0c;從而減少鄰居 LSDB 的…

智能安全用電系統預防電氣線路老化、線路或設備絕緣故障

智能安全用電系統預防電氣線路老化、線路或設備絕緣故障 智能安全用電系統&#xff0c;猶如一位忠實而敏銳的衛士&#xff0c;主要針對低壓供電網中一系列潛在的危險狀況進行了全方位且行之有效的預防和保護。 智能安全用電系統在低壓供電網這個復雜的體系中&#xff0c;電氣線…

使用Intel Advisor工具分析程序

使用Intel Advisor工具分析程序 Intel Advisor是一款性能分析工具&#xff0c;主要用于識別代碼中的向量化機會、線程化和內存訪問模式等問題。以下是使用Intel Advisor分析程序的基本步驟&#xff1a; 安裝與準備 從Intel官網下載并安裝Intel Advisor&#xff08;通常作為I…

【UniApp】Vue2 scss 預編譯器默認已由 node-sass 更換為 dart-sass

從 HBuilderX 4.56 &#xff0c;vue2 項目也將默認使用 dart-sass 預編譯器。 vue2開發者sass預處理注意&#xff1a; sass的預處理器&#xff0c;早年使用node-sass&#xff0c;也就是vue2最初默認的編譯器。 sass官方推出了dart-sass來替代。node-sass已經停維很久了。 另…

智慧能源安全新紀元:當能源監測遇上視頻聯網的無限可能

引言&#xff1a;在數字化浪潮席卷全球的今天&#xff0c;能源安全已成為國家安全戰略的重要組成部分。如何構建更加智能、高效的能源安全保障體系&#xff1f;能源安全監測平臺與視頻監控聯網平臺的深度融合&#xff0c;正為我們開啟一扇通向未來能源管理新世界的大門。這種創…

C++游戲服務器開發之⑦redis的使用

目錄 1.當前進度 2.守護進程 3.進程監控 4.玩家姓名添加文件 5.文件刪除玩家姓名 6.redis安裝 7.redis存取命令 8.redis鏈表存取 9.redis程序結構 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE編寫 13.游戲業務實現總結 1.當前進度 2.守護進程 3.進程監…

db中查詢關于null的sql該怎么寫

正確示例 # 等于null select * from 表名 where 字段名 is NULL; # 不等于null select * from 表名 where 字段名 is not NULL;若需要同時判斷字段不等于某個值且不為null select * from users where age ! 30 and age is not null; select * from users where age ! 30 or a…

從“堆料競賽”到“體驗深耕”,X200 Ultra和X200s打響手機價值升維戰

出品 | 何璽 排版 | 葉媛 vivo雙旗艦來襲&#xff01; 4月21日&#xff0c;vivo X系列春季新品發布會盛大開啟&#xff0c;帶來了一場科技與創新的盛宴。會上&#xff0c;消費者期待已久的X200 Ultra及X200s兩款旗艦新品正式發布。 vivo兩款旗艦新品發布后&#xff0c;其打破…

多模態大語言模型arxiv論文略讀(三十二)

Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ?? 論文標題&#xff1a;Proximity QA: Unleashing the Power of Multi-Modal Large Language Models for Spatial Proximity Analysis ?? 論文作者&#xff1a…