Search code examples
openlayersopenlayers-3

Updating feature styles in OpenLayers 3


I am pulling data from a 3rd party API that gives me lat/lon coords as well as a status for the point. I am currently able to successfully plot the points and give them the correct style for their status on the first iteration. However, every 3 seconds I need to be able to update their style if the status changed. I have tried using mySource.changed() but it did not work. I looked all over and cannot find the solution, even though this does not seem like it should be a difficult thing to accomplish?

I also tried to clear() my source every 3 seconds, but then the vector layer 'flashes' and I need it to update seamlessly. I also tried removing/re-adding the entire vector layer. Do I need to use a style function? Or a feature overlay? Why can I not just overwrite styles like I can in google maps or leaflet?

My Styles

var takenStyle = new ol.style.Style({
    image: new ol.style.Icon({
        src: '../_images/redMark.png',
        scale: .2
    })
});
var openStyle = new ol.style.Style({
    image: new ol.style.Icon({
        src: '../_images/greenMark.png',
        scale: .2
    })
});
var unsureStyle = new ol.style.Style({
    image: new ol.style.Icon({
        src: '../_images/yellowMark.png',
        scale: .2
    })
});

How I am assigning the styles/features

if ((data.scopes[i].parking_spot.status === true)) {
var feature = new ol.Feature({
  geometry: new ol.geom.Point(ol.proj.transform(pointCoords, 'EPSG:4326', 'EPSG:3857'))
});
feature.setStyle(takenStyle);
feature.setId(i);
pointSource.addFeature(feature);

UPDATE: Using Navageer Gowda's suggestion, I was able to finally figure this out. I Created a second function, and had that iterate through the features to update the styles.

if ((data.scopes[i].parking_spot.occupied === true && data.scopes[i].parking_spot.occupied === lastCheck[i].occupied)) {
     theFeatures[i].setStyle(takenStyle);
}

Solution

  • To force a refresh of layer style every 3 seconds, you can do this:

    window.setInterval(function () {
      layer.getSource().dispatchEvent('change');
    }, 3000);
    

    However, the API supports what you're trying to do in a cleaner way by using a custom loader function on your ol.source.Vector and a custom style function on your ol.layer.Vector. It looks like this:

    var layer = new ol.layer.Vector({
      source: new ol.source.Vector({
        loader: function(extent, resolution, projection) {
          var fetchData = function() {
            // Fetch data here, add features *without style* to layer.getSource()
          };
    
          // Fetch data once immediately
          fetchData();
    
          // re-fetch every 3 seconds
          window.setInterval(fetchData, 3000);
        }
      }),
      style: function(feature, resolution) {
        var props = feature.getProperties();
    
        // Psuedo-logic shown here, use your own to determine which style to return
        if (isTaken) {
          return takenStyle;
        } else if (isOpen) {
          return openStyle;
        } else {
          return unsureStyle;
        }
      }
    });