ol/interaction之——select

本文介绍了如何在OpenLayers中使用Select交互组件,包括设置条件、创建事件监听,以及如何处理选中和未选中时的样式切换。重点讲解了如何移除默认高亮样式和鼠标样式控制。

一、参数

import Select from ‘ol/interaction/Select’;

   this.selectClick = new Select({
     condition: click,//click点击,pointerMove鼠标指针移动
   });
   this.map.addInteraction(this.selectClick)

二、事件

1.select事件

   select.on('select', function (e) {
     if(e.target.getFeatures().array_.length>0){
       // e.target.getFeatures(),是选中的Features
       e.target.getFeatures().array_.forEach(item=>{
         item.setStyle(null) //这里去掉选中时默认的样式 //去除openlayers默认高亮显示样式
       })
       document.getElementById("OLmap").style.cursor = 'pointer'//选中时鼠标样式为pointer(一只手)
     }else{
       document.getElementById("OLmap").style.cursor = 'default'//未选中时鼠标为默认样式(一个箭头)
     }
   });

参考文章

openlayers 去除默认高亮显示样式:

  1. openlayers高亮显示和点击(包含去除默认高亮显示样式)
<template> <div ref="mapElement" class="map-container"></div> </template> <script setup> import { ref, defineExpose } from 'vue' const {proxy} = getCurrentInstance(); import OlMap from 'ol/Map'; import OSM from 'ol/source/OSM'; import View from 'ol/View'; import Feature from 'ol/Feature'; import Point from 'ol/geom/Point'; import Polygon from 'ol/geom/Polygon'; import TileLayer from 'ol/layer/Tile'; import ImageLayer from 'ol/layer/Image'; import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import ImageStatic from 'ol/source/ImageStatic'; import Icon from 'ol/style/Icon'; import Style from 'ol/style/Style'; import Stroke from 'ol/style/Stroke'; import Text from 'ol/style/Text'; import Transform from 'ol-ext/interaction/Transform'; import Draw from 'ol/interaction/Draw'; import { fromLonLat, toLonLat } from 'ol/proj'; import Tooltip from 'ol-ext/overlay/Tooltip'; import { click,shiftKeyOnly,always } from 'ol/events/condition'; import {v4 as uuidV4} from 'uuid' const props = defineProps({ schemeId: { type: String, default: "", }, backgroundUrl: { type: String, default: '' }, isTileLayer: { type: Boolean, default: true } }) const emit = defineEmits(['addFeature', 'updateFeature', 'featureSelected',"featureDeleted"]) const mapElement = ref(null) const bounds = ref({}) let mapInstance = ref(null) let vectorSource = null let modifyInteraction = null; let drawLine = ref(null) let drawPoly = ref(null) let selectedFeature = ref(null) var startangle = 0; var startRadius = 10; var d=[0,0]; var firstPoint = false; // 提取公共的样式创建方法 const createFeatureStyle = (featureInfo) => { return new Style({ image: new Icon({ src: featureInfo.icon, scale: 1, rotation: 1.5708, color: featureInfo.color }), text: featureInfo.showLabel ? new Text({ text: featureInfo.label, fill: new Stroke({ // color: featureInfo.type color: '#fff' }), offsetY: 30 }) : undefined }); } // 初始化地图 async function initMap ({center,zoom,minZoom,maxZoom,scope,featureList}){ console.log("init") bounds.value = scope vectorSource = new VectorSource({ features: featureList.map(featureInfo => { const {id, label, type, position, icon, color, showLabel, visible} = featureInfo; const coordinate = fromLonLat([position.longitude, position.latitude]); const feature = new Feature({ geometry: new Point(coordinate), name: label, type: type, params: featureInfo, showLabel: showLabel || false, visible: visible !== undefined ? visible : true }) feature.setId(id) // 使用公共方法创建样式 feature.setStyle(createFeatureStyle(featureInfo)) return feature }), style: null // 禁用图层默认样式 }); let backgroundLayer; if (props.isTileLayer) { backgroundLayer = new TileLayer({ source: new OSM({ url: props.backgroundUrl, attributions: '' }), }); } else { const loadImage = async () => { const img = new Image(); img.src = props.backgroundUrl; await new Promise((resolve) => { img.onload = resolve; }); const imageWidth = img.width; const imageHeight = img.height; const aspectRatio = imageWidth / imageHeight; // 假设图片高度在地图上覆盖 0.1 个经纬度单位 const targetHeight = 180; // 调整这个值可以改变图片大小 const targetWidth = targetHeight * aspectRatio; // 定义图片范围,使其中心在 [0, 0] const imageExtent = [ -targetWidth / 2, // minX (左边界) -targetHeight /2/aspectRatio, // minY (下边界) targetWidth / 2, // maxX (右边界) targetHeight /2/aspectRatio, // maxY (上边界) ]; return imageExtent; }; // 使用方式 try { const imageExtent = await loadImage(); backgroundLayer = new ImageLayer({ source: new ImageStatic({ url: props.backgroundUrl, imageExtent: imageExtent, // 使用计算后的 extent projection: 'EPSG:4326', }), }); console.log('backgroundLayer\n',backgroundLayer) } catch (error) { console.error("加载图片失败:", error); } } const vectorLayer = new VectorLayer({ source: vectorSource, }); if (mapInstance.value){ mapInstance.value.setTarget(null); mapInstance.value = null; } mapInstance.value = new OlMap({ target: mapElement.value, layers: [ backgroundLayer, vectorLayer ], view: new View({ center: fromLonLat(center), zoom: zoom, minZoom: minZoom, maxZoom: maxZoom, multiWorld: true // 允许无限缩放 }) }); mapInstance.value.on('click', (event) => { if (selectedMarkForAdding.value) { addMarkToMap(event.coordinate, selectedMarkForAdding.value) selectedMarkForAdding.value = null } }) drawLine.value = new Draw({ type: 'LineString' }); mapInstance.value.addInteraction(drawLine.value); drawPoly.value = new Draw({ type: 'Polygon' }); mapInstance.value.addInteraction(drawPoly.value); drawPoly.value.setActive(false); drawLine.value.setActive(false); drawLine.value.on('drawstart', function(evt) { const tooltip = new Tooltip({ formatLength: function(line) { const length = line; if (length > 1000) { return (length / 1000).toFixed(2) + ' km'; } else { return length.toFixed(2) + ' m'; } } }); mapInstance.value.addOverlay(tooltip); tooltip.setFeature(evt.feature); }); drawLine.value.on(['change:active','drawend'], function() { mapInstance.value.getOverlays().forEach(overlay => { if (overlay instanceof Tooltip) { mapInstance.value.removeOverlay(overlay); } }); }); drawPoly.value.on('drawstart', function(evt) { const tooltip = new Tooltip({ formatArea: function(polygon) { const area = polygon.getArea(); if (area > 1000000) { return (area / 1000000).toFixed(2) + ' km²'; } else { return area.toFixed(2) + ' m²'; } } }); mapInstance.value.addOverlay(tooltip); tooltip.setFeature(evt.feature); }); drawPoly.value.on(['change:active','drawend'], function() { mapInstance.value.getOverlays().forEach(overlay => { if (overlay instanceof Tooltip) { mapInstance.value.removeOverlay(overlay); } }); }); window.addEventListener('resize', updateMapSize); // 在地图初始化完成后绘制边界框 mapInstance.value.once('postrender', () => { setupModifyInteraction(); drawBounds(); }); } // 存储每个 feature 的自定义属性 const featureStates = new Map(); // feature => { rotation, translation } const setupModifyInteraction = () => { var transform = new Transform({ enableRotatedTransform: false, // 是否强制交互手柄匹配地图旋转角度 addCondition: shiftKeyOnly, // 按住Shift键可多选要素 hitTolerance: 2, // 点击容差(像素),避免精确点击 translateFeature: true, // 点击要素是否可直接平移 scale: true, // 是否启用缩放 rotate: true, // 是否启用旋转 keepAspectRatio: always, // 是否强制保持宽高比 translate: true, // 是否启用平移 stretch: true, // 是否启用拉伸(非均匀缩放) pointRadius: function(f) { // 点要素的缩放半径(动态获取要素的radius属性) var radius = f.get('radius') || 10; return [radius, radius]; } }); mapInstance.value.addInteraction(transform); // 将交互添加到地图 transform.on (['select'], function(e) { if (e.features.getLength()) { selectedFeature.value = e.features.getArray()[0]; emit('featureSelected', selectedFeature.value.getProperties().params) } if (firstPoint && e.features && e.features.getLength()) { transform.setCenter(e.features.getArray()[0].getGeometry().getFirstCoordinate()); } }); // 旋转开始:记录初始旋转值 transform.on('rotatestart', function(e) { const feature = e.feature; const state = ensureFeatureState(feature); state.startRotation = state.rotation; // 记录开始前的角度 console.log('Rotate start, base angle:', state.rotation); }); // 旋转中:更新 feature 的旋转状态 transform.on('rotating', function(e) { const feature = e.feature; const state = ensureFeatureState(feature); // e.angle 是相对于 rotatestart 的增量(正值表示逆时针) const newRotation = state.startRotation + e.angle; state.rotation = newRotation; // 触发样式重绘 feature.changed(); }); // 平移过程:记录累计位移(可选用于显示) let d = [0, 0]; transform.on('translating', function(e) { d[0] += e.delta[0]; d[1] += e.delta[1]; console.log(`累计平移: ${d[0].toFixed(2)}, ${d[1].toFixed(2)} px`); }); // 变换结束:清空临时数据或发送到服务器 transform.on(['rotateend', 'translateend', 'scaleend'], function(e) { console.log('最终角度 (弧度):', featureStates.get(e.feature)?.rotation); console.log('最终角度 (度):', ((featureStates.get(e.feature)?.rotation || 0) * 180 / Math.PI).toFixed(2)); // 可在此保存状态到数据库或导出 GeoJSON }); } // 初始化要素状态 const ensureFeatureState = (feature)=> { if (!featureStates.has(feature)) { // 默认旋转为0,平移为[0,0] featureStates.set(feature, { rotation: 0, // 弧度 translate: [0, 0] // 像素偏移(可选) }); } return featureStates.get(feature); } // 更新地图尺寸 const updateMapSize = () => { mapInstance.value && mapInstance.value.updateSize(); } const addMarkToMap = (coordinate, mark) => { // 检查坐标是否在边界范围内 if (!isCoordinateInBounds(coordinate)) { proxy.$message({ message: '只能在指定范围内添加军标', type: 'warning', duration: 5 * 1000 }) return; } const params = {...mark,id: uuidV4(), showLabel: true,visible: true} const feature = new Feature({ geometry: new Point(coordinate), name: mark.label, type: mark.type, params: params, showLabel: params.showLabel, visible: params.visible }) feature.setId(params.id) feature.setStyle( new Style({ image: new Icon({ src: mark.icon, scale: 1 }), text: new Text({ text: params.label, fill: new Stroke({ // color: params.type color: '#fff' }), offsetY: 30 }) }) ) vectorSource.addFeature(feature) const lonLat = toLonLat(coordinate); params.position = { longitude: lonLat[0], latitude: lonLat[1] } const featureInfo = { ...params, position: { longitude: lonLat[0], latitude: lonLat[1] }, icon: mark.icon }; emit('addFeature', featureInfo); } const updateFeature = (updatedFeature) => { const feature = vectorSource.getFeatureById(updatedFeature.id); if (feature) { feature.setProperties(updatedFeature); // 使用公共方法创建样式 feature.setStyle(createFeatureStyle(updatedFeature)) } } const updateFeatureVisibility = (updatedFeature) => { const feature = vectorSource.getFeatureById(updatedFeature.id); if (feature) { feature.setProperties(updatedFeature); feature.set('visible', updatedFeature.visible); if(updatedFeature.visible) { // 使用公共方法创建样式 feature.setStyle(createFeatureStyle(updatedFeature)) }else{ feature.setStyle([]) } emit('updateFeature', updatedFeature) } } // 删除当前选中的要素 const deleteSelectedFeature = () => { if (selectedFeature.value) { const featureId = selectedFeature.value.getId(); vectorSource.removeFeature(selectedFeature.value); emit('featureDeleted', featureId); // 通知父组件 selectedFeature.value = null; // 清空选中 } } // 移动要素到新位置 const moveFeature = (featureId, newPosition) => { const feature = vectorSource.getFeatureById(featureId); if (feature) { const newCoord = fromLonLat([newPosition.longitude, newPosition.latitude]); feature.getGeometry().setCoordinates(newCoord); // 更新要素属性 const featureProps = feature.getProperties(); const updatedFeature = { ...featureProps.params, position: newPosition }; feature.set('params', updatedFeature); emit('updateFeature', updatedFeature); } } // 批量删除要素 const deleteFeaturesByIds = (featureIds) => { featureIds.forEach(id => { const feature = vectorSource.getFeatureById(id); if (feature) { vectorSource.removeFeature(feature); } }); emit('featuresDeleted', featureIds); } function drawBounds(){ if (! bounds.value) return; const { north_east, south_west } = bounds.value; if (!north_east || !south_west) return; // 检查是否已经绘制过bounds框,避免重复绘制 const existingBoundsFeature = vectorSource.getFeatures().find(feature => feature.get('type') === 'bounds' ); if (existingBoundsFeature) return; // 转换坐标 const neCoord = fromLonLat([north_east[0], north_east[1]]); const swCoord = fromLonLat([south_west[0], south_west[1]]); // 创建边界框坐标数组 const coordinates = [ [swCoord[0], swCoord[1]], // 西南 [neCoord[0], swCoord[1]], // 西北 [neCoord[0], neCoord[1]], // 东北 [swCoord[0], neCoord[1]], // 东南 [swCoord[0], swCoord[1]] // 回到西南,闭合多边形 ]; // 创建边界框要素 const boundsFeature = new Feature({ geometry: new Polygon([coordinates]), type: 'bounds', name: '地图边界' }); // 设置样式 boundsFeature.setStyle(new Style({ stroke: new Stroke({ color: '#02eaea', width: 2, lineDash: [5, 5] // 虚线样式 }) })); // 添加到地图 vectorSource.addFeature(boundsFeature); } // 添加检查坐标是否在边界范围内的方法 const isCoordinateInBounds = (coordinate) => { console.log(bounds.value) if (!bounds.value) return true; const { north_east, south_west } = bounds.value; if (!north_east || !south_west) return true; // 转换坐标 const lonLat = toLonLat(coordinate); const [longitude, latitude] = lonLat; // 检查是否在边界范围内,不假设坐标大小关系 const minLon = Math.min(south_west[0], north_east[0]); const maxLon = Math.max(south_west[0], north_east[0]); const minLat = Math.min(south_west[1], north_east[1]); const maxLat = Math.max(south_west[1], north_east[1]); return ( longitude >= minLon && longitude <= maxLon && latitude >= minLat && latitude <= maxLat ); } const selectedMarkForAdding = ref(null) defineExpose({ initMap, updateMapSize, addMarkToMap, updateFeature, updateFeatureVisibility, mapInstance, selectedMarkForAdding, selectedFeature, drawLine, deleteSelectedFeature, moveFeature, deleteFeaturesByIds }) </script> <style scoped lang="scss"> .map-container { width: 100%; height: 100%; } </style> 优化选中旋转、放大缩小,移动图标
最新发布
09-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值