129.在 Vue3 中使用 OpenLayers 实现点击获取重叠要素信息(支持多 Feature)
🖼️ UI效果图参考
👇 演示图如下:
🎯 效果:
单个要素展示图文信息(名称、地址、头像)
多个重叠要素显示名称列表
点击空白处清除弹窗
🎯 前言
在地图应用中,用户点击地图时,如果该位置有多个图层的要素重叠,理想体验是能获取 所有要素的信息,而不是只获取最顶层的一个。
本文将带你使用 Vue3 + OpenLayers
实现这个功能 👇
📦 项目环境说明
-
Vue 版本:Vue 3 +
<script setup>
-
地图引擎:OpenLayers v7.x+
-
样式:Tailwind CSS、Element Plus(用于按钮)
-
坐标系:EPSG:4326
🧩 实现思路概览
-
使用
VectorLayer
添加多个 Feature(Polygon) -
每个 Feature 携带自定义信息(如公司名、地址、头像)
-
绑定地图
click
事件 -
使用
getFeaturesAtPixel()
获取所有命中的要素 -
展示弹窗(单个展示图文,多个展示名称列表)
📋 完整组件代码
可封装为
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 信息 的功能。无论是城市划分、设备管理还是图层叠加展示,都非常实用。