Gareth Jones
Gareth Jones

Reputation: 534

OpenLayers 3: Show vector labels on hover

I'm trying to find a way to display labels on OpenLayers vector features only when the mouse hovers over the feature's icon. I've found a few examples of similar things but nothing that quite does what I need to do. It seems to me like it would be fairly simple but I just can't work out how to start.

This is what my feature style code looks like (one example of several). Note that I'm bringing in the feature data from a few GeoJSON files, hence the feature.get(...)s in the color sections:

if (feature.get('level_pc') < 35 ) {
    var style = new ol.style.Style({
        fill: new ol.style.Fill({color: feature.get('shapefill')}),
        stroke: new ol.style.Stroke({color: feature.get('shapestroke')}),
        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
            anchor: [16, 16],
            anchorXUnits: 'pixels',
            anchorYUnits: 'pixels',
            opacity: 0.75,
            src: '/icons/aws-red.png'
        })),
        text: new ol.style.Text({
            font: '12px Roboto',
            text: feature.get('label'),
            fill: new ol.style.Fill({
                color: feature.get('textfill')
            }),
            stroke: new ol.style.Stroke({
                color: feature.get('textstroke'),
                width: 1
            })
        })
    });
    return style
} else { ...

I'm really hoping that there's a way to insert some code into the style definition that creates the hover interaction, rather than having to create a duplicate of every style and then write some extra code that switches between the hover/non-hover styles as necessary. Maybe it could be by way of setting the alpha value in the text color to 255 on mouseover and 0 at other times. Perhaps I'm being too optimistic.

Does anyone have any ideas or examples I could check out?

Thanks, Gareth


EDIT: Thanks to Jose I'm much closer to the goal now. My original code has changed somewhat since I first asked the question. I'm now applying styles to each feature through a function which reads the name of the icon file from GeoJSON data. For example gates are displayed with a 'gate-open' or 'gate-closed' icon and silos with a 'silo-high', 'silo-medium' or 'silo-low' icon. The correct icons and text are displaying on hover for all features, which is great - it's just that when I'm not hovering over the icons, the incorrect icon is displaying - it's showing the 'silo-low' icon for all features. When I hover over an icon it shows the correct icon, then reverts back when I'm no longer hovering.

Here's (the important bits of) my updated code:

var structuresStyleHover = function(feature, resolution) {
    style = new ol.style.Style({
        fill: new ol.style.Fill({color: feature.get('shapefill')}),
        stroke: new ol.style.Stroke({color: feature.get('shapestroke')}),
        image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
            anchor: [16, 16],
            anchorXUnits: 'pixels',
            anchorYUnits: 'pixels',
            opacity: 1,
            src: '/icons/'+feature.get('icon')+'-'+feature.get('status').toLowerCase()+'.png'
        })),
        text: new ol.style.Text({
            font: '12px Roboto',
            text: feature.get('label'),
            fill: new ol.style.Fill({
                color: feature.get('textfill')
            }),
            stroke: new ol.style.Stroke({
                color: feature.get('textstroke'),
                width: 1
            })
        })
    })
    return style;
};

var styleCache = {};
var styleFunction = function(feature,resolution) {
      var radius = 16;
      var style = styleCache[radius];
      if (!style) {
        style = new ol.style.Style({
            image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
                anchor: [16, 16],
                anchorXUnits: 'pixels',
                anchorYUnits: 'pixels',
                opacity: 0.5,
                src: '/icons/'+feature.get('icon')+'-'+feature.get('status').toLowerCase()+'.png'
            })),
        });
        styleCache[radius] = style;
      }
      return style;
};

var structuresLayer = new ol.layer.Vector({
    source: structuresSource,
    style: styleFunction
});

...

var map = new ol.Map({
    layers: [paddocksLayer,structuresLayer],
    interactions: ol.interaction.defaults({
        altShiftDragRotate: false,
        dragPan: false,
        rotate: false
    }).extend([new ol.interaction.DragPan({kinetic: null})]),
    target: olMapDiv,
    view: view
});

...

map.on('pointermove', function(evt) {
    if (evt.dragging) {
        return;
    }
    structuresLayer.getSource().getFeatures().forEach(f=>{
        f.setStyle(styleFunction);
    });
    var pixel = map.getEventPixel(evt.originalEvent);
    map.forEachFeatureAtPixel(pixel,function(feature,resolution) {
        feature.setStyle(structuresStyleHover(feature,resolution));
        return feature;
    });    

});

I'm not getting any errors - it's just not showing the correct icon unless the mouse is hovering over the icon.

I'm sure I'm missing something really obvious, but I can't work it out. Any ideas please?

Upvotes: 3

Views: 8039

Answers (1)

Jose Hermosilla Rodrigo
Jose Hermosilla Rodrigo

Reputation: 3683

You can use setStyle:

var mystyle = new ol.style.Style({
  fill: new ol.style.Fill({color: '#00bbff'}),
  stroke: new ol.style.Stroke({color: '#fff'}),
  image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
    anchor: [16, 16],
    anchorXUnits: 'pixels',
    anchorYUnits: 'pixels',
    scale : 0.1,
    opacity: 1,
    src: 'http://2.bp.blogspot.com/_Sdh3wYnDKG0/TUiIRjXEimI/AAAAAAAAQeU/bGdHVRjwlhk/s1600/map+pin.png'
  })),
  text: new ol.style.Text({
    font: '12px Roboto',
    text: 'AAAAAAAAAAAAAAA',
    fill: new ol.style.Fill({
      color: '#ffbb00'
    }),
    stroke: new ol.style.Stroke({
      color: '#000',
      width: 1
    })
  })
});
var styleCache = {};
var styleFunction = function(feature) {
  var radius = 3;
  var style = styleCache[radius];
  if (!style) {
    style = new ol.style.Style({
      image: new ol.style.Circle({
        radius: radius,
        fill: new ol.style.Fill({
          color: 'rgba(255, 153, 0, 0.4)'
        }),
        stroke: new ol.style.Stroke({
          color: 'rgba(255, 204, 0, 0.2)',
          width: 1
        })
      })
    });
    styleCache[radius] = style;
  }
  return style;
};

var vector = new ol.layer.Vector({
  source: new ol.source.Vector({
    url: 'http://openlayers.org/en/v3.17.1/examples/data/kml/2012_Earthquakes_Mag5.kml',
    format: new ol.format.KML({
      extractStyles: false
    })
  }),
  style: styleFunction
});

var raster = new ol.layer.Tile({
  source: new ol.source.Stamen({
    layer: 'toner'
  })
});

var map = new ol.Map({
  layers: [raster, vector],
  target: 'map',
  view: new ol.View({
    center: [0, 0],
    zoom: 2
  })
});

map.on('pointermove', function(evt) {
  if (evt.dragging) {
    return;
  }
  vector.getSource().getFeatures().forEach(f=>{
    f.setStyle(styleFunction);
  });
  var pixel = map.getEventPixel(evt.originalEvent);
  map.forEachFeatureAtPixel(pixel,function(feature) {
    feature.setStyle(mystyle);
    return feature;
  });    

});
#map {
  position: relative;
}
<title>Earthquakes in KML</title>
<link rel="stylesheet" href="http://openlayers.org/en/v3.17.1/css/ol.css" type="text/css">
<script src="http://openlayers.org/en/v3.17.1/build/ol.js"></script>
<script src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<div id="map" class="map"></div>

Upvotes: 5

Related Questions