I have a map with two custom buttons: Draw Polygon and Remove Feature, which allows me to draw- and remove drawn polygons. In addition, I create a measure overlay for the polygon(s) which show their area (see image). To identify the connection between a polygon and an overlay I attempt to set the same id on them so I can find and remove the overlay when removing the polygon by doing:
const featureId = feature.get('id')
const overlay = map.getOverlayById(featureId)
map.removeOverlay(overlay)
However, when attempting to read the id of the polygon, it always returns undefined. I also tried to set the id as a property and fetch that, but that also fails.
To make it easier to follow along:
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)
}
}
Your code is setting properties on the draw interaction, to set properties or an id on the features it draws you would need
this.on('drawend', (event) => {
event.feature.setId('randomId')