当前位置: 首页 > news >正文

129.在 Vue3 中使用 OpenLayers 实现点击获取重叠要素信息(支持多 Feature)

🖼️ UI效果图参考

👇 演示图如下:

🎯 效果:

  • 单个要素展示图文信息(名称、地址、头像)

  • 多个重叠要素显示名称列表

  • 点击空白处清除弹窗


🎯 前言

在地图应用中,用户点击地图时,如果该位置有多个图层的要素重叠,理想体验是能获取 所有要素的信息,而不是只获取最顶层的一个。

本文将带你使用 Vue3 + OpenLayers 实现这个功能 👇


📦 项目环境说明

  • Vue 版本:Vue 3 + <script setup>

  • 地图引擎:OpenLayers v7.x+

  • 样式:Tailwind CSS、Element Plus(用于按钮)

  • 坐标系:EPSG:4326


🧩 实现思路概览

  1. 使用 VectorLayer 添加多个 Feature(Polygon)

  2. 每个 Feature 携带自定义信息(如公司名、地址、头像)

  3. 绑定地图 click 事件

  4. 使用 getFeaturesAtPixel() 获取所有命中的要素

  5. 展示弹窗(单个展示图文,多个展示名称列表)


📋 完整组件代码

可封装为 PolygonClickMap.vue,支持复制粘贴到项目中

<!--* @Author: 彭麒* @Date: 2025/5/21* @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单击某点,获取多层重叠位置下的所有features信息</div></div><h4><el-button type="primary" size="small" @click="showPolygon(city1)">显示多边形1</el-button><el-button type="primary" size="small" @click="showPolygon(city2)">显示多边形2</el-button><el-button type="primary" size="small" @click="clearLayer">清除图层</el-button></h4><div id="vue-openlayers"></div><div id="popup-box" class="ol-popup"><div id="popup-content" v-if="isshow === 1"><div class="left"><img :src="cimgurl" /></div><div class="right"><div class="name">{{ cname }}</div><div class="address">{{ caddress }}</div></div></div><div id="popup-content2" v-if="isshow === 2"><div v-for="(item, index) in list" :key="index">{{ item }}</div></div></div></div>
</template><script setup>
import { ref, onMounted } from 'vue'
import 'ol/ol.css'
import { Map, View } from 'ol'
import TileLayer from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
import OSM from 'ol/source/OSM'
import Feature from 'ol/Feature'
import { Polygon } from 'ol/geom'
import Style from 'ol/style/Style'
import Fill from 'ol/style/Fill'
import Stroke from 'ol/style/Stroke'
import Overlay from 'ol/Overlay'// refs 和响应式变量
const map = ref(null)
const dataSource = new VectorSource({ wrapX: false })
const list = ref([])
const isshow = ref(0)
const cimgurl = ref('')
const cname = ref('')
const caddress = ref('')
let overlayer = null
import person1 from '@/assets/OpenLayers/person1.jpg';
import person2 from '@/assets/OpenLayers/person2.jpg';
// 多边形数据
const city1 = {name: '吉檀迦俐股份公司',address: '大连市XX豪车路48号',polygonData: [[[126.86864, 24.99068],[126.55518, 16.2742],[142.85507, 16.49975],[142.93343, 24.84854],[126.86864, 24.99068]]],imgurl: person1
}const city2 = {name: 'PQ航空科技集团',address: '北京市XXX科技路96号',polygonData: [[[121.6182, 24.56377],[121.53983, 15.89772],[136.89934, 15.82234],[136.66425, 24.27836],[121.6182, 24.56377]]],imgurl: person2
}// 样式函数
function featureStyle() {return new Style({fill: new Fill({ color: 'rgba(0,0,0,0.1)' }),stroke: new Stroke({ width: 1, color: 'blue' })})
}// 清除图层
function clearLayer() {dataSource.clear()
}// 显示多边形
function showPolygon(data) {const polygonFeature = new Feature({geometry: new Polygon(data.polygonData),infoData: data})dataSource.addFeature(polygonFeature)
}// 地图点击事件处理
function clickPoint() {const box = document.getElementById('popup-box')overlayer = new Overlay({element: box,autoPan: false})map.value.addOverlay(overlayer)map.value.on('click', (evt) => {if (evt.dragging) returnconst pixel = map.value.getEventPixel(evt.originalEvent)// 添加一个参数,确保检查所有图层const features = map.value.getFeaturesAtPixel(pixel, {layerFilter: () => true,hitTolerance: 5})console.log('Features found:', features)if (features && features.length === 1) {const info = features[0].get('infoData')if (info) { // 确保info存在cname.value = info.namecimgurl.value = info.imgurlcaddress.value = info.addressisshow.value = 1overlayer.setPosition(evt.coordinate)console.log('Single feature popup set at', evt.coordinate)}} else if (features && features.length > 1) {list.value = features.map((f) => f.get('infoData')?.name).filter(Boolean)isshow.value = 2overlayer.setPosition(evt.coordinate)console.log('Multiple features popup set at', evt.coordinate)} else {isshow.value = 0overlayer.setPosition(undefined)}})
}// 初始化地图
function initMap() {const OSM_Layer = new TileLayer({ source: new OSM() })const feature_Layer = new VectorLayer({source: dataSource,style: featureStyle()})map.value = new Map({target: 'vue-openlayers',layers: [OSM_Layer, feature_Layer],view: new View({projection: 'EPSG:4326',center: [121.6182, 24.56377],zoom: 4})})
}// 生命周期挂载
onMounted(() => {initMap()clickPoint()// 可以默认显示一个多边形用于测试showPolygon(city1)
})
</script><style scoped>
.container {width: 840px;height: 590px;margin: 50px auto;border: 1px solid #42b983;
}#vue-openlayers {width: 800px;height: 470px;margin: 0 auto;border: 1px solid #42b983;position: relative;
}.ol-popup {/* 现有样式 */display: block !important; /* 强制显示 */z-index: 1000; /* 确保在地图上层 */position: absolute;background-color: rgba(255, 0, 255, 0.8);padding: 5px;border-radius: 5px;border: 1px solid #cccccc;bottom: 12px;left: -50px;color: #ffffff;min-width: 200px;
}.ol-popup:after,
.ol-popup:before {top: 100%;border: solid transparent;content: ' ';height: 0;width: 0;position: absolute;pointer-events: none;
}.ol-popup:after {border-top-color: rgba(255, 0, 255, 0.8);border-width: 10px;left: 48px;margin-left: -10px;
}.ol-popup:before {border-top-color: #cccccc;border-width: 11px;left: 48px;margin-left: -11px;
}#popup-content {width: 270px;height: 80px;border-radius: 10px;border: 1px solid #fff;
}#popup-content .left {width: 80px;height: 80px;float: left;
}#popup-content .left img {width: 80px;height: 80px;
}#popup-content .right {width: 170px;height: 80px;float: right;text-align: left;
}#popup-content .right .name {line-height: 40px;font-size: 18px;
}#popup-content .right .address {line-height: 40px;font-size: 12px;
}
</style>

