Search code examples
performancevectorzoomingopenlayerssimplify

How to simplify a GML vector layer depending on zoom in OpenLayers?


I have a performance problem on Firefox with a pretty big (Canada wide) GML vector layer in OpenLayers. My website is in JavaScript/PHP. The layer is created like that:

map.addLayer(new OpenLayers.Layer.Vector(openlayers_gml_layer_canada_bv, {
    strategies: [   new OpenLayers.Strategy.Fixed()],
    protocol:       new OpenLayers.Protocol.HTTP({
        url: "./images/GML/BV - Canada.gml",
        format: gml_format
    }),
    styleMap: RegionsStyles,
    visibility: false
}));

I checked everywhere and the solutions given where to add this line to select renders:

OpenLayers.Layer.Vector.prototype.renderers = ["SVG2", "VML", "Canvas"];

It help with drag and drop when dragging but when I let go, it loads for 3-5 seconds (so it lags a big shot after instead of lagging the whole time, but it's still lagging a lot).

The other answers where saying to either simplify the vectors or process the tiles outside OpenLayers, with GeoServers.

I would like to simplify my vectors if possible but I have no idea how to implement correctly the simplify method that OpenLayers has. Would you have an idea on how to simplify my layer?

My code (map.addLayer...) is in a function where I add all my vector maps. The others are not lagging as they are much more simple. Is there an easy way? Do I have to loop in my features to simplify each of them? And if yes, how can I transform my features before actually loading them on the map (so it doesn't lag)?

It would be perfect if I could simplify the map depending on the zoom level. So if it's far away (if I see all of Canada) I want the layer to be more simplified, and less if I zoom (as I will have less vectors on screen and at some point it stops lagging completely).

Thanks a lot everyone. I hope my question is clear.


Solution

  • There is a simplify function built into OpenLayers based on the Douglas Peucker algorithm, you can see the source in OpenLayer.Geometry.Linestring.js and here is a very nice animated example. The tolerance determines how much simplification happens -- you will have to experiment for your use case.

    The problem with this is that you still have to load the whole GML file, which I suspect is probably where most of your lag is coming from (not from actually rendering it). However, if you were to load it just once using OpenLayers.Format.GML or using a fixed strategy, as you have, simplify it, and then just show the new simplified version, you won't get such a lag when you pan. As you would still have the original loaded, it should be a simple matter to switch versions, with high or lower tolerance as you zoom in or out. Note that the simplification works on linestrings, not polygons, but you can solve this by constructing a linestring from the points arrays that make up your polygons.

    var GML= <some gml file>;
    
    //convert gml to OpenLayers.Feature.Vector
    var reader=new OpenLayers.Format.GML();
    var feat=reader.read(polygon);
    
    //get initial points from feature's geometry and set target points (eg, 2000 here).
    var num_points=feat.geometry.components[0].components.length;
    var target_points=2000;
    
    //set simplification tolerance to one meter initially
    var tolerance=1;
    
    //convert feature's geometry to linestring
    var linestring=new OpenLayers.Geometry.LineString(feat.geometry.components[0].components);
    var simplified_linestring;
    
    //simplify linestring until target points reached
    while(target_points<num_points){
      simplified_linestring=linestring.simplify(tolerance);
      num_points=simplified_linestring.components.length;
      tolerance+=1;
    }
    
    
    //create new geometry from simplified linestring    
    var geom=new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(simplified_linestring.components));
    

    If you have many polygons within your GML, you would have to do a loop with the upper bound being feature.geometry.components.lengh.

    Another option would be to use something like Postgis to do the simplification for you, so you are not transmitting so much data down the wire. However, this won't really help if at certain zoom levels you actually want the complete polygon.

    I'm not really sure how Geoserver would help in this instance, as it is more for transmitting data in various raster and vector formats, unless you are talking about rendering you GML into a WMS and then just serving up a raster representation.