3、Vue 中使用 Cesium 實現可拖拽點標記及坐標實時顯示功能

在 Cesium 地圖開發中,實現點標記的拖拽交互并實時顯示坐標信息是一個常見的需求。本文將詳細介紹如何在 Vue 框架中使用 Cesium 的 Primitive 方式創建點標記,并實現拖拽功能及坐標提示框跟隨效果。

先看效果圖

功能實現概述

我們將實現的功能包括:

  • 使用 Primitive 方式創建可交互的點標記
  • 實現點標記的拖拽功能(點擊選中、跟隨鼠標移動、釋放確定位置)
  • 拖拽過程中實時顯示坐標信息提示框
  • 拖拽時禁用地圖默認交互,提升操作體驗

核心技術點解析

1. 創建可拖拽點標記

使用PointPrimitiveCollection創建點集合,添加點標記并設置基本樣式:

createPoint_primitives() {// 創建點集合const pointCollection = new Cesium.PointPrimitiveCollection();// 添加點this.draggablePoint = pointCollection.add({position: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 0), // 初始位置(北京坐標)color: Cesium.Color.RED, // 點顏色pixelSize: 30, // 點大小outlineColor: Cesium.Color.WHITE, // 輪廓顏色outlineWidth: 2, // 輪廓寬度id: 'draggable-point', // 唯一標識});// 將點集合添加到場景圖元中this.viewer.scene.primitives.add(pointCollection);// 設置點的拖動功能this.setupPointDragging();
}

2. 實現拖拽交互邏輯

拖拽功能主要通過監聽鼠標的三個事件實現:左鍵按下、鼠標移動和左鍵釋放。

左鍵按下事件

