16

I am using the following code to add a feature to a vector layer in OpenLayers 3 (OL3):

marker = new ol.Feature({
    geometry: new ol.geom.Point([longitude, latitude]),
    name: "Location Marker"
});
markerStyle = new ol.style.Style({
  image: new ol.style.Icon({
    anchor: [0.5, 1.0],
    anchorXUnits: "fraction",
    anchorYUnits: "fraction",
    src: "Content/Images/OpenLayers/marker_trans.png"
  }),
  zIndex: 100000
});
marker.setStyle(markerStyle);
marker.on("click", function(e) {
  // do something
}, marker);
map.getSource().addFeature(marker);

The marker displays as expected, but the click event never fires. What am I doing wrong?

I should note that there is already a handler associated with "click" at the map level, i.e.

map.on("click", function(e) {
  // do something
}, marker);
Icarus
  • 1,627
  • 7
  • 18
  • 32
ProfNimrod
  • 4,142
  • 2
  • 35
  • 54

3 Answers3

52

First: Features don't fire clicks! For information on the events features do fire, check http://openlayers.org/en/master/apidoc/ol.Feature.html.

For checking if a feature did get clicked, there is the .forEachFeatureAtPixel(pixel, callback) function of ol.Map. ( http://openlayers.org/en/master/apidoc/ol.Map.html#forEachFeatureAtPixel ) The callback is executed on every feature at the pixel. The callback gets passed 2 arguments: the feature and the layer the feature is in.

Good to know is the .getEventPixel(event) function, if you don't work with openlayers event handlers but with handlers on the viewport. If your using openlayers eventhandler, the event has a property .pixel. (http://openlayers.org/en/master/apidoc/ol.Map.html#getEventPixel) The methods .getEventCoordinate(event) and .getCoordinateFromPixels(pixels) might be useful, too.

So you would add it like this to your map.on("click", ... :

map.on("click", function(e) {
    map.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
        //do something
    })
});

Same thing with jQuery:

$(map.getViewport()).on("click", function(e) {
    map.forEachFeatureAtPixel(map.getEventPixel(e), function (feature, layer) {
        //do something
    });
});

Same thing with pure JS:

map.getViewport().addEventListener("click", function(e) {
    map.forEachFeatureAtPixel(map.getEventPixel(e), function (feature, layer) {
        //do something
    });
});

You might also want to check this example, there are two uses of this function, first with openlayers events, the second with jQuery events: http://openlayers.org/en/master/examples/icon.js

Note

There is also the possibility to do this with an ol.interaction.Select (http://openlayers.org/en/master/apidoc/ol.interaction.Select.html?unstable=true), but this is a little bit overpowered for this case. And it has some unintuitive caveats caused by openlayers internally moving the selected features to another so called unmanaged layer.

Anyhow this works by adding a listener to the collection belonging to the interaction. The collection can be retrieved with .getFeatures().

interaction.getFeatures().on("add", function (e) { 
    // do something. e.element is the feature which was added
});
Simon Zyx
  • 6,503
  • 1
  • 25
  • 37
  • According to http://openlayers.org/en/v3.0.0/apidoc/ol.Feature.html#on such a handler can be added. Any ideas what events are support for this? Is it just the "change" and "change:geometry" events? Your comment about features not being part of the DOM helps me understand why "click" doesn't fire on a feature. With OL2 I could add "hover" event to features/markers. Is the new approach the same as you describe for the "click" event, i.e., use map.on("onmouseover", function(e){map.forEachFeatureAtPixel(...)}} – ProfNimrod Oct 16 '14 at 19:12
  • Sure you can add such a handler, but it never it gets fired ... as you can read in the documentation, just below the description of the constructor. And nope there aren't any not documented ones - i checked the code for you, too :P (the documentation is quite complete btw - if you untick the stable only checkbox). I don't think ol.Map fires an event called "onmouseover", but you could do that with jquery or basic js. $(map.getViewport()).on("mousemove", function(e){map.forEachFeatureAtPixel(map.getEventPixel(e), ...)} please check the example. – Simon Zyx Oct 17 '14 at 09:20
  • 1
    I have marked this as the answer as it addresses my question regarding the particular click interaction I wanted. However, for other feature interactions, like 'hover', I think that http://openlayers.org/en/v3.0.0/examples/select-features.html provides a better approach using ol.interaction – ProfNimrod Oct 17 '14 at 13:18
  • If map has interactions, more cleaner way would be to use `ol.interaction` instance's `getFeatures()` method instead of `forEachFeatureAtPixel()` – yanot Dec 09 '14 at 22:47
  • I am getting a different event type when using map.on and jquery's $(map.getViewport()).on("click", is it possible to get the coordinates with this jquery event – Juan Diego Nov 04 '15 at 00:49
  • with map.getEventPixel(event) you get the pixels and with map.getCoordinateFromPixel(pixel) you can get the coordinate – Simon Zyx Nov 04 '15 at 10:45
  • Or just use .getEventCoordinate(event). – Simon Zyx Nov 04 '15 at 10:50
  • Is it possible to make `forEachFeatureAtPixel` stop after the first feature? – kiradotee Jan 23 '18 at 10:33
  • 1
    http://openlayers.org/en/master/apidoc/ol.Map.html#forEachFeatureAtPixel says: To stop detection, callback functions can return a truthy value. – Simon Zyx Jan 24 '18 at 10:37
3

If you just want a click on a map, this will work for you.

  var map = new ol.Map({
    target: 'map',
    layers: [
      new ol.layer.Tile({
        source: new ol.source.MapQuest({layer: 'sat'})
      })
    ],
    view: new ol.View({
      center: ol.proj.transform([37.41, 8.82], 'EPSG:4326', 'EPSG:3857'),
      zoom: 4
    })
  });

map.on("click", function(evt) {
    var coord = ol.proj.transform(evt.coordinate, 'EPSG:3857', 'EPSG:4326');
    var lon = coord[0];
    var lat = coord[1];
    alert(lon);
    alert(lat);
});
Costales
  • 2,799
  • 1
  • 17
  • 21
2

If you just need to add a marker on your map which is clickable, you can use overlays. In your HTML header define your marker's style:

<style>
    #marker {
        width: 20px;
        height: 20px;
        border: 1px solid #088;
        border-radius: 10px;
        background-color: #0FF;
        opacity: 0.5;
    }
</style>

then in script part of your file, after the map is created:

    // add marker
    var pos = ol.proj.fromLonLat([0.01123, 0.00612]);
    var marker = new ol.Overlay({
        position: pos,
        positioning: 'center-center',
        element: $('<div id="marker" title="Marker"></div>')
            .popover({
                'placement': 'top',
                'html': true,
                'content': '<strong>anything...</strong>'
            })
            .on('click', function (e) { $(".location-popover").not(this).popover('hide'); }),
        stopEvent: false
    });
    map.addOverlay(marker);
Amir Dashti
  • 369
  • 3
  • 14