本篇介绍一下使用openlayers点击多边形弹框,高亮多边形,自定义属性传递,鼠标悬浮多边形上动态修改鼠标样式
1 需求
- 加载天地图,polygon
- 传递自定义属性
- 标悬浮在polygon上,根据自定义属性,动态修改鼠标样式为pointer
- 点击polygon,根据自定义属性,高亮,弹框
2 分析
主要是 openlayers 中 地图事件,overlay等功能的使用
-
为vectorSource填充features,有两种方法:
- features: [new Feature()]
- features: new GeoJSON().readFeatures(geoJSON)
-
为vectorLayer填充style,有两种写法:
- style:new Style()
- style:{‘stroke-color’: ‘rgba(255, 128, 100, 1)’}
-
获取鼠标点击活悬浮时的features,有两种写法:
- map.value.forEachFeatureAtPixel()
- map.value.getFeaturesAtPixel()
3 实现
没有录上鼠标样式改变,复制代码查看
<template>
<div id="map" class="map"></div>
<div id="popup" class="ol-popup" ref="container">
<a href="#" id="popup-closer" class="ol-popup-closer" ref="closer"></a>
<div id="popup-content" ref="content"></div>
</div>
</template>
<script setup lang="ts">
import { Feature, Map, Overlay, View } from 'ol';
import { GeoJSON } from 'ol/format';
import { Polygon } from 'ol/geom';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer';
import { get, toLonLat } from 'ol/proj';
import { Vector, XYZ } from 'ol/source';
import { Fill, Stroke, Style } from 'ol/style';
import { toStringHDMS } from 'ol/coordinate.js';
const projection = get('EPSG:4326');
const layerTypeMap = {
vector: ['vec', 'cva'], // [矢量底图, 矢量注记]
image: ['img', 'cia'], // [影像底图, 影像注记]
terrain: ['ter', 'cta'] // [地形晕渲, 地形注记]
};
const map = ref();
const container = ref();
const content = ref();
const closer = ref();
const overlay = shallowRef();
const feature = ref();
const geoJSON = {
type: 'FeatureCollection',
crs: {
type: 'name',
properties: {
name: 'EPSG:4326'
}
},
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[112, 31],
[113, 32.2],
[114, 30.5],
[112, 31]
]
]
},
properties: {
//自定义属性
pointer: true
}
}
]
};
onMounted(() => {
initMap('image');
});
const initMap = (layerType = 'image') => {
const key = '替换为天地图key';
overlay.value = new Overlay({
element: container.value,
autoPan: {
animation: {
duration: 250
}
}
});
// c: 经纬度投影 w: 墨卡托投影
const matrixSet = 'c';
map.value = new Map({
target: 'map',
layers: [
// 底图
new TileLayer({
source: new XYZ({
url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=${layerTypeMap[layerType][0]}_${matrixSet}&tk=${key}&x={x}&y={y}&l={z}`,
projection
})
}),
// 注记
new TileLayer({
source: new XYZ({
url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=${layerTypeMap[layerType][1]}_${matrixSet}&tk=${key}&x={x}&y={y}&l={z}`,
projection
})
}),
new VectorLayer({
source: new Vector({
features: [
new Feature({
geometry: new Polygon([
[
[116, 31],
[118, 32.2],
[119, 30.5],
[116, 31]
]
]),
pointer: true //自定义属性,可用于修改鼠标样式、单击轮廓弹框,改变feature样式
})
]
}),
style: new Style({
fill: new Fill({
color: 'rgba(228, 147, 87, 0.4)'
}),
stroke: new Stroke({
color: 'rgba(228, 147, 87, 1)',
width: 3
})
})
}),
new VectorLayer({
source: new Vector({
features: [
new Feature({
geometry: new Polygon([
[
[114, 31],
[115, 32.2],
[116, 30.5],
[114, 31]
]
])
})
]
}),
style: {
//layer样式可以这样写
'stroke-color': 'rgba(255, 255, 100, 1)',
'stroke-width': 1.5,
'fill-color': 'rgba(255, 255, 100, 0.5)',
'circle-radius': 6,
'circle-fill-color': 'rgba(255, 255, 100, 1)'
}
}),
new VectorLayer({
source: new Vector({
features: new GeoJSON().readFeatures(geoJSON) //读取geojson格式数据
}),
style: {
//layer样式可以这样写
'stroke-color': 'rgba(255, 128, 100, 1)',
'stroke-width': 1.5,
'fill-color': 'rgba(255, 128, 100, 0.5)',
'circle-radius': 6,
'circle-fill-color': 'rgba(255, 128, 100, 1)'
}
})
],
overlays: [overlay.value],
view: new View({
center: [116.406393, 39.909006],
projection: projection,
zoom: 5,
maxZoom: 17,
minZoom: 1
})
});
map.value.on('pointermove', (e: any) => {
// 根据自定义属性改变鼠标样式
// 方法一
// map.value.getTargetElement().style.cursor = 'auto';
// let pixel = map.value.getEventPixel(e.originalEvent);
// map.value.forEachFeatureAtPixel(pixel, (feature: any) => {
// const property = feature.getProperties();
// if (property.pointer) {
// map.value.getTargetElement().style.cursor = 'pointer'; //设置鼠标样式
// } else {
// map.value.getTargetElement().style.cursor = 'auto';
// }
// });
// 方法二
map.value.getTargetElement().style.cursor = 'auto';
const features = map.value.getFeaturesAtPixel(e.pixel);
features.forEach(feature => {
const property = feature.getProperties();
if (property.pointer) {
map.value.getTargetElement().style.cursor = 'pointer'; //设置鼠标样式
} else {
map.value.getTargetElement().style.cursor = 'auto';
}
});
});
map.value.on('click', e => {
// 根据自定义属性改变轮廓样式,也可以进行弹框
if (feature.value) {
feature.value.setStyle();
closer.value.onclick();
}
const features = map.value.getFeaturesAtPixel(e.pixel);
const f = features.find(f => f.getProperties().pointer);
if (f) {
feature.value = f;
f.setStyle(
new Style({
fill: new Fill({
color: 'rgba(255, 255, 100, 0.5)'
}),
stroke: new Stroke({
color: 'rgba(255, 255, 100, 1)',
width: 3
})
})
);
const coordinate = e.coordinate;
const hdms = toStringHDMS(toLonLat(coordinate));
content.value.innerHTML = '<p>当前经纬度:</p><code>' + hdms + '</code>';
overlay.value.setPosition(coordinate);
}
});
closer.value.onclick = function () {
overlay.value.setPosition(undefined);
closer.value.blur();
if (feature.value) {
feature.value.setStyle();
}
return false;
};
};
</script>
<style scoped lang="scss">
.map {
width: 100%;
height: 100%;
}
.ol-popup {
position: absolute;
background-color: rgba(255, 255, 255, 0.7);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
}
.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, 255, 255, 0.7);
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: '✖';
}
</style>