TransWikia.com

How to find features in Openlayers VectorTiles

Geographic Information Systems Asked by geraldo on April 21, 2021

I use MVT vector tiles with Openlayers and everything works fine – but now I have a conceptual problem.

I need to make features selectable and searchable. The problem is that there is no way to retrieve all features from ol.source.VectorTile, as you could do with ol.source.Vector using getFeatures() method.

So what do people do in this case? The only way I see is to maintain a extra list with all the features loaded from GeoJSON. But then using MVT vector tiles doesn’t make sense any more as it is redundant to maintain the same information twice.

One Answer

An array of features can be built up as the MVT tiles load (there will be a different set of features for each zoom level of the tilegrid) making a separate GeoJSON unnecessary.

 var featuresForZ = [];

 vtSource().on('tileloadend', function(evt) {
    var z = evt.tile.getTileCoord()[0];
    var features = evt.tile.getFeatures();
    if (!Array.isArray(featuresForZ[z])) { featuresForZ[z] = []; }
    featuresForZ[z] = featuresForZ[z].concat(features);
});

You could use the appropriate array directly for searching, or you could copy the features to a normal vector source. This code is proof of concept, however practical applications would include using vSource as the source of a cluster source, something not possible with a vector tile source.

<!DOCTYPE html>
<html>
  <head>
    <title>Vector Layer from Vector Tiles</title>
    <link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/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://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
  </head>
  <body>
    <div id="map" class="map"></div>
    <script>

    var vtLayer = new ol.layer.VectorTile({
        source: new ol.source.VectorTile({
            format: new ol.format.MVT({ featureClass: ol.Feature }),
            url: 'https://basemaps.arcgis.com/v1/arcgis/rest/services/World_Basemap/VectorTileServer/tile/{z}/{y}/{x}.pbf',
            tileGrid: ol.tilegrid.createXYZ({ maxZoom: 13 })
        }),
        style: []   // layer must be added to map to load features, use empty style to avoid display 
    });

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

    var vSource = new ol.source.Vector();
    var featuresForZ = [];
    var viewZ = vtLayer.getSource().getTileGrid().getZForResolution(map.getView().getResolution());

    function vsRefresh() {
        vSource.clear();
        if (featuresForZ[viewZ]) {
            vSource.addFeatures(featuresForZ[viewZ]);
        }
    }

    vtLayer.getSource().on('tileloadend', function(evt) {
        var z = evt.tile.getTileCoord()[0];
        var features = evt.tile.getFeatures();
        features.forEach(function (feature) {
            // each vector tile has its own set of feature ids, but duplicates are not allowed in normal vector sources
            feature.setId(undefined);   
        });
        if (!Array.isArray(featuresForZ[z])) {
            featuresForZ[z] = [];
        }
        featuresForZ[z] = featuresForZ[z].concat(features);
        if (z === viewZ) {
            vsRefresh();
        }
    });

    map.getView().on('change:resolution', function() {
        // use VT features from the tile z level corresponding to view resolution
        var newZ = vtLayer.getSource().getTileGrid().getZForResolution(map.getView().getResolution());
        if (newZ !== viewZ) {
            viewZ = newZ;
            vsRefresh();
        }
    });

    var vLayer = new ol.layer.Vector({
        source: vSource,
    });
    map.addLayer(vLayer);

    </script>
  </body>
</html>

Correct answer by Mike on April 21, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP