leaflet.vectorgrid
Version:
Display gridded vector data (sliced GeoJSON or protobuf vector tiles) in Leaflet 1.0
160 lines (135 loc) • 4.84 kB
JavaScript
import Pbf from 'pbf';
import {VectorTile} from 'vector-tile';
/*
* 🍂class VectorGrid.Protobuf
* 🍂extends VectorGrid
*
* A `VectorGrid` for vector tiles fetched from the internet.
* Tiles are supposed to be protobufs (AKA "protobuffer" or "Protocol Buffers"),
* containing data which complies with the
* [MapBox Vector Tile Specification](https://github.com/mapbox/vector-tile-spec/tree/master/2.1).
*
* This is the format used by:
* - Mapbox Vector Tiles
* - Mapzen Vector Tiles
* - ESRI Vector Tiles
* - [OpenMapTiles hosted Vector Tiles](https://openmaptiles.com/hosting/)
*
* 🍂example
*
* You must initialize a `VectorGrid.Protobuf` with a URL template, just like in
* `L.TileLayer`s. The difference is that the template must point to vector tiles
* (usually `.pbf` or `.mvt`) instead of raster (`.png` or `.jpg`) tiles, and that
* you should define the styling for all the features.
*
* <br><br>
*
* For OpenMapTiles, with a key from [https://openmaptiles.org/docs/host/use-cdn/](https://openmaptiles.org/docs/host/use-cdn/),
* initialization looks like this:
*
* ```
* L.vectorGrid.protobuf("https://free-{s}.tilehosting.com/data/v3/{z}/{x}/{y}.pbf.pict?key={key}", {
* vectorTileLayerStyles: { ... },
* subdomains: "0123",
* key: 'abcdefghi01234567890',
* maxNativeZoom: 14
* }).addTo(map);
* ```
*
* And for Mapbox vector tiles, it looks like this:
*
* ```
* L.vectorGrid.protobuf("https://{s}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token={token}", {
* vectorTileLayerStyles: { ... },
* subdomains: "abcd",
* token: "pk.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTS.TUVWXTZ0123456789abcde"
* }).addTo(map);
* ```
*/
L.VectorGrid.Protobuf = L.VectorGrid.extend({
options: {
// 🍂section
// As with `L.TileLayer`, the URL template might contain a reference to
// any option (see the example above and note the `{key}` or `token` in the URL
// template, and the corresponding option).
//
// 🍂option subdomains: String = 'abc'
// Akin to the `subdomains` option for `L.TileLayer`.
subdomains: 'abc', // Like L.TileLayer
//
// 🍂option fetchOptions: Object = {}
// options passed to `fetch`, e.g. {credentials: 'same-origin'} to send cookie for the current domain
fetchOptions: {}
},
initialize: function(url, options) {
// Inherits options from geojson-vt!
// this._slicer = geojsonvt(geojson, options);
this._url = url;
L.VectorGrid.prototype.initialize.call(this, options);
},
// 🍂method setUrl(url: String, noRedraw?: Boolean): this
// Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
setUrl: function(url, noRedraw) {
this._url = url;
if (!noRedraw) {
this.redraw();
}
return this;
},
_getSubdomain: L.TileLayer.prototype._getSubdomain,
_getVectorTilePromise: function(coords) {
var data = {
s: this._getSubdomain(coords),
x: coords.x,
y: coords.y,
z: coords.z
// z: this._getZoomForUrl() /// TODO: Maybe replicate TileLayer's maxNativeZoom
};
if (this._map && !this._map.options.crs.infinite) {
var invertedY = this._globalTileRange.max.y - coords.y;
if (this.options.tms) { // Should this option be available in Leaflet.VectorGrid?
data['y'] = invertedY;
}
data['-y'] = invertedY;
}
var tileUrl = L.Util.template(this._url, L.extend(data, this.options));
return fetch(tileUrl, this.options.fetchOptions).then(function(response){
if (!response.ok) {
return {layers:[]};
}
return response.blob().then( function (blob) {
// console.log(blob);
var reader = new FileReader();
return new Promise(function(resolve){
reader.addEventListener("loadend", function() {
// reader.result contains the contents of blob as a typed array
// blob.type === 'application/x-protobuf'
var pbf = new Pbf( reader.result );
// console.log(pbf);
return resolve(new VectorTile( pbf ));
});
reader.readAsArrayBuffer(blob);
});
});
}).then(function(json){
// console.log('Vector tile:', json.layers);
// console.log('Vector tile water:', json.layers.water); // Instance of VectorTileLayer
// Normalize feature getters into actual instanced features
for (var layerName in json.layers) {
var feats = [];
for (var i=0; i<json.layers[layerName].length; i++) {
var feat = json.layers[layerName].feature(i);
feat.geometry = feat.loadGeometry();
feats.push(feat);
}
json.layers[layerName].features = feats;
}
return json;
});
}
});
// 🍂factory L.vectorGrid.protobuf(url: String, options)
// Instantiates a new protobuf VectorGrid with the given URL template and options
L.vectorGrid.protobuf = function (url, options) {
return new L.VectorGrid.Protobuf(url, options);
};