diff --git a/docs/demo-labels.html b/docs/demo-labels.html new file mode 100644 index 0000000..d901825 --- /dev/null +++ b/docs/demo-labels.html @@ -0,0 +1,86 @@ + + + + Leaflet Map Panes Example + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json index cd82d81..f961d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@geoblink/leaflet.vectorgrid", - "version": "1.4.1", + "version": "1.5.0", "description": "Display gridded vector data (sliced GeoJSON or protobuf vector tiles) in Leaflet 1.0", "main": "dist/Leaflet.VectorGrid.bundled.min.js", "scripts": { diff --git a/src/Leaflet.VectorGrid.js b/src/Leaflet.VectorGrid.js index b804758..2c10f84 100644 --- a/src/Leaflet.VectorGrid.js +++ b/src/Leaflet.VectorGrid.js @@ -25,6 +25,9 @@ L.VectorGrid = L.GridLayer.extend({ // A data structure holding initial symbolizer definitions for the vector features. vectorTileLayerStyles: {}, + // Function that will execute per feature + onEachFeature: null, + // 🍂option interactive: Boolean = false // Whether this `VectorGrid` fires `Interactive Layer` events. interactive: false, @@ -41,27 +44,22 @@ L.VectorGrid = L.GridLayer.extend({ if (this.options.getFeatureId) { this._vectorTiles = {}; this._overriddenStyles = {}; - this.on('tileunload', function(e) { - var key = this._tileCoordsToKey(e.coords), - tile = this._vectorTiles[key]; - - if (tile && this._map) { - tile.removeFrom(this._map); - } - delete this._vectorTiles[key]; - }, this); } this._dataLayerNames = {}; + this._userLayers = {}; + this.on('tileunload', function(e) { + this._tileUnload(e); + }, this); }, createTile: function(coords, done) { - var storeFeatures = this.options.getFeatureId; + var onEachFeature = this.options.onEachFeature; var tileSize = this.getTileSize(); var renderer = this.options.rendererFactory(coords, tileSize, this.options); - var tileBounds = this._tileCoordsToBounds(coords); + var tileBounds = this._tileCoordsToBounds(coords); var vectorTilePromise = this._getVectorTilePromise(coords, tileBounds); @@ -77,16 +75,16 @@ L.VectorGrid = L.GridLayer.extend({ for (var layerName in vectorTile.layers) { 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); @@ -99,31 +97,37 @@ L.VectorGrid = L.GridLayer.extend({ } } } - + if (styleOptions instanceof Function) { styleOptions = styleOptions(feat.properties, coords.z); } - + if (!(styleOptions instanceof Array)) { styleOptions = [styleOptions]; } - + if (!styleOptions.length) { + if (onEachFeature) { + onEachFeature.call(this, feat, null, layer, coords); + } continue; } - + var featureLayer = this._createLayer(feat, pxPerExtent); - + if (onEachFeature) { + onEachFeature.call(this, feat, null, layer, coords); + } + 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, @@ -131,15 +135,15 @@ L.VectorGrid = L.GridLayer.extend({ }; } } - + } - + } - + if (this._map != null) { renderer.addTo(this._map); } - + L.Util.requestAnimFrame(done.bind(coords, null, null)); }.bind(this), function (err) { @@ -205,6 +209,47 @@ L.VectorGrid = L.GridLayer.extend({ return Object.keys(this._dataLayerNames); }, + vtGeometryToPoint: function(geometry, vtLayer, tileCoords) { + var pxPerExtent = this.getTileSize().x / vtLayer.extent; + var tileSize = this.getTileSize(); + var offset = tileCoords.scaleBy(tileSize); + var point; + if (typeof geometry[0] === 'object' && 'x' in geometry[0]) { + // Protobuf vector tiles return [{x: , y:}] + point = L.point(offset.x + (geometry[0].x * pxPerExtent), offset.y + (geometry[0].y * pxPerExtent)); + } else { + // Geojson-vt returns [,] + point = L.point(offset.x + (geometry[0] * pxPerExtent), offset.y + (geometry[1] * pxPerExtent)); + } + return point; + }, + + vtGeometryToLatLng: function(geometry, vtLayer, tileCoords) { + return this._map.unproject(this.vtGeometryToPoint(geometry, vtLayer, tileCoords), tileCoords.z); + }, + + addUserLayer: function(userLayer, tileCoords) { + var tileKey = this._tileCoordsToKey(tileCoords); + this._userLayers[tileKey] = this._userLayers[tileKey] || []; + this._userLayers[tileKey].push(userLayer); + this._map.addLayer(userLayer); + }, + + _tileUnload: function(e) { + var tileKey = this._tileCoordsToKey(e.coords); + if (this._vectorTiles) { + delete this._vectorTiles[tileKey]; + } + var userLayers = this._userLayers[tileKey]; + if (!userLayers) { + return; + } + for(var i = 0; i < userLayers.length; i++) { + this._map.removeLayer(userLayers[i]); + } + delete this._userLayers[tileKey]; + }, + _updateStyles: function(feat, renderer, styleOptions) { styleOptions = (styleOptions instanceof Function) ? styleOptions(feat.properties, renderer.getCoord().z) :