OpenLayers 綜合案例-信息窗體-彈窗

看過的知識不等于學會。唯有用心總結、系統記錄,并通過溫故知新反復實踐,才能真正掌握一二
作為一名摸爬滾打三年的前端開發,開源社區給了我飯碗,我也將所學的知識體系回饋給大家,助你少走彎路!
OpenLayers、Leaflet 快速入門 ,每周保持更新 2 個案例
Cesium 快速入門,每周保持更新 4 個案例

OpenLayers 綜合案例-信息窗體-彈窗

Vue 3 + OpenLayers 實現的 WebGIS 應用提供了完整的信息窗體-彈窗功能

主要功能

  1. 地圖點擊彈窗(顯示坐標、縮放、分辨率)
  2. 雙擊添加點標記(彈窗可刪除)
  3. 框選(Ctrl+拖動)彈窗顯示包絡框
  4. 要素刪除功能(單個刪除、全部刪除)
  5. 右鍵菜單(放大、縮小、定位、關閉)
    在這里插入圖片描述

MP4效果動畫鏈接地址

技術棧

該環境下代碼即拿即用

Vue 3.5.13+
OpenLayers 10.5.0+
Vite 6.3.5+
<template><div class="ol-tutorial-demo-container"><div ref="mapContainer" class="map"></div><!-- 狀態欄 --><div class="statusbar"><span>坐標: {{ mouseCoord }}</span><span>縮放: {{ zoom }}</span><span>旋轉: {{ rotation }}°</span><span>分辨率: {{ resolution }}</span></div><!-- 框選包絡框彈窗 --><div v-if="boxInfo" class="popup" :style="boxPopupStyle"><div><strong>框選范圍</strong></div><div>西南: {{ boxInfo.sw }}</div><div>東北: {{ boxInfo.ne }}</div><button class="close-btn" @click="boxInfo = null">關閉</button></div><!-- 點標記彈窗 --><div v-if="pointPopup" class="popup" :style="pointPopupStyle"><div><strong>點標記</strong></div><div>經度: {{ pointPopup.lon }}</div><div>緯度: {{ pointPopup.lat }}</div><button class="close-btn" @click="removePoint(pointPopup.id)">刪除</button><button class="close-btn" @click="pointPopup = null">關閉</button></div><!-- 地圖點擊彈窗 --><div v-if="mapPopup" class="popup" :style="mapPopupStyle"><div><strong>地圖信息</strong></div><div>經度: {{ mapPopup.lon }}</div><div>緯度: {{ mapPopup.lat }}</div><div>縮放: {{ mapPopup.zoom }}</div><div>分辨率: {{ mapPopup.resolution }}</div><button class="close-btn" @click="mapPopup = null">關閉</button></div><!-- 右鍵菜單 --><ulv-if="contextMenu.visible"class="context-menu":style="contextMenu.style"><li @click="zoomIn">放大一級</li><li @click="zoomOut">縮小一級</li><li @click="centerBeijing">定位到北京</li><li @click="closeContextMenu">關閉</li></ul></div>
</template><script setup>
import { ref, onMounted, onUnmounted, nextTick } from "vue";
import Map from "ol/Map";
import View from "ol/View";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { XYZ, Vector as VectorSource } from "ol/source";
import { Point } from "ol/geom";
import Feature from "ol/Feature";
import { Style, Stroke, Circle, Fill } from "ol/style";
import { defaults as defaultControls, FullScreen, ScaleLine } from "ol/control";
import { defaults as defaultInteractions } from "ol/interaction/defaults";
import { fromLonLat, toLonLat } from "ol/proj";
import DragBox from "ol/interaction/DragBox";
import { platformModifierKeyOnly } from "ol/events/condition";
import "ol/ol.css";const map = ref(null);
const mapContainer = ref(null);
const vectorSource = ref(null);
const pointLayer = ref(null);
const dragBox = ref(null);// 狀態欄
const mouseCoord = ref("");
const zoom = ref(0);
const rotation = ref(0);
const resolution = ref(0);// 地圖彈窗
const mapPopup = ref(null);
const mapPopupStyle = ref({});// 點標記
const points = ref([]); // {id, lon, lat, feature}
const pointPopup = ref(null);
const pointPopupStyle = ref({});
let pointId = 1;
function addPoint(lon, lat) {const feature = new Feature({ geometry: new Point(fromLonLat([lon, lat])) });feature.setStyle(new Style({image: new Circle({radius: 8,fill: new Fill({ color: "#ff5722" }),stroke: new Stroke({ color: "#fff", width: 2 }),}),}));pointLayer.value.getSource().addFeature(feature);points.value.push({ id: pointId++, lon, lat, feature });
}
function removePoint(id) {const idx = points.value.findIndex((p) => p.id === id);if (idx !== -1) {pointLayer.value.getSource().removeFeature(points.value[idx].feature);points.value.splice(idx, 1);pointPopup.value = null;}
}// 框選
const boxInfo = ref(null);
const boxPopupStyle = ref({});// 右鍵菜單
const contextMenu = ref({ visible: false, style: {}, pixel: null });
function closeContextMenu() {contextMenu.value.visible = false;
}
function zoomIn() {map.value.getView().setZoom(map.value.getView().getZoom() + 1);closeContextMenu();
}
function zoomOut() {map.value.getView().setZoom(map.value.getView().getZoom() - 1);closeContextMenu();
}
function centerBeijing() {map.value.getView().setCenter(fromLonLat([116.4, 39.9]));map.value.getView().setZoom(12);closeContextMenu();
}onMounted(() => {// 初始化圖層const baseLayer = new TileLayer({source: new XYZ({url: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",}),});vectorSource.value = new VectorSource();pointLayer.value = new VectorLayer({ source: new VectorSource() });// 初始化地圖map.value = new Map({target: mapContainer.value,layers: [baseLayer, pointLayer.value],view: new View({ center: fromLonLat([116.4, 39.9]), zoom: 12 }),controls: defaultControls().extend([new FullScreen(), new ScaleLine()]),interactions: defaultInteractions({doubleClickZoom: false, // 禁用雙擊縮放}),});// 狀態欄zoom.value = map.value.getView().getZoom();rotation.value = ((map.value.getView().getRotation() * 180) /Math.PI).toFixed(1);resolution.value = map.value.getView().getResolution().toFixed(2);map.value.on("pointermove", (evt) => {const [lon, lat] = toLonLat(evt.coordinate);mouseCoord.value = `${lon.toFixed(6)}, ${lat.toFixed(6)}`;});map.value.getView().on("change:rotation", () => {rotation.value = ((map.value.getView().getRotation() * 180) /Math.PI).toFixed(1);});map.value.getView().on("change:resolution", () => {resolution.value = map.value.getView().getResolution().toFixed(2);zoom.value = map.value.getView().getZoom().toFixed(2);});// 單擊彈窗map.value.on("singleclick", (evt) => {closeContextMenu();// 檢查點標記const feature = map.value.forEachFeatureAtPixel(evt.pixel, (f) => f);if (feature &&pointLayer.value.getSource().getFeatures().includes(feature)) {// 點標記彈窗const [lon, lat] = toLonLat(feature.getGeometry().getCoordinates());pointPopup.value = {id: points.value.find((p) => p.feature === feature).id,lon: lon.toFixed(6),lat: lat.toFixed(6),};pointPopupStyle.value = {left: evt.pixel[0] + 20 + "px",top: evt.pixel[1] + 20 + "px",};return;}// 地圖彈窗const [lon, lat] = toLonLat(evt.coordinate);mapPopup.value = {lon: lon.toFixed(6),lat: lat.toFixed(6),zoom: zoom.value,resolution: resolution.value,};mapPopupStyle.value = {left: evt.pixel[0] + 20 + "px",top: evt.pixel[1] + 20 + "px",};});// 雙擊添加點標記map.value.on("dblclick", (evt) => {const [lon, lat] = toLonLat(evt.coordinate);addPoint(lon, lat);// 自動彈窗nextTick(() => {pointPopup.value = {id: pointId - 1,lon: lon.toFixed(6),lat: lat.toFixed(6),};pointPopupStyle.value = {left: evt.pixel[0] + 20 + "px",top: evt.pixel[1] + 20 + "px",};});evt.preventDefault();});// 框選dragBox.value = new DragBox({ condition: platformModifierKeyOnly });map.value.addInteraction(dragBox.value);dragBox.value.on("boxend", (e) => {const extent = dragBox.value.getGeometry().getExtent();const sw = toLonLat([extent[0], extent[1]]);const ne = toLonLat([extent[2], extent[3]]);boxInfo.value = {sw: `${sw[0].toFixed(6)}, ${sw[1].toFixed(6)}`,ne: `${ne[0].toFixed(6)}, ${ne[1].toFixed(6)}`,};boxPopupStyle.value = {left: e.target.box_.endPixel_[0] + 20 + "px",top: e.target.box_.endPixel_[1] + 20 + "px",};});// 右鍵菜單mapContainer.value.addEventListener("contextmenu", (e) => {e.preventDefault();contextMenu.value = {visible: true,style: { left: e.clientX + "px", top: e.clientY + "px" },};});
});onUnmounted(() => {if (map.value) map.value.dispose();
});
</script><style scoped>
.ol-tutorial-demo-container {position: relative;width: 100vw;height: 100vh;overflow: hidden;font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.map {width: 100%;height: 100%;
}
.statusbar {position: absolute;left: 20px;bottom: 20px;background: rgba(255, 255, 255, 0.95);border-radius: 8px;padding: 10px 18px;font-size: 1rem;color: #1a237e;display: flex;gap: 18px;z-index: 10;box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.popup {position: absolute;min-width: 180px;background: #fffbe7;border: 1px solid #ffe082;border-radius: 8px;padding: 15px 20px;box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);z-index: 20;color: #795548;
}
.close-btn {margin-top: 10px;background: #ffc107;border: none;border-radius: 6px;padding: 6px 12px;color: #fff;font-weight: 600;cursor: pointer;
}
.close-btn:hover {background: #ff9800;
}
.context-menu {position: absolute;z-index: 30;background: #fff;border: 1px solid #eee;border-radius: 8px;box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);list-style: none;padding: 0;margin: 0;min-width: 140px;font-size: 1rem;
}
.context-menu li {padding: 12px 18px;cursor: pointer;color: #1976d2;border-bottom: 1px solid #f5f5f5;transition: background 0.2s;
}
.context-menu li:last-child {border-bottom: none;
}
.context-menu li:hover {background: #e3f2fd;
}
</style>

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

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

相關文章

GaussDB 開發基本規范

1 集中式1.1數據庫價值特性推薦特性分類特性列表說明表類型PARTITION表數據分區存儲引擎行存儲按行順序存儲表&#xff0c;建議點查&#xff0c;增刪改操作較多場景下使用事務事務塊顯式啟動事務單語句事務不顯式啟動事務&#xff0c;單語句即為事務擴容在線擴容擴節點和數據重…

工作中使用git可能遇到的場景

1.main歷史發布版本出問題需要查看&#xff0c;怎么切換歷史發布版本&#xff1f;git reset --hard commitid 更新本地庫和代碼2.A分支的代碼已經做過一些功能&#xff0c;想遷移到B分支當前在A分支git checkout B &#xff08;切換到B分支&#xff09;git cherry-pick A的com…

【Spring AI】本地大型語言模型工具-Ollama

Ollama 是一個專注于在本地運行大型語言模型&#xff08;LLM&#xff09;的工具&#xff0c;支持多種開源模型&#xff08;如 Llama 3、Mistral、Gemma 等&#xff09;&#xff0c;提供簡單的命令行和 API 接口。<dependency><groupId>org.springframework.ai</…

電機S加減速

STM32步進電機S型加減速算法_stm32___build__-2048 AI社區 以上&#xff0c;電機加減速說的非常清楚&#xff0c;收藏點贊&#xff01;

一、初識 Linux 與基本命令

作者&#xff1a;IvanCodes 日期&#xff1a;2025年7月28日 專欄&#xff1a;Linux教程 思維導圖 一、Linux 簡介 1.1 什么是 Linux? Linux 是一種自由、開源的類Unix操作系統內核&#xff0c;由林納斯托瓦茲 (Linus Torvalds) 在1991年首次發布。我們通常所說的 “Linux 系統…

解決angular與jetty websocket 每30s自動斷連的問題

背景&#xff1a;前端&#xff1a;angular 12&#xff0c;websocket接口由lib.dom.d.ts提供后端&#xff1a;java&#xff0c;websocket接口由jetty 12提供問題現象&#xff1a;前端連上server后&#xff0c;每隔30s就會斷開&#xff0c;由于長時間空閑&#xff0c;會導致webso…

【機器學習深度學習】模型私有化部署與微調訓練:賦能特定問題處理能力

目錄 前言 一、私有化部署的背景&#xff1a;通用能力 ≠ 企業實用 暴露問題 二、微調訓練的核心目的 2.1 動作一&#xff1a;私有化部署&#xff08;Private Deployment&#xff09; 2.2 動作二&#xff1a;領域微調&#xff08;Domain Fine-Tuning&#xff09; 2.3 微…

Seq2Seq學習筆記

Seq2Seq模型概述Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;是一種基于深度學習的序列生成模型&#xff0c;主要用于處理輸入和輸出均為序列的任務&#xff0c;如機器翻譯、文本摘要、對話生成等。其核心思想是將可變長度的輸入序列映射為另一個可變長度的輸出序列。…

react useId

useId useId 是 React 18 引入的一個內置 Hook&#xff0c;用于生成唯一且穩定的 ID &#xff0c; 主要用于&#xff0c;解決在客戶端和服務器端渲染&#xff08;SSR&#xff09;時&#xff0c;動態生成 ID 可能導致的沖突問題&#xff1b; 特別適合用于&#xff0c;需要關聯 H…

排水管網實時監測筑牢城市安全防線

排水管網的實時監測工作&#xff0c;強調其對于保障城市安全的重要作用。“排水管網”明確了具體的關注對象&#xff0c;它是城市基礎設施的重要組成部分&#xff0c;承擔著雨水、污水排放等關鍵功能。“實時監測”突出了監測的及時性和持續性&#xff0c;意味著能夠隨時獲取排…

SZU大學物理實驗報告|電位差計

寫在前面&#xff1a;博文里放圖片&#xff0c;主要省去了對文檔的排版時間&#xff0c;實驗還是要自己做的&#xff0c;反正都要去實驗室上課&#xff0c;順帶鍛煉下動手能力。有些結果是實驗手寫的&#xff0c;所以看不到&#xff0c;有結果的可以對下結果差的不太多就行&…

RoPE簡單解析

文章目錄簡介拆解一些tricks簡介 因為RoPE的優異性能&#xff0c;其已成為各種大模型中位置編碼的首選&#xff0c;包括多模態模型&#xff1b;在一些多模態模型或視頻理解模型中&#xff0c;甚至會用到多維度RoPE。雖然RoPE已廣泛應用&#xff0c;之前也看了不少針對其原理解…

windows 獲取 APK 文件的包名和啟動 Activity 名稱

使用 aapt 命令確保環境變量配置正確&#xff1a;首先需要確保你的系統環境變量中包含了 Android SDK 的 build-tools 目錄路徑。這是因為 aapt 工具位于該目錄下。運行命令&#xff1a; 打開命令提示符&#xff08;CMD&#xff09;&#xff0c;然后輸入以下命令來查看 APK 的詳…

【Mac版】Linux 入門命令行快捷鍵+聯想記憶

Linux Mac 用戶終端命令行快捷鍵 符號速查全解作為一個剛接觸 Linux 和終端的 macOS 用戶&#xff0c;常常被命令行的各種快捷鍵和符號弄得頭暈腦脹&#xff0c;本文將帶你系統地掌握命令行中最常用的快捷鍵和符號&#xff0c;并通過邏輯聯想幫助你輕松記住每一個組合。一、基…

AUTOSAR Mcal Dio - 模塊介紹 + EB配置工具介紹

文章目錄1. 模塊簡介2. 主要功能3. 縮略語4. API接口5. 功能介紹5.1. ChannelGroup5.2. Dio_MaskedWritePort6. 序列圖6.1.讀GPIO電平6.2. 設置GPIO電平7. EB 工具配置7.1.General7.2.DioPort8. 參考資料1. 模塊簡介 Dio&#xff0c;全稱“Digital Input Output”。Dio模塊&am…

ICT模擬零件測試方法--晶體管測試

ICT模擬零件測試方法–晶體管測試 文章目錄ICT模擬零件測試方法--晶體管測試晶體管測試晶體管測試配置晶體管測試配置晶體管測量選項晶體管測試 i3070 在線測試軟件為每個晶體管提供兩種測試&#xff1a; 使用二極管測試對晶體管的兩個 PN 結進行測試。這是檢查設備存在的快速…

AI算法實現解析-C++實例

基于C++實現的AI 以下是基于C++實現的AI/機器學習相關示例,涵蓋基礎算法、計算機視覺、自然語言處理等領域,適合不同階段的學習者參考: 基礎機器學習算法 線性回歸 使用梯度下降法預測連續值,核心公式: 損失函數: 邏輯回歸 二分類問題實現,Sigmoid函數: K-Means…

亞馬遜云科技實戰架構:構建可擴展、高效率、無服務器應用

對于今天的開發者、架構師和技術愛好者而言&#xff0c;云計算早已超越了簡單的“虛擬機租賃”或“服務器托管”階段。它已經演化為一套豐富、強大且精密的工具集&#xff0c;能夠以前所未有的方式設計、部署和擴展應用程序。真正的云原生思維&#xff0c;是掌握并運用多種架構…

論文閱讀:《無約束多目標優化的遺傳算法,群體和進化計算》

前言 提醒&#xff1a; 文章內容為方便作者自己后日復習與查閱而進行的書寫與發布&#xff0c;其中引用內容都會使用鏈接表明出處&#xff08;如有侵權問題&#xff0c;請及時聯系&#xff09;。 其中內容多為一次書寫&#xff0c;缺少檢查與訂正&#xff0c;如有問題或其他拓展…

嵌入式單片機中位帶操作控制與實現

STM32 單片機的SRAM有兩個區支持位帶(bit-band)操作。 那么,什么是位帶,位帶操作的原理是怎樣的呢? 今天來梳理一下這個知識點。 在介紹位帶操作之前,先看一看 ARM Crotext-M3 的存儲器映射。 CM3 的地址空間是 4GB, 程序可以在代碼區,內部 SRAM 區以及外部 RAM 區中執…