🧠 拓展建议

功能方向可行扩展
支持图层筛选使用 layerFilter 过滤
多样式高亮被选中要素高亮边框或图标
自定义弹窗使用 Element 弹窗或第三方浮窗组件
后端接入点击后获取服务端详情
组合封装提取为 useFeatureClick() 组合式函数


✅ 总结

本文介绍了如何在 Vue3 + OpenLayers 项目中,实现 点击获取所有重叠位置的 Feature 信息 的功能。无论是城市划分、设备管理还是图层叠加展示,都非常实用。

http://www.xdnf.cn/news/572671.html

相关文章:

  • Spring Boot 登录实现:JWT 与 Session 全面对比与实战讲解
  • ES的倒排索引和正排索引的区别及适用场景?为什么倒排索引适合全文搜索?
  • 目标检测基础知识
  • 使用 electron-builder 打包与发布 Electron 应用
  • Prometheus+Grafana实现对服务的监控
  • 黑色矩形大于6识别
  • RTMP协议解析【二】
  • RTMP协议解析[一]
  • lcd气压表研发方案芯片——用于胎压检测
  • Ubuntu 20.04安装及配置docker
  • 进程调度算法深度剖析:FCFS、SJF、RR、优先级及多级反馈队列全解
  • 算法第25天 | 491. 非递减子序列、46. 全排列、47. 全排列 II
  • Java 实现二进制与十进制之间的互相转换
  • 校平机的原理、应用及发展趋势
  • Vue3学习(Vue3.3新特性——defineModel宏详解)
  • OpenCv高阶(十六)——Fisherface人脸识别
  • MySQL 索引的增删改查
  • Docusaurus Umami
  • 算法优选系列(9.BFS 解决拓扑排序)
  • GStreamer (四)交叉编译
  • 华为eNSP无线AC/AP组网实战
  • 基于大模型的闭合性尺桡骨干骨折全方位诊疗研究报告
  • 现代计算机图形学Games101入门笔记(二十)
  • V少JS基础班之第五弹
  • ElasticSearch导读
  • 【网络安全】日志采集、监控任务守护进程详细教程(附实战案例)
  • 打卡31天
  • Python学习Day1:安装
  • 谷歌2025年I/O开发者大会热点总结
  • shell脚本总结3