I can't find features on a custom layer with the forEachFeatureAtPixel() using the coordinates from a map.on('click') event listener. The process is simple:
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 { FeatureLike } from 'ol/Feature';
/**
* 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 draw polygons (features) 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) => {
const featuresInFeatureLayer = mapComponent.getFeatureLayer()?.getSource()?.getFeatures()
console.log('Features in FeatureLayer: ', featuresInFeatureLayer) // Prints two polygon features
const coordinate = transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
const pixel = map.getPixelFromCoordinate(coordinate)
const features: FeatureLike[] = []
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
features.push(feature);
})
console.log('Features: ', features) // Always shows empty array
})
})
}
}
/**
* The Polygon will be added to the map
*/
class Polygon extends Draw {
constructor(map: Map, featureLayerSource: VectorSource) {
super({
type: 'Polygon',
source: featureLayerSource
})
this.on('drawend', () => {
map.removeInteraction(this)
})
map.addInteraction(this);
}
}
It does find the two polygon features on the FeatureLayer, but it never finds the feature(s) on the clicked/selected coordinate when clicking on any of the polygons. I also tried clicking all around the map, but the Feature array is always empty:
Note: I tried put this in a code snippet, but it does not always like TypeScript e.g. when extending ol.Control... so I just pass the code.
Thanks!
You do not need to transform the coordinate so
const coordinate = transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326')
const pixel = map.getPixelFromCoordinate(coordinate)
const features: FeatureLike[] = []
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
features.push(feature);
})
can become
const pixel = map.getPixelFromCoordinate(evt.coordinate)
const features: FeatureLike[] = []
map.forEachFeatureAtPixel(pixel, function(feature, layer) {
features.push(feature);
})
but as the event also has a pixel property you can use
const features: FeatureLike[] = []
map.forEachFeatureAtPixel(evt.pixel, function(feature, layer) {
features.push(feature);
})