mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-14 21:28:11 +02:00
Add a vector tile service
This commit is contained in:
Vendored
+1
-1
@@ -646,7 +646,7 @@
|
||||
},
|
||||
"custom_data": {
|
||||
"tooltip": "Edit custom data layer",
|
||||
"header": "Custom Data Settings",
|
||||
"header": "Custom Map Data Settings",
|
||||
"file": {
|
||||
"instructions": "Choose a local data file. Supported types are:\n .gpx, .kml, .geojson/.json, .pbf, .mvt",
|
||||
"label": "Browse files"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import _find from 'lodash-es/find';
|
||||
import _omit from 'lodash-es/omit';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { interpolateNumber as d3_interpolateNumber } from 'd3-interpolate';
|
||||
|
||||
@@ -4,6 +4,7 @@ import serviceOpenstreetcam from './openstreetcam';
|
||||
import serviceOsm from './osm';
|
||||
import serviceStreetside from './streetside';
|
||||
import serviceTaginfo from './taginfo';
|
||||
import serviceVectorTile from './vector_tile';
|
||||
import serviceWikidata from './wikidata';
|
||||
import serviceWikipedia from './wikipedia';
|
||||
|
||||
@@ -14,6 +15,7 @@ export var services = {
|
||||
osm: serviceOsm,
|
||||
streetside: serviceStreetside,
|
||||
taginfo: serviceTaginfo,
|
||||
vectorTile: serviceVectorTile,
|
||||
wikidata: serviceWikidata,
|
||||
wikipedia: serviceWikipedia
|
||||
};
|
||||
@@ -25,6 +27,7 @@ export {
|
||||
serviceOsm,
|
||||
serviceStreetside,
|
||||
serviceTaginfo,
|
||||
serviceVectorTile,
|
||||
serviceWikidata,
|
||||
serviceWikipedia
|
||||
};
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
import _find from 'lodash-es/find';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { request as d3_request } from 'd3-request';
|
||||
|
||||
import Protobuf from 'pbf';
|
||||
import vt from '@mapbox/vector-tile';
|
||||
|
||||
import { utilRebind, utilTiler } from '../util';
|
||||
|
||||
|
||||
var tiler = utilTiler().tileSize(512);
|
||||
var dispatch = d3_dispatch('loadedData');
|
||||
var _vtCache;
|
||||
|
||||
|
||||
function abortRequest(i) {
|
||||
i.abort();
|
||||
}
|
||||
|
||||
|
||||
function vtToGeoJSON(bufferdata) {
|
||||
var tile = new vt.VectorTile(new Protobuf(bufferdata.data.response));
|
||||
var layers = Object.keys(tile.layers);
|
||||
if (!Array.isArray(layers)) { layers = [layers]; }
|
||||
|
||||
var collection = { type: 'FeatureCollection', features: [] };
|
||||
|
||||
layers.forEach(function (layerID) {
|
||||
var layer = tile.layers[layerID];
|
||||
if (layer) {
|
||||
for (var i = 0; i < layer.length; i++) {
|
||||
var feature = layer.feature(i).toGeoJSON(bufferdata.zxy[2], bufferdata.zxy[3], bufferdata.zxy[1]);
|
||||
if (layers.length > 1) feature.properties.vt_layer = layerID;
|
||||
collection.features.push(feature);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
function loadTile(source, tile) {
|
||||
if (source.loaded[tile.id] || source.inflight[tile.id]) return;
|
||||
|
||||
var url = source.template
|
||||
.replace('{x}', tile.xyz[0])
|
||||
.replace('{y}', tile.xyz[1])
|
||||
// TMS-flipped y coordinate
|
||||
.replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
|
||||
.replace(/\{z(oom)?\}/, tile.xyz[2])
|
||||
.replace(/\{switch:([^}]+)\}/, function(s, r) {
|
||||
var subdomains = r.split(',');
|
||||
return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
|
||||
});
|
||||
|
||||
|
||||
source.inflight[tile.id] = d3_request(url)
|
||||
.responseType('arraybuffer')
|
||||
.get(function(err, data) {
|
||||
source.loaded[tile.id] = true;
|
||||
delete source.inflight[tile.id];
|
||||
if (err || !data) return;
|
||||
|
||||
source.loaded[tile.id] = {
|
||||
bufferdata: data,
|
||||
geojson: vtToGeoJSON(data)
|
||||
};
|
||||
|
||||
dispatch.call('loadedData');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
init: function() {
|
||||
if (!_vtCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this.event = utilRebind(this, dispatch, 'on');
|
||||
},
|
||||
|
||||
|
||||
reset: function() {
|
||||
for (var sourceID in _vtCache) {
|
||||
var source = _vtCache[sourceID];
|
||||
if (source && source.inflight) {
|
||||
_forEach(source.inflight, abortRequest);
|
||||
}
|
||||
}
|
||||
|
||||
_vtCache = {};
|
||||
},
|
||||
|
||||
|
||||
addSource: function(sourceID, template) {
|
||||
_vtCache[sourceID] = { template: template, inflight: {}, loaded: {} };
|
||||
return _vtCache[sourceID];
|
||||
},
|
||||
|
||||
|
||||
data: function(sourceID, projection) {
|
||||
var source = _vtCache[sourceID];
|
||||
if (!source) return [];
|
||||
|
||||
// for now, return the FeatureCollection for each tile
|
||||
var tiles = tiler.getTiles(projection);
|
||||
return tiles.map(function(tile) {
|
||||
var loaded = source.loaded[tile.id];
|
||||
return loaded && loaded.geojson;
|
||||
}).filter(Boolean);
|
||||
},
|
||||
|
||||
|
||||
loadTiles: function(sourceID, template, projection) {
|
||||
var source = _vtCache[sourceID];
|
||||
if (!source) {
|
||||
source = this.addSource(sourceID, template);
|
||||
}
|
||||
|
||||
var tiles = tiler.getTiles(projection);
|
||||
|
||||
// abort inflight requests that are no longer needed
|
||||
_forEach(source.inflight, function(v, k) {
|
||||
var wanted = _find(tiles, function(tile) { return k === tile.id; });
|
||||
|
||||
if (!wanted) {
|
||||
abortRequest(v);
|
||||
delete source.inflight[k];
|
||||
}
|
||||
});
|
||||
|
||||
tiles.forEach(function(tile) {
|
||||
loadTile(source, tile);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
cache: function() {
|
||||
return _vtCache;
|
||||
}
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user