import { Map as OLMap } from 'ol'; import VectorSource from 'ol/source/Vector'; import VectorLayer from 'ol/layer/Vector'; import { LineString, Polygon } from 'ol/geom'; import { Feature } from 'ol'; import { Style, Stroke, Fill } from 'ol/style'; import GeoJSON from 'ol/format/GeoJSON'; import { polygon as turfPolygon, booleanIntersects } from '@turf/turf'; import { toLonLat } from 'ol/proj'; export class LassoSelector { private map: OLMap; private drawLayer: VectorLayer; private drawSource: VectorSource; private overlaySource: VectorSource; // 新增用于闭合多边形 private overlayLayer: VectorLayer; private drawing = false; private coordinates: [number, number][] = []; private targetSource: VectorSource; private onSelectCallback: (selected: Feature[]) => void; constructor(map: OLMap, targetSource: VectorSource, onSelect: (selected: Feature[]) => void) { this.map = map; this.targetSource = targetSource; this.onSelectCallback = onSelect; this.drawSource = new VectorSource(); this.drawLayer = new VectorLayer({ source: this.drawSource, style: new Style({ stroke: new Stroke({ color: '#ff0000', width: 2 }) }) }); this.map.addLayer(this.drawLayer); // 新增:闭合多边形图层(半透明填充) this.overlaySource = new VectorSource(); this.overlayLayer = new VectorLayer({ source: this.overlaySource, style: new Style({ stroke: new Stroke({ color: 'rgba(255, 0, 0, 0.8)', width: 2 }), fill: new Fill({ color: 'rgba(255, 0, 0, 0.3)' }) }) }); this.map.addLayer(this.overlayLayer); this.bindEvents(); } private bindEvents() { // 禁用默认右键菜单 this.map.getViewport().addEventListener('contextmenu', (e) => e.preventDefault()); // 右键按下开始绘制 this.map.getViewport().addEventListener('mousedown', (e) => { if (e.button === 2 && !this.drawing) { this.drawing = true; this.coordinates = []; this.drawSource.clear(); this.overlaySource.clear(); } }); // 鼠标移动实时绘制线和闭合多边形 this.map.on('pointermove', (evt) => { if (!this.drawing) return; const coord = evt.coordinate as [number, number]; this.coordinates.push(coord); this.renderLine(); this.renderPolygon(); }); // 右键抬起结束绘制并选中 this.map.getViewport().addEventListener('mouseup', (e) => { if (e.button === 2 && this.drawing) { this.drawing = false; this.handleDrawEnd(); } }); } private renderLine() { this.drawSource.clear(); if (this.coordinates.length >= 2) { const line = new LineString(this.coordinates); const feature = new Feature({ geometry: line }); this.drawSource.addFeature(feature); } } private renderPolygon() { this.overlaySource.clear(); if (this.coordinates.length < 3) return; // 闭合多边形坐标(首尾闭合) const polygonCoords = [...this.coordinates, this.coordinates[0]]; const polygon = new Polygon([polygonCoords]); const feature = new Feature({ geometry: polygon }); this.overlaySource.addFeature(feature); } private handleDrawEnd() { if (this.coordinates.length < 3) { this.drawSource.clear(); this.overlaySource.clear(); return; } const first = this.coordinates[0]; const last = this.coordinates[this.coordinates.length - 1]; if (first[0] !== last[0] || first[1] !== last[1]) { this.coordinates.push([...first]); } const coords4326 = this.coordinates.map((c) => toLonLat(c)); const turfPoly = turfPolygon([coords4326]); const geojson = new GeoJSON(); const selected: Feature[] = []; this.targetSource.getFeatures().forEach((feature) => { const geom = feature.getGeometry(); if (!geom) return; const geomObj = geojson.writeGeometryObject(geom, { featureProjection: 'EPSG:3857', dataProjection: 'EPSG:4326' }) as any; if ((geomObj.type === 'Polygon' || geomObj.type === 'MultiPolygon') && booleanIntersects(turfPoly, geomObj)) { selected.push(feature); } }); this.onSelectCallback(selected); this.drawSource.clear(); this.overlaySource.clear(); } destroy() { this.map.removeLayer(this.drawLayer); this.map.removeLayer(this.overlayLayer); // 如有需要,解绑事件 } }