JCB
JCB

Reputation: 109

Openlayers/GeoServer WFS caching, tiles or any optimization

I'm developping a Web application using OpenLayers 3.20.0 with layers coming from GeoServer, linked to an Oracle data source. This application is using mostly ImageWMS layer, and also a Vector layer for interaction and edition. The issue is that the map is very slow with more than 30000 polylines drawn, and I would like to make the process faster :-)

So I would like to know what is the best way to do. I found two ways :

I looked for samples of VectorTile, but they are not very numerous (most of the time it's about OpenLayers 2), and the documentation is a little poor.

The big unknown about the layer declaration is about the VectorTile source. An URL must be defined, and I found in documentation that I have to put {x}/{y}/{z} parameters, but where exactly, and how is built this URL ? (see https://openlayers.org/en/latest/apidoc/module-ol_source_VectorTile-VectorTile.html, the 'url' option)

As sample, my current Vector source has an URL like this : /geoserver/ANF/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=ANF:myLayer&outputFormat=application%2Fjson

So to define VectorTile source, how do I define my URL, and what do have I to do on GeoServer side to configure my layer in the correct way ? I found this resource : https://docs.geoserver.org/latest/en/user/extensions/vectortiles/tutorial.html

The only vector tile format I have that is not image type on GeoServer is 'application/json;type=utfgrid'. And when I put '@pbf/{z}/{x}/{-y}.pbf' at the end of my URL like the sample, I got errors, but I guess it's not the right way.

Any help would be greatly appreciated to give me more precision on how to make work a VectorTile layer and source with GeoServer, or another way to optimize the map I create.

Many thanks.

EDIT

After some answers, I'm coming to this sample of code :

this._view = new ol.View({
    center: [74000, 96000],
    projection: 'EPSG:2169',
    zoom: 13,
    maxZoom: 24,
    minZoom: 11
});

this._map = new ol.Map(
    {
        view: this._view,
        controls: [
            new ol.control.Zoom(),
            new ol.control.ScaleLine()
        ]                
    });

let vectorSourceURL: string = `/geoserver/ANF/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=ANF:myLayer&outputFormat=application%2Fjson`;

let source = new ol.source.VectorTile({
    format: new ol.format.GeoJSON({
        defaultDataProjection: 'EPSG:2169',
        geometryName: 'GEOLOC'
    }),
    tileUrlFunction: function (tileCoord, pixelRatio, projection) {
        return vectorSourceURL + '&bbox=' + source.getTileGrid().getTileCoordExtent(tileCoord).join(',') + ',EPSG:2169';
    },
    tileGrid: ol.tilegrid.createXYZ(),
    projection: 'EPSG:2169'
});

let layer = new ol.layer.VectorTile({
    source: source,
    renderOrder: null
});

layer.set('name', 'myLayer');
layer.set('title', 'myLayer');

This code falls to the following error :

ERROR TypeError: Cannot read property 'getUnits' of null at ol.renderer.canvas.VectorTileLayer.createReplayGroup_ (ol-debug.js:29814) at ol.renderer.canvas.VectorTileLayer.drawTileImage (ol-debug.js:29886) at ol.renderer.canvas.VectorTileLayer.ol.renderer.canvas.TileLayer.prepareFrame (ol-debug.js:26557) at ol.renderer.canvas.Map.renderFrame (ol-debug.js:30302) at ol.Map.renderFrame_ (ol-debug.js:42107) at ol.Map. (ol-debug.js:41013) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421) at Object.onInvokeTask (core.js:3815) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)

It appears that the issue comes now from EPSG:2169. An sample with EPSG:3857 works well (see answers).

Am I missing something ?

Many thanks !

Upvotes: 1

Views: 3384

Answers (2)

Mike
Mike

Reputation: 17942

Vector tiles don't need to be .pbf or use XYZ urls. Here's the OpenLayers WFS example reworked to use the WFS url as the source for vector tiles. When zoomed out to cover the whole of Canada it does seem more responsive than the original example.

  var vectorSource = new ol.source.VectorTile({
    format: new ol.format.GeoJSON(),
    tileUrlFunction: function(tileCoord, pixelRatio, projection) {
      return 'https://ahocevar.com/geoserver/wfs?service=WFS&' +
          'version=1.1.0&request=GetFeature&typename=osm:water_areas&' +
          'outputFormat=application/json&srsname=EPSG:3857&' +
          'bbox=' + vectorSource.getTileGrid().getTileCoordExtent(tileCoord).join(',') + ',EPSG:3857';
    },
    tileGrid: ol.tilegrid.createXYZ()
  });

  var vector = new ol.layer.VectorTile({
    source: vectorSource,
    style: new ol.style.Style({
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 255, 1.0)',
        width: 2
      })
    })
  });

  var raster = new ol.layer.Tile({
    source: new ol.source.OSM()
  });

  var map = new ol.Map({
    layers: [raster, vector],
    target: document.getElementById('map'),
    view: new ol.View({
      center: [-8908887.277395891, 5381918.072437216],
      maxZoom: 19,
      zoom: 12
    })
  });
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/3.20.0/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/3.20.0/ol.js"></script>
<div id="map"></div>

There does seem to be a problem using that code with projections based on proj4 but it should work using a tile load function

let viewProjection = ol.proj.get('EPSG:2169');

let format = new ol.format.GeoJSON({
        defaultDataProjection: viewProjection,
        featureProjection: viewProjection,
        geometryName: 'GEOLOC'
    });

let source = new ol.source.VectorTile({
    tileUrlFunction: function (tileCoord, pixelRatio, projection) {
        return vectorSourceURL + '&bbox=' + source.getTileGrid().getTileCoordExtent(tileCoord).join(',') + ',EPSG:2169';
    },
    tileLoadFunction: function (tile, url) {
        tile.setProjection(viewProjection);
        tile.setLoader(function() {
            var xhr = new XMLHttpRequest();
            xhr.onload = function() {
                tile.setFeatures(format.readFeatures(xhr.responseText));
            }
            xhr.open("GET", url, true);
            xhr.send();
        });
    },
    tileGrid: ol.tilegrid.createXYZ(),
    projection: viewProjection
});

Upvotes: 1

Ian Turton
Ian Turton

Reputation: 10976

The easiest way to speed up your application is to switch to using a WMTS (or tiled WMS) layer. This way your application can make use of the browser cache to only request tiles it hasn't seen before and the server need only render them once as they are cached to disk there too.

You almost certainly don't need all 300K features for your editing so trying to filter the WFS to just the bounding box of the requested area will help.

Finally, the biggest win would probably come from switching to a proper spatial database like PostGIS.

Upvotes: 0

Related Questions