Files
td_official/src/utils/lassoSelect.ts

154 lines
4.5 KiB
TypeScript
Raw Normal View History

2025-06-06 20:03:35 +08:00
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<VectorSource>;
private drawSource: VectorSource;
2025-07-08 16:39:42 +08:00
private overlaySource: VectorSource; // 新增用于闭合多边形
2025-06-06 20:03:35 +08:00
private overlayLayer: VectorLayer<VectorSource>;
private drawing = false;
private coordinates: [number, number][] = [];
private targetSource: VectorSource;
2025-07-08 16:39:42 +08:00
private onSelectCallback: (selected: Feature[]) => void;
2025-06-06 20:03:35 +08:00
2025-07-08 16:39:42 +08:00
constructor(map: OLMap, targetSource: VectorSource, onSelect: (selected: Feature[]) => void) {
2025-06-06 20:03:35 +08:00
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',
2025-06-17 09:49:15 +08:00
width: 2
})
})
2025-06-06 20:03:35 +08:00
});
this.map.addLayer(this.drawLayer);
2025-07-08 16:39:42 +08:00
// 新增:闭合多边形图层(半透明填充)
2025-06-06 20:03:35 +08:00
this.overlaySource = new VectorSource();
this.overlayLayer = new VectorLayer({
source: this.overlaySource,
style: new Style({
stroke: new Stroke({
color: 'rgba(255, 0, 0, 0.8)',
2025-06-17 09:49:15 +08:00
width: 2
2025-06-06 20:03:35 +08:00
}),
fill: new Fill({
2025-06-17 09:49:15 +08:00
color: 'rgba(255, 0, 0, 0.3)'
})
})
2025-06-06 20:03:35 +08:00
});
this.map.addLayer(this.overlayLayer);
this.bindEvents();
}
private bindEvents() {
// 禁用默认右键菜单
this.map.getViewport().addEventListener('contextmenu', (e) => e.preventDefault());
2025-07-08 16:39:42 +08:00
// 右键按下开始绘制
this.map.getViewport().addEventListener('mousedown', (e) => {
if (e.button === 2 && !this.drawing) {
2025-06-06 20:03:35 +08:00
this.drawing = true;
this.coordinates = [];
this.drawSource.clear();
this.overlaySource.clear();
}
});
2025-07-08 16:39:42 +08:00
// 鼠标移动实时绘制线和闭合多边形
2025-06-06 20:03:35 +08:00
this.map.on('pointermove', (evt) => {
if (!this.drawing) return;
const coord = evt.coordinate as [number, number];
this.coordinates.push(coord);
this.renderLine();
this.renderPolygon();
});
2025-07-08 16:39:42 +08:00
// 右键抬起结束绘制并选中
this.map.getViewport().addEventListener('mouseup', (e) => {
if (e.button === 2 && this.drawing) {
2025-06-06 20:03:35 +08:00
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;
2025-07-08 16:39:42 +08:00
// 闭合多边形坐标(首尾闭合)
2025-06-06 20:03:35 +08:00
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',
2025-06-17 09:49:15 +08:00
dataProjection: 'EPSG:4326'
2025-06-06 20:03:35 +08:00
}) as any;
2025-06-17 09:49:15 +08:00
if ((geomObj.type === 'Polygon' || geomObj.type === 'MultiPolygon') && booleanIntersects(turfPoly, geomObj)) {
2025-06-06 20:03:35 +08:00
selected.push(feature);
}
});
2025-07-08 16:39:42 +08:00
this.onSelectCallback(selected);
2025-06-06 20:03:35 +08:00
this.drawSource.clear();
this.overlaySource.clear();
}
destroy() {
this.map.removeLayer(this.drawLayer);
this.map.removeLayer(this.overlayLayer);
2025-07-08 16:39:42 +08:00
// 如有需要,解绑事件
2025-06-06 20:03:35 +08:00
}
}