diff --git a/modules/geo-layers/package.json b/modules/geo-layers/package.json index e11b87fe6d8..9e2240465d9 100644 --- a/modules/geo-layers/package.json +++ b/modules/geo-layers/package.json @@ -30,8 +30,11 @@ "prepublishOnly": "npm run build-bundle && npm run build-bundle -- --env.dev" }, "dependencies": { - "@loaders.gl/3d-tiles": "^2.1.0-alpha.4", + "@loaders.gl/3d-tiles": "2.1.0-alpha.4", "@loaders.gl/terrain": "^2.1.0-alpha.4", + "@loaders.gl/mvt": "^2.1.0-alpha.5", + "@loaders.gl/core": "^2.0.4", + "math.gl": "^3.1.3", "@math.gl/web-mercator": "^3.1.3", "h3-js": "^3.6.0", "long": "^3.2.0", diff --git a/modules/geo-layers/src/index.js b/modules/geo-layers/src/index.js index 32ede66603d..7bb7ca3bc86 100644 --- a/modules/geo-layers/src/index.js +++ b/modules/geo-layers/src/index.js @@ -27,3 +27,4 @@ export {default as H3ClusterLayer} from './h3-layers/h3-cluster-layer'; export {default as H3HexagonLayer} from './h3-layers/h3-hexagon-layer'; export {default as Tile3DLayer} from './tile-3d-layer/tile-3d-layer'; export {default as TerrainLayer} from './terrain-layer/terrain-layer'; +export {default as MVTTileLayer} from './mvt-tile-layer/mvt-tile-layer'; diff --git a/modules/geo-layers/src/mvt-tile-layer/mvt-tile-layer.js b/modules/geo-layers/src/mvt-tile-layer/mvt-tile-layer.js new file mode 100644 index 00000000000..f4d17cc6426 --- /dev/null +++ b/modules/geo-layers/src/mvt-tile-layer/mvt-tile-layer.js @@ -0,0 +1,66 @@ +import {Matrix4} from 'math.gl'; +import {MVTLoader} from '@loaders.gl/mvt'; +import {load} from '@loaders.gl/core'; +import {COORDINATE_SYSTEM} from '@deck.gl/core'; +import {GeoJsonLayer} from '@deck.gl/layers'; + +import TileLayer from '../tile-layer/tile-layer'; + +const defaultProps = Object.assign({}, TileLayer.defaultProps, { + renderSubLayers: {type: 'function', value: renderSubLayers, compare: false}, + urlTemplates: [] +}); + +export default class MVTTileLayer extends TileLayer { + initializeState() { + super.initializeState(); + const {urlTemplates} = this.props; + + this.state = { + ...this.state, + urlTemplates + }; + } + + async getTileData(tileProperties) { + const {urlTemplates} = this.state; + + if (!urlTemplates || !urlTemplates.length) { + return Promise.reject(); + } + + const templateReplacer = (_, property) => tileProperties[property]; + const tileURLIndex = getTileURLIndex(tileProperties, urlTemplates.length); + const tileURL = urlTemplates[tileURLIndex].replace(/\{ *([\w_-]+) *\}/g, templateReplacer); + + return await load(tileURL, MVTLoader); + } +} + +function renderSubLayers(tileProperties) { + return new GeoJsonLayer({ + ...tileProperties, + modelMatrix: getModelMatrix(tileProperties.tile), + coordinateSystem: COORDINATE_SYSTEM.CARTESIAN + }); +} + +function getModelMatrix(tile) { + const WORLD_SIZE = 512; + const worldScale = Math.pow(2, tile.z); + + const xScale = WORLD_SIZE / worldScale; + const yScale = -xScale; + + const xOffset = (WORLD_SIZE * tile.x) / worldScale; + const yOffset = WORLD_SIZE * (1 - tile.y / worldScale); + + return new Matrix4().translate([xOffset, yOffset, 0]).scale([xScale, yScale, 1]); +} + +function getTileURLIndex({x, y}, templatesLength) { + return Math.abs(x + y) % templatesLength; +} + +MVTTileLayer.layerName = 'MVTTileLayer'; +MVTTileLayer.defaultProps = defaultProps; diff --git a/modules/geo-layers/src/tile-layer/tile-layer.js b/modules/geo-layers/src/tile-layer/tile-layer.js index e574d10b395..68ab897e0a3 100644 --- a/modules/geo-layers/src/tile-layer/tile-layer.js +++ b/modules/geo-layers/src/tile-layer/tile-layer.js @@ -43,9 +43,17 @@ export default class TileLayer extends CompositeLayer { (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getTileData)); if (createTileCache) { - const {maxZoom, minZoom, maxCacheSize, maxCacheByteSize, refinementStrategy} = props; + const { + maxZoom, + minZoom, + maxCacheSize, + maxCacheByteSize, + refinementStrategy, + tileToBoundingBox + } = props; tileset = new Tileset2D({ - getTileData: props.getTileData, + getTileData: this.getTileData.bind(this), + tileToBoundingBox, maxCacheSize, maxCacheByteSize, maxZoom, @@ -102,6 +110,10 @@ export default class TileLayer extends CompositeLayer { layer._updateTileset(); } + getTileData(tilePosition) { + return this.props.getTileData(tilePosition); + } + getPickingInfo({info, sourceLayer}) { info.sourceLayer = sourceLayer; info.tile = sourceLayer.props.tile; diff --git a/modules/main/src/index.js b/modules/main/src/index.js index 101da59ba16..e0c74aa7495 100644 --- a/modules/main/src/index.js +++ b/modules/main/src/index.js @@ -119,7 +119,8 @@ export { TileLayer, TripsLayer, Tile3DLayer, - TerrainLayer + TerrainLayer, + MVTTileLayer } from '@deck.gl/geo-layers'; export {SimpleMeshLayer, ScenegraphLayer} from '@deck.gl/mesh-layers'; diff --git a/test/modules/geo-layers/index.js b/test/modules/geo-layers/index.js index ac591da2d83..10f3d12fd49 100644 --- a/test/modules/geo-layers/index.js +++ b/test/modules/geo-layers/index.js @@ -48,3 +48,4 @@ import './great-circle-layer.spec'; import './h3-layers.spec'; import './tile-3d-layer'; import './terrain-layer.spec'; +import './mvt-tile-layer.spec'; diff --git a/test/modules/geo-layers/mvt-tile-layer.spec.js b/test/modules/geo-layers/mvt-tile-layer.spec.js new file mode 100644 index 00000000000..320be7f3518 --- /dev/null +++ b/test/modules/geo-layers/mvt-tile-layer.spec.js @@ -0,0 +1,18 @@ +import test from 'tape-catch'; +import {generateLayerTests, testLayer} from '@deck.gl/test-utils'; +import {MVTTileLayer} from '@deck.gl/geo-layers'; + +test('MVTTileLayer', t => { + const testCases = generateLayerTests({ + Layer: MVTTileLayer, + assert: t.ok, + sampleProps: { + urlTemplates: [ + 'https://a.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}.png' + ] + }, + onBeforeUpdate: ({testCase}) => t.comment(testCase.title) + }); + testLayer({Layer: MVTTileLayer, testCases, onError: t.notOk}); + t.end(); +}); diff --git a/yarn.lock b/yarn.lock index 09da53ea8ac..9d8658e5580 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1558,7 +1558,7 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@loaders.gl/3d-tiles@^2.1.0-alpha.4": +"@loaders.gl/3d-tiles@2.1.0-alpha.4", "@loaders.gl/3d-tiles@^2.1.0-alpha.4": version "2.1.0-alpha.4" resolved "https://registry.yarnpkg.com/@loaders.gl/3d-tiles/-/3d-tiles-2.1.0-alpha.4.tgz#9a94aef87dea86b25ca8a104600214aacb5fa20b" integrity sha512-9qqDj2QHO7KiPED77wdIQRhmW+avDy0KaQLcaIAdIk2/HQ0uy90Eb3X7di5Jf/ggRUOs4hFFm+0AQT0PLe6dEQ== @@ -1580,6 +1580,14 @@ "@babel/runtime" "^7.3.1" "@loaders.gl/loader-utils" "2.1.0-alpha.4" +"@loaders.gl/core@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@loaders.gl/core/-/core-2.0.4.tgz#10f647bf0e10af971a7d4e60031992f54c76a91b" + integrity sha512-rBj2QwZh5wUEYblI5tEf0uFRps23Hu/0OM4lA27y+9BUNrT1D4CVBktqsQ6EyMRFWoBGO6Q6G9SEg/49I6MM5w== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils" "2.0.4" + "@loaders.gl/csv@^2.1.0-alpha.4": version "2.1.0-alpha.4" resolved "https://registry.yarnpkg.com/@loaders.gl/csv/-/csv-2.1.0-alpha.4.tgz#c570422229f4c783fd2347d75e17db35244c5b96" @@ -1609,6 +1617,13 @@ resolved "https://registry.yarnpkg.com/@loaders.gl/images/-/images-2.0.2.tgz#546929146b38a428e55905e513b4b9ec7afc58e2" integrity sha512-H6xhSvM24CI++P46TZRLX+95mn5B4HaP0jWGSIn2Lo0aBpcI9/T8z2EZhJyihjoR450fph1ER0j+Mnz+ovHEGg== +"@loaders.gl/loader-utils@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-2.0.4.tgz#de2494f85e6e36fc05ec43ee1d2ada65bcd7f3f6" + integrity sha512-e+Grafj+6pk+7WDClWW/OsuTg/wpaJ3+pLX7c+eG3KmcxiLEDoCNWSvQVbR8ALU4Y6m3S32+WlHptBnN1LL48A== + dependencies: + "@babel/runtime" "^7.3.1" + "@loaders.gl/loader-utils@2.1.0-alpha.4": version "2.1.0-alpha.4" resolved "https://registry.yarnpkg.com/@loaders.gl/loader-utils/-/loader-utils-2.1.0-alpha.4.tgz#92d623f2edf12084de48ae359958baac95ab8085" @@ -1624,6 +1639,11 @@ "@loaders.gl/images" "2.1.0-alpha.4" math.gl "^3.1.2" +"@loaders.gl/mvt@^2.1.0-alpha.5": + version "2.1.0-alpha.5" + resolved "https://registry.yarnpkg.com/@loaders.gl/mvt/-/mvt-2.1.0-alpha.5.tgz#46ace83d3a143c455f1a45133742f7100937d111" + integrity sha512-R5dFyxtyRuA6hJRWlWuo1KAFw/YJm3wMJ0u0H9ZL/v9GfSvgScepThj93uIbxeOAVXn6ZY0JwL3WwGgui2pSLA== + "@loaders.gl/polyfills@^2.1.0-alpha.4": version "2.1.0-alpha.4" resolved "https://registry.yarnpkg.com/@loaders.gl/polyfills/-/polyfills-2.1.0-alpha.4.tgz#4db9e7532785fb5f3317d016693ce15bd5b0b79c" @@ -7024,7 +7044,7 @@ mapbox-gl@^1.0.0, mapbox-gl@^1.2.1: tinyqueue "^2.0.0" vt-pbf "^3.1.1" -math.gl@^3.1.2: +math.gl@^3.1.2, math.gl@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/math.gl/-/math.gl-3.1.3.tgz#1e702df8cba024cc229e18e3c57c60a27943cb95" integrity sha512-3OYCtzTOW1wkZufjsxk8g1D77+z+x7QehOZcMFPGgw1QjVEFNcyy3ql6hdrZpUlkyE3pX4lYak7WOIE+n0obYg==