diff --git a/docs/config.json b/docs/config.json index 5de104f5fd..98a3ff15fd 100644 --- a/docs/config.json +++ b/docs/config.json @@ -49,7 +49,8 @@ "WFSSource", "TMSSource", "FileSource", - "OrientedImageSource" + "OrientedImageSource", + "VectorTilesSource" ], "Provider": [ diff --git a/examples/vector_tile_raster_2d.html b/examples/vector_tile_raster_2d.html index f5892bb44c..2feb10c8c9 100644 --- a/examples/vector_tile_raster_2d.html +++ b/examples/vector_tile_raster_2d.html @@ -54,69 +54,53 @@ var count = 0; // Add a vector tile layer - itowns.Fetcher.json('https://raw.githubusercontent.com/Oslandia/postile-openmaptiles/master/style.json').then(function (style) { - var supportedLayers = []; - var backgroundLayer; - style.layers.forEach(function(layer) { - if (layer.type === 'background') { - backgroundLayer = layer; - } else if (['fill', 'line'].includes(layer.type)) { - supportedLayers.push(layer); - } - }); - - function isValidData(data, extentDestination) { - // re-use the same vector tiles by interval 4 - var z = extentDestination.zoom - 2; - return extentDestination.zoom, z - z % 4 == data.extent.zoom - 2; - } - var mvtSource = new itowns.TMSSource({ - // eslint-disable-next-line no-template-curly-in-string - url: 'https://osm.oslandia.io/data/v3/${z}/${x}/${y}.pbf', - format: 'application/x-protobuf;type=mapbox-vector', - attribution: { - name: 'OpenStreetMap', - url: 'http://www.openstreetmap.org/', - }, - zoom: { - min: 2, - max: 16, - }, - tileMatrixSet: 'PM', - projection: 'EPSG:3857', - isInverted: true, - }); + function isValidData(data, extentDestination) { + // re-use the same vector tiles by interval 4 + // var z = extentDestination.zoom - 2; + return false; + } - var mvtLayer = new itowns.ColorLayer('MVT', { - isValidData: isValidData, - source: mvtSource, - filter: supportedLayers, - backgroundLayer, - projection: 'EPSG:3857', - }); + var mvtSource = new itowns.VectorTilesSource({ + style: 'https://raw.githubusercontent.com/Oslandia/postile-openmaptiles/master/style.json', + // eslint-disable-next-line no-template-curly-in-string + url: 'https://osm.oslandia.io/data/v3/${z}/${x}/${y}.pbf', + attribution: { + name: 'OpenStreetMap', + url: 'http://www.openstreetmap.org/', + }, + filter: function (layer) { return ['fill', 'line'].includes(layer.type) }, + zoom: { + min: 2, + max: 16, + }, + }); - view.addLayer(mvtLayer).then(function _(layer) { - FeatureToolTip.addLayer(layer, { - filterGeometries: function _(geometries) { - var uniqueClasses = []; - return geometries.filter(function _(g) { - if (g.geometry.properties.class && !uniqueClasses.includes(g.geometry.properties.class)) { - uniqueClasses.push(g.geometry.properties.class); - return true; - } - }); - }, - filterAllProperties: false, - format: function _(n, p) { - if (p !== undefined && n == 'class') return p; - } - }); - return layer; - }).then(menuGlobe.addLayerGUI.bind(menuGlobe)); + var mvtLayer = new itowns.ColorLayer('MVT', { + isValidData: isValidData, + source: mvtSource, }); + view.addLayer(mvtLayer).then(function _(layer) { + FeatureToolTip.addLayer(layer, { + filterGeometries: function _(geometries) { + var uniqueClasses = []; + return geometries.filter(function _(g) { + if (g.geometry.properties.class && !uniqueClasses.includes(g.geometry.properties.class)) { + uniqueClasses.push(g.geometry.properties.class); + return true; + } + }); + }, + filterAllProperties: false, + format: function _(n, p) { + if (p !== undefined && n == 'class') return p; + } + }); + return layer; + }).then(menuGlobe.addLayerGUI.bind(menuGlobe)); + // Request redraw view.notifyChange(true); debug.createTileDebugUI(menuGlobe.gui, view); diff --git a/examples/vector_tile_raster_3d.html b/examples/vector_tile_raster_3d.html index f7c6bed065..c534a0460a 100644 --- a/examples/vector_tile_raster_3d.html +++ b/examples/vector_tile_raster_3d.html @@ -44,54 +44,37 @@ })); // Add a vector tile layer - promises.push(itowns.Fetcher.json('https://raw.githubusercontent.com/Oslandia/postile-openmaptiles/master/style.json').then(function (style) { - var supportedLayers = []; - var backgroundLayer; - - style.layers.forEach(function(layer) { - if (layer.type === 'background') { - backgroundLayer = layer; - } else if (['fill', 'line'].includes(layer.type)) { - supportedLayers.push(layer); - } - }); - - function inter(z) { - return z - (z % 5); - } - - function isValidData(data, extentDestination) { - const isValid = inter(extentDestination.zoom) == inter(data.extent.zoom); - return isValid; - } - - var mvtSource = new itowns.TMSSource({ - // eslint-disable-next-line no-template-curly-in-string - url: 'https://osm.oslandia.io/data/v3/${z}/${x}/${y}.pbf', - format: 'application/x-protobuf;type=mapbox-vector', - attribution: { - name: 'OpenStreetMap', - url: 'http://www.openstreetmap.org/', - }, - zoom: { - min: 0, - max: 13, - }, - tileMatrixSet: 'PM', - projection: 'EPSG:3857', - isInverted: true, - }); - - var mvtLayer = new itowns.ColorLayer('MVT', { - isValidData: isValidData, - source: mvtSource, - filter: supportedLayers, - backgroundLayer, - fx: 2.5, - }); - - return view.addLayer(mvtLayer); - })); + function inter(z) { + return z - (z % 5); + } + + function isValidData(data, extentDestination) { + const isValid = inter(extentDestination.zoom) == inter(data.extent.zoom); + return isValid; + } + + var mvtSource = new itowns.VectorTilesSource({ + style: 'https://raw.githubusercontent.com/Oslandia/postile-openmaptiles/master/style.json', + // eslint-disable-next-line no-template-curly-in-string + url: 'https://osm.oslandia.io/data/v3/${z}/${x}/${y}.pbf', + attribution: { + name: 'OpenStreetMap', + url: 'http://www.openstreetmap.org/', + }, + filter: function (layer) { return ['fill', 'line'].includes(layer.type) }, + zoom: { + min: 2, + max: 16, + }, + }); + + var mvtLayer = new itowns.ColorLayer('MVT', { + isValidData: isValidData, + source: mvtSource, + fx: 2.5, + }); + + view.addLayer(mvtLayer); var menuGlobe = new GuiTools('menuDiv', view, 300); // Listen for globe full initialisation event diff --git a/src/Converter/textureConverter.js b/src/Converter/textureConverter.js index 60534d0181..fed1f561cf 100644 --- a/src/Converter/textureConverter.js +++ b/src/Converter/textureConverter.js @@ -22,8 +22,9 @@ export default { convert(data, extentDestination, layer) { let texture; if (data.isFeatureCollection) { - const backgroundColor = (layer.backgroundLayer && layer.backgroundLayer.paint) ? - new THREE.Color(layer.backgroundLayer.paint['background-color']) : + const backgroundLayer = layer.source.backgroundLayer; + const backgroundColor = (backgroundLayer && backgroundLayer.paint) ? + new THREE.Color(backgroundLayer.paint['background-color']) : undefined; extentDestination.as(CRS.formatToEPSG(layer.projection), extentTexture); diff --git a/src/Main.js b/src/Main.js index d2eb0746f1..14ad5321b6 100644 --- a/src/Main.js +++ b/src/Main.js @@ -55,6 +55,7 @@ export { default as TMSSource } from 'Source/TMSSource'; export { default as WFSSource } from 'Source/WFSSource'; export { default as WMSSource } from 'Source/WMSSource'; export { default as WMTSSource } from 'Source/WMTSSource'; +export { default as VectorTilesSource } from 'Source/VectorTilesSource'; export { default as OrientedImageSource } from 'Source/OrientedImageSource'; // Parsers provided by default in iTowns diff --git a/src/Provider/DataSourceProvider.js b/src/Provider/DataSourceProvider.js index bc657b9c46..b2bcdd43d1 100644 --- a/src/Provider/DataSourceProvider.js +++ b/src/Provider/DataSourceProvider.js @@ -18,12 +18,12 @@ function parseSourceData(data, extDest, layer) { buildExtent: source.isFileSource || !layer.isGeometryLayer, crsIn: source.projection, crsOut: layer.projection, - sprites: layer.sprites, + sprites: layer.sprites || source.sprites, // TODO FIXME: error in filtering vector tile // filteringExtent: extentDestination.as(layer.projection), filteringExtent: !source.isFileSource && layer.isGeometryLayer ? extDest.as(source.projection) : undefined, overrideAltitudeInToZero: layer.overrideAltitudeInToZero, - filter: layer.filter, + filter: layer.filter || source.filter, isInverted: source.isInverted, mergeFeatures: layer.mergeFeatures === undefined ? true : layer.mergeFeatures, withNormal: layer.isGeometryLayer, diff --git a/src/Source/Source.js b/src/Source/Source.js index 86db1c1d9c..ff35f8bf57 100644 --- a/src/Source/Source.js +++ b/src/Source/Source.js @@ -110,6 +110,7 @@ class Source { this.networkOptions = source.networkOptions || { crossOrigin: 'anonymous' }; this.projection = source.projection; this.attribution = source.attribution; + this.whenReady = Promise.resolve(); if (source.extent && !(source.extent.isExtent)) { this.extent = new Extent(this.projection, source.extent); } else { diff --git a/src/Source/VectorTilesSource.js b/src/Source/VectorTilesSource.js new file mode 100644 index 0000000000..8325b2f65a --- /dev/null +++ b/src/Source/VectorTilesSource.js @@ -0,0 +1,74 @@ +import TMSSource from 'Source/TMSSource'; +import Fetcher from 'Provider/Fetcher'; + +function toTMSUrl(url) { + return url.replace(/\{/g, '${'); +} + +/** + * @classdesc + * VectorTilesSource are object containing informations on how to fetch vector tiles resources. + * + * @property {string} style - the url to json style. + * @property {string} sprite - the base url to sprites folder. + * @property {function} filter - function to filter vector tiles layers, the parameter function is a layer. + * + */ +class VectorTilesSource extends TMSSource { + /** + * @param {Object} source - An object that can contain all properties of a + * VectorTilesSource and {@link Source}. + * + * @constructor + */ + constructor(source) { + source.format = 'application/x-protobuf;type=mapbox-vector'; + source.projection = 'EPSG:3857'; + source.isInverted = true; + source.url = source.url || '.'; + super(source); + const ffilter = source.filter || (() => true); + this.filter = []; + const promises = []; + + if (source.style) { + promises.push(Fetcher.json(source.style).then((style) => { + const s = Object.keys(style.sources)[0]; + const os = style.sources[s]; + + style.layers.forEach((layer) => { + if (layer.type === 'background') { + this.backgroundLayer = layer; + } else if (ffilter(layer)) { + this.filter.push(layer); + } + }); + if (this.url == '.') { + if (os.url) { + return Fetcher.json(os.url).then((tileJSON) => { + if (tileJSON.tiles[0]) { + this.url = toTMSUrl(tileJSON.tiles[0]); + } + }); + } else if (os.tiles[0]) { + this.url = toTMSUrl(os.tiles[0]); + } + } + })); + } else { + throw new Error('New VectorTilesSource: style is required'); + } + if (source.sprite) { + promises.push(Fetcher.json(`${source.sprite}.json`).then((sprites) => { + this.sprites = sprites; + return Fetcher.texture(`${source.sprite}.png`, { crossOrigin: 'anonymous' }).then((texture) => { + this.sprites.img = texture.image; + }); + })); + } + + this.whenReady = Promise.all(promises); + } +} + +export default VectorTilesSource;