TypeScript+OpenLayers7:要素ID设置与获取失败问题求助
问题:OpenLayers 7中多边形要素ID无法读取,无法关联对应测量Overlay
我用TypeScript和OpenLayers 7开发地图,实现了「绘制多边形」和「删除要素」两个自定义按钮,支持多边形的绘制与删除,同时为每个多边形创建显示面积的测量Overlay。为建立多边形与Overlay的关联,我尝试给两者设置相同ID,期望删除多边形时通过ID找到并移除对应Overlay,核心代码如下:
const featureId = feature.get('id') const overlay = map.getOverlayById(featureId) map.removeOverlay(overlay)
但实际读取多边形ID时始终返回undefined,尝试过用set()、setProperties()设置ID属性,调用feature.getId()获取也都失败。ID在Polygon类中赋值,在RemovePolygonButton类中读取,完整代码如下:
import Map from 'ol/Map' import OSM from 'ol/source/OSM' import Draw from 'ol/interaction/Draw'; import TileLayer from 'ol/layer/Tile' import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; import View from 'ol/View' import { Control } from 'ol/control' import { Geometry } from 'ol/geom'; import { transform, useGeographic } from 'ol/proj'; import Feature, { FeatureLike } from 'ol/Feature'; import { Overlay } from 'ol'; import { getArea } from 'ol/sphere'; import { unByKey } from 'ol/Observable'; import { EventsKey } from 'ol/events'; import { Coordinate } from 'ol/coordinate'; /** * Map component */ export default class MapComponent { map: Map featureLayer: FeatureLayer constructor() { useGeographic() this.map = new Map({ controls: [], layers: [ new TileLayer({ source: new OSM(), }) ], view: new View({ center: [0, 0], zoom: 2 }), target: "map" }) this.featureLayer = new FeatureLayer() // Add Feature Layer this.map.addLayer(this.featureLayer) // Add Custom Controls to Draw- and Remove Polygon this.map.addControl(new DrawPolygonButton(this, this.map)) this.map.addControl(new RemovePolygonButton(this, this.map)) } public getFeatureLayer(): FeatureLayer { return this.featureLayer } public getMap() { return this.map } } /** * This is the layer we want to draw the polygon (feature) on */ class FeatureLayer extends VectorLayer<VectorSource<Geometry>> { constructor() { super({ source: new VectorSource(), properties: { title: 'featureLayer' } }) } } /** * A button which starts the drawing process */ class DrawPolygonButton extends Control { constructor(mapComponent: MapComponent, map: Map) { const button = document.createElement('button') button.innerHTML = 'Draw Polygon' super({ element: button }) button.addEventListener('click', () => { const featureLayerSource = mapComponent.getFeatureLayer()?.getSource() if (featureLayerSource) { new Polygon(map, featureLayerSource) } }) } } /** * A button which should listen to clicking on the map, and if it hits a polygon on the * FeatureLayer it should remove it */ class RemovePolygonButton extends Control { constructor(mapComponent: MapComponent, map: Map) { const button = document.createElement('button') button.innerHTML = 'Remove Feature' super({ element: button }) button.addEventListener('click', () => { map.on('click', (evt: any) => { try { const featureLayerSource = mapComponent.getFeatureLayer()?.getSource() const features: any[] = [] // It does find features map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) { features.push(feature); }) for (const feature of features) { console.log('Feature: ', feature) // Feature exist console.log('Title: ', feature.get('title')) // undefined console.log('ID: ', feature.get('id')) // undefined console.log('ID: ', feature.getId()) // undefined const featureId = feature.get('id') const overlay = map.getOverlayById(featureId) // Remove overlay map.removeOverlay(overlay) // Remove feature featureLayerSource?.removeFeature(feature) } } catch (err) { console.log('Error: ', err) } }) }) } } /** * The Polygon will be added to the map */ class Polygon extends Draw { id: string sketch: Feature | undefined measureTooltipElement: HTMLElement | undefined measureTooltip: Overlay | undefined constructor(map: Map, featureLayerSource: VectorSource) { super({ type: 'Polygon', source: featureLayerSource }) this.id = 'randomid' // Creating id // this.setId('randomId') This does not work: Property 'setId' does not exist on type 'Polygon'.ts(2339) this.set('id', this.id) // Assigning id with set() this.setProperties({ // Assigning id (and title) with setProperties() 'id': this.id, 'title': this.id }) this.createMeasureTooltip(map) let listener: EventsKey | EventsKey[] | undefined = undefined this.on('drawstart', (event: any) => { this.sketch = event.feature if (!this.sketch) { return } let tooltipCoord: Coordinate = event.coordinate; // Show measurements while drawing listener = this.sketch.getGeometry()?.on('change', (evt: any) => { const geom = evt.target const output = this.formatArea(geom) tooltipCoord = geom.getInteriorPoint().getCoordinates() if (this.measureTooltipElement && this.measureTooltip) { this.measureTooltipElement.innerHTML = output this.measureTooltip.setPosition(tooltipCoord) } }) }) this.on('drawend', () => { map.removeInteraction(this) if (this.measureTooltipElement && this.measureTooltip) { this.measureTooltipElement.className = 'ol-tooltip ol-tooltip-static' this.measureTooltip.setOffset([0, -7]) // unset sketch this.sketch = undefined // unset tooltip so that a new one can be created this.measureTooltipElement = undefined this.createMeasureTooltip(map) if (listener) { unByKey(listener) } } // Create measure tooltip this.createMeasureTooltip(map) }) map.addInteraction(this); } private formatArea(polygon: any): string { const area = getArea(polygon) return area > 10000 ? Math.round((area / 1000000) * 100) / 100 + ' ' + 'km<sup>2</sup>' : Math.round(area * 100) / 100 + ' ' + 'm<sup>2</sup>' } private createMeasureTooltip(map: Map): void { if (this.measureTooltipElement) { this.measureTooltipElement?.parentNode?.removeChild(this.measureTooltipElement) } this.measureTooltipElement = document.createElement('div') this.measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure' this.measureTooltip = new Overlay({ id: this.id, // Assigning overlay id element: this.measureTooltipElement, offset: [0, -15], positioning: 'bottom-center', stopEvent: false, insertFirst: false, }) map.addOverlay(this.measureTooltip) } }


问题原因
你把ID设置到了Draw交互对象上,而不是设置到绘制生成的多边形Feature对象上。在Polygon类(继承自Draw)中调用this.set('id', this.id),是给Draw交互本身添加属性,不是给最终的多边形要素加属性。删除时点击获取到的是多边形Feature,自然读不到你设置的ID。
解决步骤
给多边形Feature设置ID
在Polygon类的drawend事件回调中,通过event.feature拿到绘制完成的多边形,给它设置与Overlay对应的ID:this.on('drawend', (event: any) => { const drawnFeature = event.feature; // 用setId设置官方ID,对应getId()读取 drawnFeature.setId(this.id); // 或者用set设置自定义属性,对应get('id')读取 // drawnFeature.set('id', this.id); // 原有逻辑代码... map.removeInteraction(this); // ... })确保ID唯一性
固定的'randomid'会导致多个多边形ID重复,建议生成唯一ID:this.id = `polygon-${Date.now()}`;删除时正确读取ID
在RemovePolygonButton中,优先用getId()读取官方ID,兜底用自定义属性:for (const feature of features) { const featureId = feature.getId() || feature.get('id'); if (featureId) { const overlay = map.getOverlayById(featureId); if (overlay) { map.removeOverlay(overlay); } } featureLayerSource?.removeFeature(feature); }清理无效代码
删除Polygon类中给Draw交互设置ID的无效代码:// 删除以下两行 // this.set('id', this.id) // this.setProperties({ 'id': this.id, 'title': this.id })
额外优化
- 避免重复创建Overlay:
drawend中调用了两次createMeasureTooltip(map),会生成多余Overlay,保留一次即可。 - 防止重复绑定点击事件:每次点击删除按钮都会给map绑定新的
click事件,多次点击会触发多次删除逻辑,可先解绑再绑定:button.addEventListener('click', () => { map.un('click'); map.on('click', (evt: any) => { // 删除逻辑... }) })
内容的提问来源于stack exchange,提问作者robtot
相关产品推荐
相关产品推荐

