4

I am new to OpenLayers and I'm trying to label features on a map using a GeoJSON data. Here is my code:

var layer = new ol.layer.VectorTile({
    source: new ol.source.VectorTile({
        format: new ol.format.GeoJSON(),
        tileGrid: ol.tilegrid.createXYZ(),
        url: "http://abcd.com/cities/{z}/{x}/{y}.geojson"
    })
})

Sometimes I get "features": null in the GeoJSON data:

{ "type": "FeatureCollection", "features": null }

and features parsing fails with error Uncaught TypeError: Cannot read property 'length' of null. Is it possible to replace null by [] before parsing features?

UPDATE. I found a solution, but got a new issue: I can't add features to the source. Here is my code (I'm using OpenLayers 3.9.0):

var format = new ol.format.GeoJSON();
var source = new ol.source.TileVector({
    tileGrid: ol.tilegrid.createXYZ(),
    tileLoadFunction: function (url) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onreadystatechange = function () {
            if (XMLHttpRequest.DONE === xhr.readyState && 200 === xhr.status) {
                var json = xhr.responseText.replace("null", "[]");
                var features = format.readFeatures(json, {
                    featureProjection: "EPSG:3857"
                });
                features.forEach(function (f) {
                    console.log(f.getGeometry().getCoordinates()); // features are parsed correctly
                });
                source.addFeatures(features);
            }
        };
        xhr.send();
    },
    url: "http://abcd.com/cities/{z}/{x}/{y}.geojson"
});
source.on("addfeature", function () {
    console.log(source.getFeatures()); // outputs an empty array of the features; it seems that the features haven't been added
});
new ol.Map({
    target: "map",
    layers: [
        new ol.layer.Vector({
            source: source
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([0, 0]),
        maxZoom: 12,
        minZoom: 1,
        zoom: 6
    })
});

Any suggestions?

3 Answers 3

2

First of all, please don't use OpenLayers 3.9 and ol.source.TileVector. That has many issues. Use a recent version and ol.source.VectorTile, like in your first snippet.

If your GeoJSON has "features": null, then it is is invalid. From the GeoJSON spec:

An object of type "FeatureCollection" must have a member with the name "features". The value corresponding to "features" is an array. Each element in the array is a feature object as defined above.

So if a tile has no features, the GeoJSON for the tile should contain "features": [] instead.

If you do not have access to the server that provides the tiles, you can work around the issue on the client side:

new ol.source.VectorTile({
  format: new ol.format.GeoJSON(),
  tileGrid: ol.tilegrid.createXYZ(),
  url: "http://abcd.com/cities/{z}/{x}/{y}.geojson"
  tileLoadFunction: function(tile, url) {
    tile.setLoader(function() {
      var xhr = new XMLHttpRequest();
      xhr.open('GET', url);
      xhr.onload = function() {
        var json = JSON.parse(xhr.responseText);
        if (json.features === null) {
          json.features = [];
        }
        var features = tile.getFormat().readFeatures(json, {featureProjection: map.getView().getProjection()});
        tile.setFeatures(features);
      }
      xhr.send();
    })
  }
})
9
  • Thank you for your answer, but, unfortunately, your code doesn't work for me with latest 3.14: I get two errors (Uncaught TypeError: this.s is not a function and Uncaught TypeError: Cannot read property 'a' of undefined). The reason I use 3.9 is that the features are cut off with VectorTile since version 3.12 or higher.
    – popov895
    Commented Feb 27, 2016 at 14:42
  • I updated the code in my answer. It should work now. What do you mean by "features are cut off"? They are, but you should not see it because they are also drawn on adjacent tiles.
    – ahocevar
    Commented Feb 28, 2016 at 19:36
  • now I get error TypeError: tile.setProjection is not a function(…), and if I comment tile.setProjection(...);, then I get error Uncaught TypeError: Cannot read property 'a' of undefined. As for "features are cut off": please, see this question.
    – popov895
    Commented Feb 29, 2016 at 12:22
  • I updated the answer so it should work now, like in your working solution below. Regarding cut off features, see my comment in the linked question. Or do like tsauerwein suggested, using ol.layer.Vector.
    – ahocevar
    Commented Mar 1, 2016 at 7:23
  • Please, see my comment above: your code doesn't work with the letest 3.14.1. I found this issue and, I think, it explains why tile.setFeatures(...) throws an exception. As for cutt off features: buffering doesn't solve my problem (ie. setting renderBuffer to 512).
    – popov895
    Commented Mar 1, 2016 at 10:52
0

The solution for OpenLayers 3.9.0:

var format = new ol.format.GeoJSON();
var source = new ol.source.TileVector({
    tileGrid: ol.tilegrid.createXYZ(),
    tileLoadFunction: function (url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = function () {
            try {
                var json = JSON.parse(xhr.responseText);
                if (!json.features) {
                    json.features = [];
                }
                var features = mapFormat.readFeatures(json, {
                    featureProjection: "EPSG:3857"
                });
                callback(features);
            } catch (e) {
                console.log(e);
            }
        };
        xhr.send();
    },
    url: "http://abcd.com/cities/{z}/{x}/{y}.geojson"
});
new ol.Map({
    target: "map",
    layers: [
        new ol.layer.Vector({
            source: source
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([0, 0]),
        maxZoom: 12,
        minZoom: 1,
        zoom: 6
    })
});
0

The solution for OpenLayers 3.13.0:

new ol.Map({
    target: "map",
    layers: [
        new ol.layer.VectorTile({
            source: new ol.source.VectorTile({
                format: new ol.format.GeoJSON(),
                tileGrid: ol.tilegrid.createXYZ(),
                url: "http://abcd.com/cities/{z}/{x}/{y}.geojson",
                tileLoadFunction: function(tile, url) {
                    tile.setLoader(function() {
                        var xhr = new XMLHttpRequest();
                        xhr.open("GET", url);
                        xhr.onload = function () {
                            try {
                                var json = JSON.parse(xhr.responseText);
                                if (!json.features) {
                                    json.features = [];
                                }
                                var features = tile.getFormat().readFeatures(json, {
                                    featureProjection: "EPSG:3857"
                                });
                                tile.setFeatures(features);
                            } catch (e) {
                                console.log(e);
                            }
                        };
                        xhr.send();
                    });
                }
            })
        })
    ],
    view: new ol.View({
        center: ol.proj.fromLonLat([0, 0]),
        maxZoom: 12,
        minZoom: 1,
        zoom: 6
    })
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.