Leaflet / Leaflet.VectorGrid

Display gridded vector data (sliced GeoJSON or protobuf vector tiles) in Leaflet 1.0.0
598 stars 194 forks source link

Broken lines #167

Open zedsh opened 6 years ago

zedsh commented 6 years ago

I'm trying to map routes from GeoJson. Lines are displayed, but with a small increase they are broken. I suspect that somehow they are not redrawn. Help me please.

https://i.imgur.com/vzGIeHL.png https://i.imgur.com/VY37RL9.png

perliedman commented 6 years ago

In the first shot, it looks like an entire tile is missing. Notice how the lines are cut perfectly along the y and x axis at the same coordinates, like a square was cut out there.

Of course hard to tell what's going on without a bit more information about what you're doing.

mngyng commented 6 years ago

I've got similar thing happening here:

https://i.imgur.com/5AHlclA.jpg

The problem is that VectorGrid does not render multiple "named layers" in their order when grouped as one Leaflet gridLayer. For example you would have "landuse", "admin", "water" as "named layers," and "pbfLayer" as the Leaflet gridLayer.

So the tiles may or may not be consistent in their rendering order and, for example, you would have one tile that has "landuse" on top, while it's neighboring tile has "admin" on top.

Any workaround approach would be appreciated.

mngyng commented 6 years ago

Managed to have a workaround. The source of the problem is that the vector tile server serves those named layers of a tile without taking care of the order of rendering. I ended up assigning the rendering order in the styling options, and modified Leaflet.VectorGrid.bundle.js to make them render in order (somewhere from line 1997, for the latest version on Aug.3, 2018). Not a pretty code, though.

zawarski commented 5 years ago

mngyng, any chance of getting hold of your work around?

mngyng commented 5 years ago

@zawarski , I'm posting my entire createTile() here. You could probably do a diff among different versions of Leaflet.VectorGrid.bundle.js to find out what's changed. You'll now have to assign a non-negative integer zorder to indicate the rendering order of each layer in the composite layer in your styling options/function, e.g. admin:{weight: 1, fillColor: '#ffffff', fillOpacity: 1, fill: true, zorder:0}, landuse: {weight: 0, fillColor: '#9bc2c4', fillOpacity: 1, fill: true, zorder:1}. The layer that has a larger zorder is rendered later, i.e., rendered as the upper layer. The downside is the backward-compatibility for your old codes that doesn't have a zorder in the styling options.

Now the code:

    createTile: function(coords, done) {
        var storeFeatures = this.options.getFeatureId;

        var tileSize = this.getTileSize();
        var renderer = this.options.rendererFactory(coords, tileSize, this.options);

        var vectorTilePromise = this._getVectorTilePromise(coords);

        if (storeFeatures) {
            this._vectorTiles[this._tileCoordsToKey(coords)] = renderer;
            renderer._features = {};
        }

        vectorTilePromise.then( function renderTile(vectorTile) {
            var layerorder = new Array(Object.keys(vectorTile.layers).length);
            var cc=0;
            Array.prototype.clean = function(deleteValue) {
                for (var i = 0; i < this.length; i++) {
                    if (this[i] == deleteValue) {         
                        this.splice(i, 1);
                        i--;
                    }
                }
                return this;
            };
            for (var layerName0 in vectorTile.layers) {
                var layerStyle = this.options.vectorTileLayerStyles[ layerName0 ];
                if (layerStyle instanceof Function) {
                    layerStyle = layerStyle(vectorTile.layers[layerName0].features[0].properties, coords.z);
                }
                layerorder[layerStyle.zorder]=layerName0;
                cc++;
                if (cc == Object.keys(vectorTile.layers).length){

                    layerorder.clean(undefined);
                    for (var ilayer = 0; ilayer < layerorder.length; ilayer++) {
                        var layerName = layerorder[ilayer];
                        this._dataLayerNames[layerName] = true;
                        var layer = vectorTile.layers[layerName];

                        var pxPerExtent = this.getTileSize().divideBy(layer.extent);

                        var layerStyle = this.options.vectorTileLayerStyles[ layerName ] ||
                        L.Path.prototype.options;

                        for (var i = 0; i < layer.features.length; i++) {
                            var feat = layer.features[i];
                            var id;

                            var styleOptions = layerStyle;
                            if (storeFeatures) {
                                id = this.options.getFeatureId(feat);
                                var styleOverride = this._overriddenStyles[id];
                                if (styleOverride) {
                                    if (styleOverride[layerName]) {
                                        styleOptions = styleOverride[layerName];
                                    } else {
                                        styleOptions = styleOverride;
                                    }
                                }
                            }

                            if (styleOptions instanceof Function) {
                                styleOptions = styleOptions(feat.properties, coords.z);
                            }

                            if (!(styleOptions instanceof Array)) {
                                styleOptions = [styleOptions];
                            }

                            if (!styleOptions.length) {
                                continue;
                            }

                            var featureLayer = this._createLayer(feat, pxPerExtent);

                            for (var j = 0; j < styleOptions.length; j++) {
                                var style = L.extend({}, L.Path.prototype.options, styleOptions[j]);
                                featureLayer.render(renderer, style);
                                renderer._addPath(featureLayer);
                            }

                            if (this.options.interactive) {
                                featureLayer.makeInteractive();
                            }

                            if (storeFeatures) {
                                renderer._features[id] = {
                                    layerName: layerName,
                                    feature: featureLayer
                                };
                            }
                        }

                    }

                }
            }
            if (this._map != null) {
                renderer.addTo(this._map);
            }
            L.Util.requestAnimFrame(done.bind(coords, null, null));
        }.bind(this));

        return renderer.getContainer();
    },