4

When I load more than around 500 vector features in Openlayers, performance on tablets and telephones reduces severely. My solution has been to clear the source on the map's moveend event. This works, but results in an annoying flash, presumably between clear and reload of the source.

An earlier solution to this "flashing" here no longer works, it results in recursive calls to the WFS.

Here is my current code:

import {GeoJSON} from 'ol/format';
import {bbox as bboxStrategy} from 'ol/loadingstrategy.js';
import VectorSource from 'ol/source/Vector.js';
import VectorLayer from 'ol/layer/Vector';

var vectorSource = new VectorSource({
 format: new GeoJSON({ dataProjection: layerProjection }),
 loader: function(extent, resolution, projection) {
   var proj = projection.getCode();
   var url = 'https://xxx.xxx/geoserver/wfs?service=WFS' +
     '&version=1.1.0&request=GetFeature&typename=' + layer + 
     '&maxFeatures=200&outputFormat=application/json&srsname=' + proj +
     '&bbox=' + extent.join(',') + ',' + proj;
   var xhr = new XMLHttpRequest();
   xhr.open('GET', url);
   var onError = function() {
     vectorSource.removeLoadedExtent(extent);
   }
   xhr.onerror = onError;
   xhr.onload = function() {
     if (xhr.status == 200) {
       //vectorSource.clear();// Causes recursion          
       vectorSource.addFeatures(
         vectorSource.getFormat().readFeatures(xhr.responseText));
     } else {
       onError();
     }
   }
   xhr.send();
 },
 strategy: bboxStrategy
});

var vector = new VectorLayer({
  source: vectorSource,
  style: style,
  visible: visible
});
map.on('moveend', function(evt) {
  console.log('refreshing vector');
  vectorSource.clear()
});

Is there a way to avoid the clear/reload flash in Openlayers 5? Or is there another method I should be using to load vector layers (the layers I have are typically point layers with 5000 to 10000 features).

[EDIT] After comment from @ahocevar, here is the style I am using:

import {Style, Circle, Fill, Stroke} from 'ol/style';
import Defaults from './PointDefaults';

export default function() {
  return new Style({
    image: new Circle({
      radius: Defaults.radius,
      fill: new Fill({
        color: 'green',
      }),
      stroke: new Stroke({
        color: Defaults.strokeColor,
        width: Defaults.strokeWidth
      })
    }),
  });
}
minisaurus
  • 1,099
  • 4
  • 17
  • 30
  • 1
    The problem is likely your `style`. Reusing style objects is key to good vector performance. If in doubt, edit your question to also include the style. – ahocevar Feb 04 '19 at 00:01
  • Much doubt I am in :) I've added the style; it's imported once, at time of layer creation. – minisaurus Feb 04 '19 at 07:40
  • Like I thought. You are creating a new Circle images with every feature. The default style cache size is 32 images, so with more than 32 features things will be very, very slow. I'm adding an answer that shows how you should write your style. – ahocevar Feb 05 '19 at 08:05

3 Answers3

1

Don't clear the source on each moveend but only when it exceed a number of features max, this will prevent too frequent reloads:

map.on('moveend', function(evt) {
  if (vectorSource.getFeatures().length > max) {
    vectorSource.clear()
  }
});

Openlayers should be able to display more than 500 features, even on slow devices.

  • As ahocevar says the style may be involved let's see how you use it.
  • You may also try to use the renderMode: 'image' option for the vector layer for faster rendering during interaction and animations.
  • you could use clustering at large scale to reduce the number of features to draw.

As example, this site display more than 18.000 point features even on mobile devices: https://c-conforme.fr

Viglino Jean-Marc
  • 1,371
  • 8
  • 6
1

The problem is that you are creating a new Style with a new Circle image for every feature at every render frame. In your case, you do not even need a style function, because the style is the same for all features. So your style module should export the style, not a function that returns the style:

import {Style, Circle, Fill, Stroke} from 'ol/style';
import Defaults from './PointDefaults';

export default new Style({
  image: new Circle({
    radius: Defaults.radius,
    fill: new Fill({
      color: 'green',
    }),
    stroke: new Stroke({
      color: Defaults.strokeColor,
      width: Defaults.strokeWidth
    })
  })
});
ahocevar
  • 5,448
  • 15
  • 29
  • I was a bit hasty accepting the answer. Mobile devices still get slow after around 1000 features are loaded. Seems like I need to find a way to throttle the number of features in the device's memory that also avoids the flash caused by calling vectorSource.clear() – minisaurus Feb 05 '19 at 09:30
  • 1
    You should consider using vector tiles instead of wfs. Then you don't have to clear features at all. – ahocevar Feb 08 '19 at 17:31
1

2 years later, I have found a solution that isn't hugely "flashy", but is a bit (I think my phrasing of the question must have been too vague).

I'm using vectorSource.refresh()

Giving this:

map.on('moveend', function(evt) {
 console.log('refreshing vector');
 vectorSource.refresh()
});

The OpenLayers version I'm using now is v6.5.0

minisaurus
  • 1,099
  • 4
  • 17
  • 30