// 左鍵按下事件 - 開始拖動
handler.setInputAction((click) => {// 拾取鼠標點擊位置的對象const pickedObject = this.viewer.scene.pick(click.position);// 檢查是否拾取到了我們創建的點if (Cesium.defined(pickedObject) &&  // 確保拾取對象存在pickedObject.primitive === this.draggablePoint  // 確認是目標點) {isDragging = true;currentPoint = pickedObject.primitive;currentPoint.color = Cesium.Color.BLUE; // 改變顏色表示選中狀態// 顯示坐標提示框并更新位置this.updateTooltip(currentPoint.position);this.showCoordinates = true;this.disableMapInteraction(); // 禁用地圖默認交互}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);

這里的Cesium.defined(pickedObject)是 Cesium 提供的工具函數,用于檢查一個值是否 "已定義且非空",避免后續操作出現空指針錯誤。

鼠標移動事件

在鼠標移動時,實時更新點的位置和坐標提示框:

// 鼠標移動事件 - 更新點位置
handler.setInputAction((movement) => {if (!isDragging || !currentPoint) return;// 將鼠標位置轉換為地理坐標const ray = this.viewer.camera.getPickRay(movement.endPosition);const position = this.viewer.scene.globe.pick(ray, this.viewer.scene);if (Cesium.defined(position)) {// 更新點的位置currentPoint.position = position;// 移動時實時更新提示框位置this.updateTooltip(position);}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
左鍵釋放事件

拖拽結束時,恢復點的樣式并保存最終位置:

// 左鍵釋放事件 - 結束拖動
handler.setInputAction(() => {if (isDragging && currentPoint) {// 恢復點的原始顏色currentPoint.color = Cesium.Color.RED;// 獲取最終位置的經緯度const cartographic = Cesium.Cartographic.fromCartesian(currentPoint.position);const longitude = Cesium.Math.toDegrees(cartographic.longitude);const latitude = Cesium.Math.toDegrees(cartographic.latitude);console.log(`點位置已更新至: 經度 ${longitude.toFixed(4)}, 緯度 ${latitude.toFixed(4)}`);this.savePointPosition(longitude, latitude);}// 恢復地圖的默認鼠標交互this.enableMapInteraction();isDragging = false;currentPoint = null;
}, Cesium.ScreenSpaceEventType.LEFT_UP);

3. 坐標提示框跟隨功能

實現坐標提示框跟隨點移動的核心是updateTooltip方法,該方法完成兩件事:將三維坐標轉換為經緯度,以及將三維坐標轉換為屏幕坐標用于定位提示框。

updateTooltip(position) {// 1. 計算經緯度信息const cartographic = Cesium.Cartographic.fromCartesian(position);this.coordinate = {lng: Cesium.Math.toDegrees(cartographic.longitude),lat: Cesium.Math.toDegrees(cartographic.latitude),};// 2. 計算屏幕坐標(兼容不同Cesium版本)const screenPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates? Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene,position): Cesium.SceneTransforms.worldToWindowCoordinates(this.viewer.scene,position);// 3. 設置提示框位置(偏移20px避免遮擋點)if (screenPosition) {this.tooltipPosition = {x: screenPosition.x + 20,y: screenPosition.y - 20,};}
}

4. 地圖交互控制

為了提升拖拽體驗,在拖拽過程中需要禁用地圖的默認交互,拖拽結束后再恢復:

// 禁用地圖交互的方法
disableMapInteraction() {// 禁用鼠標左鍵拖動地圖this.viewer.scene.screenSpaceCameraController.enableRotate = false;// 禁用鼠標右鍵縮放this.viewer.scene.screenSpaceCameraController.enableZoom = false;// 禁用中鍵平移this.viewer.scene.screenSpaceCameraController.enableTranslate = false;// 禁用傾斜this.viewer.scene.screenSpaceCameraController.enableTilt = false;// 禁用旋轉this.viewer.scene.screenSpaceCameraController.enableLook = false;
}// 恢復地圖交互的方法
enableMapInteraction() {// 恢復所有鼠標交互this.viewer.scene.screenSpaceCameraController.enableRotate = true;this.viewer.scene.screenSpaceCameraController.enableZoom = true;this.viewer.scene.screenSpaceCameraController.enableTranslate = true;this.viewer.scene.screenSpaceCameraController.enableTilt = true;this.viewer.scene.screenSpaceCameraController.enableLook = true;
}

完整代碼實現

下面是完整的 Vue 組件代碼,包含了上述所有功能:

高德地圖三個地址
export const mapConfig = {gaode: {url1: 'http://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8', //'高德路網中文注記'url2: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}', //高德影像url3: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', //高德矢量},
};
?封裝初始化地圖實例,組件直接引用
/*** 初始化地圖實例* @param {string} url - 地圖瓦片服務的URL模板* @param {boolean} is3d - 是否啟用3D地圖模式*/
export default function initMap(url, is3d) {// 初始化地圖// 創建一個Cesium Viewer實例,綁定到ID為'cesiumContainer'的DOM元素const viewer = new Cesium.Viewer('cesiumContainer', {animation: false, // 隱藏動畫控件baseLayerPicker: false, // 隱藏基礎圖層選擇器fullscreenButton: false, // 隱藏全屏按鈕geocoder: false, // 隱藏地理編碼搜索框homeButton: false, // 隱藏主頁按鈕infoBox: false, // 隱藏信息框sceneModePicker: false, // 隱藏場景模式選擇器scene3DOnly: is3d ? true : false, // 是否啟用3D-only模式優化性能,若is3d為true則啟用sceneMode: is3d ? Cesium.SceneMode.SCENE3D : Cesium.SceneMode.SCENE2D, // 場景模式,is3d為true時使用3D場景,否則使用2D場景selectionIndicator: false, // 隱藏選擇指示器timeline: false, // 隱藏時間軸navigationHelpButton: false, // 隱藏導航幫助按鈕navigationInstructionsInitiallyVisible: false, // 初始時不顯示導航說明shouldAnimate: true, // 啟用場景動畫projection: new Cesium.WebMercatorProjection(), // 地圖投影,使用Web墨卡托投影});// 移除默認影像圖層viewer.scene.imageryLayers.remove(viewer.scene.imageryLayers.get(0));// 去除版權信息viewer._cesiumWidget._creditContainer.style.display = 'none';// 向地圖的影像圖層添加一個自定義的瓦片影像服務viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({url: url, // 瓦片服務的URL模板subdomains: ['0', '1', '2', '3'], // 子域名列表maximumLevel: 18, // 最大縮放級別}));// 初始定位viewer.camera.setView({destination: Cesium.Cartesian3.fromDegrees(118.006, 39.7128, 1500000), // (lng, lat, height)});return viewer;
}
地圖組件代碼
<template><div id="cesiumContainer" style="width: 100%; height: 100vh"><!-- 坐標信息提示框 --><divv-if="showCoordinates"class="coordinate-tooltip":style="{ left: tooltipPosition.x + 'px', top: tooltipPosition.y + 'px' }">經度: {{ coordinate.lng.toFixed(6) }}<br />緯度: {{ coordinate.lat.toFixed(6) }}</div></div>
</template><script>
import initMap from '@/config/initMap.js';
import { mapConfig } from '@/config/mapConfig';
export default {data() {return {viewer: null,showCoordinates: false,coordinate: { lng: 0, lat: 0 },tooltipPosition: { x: 0, y: 0 },};},mounted() {this.viewer = initMap(mapConfig.gaode.url3, false);this.$nextTick(async () => {await this.createPoint_primitives(); // primitives方式創建});},methods: {// 創建點標記createPoint_primitives() {// 創建點集合const pointCollection = new Cesium.PointPrimitiveCollection();// 添加點this.draggablePoint = pointCollection.add({position: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 0),color: Cesium.Color.RED,pixelSize: 30,outlineColor: Cesium.Color.WHITE,outlineWidth: 2,id: 'draggable-point',});// 將點集合添加到場景圖元中this.viewer.scene.primitives.add(pointCollection);// 設置點的拖動功能this.setupPointDragging();},setupPointDragging() {const handler = new Cesium.ScreenSpaceEventHandler(this.viewer.canvas);let isDragging = false;let currentPoint = null;// 左鍵按下事件 - 開始拖動handler.setInputAction((click) => {const pickedObject = this.viewer.scene.pick(click.position); // 拾取鼠標點擊位置的對象// 檢查是否拾取到了目標點if (Cesium.defined(pickedObject) &&  // 確保拾取對象存在pickedObject.primitive === this.draggablePoint  // 確認是我們創建的點) {isDragging = true;currentPoint = pickedObject.primitive;currentPoint.color = Cesium.Color.BLUE; // 改變點的外觀以指示正在拖動// 初始顯示提示框并更新位置this.updateTooltip(currentPoint.position);this.showCoordinates = true;this.disableMapInteraction(); // 禁用地圖的默認鼠標交互}}, Cesium.ScreenSpaceEventType.LEFT_DOWN);// 鼠標移動事件 - 更新點位置handler.setInputAction((movement) => {if (!isDragging || !currentPoint) return;// 將鼠標位置轉換為地理坐標const ray = this.viewer.camera.getPickRay(movement.endPosition);const position = this.viewer.scene.globe.pick(ray, this.viewer.scene);if (Cesium.defined(position)) {// 更新點的位置currentPoint.position = position;// 移動時實時更新提示框位置this.updateTooltip(position);}}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);// 左鍵釋放事件 - 結束拖動handler.setInputAction(() => {if (isDragging && currentPoint) {// 恢復點的原始顏色currentPoint.color = Cesium.Color.RED;// 獲取最終位置的經緯度const cartographic = Cesium.Cartographic.fromCartesian(currentPoint.position);const longitude = Cesium.Math.toDegrees(cartographic.longitude);const latitude = Cesium.Math.toDegrees(cartographic.latitude);console.log(`點位置已更新至: 經度 ${longitude.toFixed(4)}, 緯度 ${latitude.toFixed(4)}`);this.savePointPosition(longitude, latitude);}// 恢復地圖的默認鼠標交互this.enableMapInteraction();isDragging = false;currentPoint = null;}, Cesium.ScreenSpaceEventType.LEFT_UP);},// 更新提示框位置和坐標信息updateTooltip(position) {// 1. 計算經緯度信息const cartographic = Cesium.Cartographic.fromCartesian(position);this.coordinate = {lng: Cesium.Math.toDegrees(cartographic.longitude),lat: Cesium.Math.toDegrees(cartographic.latitude),};// 2. 計算屏幕坐標(兼容不同Cesium版本)const screenPosition = Cesium.SceneTransforms.wgs84ToWindowCoordinates? Cesium.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene,position): Cesium.SceneTransforms.worldToWindowCoordinates(this.viewer.scene,position);// 3. 設置提示框位置(偏移20px避免遮擋點)if (screenPosition) {this.tooltipPosition = {x: screenPosition.x + 20,y: screenPosition.y - 20,};}},// 禁用地圖交互的方法disableMapInteraction() {// 禁用鼠標左鍵拖動地圖this.viewer.scene.screenSpaceCameraController.enableRotate = false;// 禁用鼠標右鍵縮放this.viewer.scene.screenSpaceCameraController.enableZoom = false;// 禁用中鍵平移this.viewer.scene.screenSpaceCameraController.enableTranslate = false;// 禁用傾斜this.viewer.scene.screenSpaceCameraController.enableTilt = false;// 禁用旋轉this.viewer.scene.screenSpaceCameraController.enableLook = false;},// 恢復地圖交互的方法enableMapInteraction() {// 恢復所有鼠標交互this.viewer.scene.screenSpaceCameraController.enableRotate = true;this.viewer.scene.screenSpaceCameraController.enableZoom = true;this.viewer.scene.screenSpaceCameraController.enableTranslate = true;this.viewer.scene.screenSpaceCameraController.enableTilt = true;this.viewer.scene.screenSpaceCameraController.enableLook = true;},// 保存點位置的方法(可根據實際需求實現)savePointPosition(longitude, latitude) {// 示例:可以將位置發送到服務器或保存到本地存儲console.log(`保存點位置: 經度 ${longitude}, 緯度 ${latitude}`);// 實際應用中可能會調用API// fetch('/api/save-point', {//   method: 'POST',//   body: JSON.stringify({ longitude, latitude }),// });},},beforeDestroy() {// 組件銷毀時釋放資源if (this.viewer) {this.viewer.destroy();}},
};
</script><style lang="scss" scoped>
#cesiumContainer {width: 100%;height: 100vh;touch-action: none; /* 優化移動端交互 */position: relative; /* 確保提示框定位正確 */overflow: hidden;
}
.coordinate-tooltip {position: absolute;background-color: rgba(0, 0, 0, 0.7);color: white;padding: 8px 12px;border-radius: 4px;font-size: 12px;pointer-events: none; /* 確保鼠標事件能穿透提示框 */z-index: 1000; /* 確保提示框顯示在最上層 */animation: fadeIn 0.3s ease-out; /* 淡入動畫 */
}@keyframes fadeIn {from {opacity: 0;transform: translateY(10px);}to {opacity: 1;transform: translateY(0);}
}
</style>

總結

本文詳細介紹了在 Vue 中使用 Cesium 實現可拖拽點標記及坐標實時顯示的功能。通過 Primitive 方式創建點標記,結合 Cesium 的事件處理機制實現拖拽交互,并通過坐標轉換實現提示框跟隨效果。

這種實現方式具有較好的性能表現,適用于需要在地圖上進行點位調整和標記的場景。你可以根據實際需求擴展功能,如添加拖拽范圍限制、保存歷史位置、添加更多樣式的視覺反饋等。

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

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

相關文章

Anthropic:從OpenAI分支到AI領域的領軍者

自2021年由前OpenAI高管Dario和Daniela Amodei創立以來&#xff0c;Anthropic已迅速崛起為人工智能&#xff08;AI&#xff09;領域的重要力量。 公司專注于開發安全、可控且具備深度推理能力的AI系統&#xff0c;其Claude系列模型在生成式AI領域取得了顯著成就。 此外&#xf…

前端開發中的輸出問題

前端開發中的輸出問題&#xff1a;console.log輸出[object Object]在前端開發中&#xff0c;一個常見問題是使用console.log輸出對象時顯示為[object Object]&#xff0c;而不是對象的詳細內容。這通常發生在開發者試圖直接打印對象時&#xff0c;瀏覽器默認只顯示對象的字符串…

DSSA(Domain-Specific Software Architecture)特定領域架構

DSSA&#xff08;Domain-Specific Software Architecture&#xff09; 定義&#xff1a;針對特定應用領域設計的可復用軟件架構&#xff0c;為領域內產品族提供統一基礎。 目標&#xff1a; ? 最大化復用&#xff08;需求/設計/代碼&#xff09;? 保證系統一致性? 降低開發成…

單調棧單調隊列【算法進階】

這周學完之后最大的收獲就是單調棧和單調隊列了&#xff01;&#xff01;&#xff01;感覺好厲害能把時間復雜度瞬間壓縮為O(N)&#xff0c;不行我必須再紀念一下這么美妙的算法&#xff01;&#xff01;&#xff01; 單調棧問題&#xff1a; 如果題目要求一個元素左邊或右邊…

C++編程基礎

編程題一問題分析 題目要求使用 n 根小木棒&#xff0c;按照特定的方式排列&#xff0c;形成一個數字。具體規則如下&#xff1a; 每個數字由小木棒組成&#xff0c;例如&#xff1a; 1 需要 2 根小木棒。0 需要 6 根小木棒。其他數字&#xff08;如 2, 3, 4, 5, 6, 7, 8, 9&am…

張量拼接操作

一.前言本章節來介紹一下張量拼接的操作&#xff0c;掌握torch.cat torch.stack使?&#xff0c;張量的拼接操作在神經?絡搭建過程中是?常常?的?法&#xff0c;例如: 在后?將要學習到的殘差?絡、注意?機 制中都使?到了張量拼接。二.torch.cat 函數的使用torch.cat 函數…

Dify 連接本地 SpringAI MCP Server

Dify 連接本地 SpringAI MCP server 連接 MCP server 的方式大致有兩種&#xff0c;一種是基于 stdio&#xff0c;一種是基于 sse&#xff0c;如果對于穩定和性能好的方案的話&#xff0c;sse 要比 stdio 好的多&#xff0c;所以本文采用的是基于 sse 和 Spring AI 部署本地 MC…

基于 Python 的數據分析技術綜述

先說一點個人的看法“”MDX、OLAP&#xff08;Mondrian&#xff09;技術更適合構建面向業務用戶的標準化分析產品&#xff0c;尤其當產品需要滿足以下特點時&#xff1a;分析維度固定&#xff08;如時間、區域、產品類別&#xff09;&#xff1b;需支持高并發查詢&#xff08;如…

Live555-RTSP服務器

RTSP Server創建 RTSP服務器初始化&#xff1a; RTSPServer::createNew->new RTSPServer::RTSPServer->GenericMediaServer::GenericMediaServer->turnOnBackgroundReadHandling(IPV4sock/IPV6sock,incomingConnectionHandlerIPv4)如上流程&#xff0c;創建RTSP服務器…

Redis Stack擴展功能

Redis JSONRedisJSON是Redis的一個擴展模塊&#xff0c;它提供了對JSON數據的原生支持。常用操作&#xff1a;-- 設置一個JSON數據JSON.SET user $ {"name":"loulan","age":18}## key是user&#xff0c;value就是一個JSON數據。其中$表示JSON數據…

Takebishi旗下智能硬件網關產品devicegateway詳細介紹

一、產品概述 DeviceGateway是由日本Takebishi公司研發的一款專業工業物聯網&#xff08;IIoT&#xff09;硬件網關產品&#xff0c;專為實現現場工業設備與云端平臺、IT系統之間的高效、安全數據傳輸而設計。作為一款可靠的硬件網關&#xff0c;DeviceGateway具有即插即用、穩…

單向鏈表反轉 如何實現

單向鏈表反轉的實現方法 ? https://www.zhihu.com/question/441865393/answer/3208578798 ? 單向鏈表反轉是數據結構中的經典問題&#xff0c;在面試和實際開發中經常遇到。以下是 多種實現方式&#xff08;包括遞歸和迭代&#xff09;&#xff0c;以 Go 語言為例。1. 單向鏈…

php+vue+Laravel音樂媒體播放及周邊產品運營平臺-nodejs-計算機畢業設計

目錄具體實現截圖課程項目技術路線開發技術介紹設計思路流程PHP核心代碼部分展示詳細視頻演示/源碼獲取##項目介紹網絡技術的廣泛應用顯著地推動了生活服務的信息化進程。結合音樂流媒體與周邊產品的運營需求&#xff0c;構建一套音樂媒體播放及周邊產品運營平臺&#xff0c;成…

Python爬蟲實戰:研究xlwt 和 xlrd 庫相關技術

1. 引言 1.1 研究背景與意義 隨著電子商務的快速發展,電商平臺積累了海量的商品數據。如何從這些數據中提取有價值的信息,為商家提供決策支持,成為電商領域的重要研究方向。傳統人工采集和分析數據的方式效率低下,且容易出現錯誤。自動化數據采集與分析系統能夠通過爬蟲技…

【QGC】深入解析 QGC 配置管理

引言 在軟件開發中&#xff0c;配置管理是一項至關重要的任務&#xff0c;它能幫助我們靈活地管理應用程序的各種參數和設置。QGroundControl&#xff08;QGC&#xff09;作為一款強大的開源無人機地面站軟件&#xff0c;其配置管理系統設計精巧&#xff0c;值得我們深入學習。…

ChatGPT,從規則到強化學習

要了解 ChatGPT&#xff08;Chat Generative Pre-training Transformer&#xff09;&#xff0c;我們不得不先看看 NLP 自然語言處理&#xff08;Natural Language Processing&#xff09;。因為 ChatGPT 屬于 NLP 領域&#xff0c;而 NLP 則又是人工智能的一個分支。 那么什么…

【目標檢測之Ultralytics預測框顏色修改】

在 Ultralytics YOLOv8 中修改預測框顏色為紅色&#xff0c;以下是三種實用方案&#xff1a;方案 1&#xff1a;直接修改 plot() 方法的 colors 參數 在調用 results.plot() 時直接指定顏色參數&#xff1a; from ultralytics import YOLO# 加載模型 model YOLO("yolov8n…

讓 VSCode 調試器像 PyCharm 一樣顯示 Tensor Shape、變量形狀、變量長度、維度信息

文章目錄&#x1f3af; 目標&#xff1a;在 VS Code 調試器中自動顯示這些變量信息&#x1f50d; 原理簡介?? 其他方案的局限性? 方案一&#xff1a;重寫 __repr__? 方案二&#xff1a;向 debugpy 注冊自定義變量顯示器&#xff08;StrPresentationProvider&#xff09;? …

pip國內鏡像源一覽

以下是2025年主流pip國內鏡像源完整清單及配置指南&#xff0c;綜合多個權威來源整理的最新數據&#xff1a;一、核心鏡像源推薦&#xff08;2025年穩定可用&#xff09;?阿里云鏡像?https://mirrors.aliyun.com/pypi/simple/優勢&#xff1a;依托阿里云CDN&#xff0c;全國平…

當大模型遇見毫米波:用Wi-Fi信號做“透視”的室內語義SLAM實踐——從CSI到神經輻射場的端到端開源方案

作者 | Blossom.118 2025-07-12 關鍵詞&#xff1a;CSI-SLAM、神經輻射場、毫米波、Transformer、數字孿生、開源 ---- 1. 為什么要“無攝像頭”語義SLAM&#xff1f; ? 隱私紅線&#xff1a;歐盟GDPR 2024修訂版把“攝像頭點云”列入高風險生物特征&#xff0c;落地成本高。…