From 523d3a4e8c7be0f6df231bd6d773b9cc6923d060 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Wed, 12 Dec 2018 15:51:20 +0100 Subject: [PATCH 1/5] Let the source load source tiles --- src/ol/VectorImageTile.js | 51 +------- src/ol/source/VectorTile.js | 54 ++++++++- .../renderer/canvas/vectortilelayer.test.js | 7 +- test/spec/ol/vectorimagetile.test.js | 113 +++++++++--------- 4 files changed, 112 insertions(+), 113 deletions(-) diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 8e0ea79d23..98e34d2c77 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -6,7 +6,6 @@ import Tile from './Tile.js'; import TileState from './TileState.js'; import {createCanvasContext2D} from './dom.js'; import {listen, unlistenByKey} from './events.js'; -import {getHeight, getIntersection, getWidth} from './extent.js'; import EventType from './events/EventType.js'; import {loadFeaturesXhr} from './featureloader.js'; @@ -26,23 +25,11 @@ class VectorImageTile extends Tile { * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {TileState} state State. * @param {number} sourceRevision Source revision. - * @param {import("./format/Feature.js").default} format Feature format. - * @param {import("./Tile.js").LoadFunction} tileLoadFunction Tile load function. * @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls. - * @param {import("./Tile.js").UrlFunction} tileUrlFunction Tile url function. * @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source. - * @param {import("./tilegrid/TileGrid.js").default} tileGrid Tile grid of the renderer. * @param {Object} sourceTiles Source tiles. - * @param {number} pixelRatio Pixel ratio. - * @param {import("./proj/Projection.js").default} projection Projection. - * @param {typeof import("./VectorTile.js").default} tileClass Class to - * instantiate for source tiles. - * @param {function(this: import("./source/VectorTile.js").default, import("./events/Event.js").default): void} handleTileChange - * Function to call when a source tile's state changes. */ - constructor(tileCoord, state, sourceRevision, format, tileLoadFunction, - urlTileCoord, tileUrlFunction, sourceTileGrid, tileGrid, sourceTiles, - pixelRatio, projection, tileClass, handleTileChange) { + constructor(tileCoord, state, sourceRevision, urlTileCoord, sourceTileGrid, sourceTiles) { super(tileCoord, state, {transition: 0}); @@ -114,37 +101,6 @@ class VectorImageTile extends Tile { this.sourceTileListenerKeys_ = []; this.key = sourceRevision.toString(); - - if (urlTileCoord && sourceTileGrid) { - const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord); - const resolution = this.resolution_ = tileGrid.getResolution(urlTileCoord[0]); - const sourceZ = sourceTileGrid.getZForResolution(resolution); - sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { - let sharedExtent = getIntersection(extent, - sourceTileGrid.getTileCoordExtent(sourceTileCoord)); - const sourceExtent = sourceTileGrid.getExtent(); - if (sourceExtent) { - sharedExtent = getIntersection(sharedExtent, sourceExtent, sharedExtent); - } - if (getWidth(sharedExtent) / resolution >= 0.5 && - getHeight(sharedExtent) / resolution >= 0.5) { - // only include source tile if overlap is at least 1 pixel - const sourceTileKey = sourceTileCoord.toString(); - let sourceTile = sourceTiles[sourceTileKey]; - if (!sourceTile) { - const tileUrl = tileUrlFunction(sourceTileCoord, pixelRatio, projection); - sourceTile = sourceTiles[sourceTileKey] = new tileClass(sourceTileCoord, - tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, - tileUrl == undefined ? '' : tileUrl, - format, tileLoadFunction); - this.sourceTileListenerKeys_.push( - listen(sourceTile, EventType.CHANGE, handleTileChange)); - } - sourceTile.consumers++; - this.tileKeys.push(sourceTileKey); - } - }.bind(this)); - } } /** @@ -230,9 +186,8 @@ class VectorImageTile extends Tile { for (let i = 0, ii = tileKeys.length; i < ii; ++i) { this.sourceTiles_[tileKeys[i]].consumers++; } - const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, Number(this.key), null, null, - this.wrappedTileCoord, null, null, null, this.sourceTiles_, - undefined, null, null, null); + const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, Number(this.key), + this.wrappedTileCoord, null, this.sourceTiles_); tile.extent = this.extent; tile.tileKeys = tileKeys; tile.context_ = this.context_; diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 5be126a92f..33918dd80f 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -9,6 +9,9 @@ import {toSize} from '../size.js'; import UrlTile from './UrlTile.js'; import {getKeyZXY} from '../tilecoord.js'; import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.js'; +import {getIntersection, getWidth, getHeight} from '../extent.js'; +import {listen} from '../events.js'; +import EventType from '../events/EventType.js'; /** * @typedef {Object} Options @@ -151,6 +154,49 @@ class VectorTile extends UrlTile { this.sourceTiles_ = {}; } + /** + * Finds and assigns source tiles for a vector image tile. + * @param {VectorImageTile} tile Tile. + * @param {number} pixelRatio Pixel ratio. + * @param {import("../proj/Projection").default} projection Projection. + */ + assignTiles(tile, pixelRatio, projection) { + if (!tile.wrappedTileCoord) { + return; + } + const sourceTileGrid = this.tileGrid; + const tileGrid = this.getTileGridForProjection(projection); + const urlTileCoord = tile.wrappedTileCoord; + const extent = tile.extent = tileGrid.getTileCoordExtent(urlTileCoord); + const resolution = this.resolution_ = tileGrid.getResolution(urlTileCoord[0]); + const sourceZ = sourceTileGrid.getZForResolution(resolution); + sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { + let sharedExtent = getIntersection(extent, + sourceTileGrid.getTileCoordExtent(sourceTileCoord)); + const sourceExtent = sourceTileGrid.getExtent(); + if (sourceExtent) { + sharedExtent = getIntersection(sharedExtent, sourceExtent, sharedExtent); + } + if (getWidth(sharedExtent) / resolution >= 0.5 && + getHeight(sharedExtent) / resolution >= 0.5) { + // only include source tile if overlap is at least 1 pixel + const sourceTileKey = sourceTileCoord.toString(); + let sourceTile = this.sourceTiles_[sourceTileKey]; + if (!sourceTile) { + const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection); + sourceTile = this.sourceTiles_[sourceTileKey] = new this.tileClass(sourceTileCoord, + tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, + tileUrl == undefined ? '' : tileUrl, + this.format_, this.tileLoadFunction); + tile.sourceTileListenerKeys_.push( + listen(sourceTile, EventType.CHANGE, this.handleTileChange.bind(this))); + } + sourceTile.consumers++; + tile.tileKeys.push(sourceTileKey); + } + }.bind(this)); + } + /** * @inheritDoc */ @@ -168,11 +214,11 @@ class VectorTile extends UrlTile { tileCoord, urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY, this.getRevision(), - this.format_, this.tileLoadFunction, urlTileCoord, this.tileUrlFunction, - this.tileGrid, this.getTileGridForProjection(projection), - this.sourceTiles_, pixelRatio, projection, this.tileClass, - this.handleTileChange.bind(this)); + urlTileCoord, + this.tileGrid, + this.sourceTiles_); + this.assignTiles(tile, pixelRatio, projection); this.tileCache.set(tileCoordKey, tile); return tile; } diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 8e44634d7a..22a17d62d2 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -244,11 +244,10 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { sourceTile.getImage = function() { return document.createElement('canvas'); }; - const tile = new VectorImageTile([0, 0, 0], undefined, 1, undefined, - undefined, [0, 0, 0], undefined, createXYZ(), createXYZ(), {'0,0,0': sourceTile}, undefined, - undefined, undefined, undefined); + const tile = new VectorImageTile([0, 0, 0], undefined, 1, [0, 0, 0], createXYZ(), {'0,0,0': sourceTile}); tile.transition_ = 0; - tile.wrappedTileCoord = [0, 0, 0]; + tile.tileKeys = ['0,0,0']; + tile.extent = getProjection('EPSG:3857').getExtent(); tile.setState(TileState.LOADED); tile.getSourceTile = function() { return sourceTile; diff --git a/test/spec/ol/vectorimagetile.test.js b/test/spec/ol/vectorimagetile.test.js index b1b093a68b..6431d8fea5 100644 --- a/test/spec/ol/vectorimagetile.test.js +++ b/test/spec/ol/vectorimagetile.test.js @@ -1,9 +1,8 @@ import TileState from '../../../src/ol/TileState.js'; -import VectorImageTile, {defaultLoadFunction} from '../../../src/ol/VectorImageTile.js'; -import VectorTile from '../../../src/ol/VectorTile.js'; +import {defaultLoadFunction} from '../../../src/ol/VectorImageTile.js'; +import VectorTileSource from '../../../src/ol/source/VectorTile.js'; import {listen, listenOnce} from '../../../src/ol/events.js'; import GeoJSON from '../../../src/ol/format/GeoJSON.js'; -import {get as getProjection} from '../../../src/ol/proj.js'; import {createXYZ} from '../../../src/ol/tilegrid.js'; import TileGrid from '../../../src/ol/tilegrid/TileGrid.js'; @@ -11,13 +10,11 @@ import TileGrid from '../../../src/ol/tilegrid/TileGrid.js'; describe('ol.VectorImageTile', function() { it('configures loader that sets features on the source tile', function(done) { - const format = new GeoJSON(); - const url = 'spec/ol/data/point.json'; - const tile = new VectorImageTile([0, 0, 0], 0, url, format, - defaultLoadFunction, [0, 0, 0], function() { - return url; - }, createXYZ(), createXYZ(), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/point.json' + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); const sourceTile = tile.getTile(tile.tileKeys[0]); @@ -31,17 +28,16 @@ describe('ol.VectorImageTile', function() { }); it('sets sourceTilesLoaded when previously failed source tiles are loaded', function(done) { - const format = new GeoJSON(); - const url = 'spec/ol/data/unavailable.json'; let sourceTile; - const tile = new VectorImageTile([0, 0, 0] /* one world away */, 0, url, format, - function(tile, url) { + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/unavailable.json', + tileLoadFunction: function(tile, url) { sourceTile = tile; defaultLoadFunction(tile, url); - }, [0, 0, 0], function() { - return url; - }, createXYZ(), createXYZ(), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + } + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); let calls = 0; @@ -63,13 +59,11 @@ describe('ol.VectorImageTile', function() { }); it('sets ERROR state when source tiles fail to load', function(done) { - const format = new GeoJSON(); - const url = 'spec/ol/data/unavailable.json'; - const tile = new VectorImageTile([0, 0, 0], 0, url, format, - defaultLoadFunction, [0, 0, 0], function() { - return url; - }, createXYZ(), createXYZ(), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/unavailable.json' + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); @@ -80,12 +74,11 @@ describe('ol.VectorImageTile', function() { }); it('sets EMPTY state when tile has only empty source tiles', function(done) { - const format = new GeoJSON(); - const url = ''; - const tile = new VectorImageTile([0, 0, 0], 0, url, format, - defaultLoadFunction, [0, 0, 0], function() {}, - createXYZ(), createXYZ(), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + const source = new VectorTileSource({ + format: new GeoJSON(), + url: '' + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); @@ -96,33 +89,37 @@ describe('ol.VectorImageTile', function() { }); it('only loads tiles within the source tileGrid\'s extent', function() { - const format = new GeoJSON(); const url = 'spec/ol/data/point.json'; - const tileGrid = new TileGrid({ - resolutions: [0.02197265625, 0.010986328125, 0.0054931640625], - origin: [-180, 90], - extent: [-88, 35, -87, 36] - }); - const sourceTiles = {}; - const tile = new VectorImageTile([1, 0, 0], 0, url, format, - defaultLoadFunction, [1, 0, 0], function(zxy) { + const source = new VectorTileSource({ + projection: 'EPSG:4326', + format: new GeoJSON(), + tileGrid: new TileGrid({ + resolutions: [0.02197265625, 0.010986328125, 0.0054931640625], + origin: [-180, 90], + extent: [-88, 35, -87, 36] + }), + tileUrlFunction: function(zxy) { return url; - }, tileGrid, - createXYZ({extent: [-180, -90, 180, 90], tileSize: 512}), - sourceTiles, 1, getProjection('EPSG:4326'), VectorTile, function() {}); + }, + url: url + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); + tile.load(); expect(tile.tileKeys.length).to.be(1); expect(tile.getTile(tile.tileKeys[0]).tileCoord).to.eql([0, 16, 9]); }); it('#dispose() while loading', function() { - const format = new GeoJSON(); - const url = 'spec/ol/data/point.json'; - const tile = new VectorImageTile([0, 0, 0] /* one world away */, 0, url, format, - defaultLoadFunction, [0, 0, 0], function() { - return url; - }, createXYZ(), createXYZ({tileSize: 512}), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/point.json', + tileGrid: createXYZ() + }); + source.getTileGridForProjection = function() { + return createXYZ({tileSize: 512}); + }; + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); expect(tile.loadListenerKeys_.length).to.be(4); @@ -136,13 +133,15 @@ describe('ol.VectorImageTile', function() { }); it('#dispose() when source tiles are loaded', function(done) { - const format = new GeoJSON(); - const url = 'spec/ol/data/point.json'; - const tile = new VectorImageTile([0, 0, 0], 0, url, format, - defaultLoadFunction, [0, 0, 0], function() { - return url; - }, createXYZ(), createXYZ({tileSize: 512}), {}, - 1, getProjection('EPSG:3857'), VectorTile, function() {}); + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/point.json', + tileGrid: createXYZ() + }); + source.getTileGridForProjection = function() { + return createXYZ({tileSize: 512}); + }; + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); listenOnce(tile, 'change', function() { From ab797b7160fd979028e40883000ad106ed92fbc6 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 13 Dec 2018 10:50:38 +0100 Subject: [PATCH 2/5] Let source set the key, like other sources do --- src/ol/VectorImageTile.js | 8 +++----- src/ol/source/VectorTile.js | 2 +- test/spec/ol/renderer/canvas/vectortilelayer.test.js | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 98e34d2c77..882189e066 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -24,12 +24,11 @@ class VectorImageTile extends Tile { /** * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {TileState} state State. - * @param {number} sourceRevision Source revision. * @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls. * @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source. * @param {Object} sourceTiles Source tiles. */ - constructor(tileCoord, state, sourceRevision, urlTileCoord, sourceTileGrid, sourceTiles) { + constructor(tileCoord, state, urlTileCoord, sourceTileGrid, sourceTiles) { super(tileCoord, state, {transition: 0}); @@ -99,8 +98,6 @@ class VectorImageTile extends Tile { * @type {Array} */ this.sourceTileListenerKeys_ = []; - - this.key = sourceRevision.toString(); } /** @@ -186,9 +183,10 @@ class VectorImageTile extends Tile { for (let i = 0, ii = tileKeys.length; i < ii; ++i) { this.sourceTiles_[tileKeys[i]].consumers++; } - const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, Number(this.key), + const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, this.wrappedTileCoord, null, this.sourceTiles_); tile.extent = this.extent; + tile.key = this.key; tile.tileKeys = tileKeys; tile.context_ = this.context_; setTimeout(function() { diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 33918dd80f..df0235e04e 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -213,11 +213,11 @@ class VectorTile extends UrlTile { const tile = new VectorImageTile( tileCoord, urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY, - this.getRevision(), urlTileCoord, this.tileGrid, this.sourceTiles_); + tile.key = this.getRevision().toString(); this.assignTiles(tile, pixelRatio, projection); this.tileCache.set(tileCoordKey, tile); return tile; diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 22a17d62d2..d4c1e56c37 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -244,7 +244,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { sourceTile.getImage = function() { return document.createElement('canvas'); }; - const tile = new VectorImageTile([0, 0, 0], undefined, 1, [0, 0, 0], createXYZ(), {'0,0,0': sourceTile}); + const tile = new VectorImageTile([0, 0, 0], 1, [0, 0, 0], createXYZ(), {'0,0,0': sourceTile}); tile.transition_ = 0; tile.tileKeys = ['0,0,0']; tile.extent = getProjection('EPSG:3857').getExtent(); From 32696638d299d4ea12bf4fad40f9d318bb3bc75e Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 31 Dec 2018 00:34:56 +0100 Subject: [PATCH 3/5] Simplify vector tile code --- src/ol/Tile.js | 13 +- src/ol/TileQueue.js | 2 +- src/ol/VectorImageTile.js | 235 ++++-------------- src/ol/VectorTile.js | 29 +-- src/ol/renderer/canvas/VectorTileLayer.js | 214 +++++++--------- src/ol/source/VectorTile.js | 198 +++++++++++---- .../renderer/canvas/vectortilelayer.test.js | 64 ++--- test/spec/ol/source/vectortile.test.js | 21 ++ test/spec/ol/vectorimagetile.test.js | 80 +++--- test/spec/ol/vectortile.test.js | 2 +- 10 files changed, 396 insertions(+), 462 deletions(-) diff --git a/src/ol/Tile.js b/src/ol/Tile.js index aba97d71fb..c8d0738661 100644 --- a/src/ol/Tile.js +++ b/src/ol/Tile.js @@ -5,6 +5,7 @@ import TileState from './TileState.js'; import {easeIn} from './easing.js'; import EventTarget from './events/Target.js'; import EventType from './events/EventType.js'; +import {abstract} from './util.js'; /** @@ -105,6 +106,14 @@ class Tile extends EventTarget { */ this.interimTile = null; + /** + * The tile is available at the highest possible resolution. Subclasses can + * set this to `false` initially. Tile load listeners will not be + * unregistered before this is set to `true` and a `#changed()` is called. + * @type {boolean} + */ + this.hifi = true; + /** * A key assigned to the tile. This is used by the tile source to determine * if this tile can effectively be used, or if a new tile should be created @@ -240,7 +249,9 @@ class Tile extends EventTarget { * @abstract * @api */ - load() {} + load() { + abstract(); + } /** * Get the alpha value for rendering. diff --git a/src/ol/TileQueue.js b/src/ol/TileQueue.js index fbce910fcd..8ad585c290 100644 --- a/src/ol/TileQueue.js +++ b/src/ol/TileQueue.js @@ -82,7 +82,7 @@ class TileQueue extends PriorityQueue { handleTileChange(event) { const tile = /** @type {import("./Tile.js").default} */ (event.target); const state = tile.getState(); - if (state === TileState.LOADED || state === TileState.ERROR || + if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY || state === TileState.ABORT) { unlisten(tile, EventType.CHANGE, this.handleTileChange, this); const tileKey = tile.getKey(); diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorImageTile.js index 882189e066..e13d353d86 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorImageTile.js @@ -5,9 +5,6 @@ import {getUid} from './util.js'; import Tile from './Tile.js'; import TileState from './TileState.js'; import {createCanvasContext2D} from './dom.js'; -import {listen, unlistenByKey} from './events.js'; -import EventType from './events/EventType.js'; -import {loadFeaturesXhr} from './featureloader.js'; /** @@ -16,6 +13,8 @@ import {loadFeaturesXhr} from './featureloader.js'; * @property {null|import("./render.js").OrderFunction} renderedRenderOrder * @property {number} renderedTileRevision * @property {number} renderedRevision + * @property {number} renderedZ + * @property {number} renderedTileZ */ @@ -26,9 +25,12 @@ class VectorImageTile extends Tile { * @param {TileState} state State. * @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls. * @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source. - * @param {Object} sourceTiles Source tiles. + * @param {function(VectorImageTile):Array} getSourceTiles Function + * to get an source tiles for this tile. + * @param {function(VectorImageTile):void} removeSourceTiles Function to remove this tile from its + * source tiles's consumer count. */ - constructor(tileCoord, state, urlTileCoord, sourceTileGrid, sourceTiles) { + constructor(tileCoord, state, urlTileCoord, sourceTileGrid, getSourceTiles, removeSourceTiles) { super(tileCoord, state, {transition: 0}); @@ -39,10 +41,22 @@ class VectorImageTile extends Tile { this.context_ = {}; /** - * @private - * @type {import("./featureloader.js").FeatureLoader} + * Executor groups by layer uid. Entries are read/written by the renderer. + * @type {Object>} */ - this.loader_; + this.executorGroups = {}; + + /** + * Number of loading source tiles. Read/written by the source. + * @type {number} + */ + this.loadingSourceTiles = 0; + + /** + * Tile keys of error source tiles. Read/written by the source. + * @type {Object} + */ + this.errorSourceTileKeys = {}; /** * @private @@ -51,10 +65,14 @@ class VectorImageTile extends Tile { this.replayState_ = {}; /** - * @private - * @type {Object} + * @type {!function(import("./VectorImageTile.js").default):Array} */ - this.sourceTiles_ = sourceTiles; + this.getSourceTiles_ = getSourceTiles; + + /** + * @type {!function(import("./VectorImageTile.js").default):void} + */ + this.removeSourceTiles_ = removeSourceTiles; /** * @private @@ -63,69 +81,29 @@ class VectorImageTile extends Tile { this.sourceTileGrid_ = sourceTileGrid; /** - * @private + * z of the source tiles of the last getSourceTiles call. + * @type {number} + */ + this.sourceZ = -1; + + /** + * True when all tiles for this tile's nominal resolution are available. * @type {boolean} */ - this.sourceTilesLoaded = false; - - /** - * Keys of source tiles used by this tile. Use with {@link #getTile}. - * @type {Array} - */ - this.tileKeys = []; - - /** - * @type {import("./extent.js").Extent} - */ - this.extent = null; + this.hifi = false; /** * @type {import("./tilecoord.js").TileCoord} */ this.wrappedTileCoord = urlTileCoord; - - /** - * @type {Array} - */ - this.loadListenerKeys_ = []; - - /** - * @type {boolean} - */ - this.isInterimTile = !sourceTileGrid; - - /** - * @type {Array} - */ - this.sourceTileListenerKeys_ = []; } /** * @inheritDoc */ disposeInternal() { - if (!this.isInterimTile) { - this.setState(TileState.ABORT); - } - if (this.interimTile) { - this.interimTile.dispose(); - this.interimTile = null; - } - for (let i = 0, ii = this.tileKeys.length; i < ii; ++i) { - const sourceTileKey = this.tileKeys[i]; - const sourceTile = this.getTile(sourceTileKey); - sourceTile.consumers--; - if (sourceTile.consumers == 0) { - delete this.sourceTiles_[sourceTileKey]; - sourceTile.dispose(); - } - } - this.tileKeys.length = 0; - this.sourceTiles_ = null; - this.loadListenerKeys_.forEach(unlistenByKey); - this.loadListenerKeys_.length = 0; - this.sourceTileListenerKeys_.forEach(unlistenByKey); - this.sourceTileListenerKeys_.length = 0; + this.removeSourceTiles_(this); + this.setState(TileState.ABORT); super.disposeInternal(); } @@ -158,50 +136,6 @@ class VectorImageTile extends Tile { return this.hasContext(layer) ? this.getContext(layer).canvas : null; } - /** - * @override - * @return {VectorImageTile} Interim tile. - */ - getInterimTile() { - const sourceTileGrid = this.sourceTileGrid_; - const state = this.getState(); - if (state < TileState.LOADED && !this.interimTile) { - let z = this.tileCoord[0]; - const minZoom = sourceTileGrid.getMinZoom(); - while (--z > minZoom) { - let covered = true; - const tileKeys = []; - sourceTileGrid.forEachTileCoord(this.extent, z, function(tileCoord) { - const key = tileCoord.toString(); - if (key in this.sourceTiles_ && this.sourceTiles_[key].getState() === TileState.LOADED) { - tileKeys.push(key); - } else { - covered = false; - } - }.bind(this)); - if (covered && tileKeys.length) { - for (let i = 0, ii = tileKeys.length; i < ii; ++i) { - this.sourceTiles_[tileKeys[i]].consumers++; - } - const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, - this.wrappedTileCoord, null, this.sourceTiles_); - tile.extent = this.extent; - tile.key = this.key; - tile.tileKeys = tileKeys; - tile.context_ = this.context_; - setTimeout(function() { - tile.sourceTilesLoaded = true; - tile.changed(); - }, 16); - this.interimTile = tile; - break; - } - } - } - const interimTile = /** @type {VectorImageTile} */ (this.interimTile); - return state === TileState.LOADED ? this : (interimTile || this); - } - /** * @param {import("./layer/Layer.js").default} layer Layer. * @return {ReplayState} The replay state. @@ -213,103 +147,22 @@ class VectorImageTile extends Tile { dirty: false, renderedRenderOrder: null, renderedRevision: -1, - renderedTileRevision: -1 + renderedTileRevision: -1, + renderedZ: -1, + renderedTileZ: -1 }; } return this.replayState_[key]; } - /** - * @param {string} tileKey Key (tileCoord) of the source tile. - * @return {import("./VectorTile.js").default} Source tile. - */ - getTile(tileKey) { - return this.sourceTiles_[tileKey]; - } - /** * @inheritDoc + * @return {Array} Source tiles for this tile. */ load() { - // Source tiles with LOADED state - we just count them because once they are - // loaded, we're no longer listening to state changes. - let leftToLoad = 0; - // Source tiles with ERROR state - we track them because they can still have - // an ERROR state after another load attempt. - const errorSourceTiles = {}; - - if (this.state == TileState.IDLE) { - this.setState(TileState.LOADING); - } - if (this.state == TileState.LOADING) { - this.tileKeys.forEach(function(sourceTileKey) { - const sourceTile = this.getTile(sourceTileKey); - if (sourceTile.state == TileState.IDLE) { - sourceTile.setLoader(this.loader_); - sourceTile.load(); - } - if (sourceTile.state == TileState.LOADING) { - const key = listen(sourceTile, EventType.CHANGE, function(e) { - const state = sourceTile.getState(); - if (state == TileState.LOADED || - state == TileState.ERROR) { - const uid = getUid(sourceTile); - if (state == TileState.ERROR) { - errorSourceTiles[uid] = true; - } else { - --leftToLoad; - delete errorSourceTiles[uid]; - } - if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { - this.finishLoading_(); - } - } - }.bind(this)); - this.loadListenerKeys_.push(key); - ++leftToLoad; - } - }.bind(this)); - } - if (leftToLoad - Object.keys(errorSourceTiles).length == 0) { - setTimeout(this.finishLoading_.bind(this), 16); - } - } - - /** - * @private - */ - finishLoading_() { - let loaded = this.tileKeys.length; - let empty = 0; - for (let i = loaded - 1; i >= 0; --i) { - const state = this.getTile(this.tileKeys[i]).getState(); - if (state != TileState.LOADED) { - --loaded; - } - if (state == TileState.EMPTY) { - ++empty; - } - } - if (loaded == this.tileKeys.length) { - this.loadListenerKeys_.forEach(unlistenByKey); - this.loadListenerKeys_.length = 0; - this.sourceTilesLoaded = true; - this.changed(); - } else { - this.setState(empty == this.tileKeys.length ? TileState.EMPTY : TileState.ERROR); - } + return this.getSourceTiles_(this); } } export default VectorImageTile; - -/** - * Sets the loader for a tile. - * @param {import("./VectorTile.js").default} tile Vector tile. - * @param {string} url URL. - */ -export function defaultLoadFunction(tile, url) { - const loader = loadFeaturesXhr(url, tile.getFormat(), tile.onLoad.bind(tile), tile.onError.bind(tile)); - tile.setLoader(loader); -} diff --git a/src/ol/VectorTile.js b/src/ol/VectorTile.js index e7ae5f0acf..e9e0350acb 100644 --- a/src/ol/VectorTile.js +++ b/src/ol/VectorTile.js @@ -61,12 +61,6 @@ class VectorTile extends Tile { */ this.projection_ = null; - /** - * @private - * @type {Object} - */ - this.executorGroups_ = {}; - /** * @private * @type {import("./Tile.js").LoadFunction} @@ -85,10 +79,7 @@ class VectorTile extends Tile { * @inheritDoc */ disposeInternal() { - this.features_ = null; - this.executorGroups_ = {}; - this.state = TileState.ABORT; - this.changed(); + this.setState(TileState.ABORT); super.disposeInternal(); } @@ -137,15 +128,6 @@ class VectorTile extends Tile { return this.projection_; } - /** - * @param {string} layerId UID of the layer. - * @param {string} key Key. - * @return {import("./render/canvas/ExecutorGroup.js").default} Executor group. - */ - getExecutorGroup(layerId, key) { - return this.executorGroups_[layerId + ',' + key]; - } - /** * @inheritDoc */ @@ -214,15 +196,6 @@ class VectorTile extends Tile { this.projection_ = projection; } - /** - * @param {string} layerId UID of the layer. - * @param {string} key Key. - * @param {import("./render/canvas/ExecutorGroup.js").default} executorGroup Executor group. - */ - setExecutorGroup(layerId, key, executorGroup) { - this.executorGroups_[layerId + ',' + key] = executorGroup; - } - /** * Set the feature loader for reading this tile's features. * @param {import("./featureloader.js").FeatureLoader} loader Feature loader. diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 571aac80de..feef1f5178 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -3,7 +3,6 @@ */ import {getUid} from '../../util.js'; import {createCanvasContext2D} from '../../dom.js'; -import {getValues} from '../../obj.js'; import TileState from '../../TileState.js'; import ViewHint from '../../ViewHint.js'; import {listen, unlisten, unlistenByKey} from '../../events.js'; @@ -30,6 +29,7 @@ import { makeInverse } from '../../transform.js'; import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js'; +import {isEmpty} from '../../obj.js'; /** @@ -125,15 +125,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** * @private - * @type {Array} + * @type {!Object} */ - this.tilesWithoutImage_ = null; + this.renderTileImageQueue_ = {}; /** - * @private - * @type {Object} + * @type {Object} */ - this.tileChangeKeys_ = {}; + this.tileListenerKeys_ = {}; /** * @private @@ -153,34 +152,23 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { */ disposeInternal() { unlisten(labelCache, EventType.CLEAR, this.handleFontsChanged_, this); - getValues(this.tileChangeKeys_).forEach(unlistenByKey); super.disposeInternal(); } /** - * Listen to tile changes and mark tile as loaded when source tiles are loaded. - * @param {import("../../VectorImageTile").default} tile Tile to listen on. + * @param {import("../../VectorImageTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. - * @param {number} projection Projection. - * @private + * @param {import("../../proj/Projection").default} projection Projection. */ - listenTileChange_(tile, pixelRatio, projection) { - const uid = getUid(tile); - if (!(uid in this.tileChangeKeys_) && tile.getState() === TileState.IDLE) { - this.tileChangeKeys_[uid] = listen(tile, EventType.CHANGE, function() { - const state = tile.getState(); - if (state === TileState.ABORT || tile.sourceTilesLoaded) { - unlistenByKey(this.tileChangeKeys_[uid]); - delete this.tileChangeKeys_[uid]; - if (tile.sourceTilesLoaded) { - // Create render instructions immediately when all source tiles are available. - //TODO Make sure no canvas operations are involved in instruction creation. - this.updateExecutorGroup_(tile, pixelRatio, projection); - //FIXME This should be done by the tile, and VectorImage tiles should be layer specific - tile.setState(TileState.LOADED); - } - } - }.bind(this)); + prepareTile(tile, pixelRatio, projection) { + const tileUid = getUid(tile); + if (tile.getState() === TileState.LOADED || tile.getState() === TileState.ERROR) { + unlistenByKey(this.tileListenerKeys_[tileUid]); + delete this.tileListenerKeys_[tileUid]; + this.updateExecutorGroup_(tile, pixelRatio, projection); + if (this.tileImageNeedsRender_(tile, pixelRatio, projection)) { + this.renderTileImageQueue_[tileUid] = tile; + } } } @@ -189,39 +177,17 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { */ getTile(z, x, y, pixelRatio, projection) { const tile = /** @type {import("../../VectorImageTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection)); - this.listenTileChange_(tile, pixelRatio, projection); - if (tile.isInterimTile) { - // Register change listener also on the original tile - const source = /** @type {import("../../source/VectorTile").default} */ (this.getLayer().getSource()); - const originalTile = /** @type {import("../../VectorImageTile").default} */ (source.getTile(z, x, y, pixelRatio, projection)); - this.listenTileChange_(originalTile, pixelRatio, projection); - } - if (tile.getState() === TileState.LOADED) { - // Update existing instructions if necessary (e.g. when the style has changed) - this.updateExecutorGroup_(tile, pixelRatio, projection); - const layer = this.getLayer(); - if (tile.getReplayState(layer).renderedTileRevision !== -1) { - // Update existing tile image if necessary (e.g. when the style has changed) - this.renderTileImage_(tile, pixelRatio, projection); - } else { - // Render new tile images after existing tiles have been drawn to the target canvas. - this.tilesWithoutImage_.push(tile); + this.prepareTile(tile, pixelRatio, projection); + if (tile.getState() < TileState.LOADED) { + const tileUid = getUid(tile); + if (!(tileUid in this.tileListenerKeys_)) { + const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection)); + this.tileListenerKeys_[tileUid] = listenerKey; } } return tile; } - /** - * @inheritDoc - */ - loadedTileCallback(tiles, zoom, tile) { - if (!tile.hasContext(this.getLayer())) { - this.tilesWithoutImage_.push(tile); - return false; - } - return super.loadedTileCallback(tiles, zoom, tile); - } - /** * @inheritdoc */ @@ -263,7 +229,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const builderState = tile.getReplayState(layer); if (!builderState.dirty && builderState.renderedRevision == revision && - builderState.renderedRenderOrder == renderOrder) { + builderState.renderedRenderOrder == renderOrder && builderState.renderedZ === tile.sourceZ) { return; } @@ -272,10 +238,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const tileGrid = source.getTileGridForProjection(projection); const zoom = tile.tileCoord[0]; const resolution = tileGrid.getResolution(zoom); - const tileExtent = tile.extent; + const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); + const sourceTiles = tile.load(); + const layerUid = getUid(layer); + tile.executorGroups[layerUid] = []; + for (let t = 0, tt = sourceTiles.length; t < tt; ++t) { + const sourceTile = sourceTiles[t]; if (sourceTile.getState() != TileState.LOADED) { continue; } @@ -334,9 +303,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const executorGroupInstructions = builderGroup.finish(); const renderingReplayGroup = new CanvasExecutorGroup(sharedExtent, resolution, pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer()); - sourceTile.setExecutorGroup(getUid(layer), tile.tileCoord.toString(), renderingReplayGroup); + tile.executorGroups[layerUid].push(renderingReplayGroup); } builderState.renderedRevision = revision; + builderState.renderedZ = tile.sourceZ; builderState.renderedRenderOrder = renderOrder; } @@ -348,6 +318,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const rotation = frameState.viewState.rotation; hitTolerance = hitTolerance == undefined ? 0 : hitTolerance; const layer = this.getLayer(); + const source = /** @type {import("../../source/VectorTile").default} */ (layer.getSource()); + const tileGrid = source.getTileGridForProjection(frameState.viewState.projection); /** @type {!Object} */ const features = {}; @@ -357,17 +329,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { let i, ii; for (i = 0, ii = renderedTiles.length; i < ii; ++i) { const tile = renderedTiles[i]; - bufferedExtent = buffer(tile.extent, hitTolerance * resolution, bufferedExtent); + const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); + bufferedExtent = buffer(tileExtent, hitTolerance * resolution, bufferedExtent); if (!containsCoordinate(bufferedExtent, coordinate)) { continue; } - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer), - tile.tileCoord.toString())); + const executorGroups = tile.executorGroups[getUid(layer)]; + for (let t = 0, tt = executorGroups.length; t < tt; ++t) { + const executorGroup = executorGroups[t]; found = found || executorGroup.forEachFeatureAtCoordinate(coordinate, resolution, rotation, hitTolerance, {}, /** * @param {import("../../Feature.js").FeatureLike} feature Feature. @@ -437,7 +406,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @inheritDoc */ renderFrame(frameState, layerState) { - this.tilesWithoutImage_ = []; super.renderFrame(frameState, layerState); const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); @@ -445,7 +413,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]); const renderMode = layer.getRenderMode(); if (renderMode === VectorTileRenderType.IMAGE) { - this.renderMissingTileImages_(hifi, frameState); + this.renderTileImages_(hifi, frameState); return this.container_; } @@ -489,20 +457,18 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { continue; } const tileCoord = tile.tileCoord; - const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tile.extent[0]; + const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); + const worldOffset = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent)[0] - tileExtent[0]; const transform = this.getRenderTransform(frameState, width, height, worldOffset); - for (let t = 0, tt = tile.tileKeys.length; t < tt; ++t) { - const sourceTile = tile.getTile(tile.tileKeys[t]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer), tileCoord.toString())); - if (!executorGroup || !executorGroup.hasExecutors(replayTypes)) { + const executorGroups = tile.executorGroups[getUid(layer)]; + for (let t = 0, tt = executorGroups.length; t < tt; ++t) { + const executorGroup = executorGroups[t]; + if (!executorGroup.hasExecutors(replayTypes)) { // sourceTile was not yet loaded when this.createReplayGroup_() was // called, or it has no replays of the types we want to render continue; } - const currentZ = sourceTile.tileCoord[0]; + const currentZ = tile.tileCoord[0]; const currentClip = executorGroup.getClipCoords(transform); context.save(); @@ -540,9 +506,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { canvas.style.opacity = opacity; } - // Now that we have rendered the tiles we have already, let's prepare new tiles for the - // next frame - this.renderMissingTileImages_(hifi, frameState); + // Now that we have rendered the tiles we have already, let's prepare new tile images + // for the next frame + this.renderTileImages_(hifi, frameState); return this.container_; } @@ -551,19 +517,22 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @param {boolean} hifi We have time to render a high fidelity map image. * @param {import('../../PluggableMap.js').FrameState} frameState Frame state. */ - renderMissingTileImages_(hifi, frameState) { + renderTileImages_(hifi, frameState) { // Even when we have time to render hifi, do not spend more than 100 ms in this render frame, // to avoid delays when the user starts interacting again with the map. - while (this.tilesWithoutImage_.length && Date.now() - frameState.time < 100) { - frameState.animate = true; - const tile = this.tilesWithoutImage_.pop(); - // When we don't have time to render hifi, only render interim tiles until we have used up - // half of the frame budget of 16 ms - if (hifi || (tile.isInterimTile && Date.now() - frameState.time < 8)) { - this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection); + // When we don't have time to render hifi, only render lowres tiles until we have used up + // half of the frame budget of 16 ms + for (const uid in this.renderTileImageQueue_) { + if (Date.now() - frameState.time > (hifi ? 100 : 8)) { + break; } + const tile = this.renderTileImageQueue_[uid]; + frameState.animate = true; + delete this.renderTileImageQueue_[uid]; + this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection); } - if (this.tilesWithoutImage_.length) { + if (!isEmpty(this.renderTileImageQueue_)) { + // If there's items left in the queue, render them in another frame frameState.animate = true; } } @@ -594,6 +563,21 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { return loading; } + /** + * @param {import("../../VectorImageTile.js").default} tile Tile. + * @param {number} pixelRatio Pixel ratio. + * @param {import("../../proj/Projection.js").default} projection Projection. + * @return {boolean} A new tile image was rendered. + * @private + */ + tileImageNeedsRender_(tile, pixelRatio, projection) { + const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); + const replayState = tile.getReplayState(layer); + const revision = layer.getRevision(); + const sourceZ = tile.sourceZ; + return replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ; + } + /** * @param {import("../../VectorImageTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. @@ -604,32 +588,26 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const replayState = tile.getReplayState(layer); const revision = layer.getRevision(); - const replays = IMAGE_REPLAYS[layer.getRenderMode()]; - if (replays && replayState.renderedTileRevision !== revision) { - replayState.renderedTileRevision = revision; - const tileCoord = tile.wrappedTileCoord; - const z = tileCoord[0]; - const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); - const tileGrid = source.getTileGridForProjection(projection); - const resolution = tileGrid.getResolution(z); - const context = tile.getContext(layer); - const size = source.getTilePixelSize(z, pixelRatio, projection); - context.canvas.width = size[0]; - context.canvas.height = size[1]; - const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); - for (let i = 0, ii = tile.tileKeys.length; i < ii; ++i) { - const sourceTile = tile.getTile(tile.tileKeys[i]); - if (sourceTile.getState() != TileState.LOADED) { - continue; - } - const pixelScale = pixelRatio / resolution; - const transform = resetTransform(this.tmpTransform_); - scaleTransform(transform, pixelScale, -pixelScale); - translateTransform(transform, -tileExtent[0], -tileExtent[3]); - const executorGroup = /** @type {CanvasExecutorGroup} */ (sourceTile.getExecutorGroup(getUid(layer), - tile.tileCoord.toString())); - executorGroup.execute(context, transform, 0, {}, true, replays); - } + const executorGroups = tile.executorGroups[getUid(layer)]; + replayState.renderedTileRevision = revision; + replayState.renderedTileZ = tile.sourceZ; + const tileCoord = tile.wrappedTileCoord; + const z = tileCoord[0]; + const source = /** @type {import("../../source/VectorTile.js").default} */ (layer.getSource()); + const tileGrid = source.getTileGridForProjection(projection); + const resolution = tileGrid.getResolution(z); + const context = tile.getContext(layer); + const size = source.getTilePixelSize(z, pixelRatio, projection); + context.canvas.width = size[0]; + context.canvas.height = size[1]; + const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + for (let i = 0, ii = executorGroups.length; i < ii; ++i) { + const executorGroup = executorGroups[i]; + const pixelScale = pixelRatio / resolution; + const transform = resetTransform(this.tmpTransform_); + scaleTransform(transform, pixelScale, -pixelScale); + translateTransform(transform, -tileExtent[0], -tileExtent[3]); + executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]); } } diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index df0235e04e..9c61cb0793 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -3,15 +3,18 @@ */ import TileState from '../TileState.js'; -import VectorImageTile, {defaultLoadFunction} from '../VectorImageTile.js'; +import VectorImageTile from '../VectorImageTile.js'; import Tile from '../VectorTile.js'; import {toSize} from '../size.js'; import UrlTile from './UrlTile.js'; -import {getKeyZXY} from '../tilecoord.js'; +import {getKeyZXY, getKey} from '../tilecoord.js'; import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.js'; -import {getIntersection, getWidth, getHeight} from '../extent.js'; -import {listen} from '../events.js'; +import {buffer as bufferExtent, getIntersection} from '../extent.js'; +import {listen, unlistenByKey} from '../events.js'; import EventType from '../events/EventType.js'; +import {loadFeaturesXhr} from '../featureloader.js'; +import {isEmpty} from '../obj.js'; +import {equals} from '../array.js'; /** * @typedef {Object} Options @@ -113,11 +116,22 @@ class VectorTile extends UrlTile { this.format_ = options.format ? options.format : null; /** - * @private - * @type {Object} - */ + * @type {Object} + */ + this.loadingTiles_ = {}; + + /** + * @private + * @type {Object} + */ this.sourceTiles_ = {}; + /** + * @private + * @type {Object>} + */ + this.sourceTilesByTileKey_ = {}; + /** * @private * @type {boolean} @@ -152,49 +166,139 @@ class VectorTile extends UrlTile { clear() { this.tileCache.clear(); this.sourceTiles_ = {}; + this.sourceTilesByTileKey_ = {}; } /** - * Finds and assigns source tiles for a vector image tile. - * @param {VectorImageTile} tile Tile. * @param {number} pixelRatio Pixel ratio. * @param {import("../proj/Projection").default} projection Projection. + * @param {VectorImageTile} tile Vector image tile. + * @return {Array} Tile keys. */ - assignTiles(tile, pixelRatio, projection) { - if (!tile.wrappedTileCoord) { - return; - } - const sourceTileGrid = this.tileGrid; - const tileGrid = this.getTileGridForProjection(projection); + getSourceTiles(pixelRatio, projection, tile) { + const sourceTiles = []; const urlTileCoord = tile.wrappedTileCoord; - const extent = tile.extent = tileGrid.getTileCoordExtent(urlTileCoord); - const resolution = this.resolution_ = tileGrid.getResolution(urlTileCoord[0]); - const sourceZ = sourceTileGrid.getZForResolution(resolution); - sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) { - let sharedExtent = getIntersection(extent, - sourceTileGrid.getTileCoordExtent(sourceTileCoord)); + if (urlTileCoord) { + const tileGrid = this.getTileGridForProjection(projection); + const extent = tileGrid.getTileCoordExtent(urlTileCoord); + const z = urlTileCoord[0]; + const resolution = tileGrid.getResolution(z); + // make extent 1 pixel smaller so we don't load tiles for < 0.5 pixel render space + bufferExtent(extent, -1 / resolution, extent); + const sourceTileGrid = this.tileGrid; const sourceExtent = sourceTileGrid.getExtent(); if (sourceExtent) { - sharedExtent = getIntersection(sharedExtent, sourceExtent, sharedExtent); + getIntersection(extent, sourceTileGrid.getExtent(), extent); } - if (getWidth(sharedExtent) / resolution >= 0.5 && - getHeight(sharedExtent) / resolution >= 0.5) { - // only include source tile if overlap is at least 1 pixel - const sourceTileKey = sourceTileCoord.toString(); - let sourceTile = this.sourceTiles_[sourceTileKey]; - if (!sourceTile) { - const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection); - sourceTile = this.sourceTiles_[sourceTileKey] = new this.tileClass(sourceTileCoord, - tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, - tileUrl == undefined ? '' : tileUrl, - this.format_, this.tileLoadFunction); - tile.sourceTileListenerKeys_.push( - listen(sourceTile, EventType.CHANGE, this.handleTileChange.bind(this))); + const sourceZ = sourceTileGrid.getZForResolution(resolution); + const minZoom = sourceTileGrid.getMinZoom(); + + let loadedZ = sourceZ + 1; + let covered, empty; + do { + --loadedZ; + covered = true; + empty = true; + sourceTileGrid.forEachTileCoord(extent, loadedZ, function(sourceTileCoord) { + const tileKey = getKey(sourceTileCoord); + let sourceTile; + if (tileKey in this.sourceTiles_) { + sourceTile = this.sourceTiles_[tileKey]; + const state = sourceTile.getState(); + if (state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) { + empty = empty && state === TileState.EMPTY; + sourceTiles.push(sourceTile); + return; + } + } else if (loadedZ === sourceZ) { + const tileUrl = this.tileUrlFunction(sourceTileCoord, pixelRatio, projection); + sourceTile = new this.tileClass(sourceTileCoord, + tileUrl == undefined ? TileState.EMPTY : TileState.IDLE, + tileUrl == undefined ? '' : tileUrl, + this.format_, this.tileLoadFunction); + this.sourceTiles_[tileKey] = sourceTile; + empty = empty && sourceTile.getState() === TileState.EMPTY; + listen(sourceTile, EventType.CHANGE, this.handleTileChange, this); + sourceTile.load(); + } else { + empty = false; + } + covered = false; + if (!sourceTile) { + return; + } + if (sourceTile.getState() !== TileState.EMPTY && tile.getState() === TileState.IDLE) { + tile.loadingSourceTiles++; + const key = listen(sourceTile, EventType.CHANGE, function() { + const state = sourceTile.getState(); + const sourceTileKey = getKey(sourceTile.tileCoord); + if (state === TileState.LOADED || state === TileState.ERROR) { + if (state === TileState.LOADED) { + unlistenByKey(key); + tile.loadingSourceTiles--; + delete tile.errorSourceTileKeys[sourceTileKey]; + } else if (state === TileState.ERROR) { + tile.errorSourceTileKeys[sourceTileKey] = true; + } + if (tile.loadingSourceTiles - Object.keys(tile.errorSourceTileKeys).length === 0) { + tile.hifi = true; + tile.sourceZ = sourceZ; + tile.setState(isEmpty(tile.errorSourceTileKeys) ? TileState.LOADED : TileState.ERROR); + } + } + }); + } + }.bind(this)); + if (!covered) { + sourceTiles.length = 0; } - sourceTile.consumers++; - tile.tileKeys.push(sourceTileKey); + } while (!covered && loadedZ > minZoom); + if (!empty && tile.getState() === TileState.IDLE) { + tile.setState(TileState.LOADING); } - }.bind(this)); + if (covered || empty) { + tile.hifi = sourceZ === loadedZ; + tile.sourceZ = loadedZ; + const previousSourceTiles = this.sourceTilesByTileKey_[getKey(tile.tileCoord)]; + if (tile.getState() < TileState.LOADED) { + tile.setState(empty ? TileState.EMPTY : TileState.LOADED); + } else if (!previousSourceTiles || !equals(sourceTiles, previousSourceTiles)) { + this.removeSourceTiles(tile); + this.addSourceTiles(tile, sourceTiles); + } + } + } + return sourceTiles; + } + + /** + * @param {VectorImageTile} tile Tile. + * @param {Array} sourceTiles Source tiles. + */ + addSourceTiles(tile, sourceTiles) { + this.sourceTilesByTileKey_[getKey(tile.tileCoord)] = sourceTiles; + for (let i = 0, ii = sourceTiles.length; i < ii; ++i) { + sourceTiles[i].consumers++; + } + } + + /** + * @param {VectorImageTile} tile Tile. + */ + removeSourceTiles(tile) { + const tileKey = getKey(tile.tileCoord); + if (tileKey in this.sourceTilesByTileKey_) { + const sourceTiles = this.sourceTilesByTileKey_[tileKey]; + for (let i = 0, ii = sourceTiles.length; i < ii; ++i) { + const sourceTile = sourceTiles[i]; + sourceTile.consumers--; + if (sourceTile.consumers === 0) { + sourceTile.dispose(); + delete this.sourceTiles_[getKey(sourceTile.tileCoord)]; + } + } + } + delete this.sourceTilesByTileKey_[tileKey]; } /** @@ -215,16 +319,15 @@ class VectorTile extends UrlTile { urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY, urlTileCoord, this.tileGrid, - this.sourceTiles_); + this.getSourceTiles.bind(this, pixelRatio, projection), + this.removeSourceTiles.bind(this)); tile.key = this.getRevision().toString(); - this.assignTiles(tile, pixelRatio, projection); this.tileCache.set(tileCoordKey, tile); return tile; } } - /** * @inheritDoc */ @@ -241,7 +344,6 @@ class VectorTile extends UrlTile { return tileGrid; } - /** * @inheritDoc */ @@ -249,7 +351,6 @@ class VectorTile extends UrlTile { return pixelRatio; } - /** * @inheritDoc */ @@ -262,3 +363,14 @@ class VectorTile extends UrlTile { export default VectorTile; + + +/** + * Sets the loader for a tile. + * @param {import("../VectorTile.js").default} tile Vector tile. + * @param {string} url URL. + */ +export function defaultLoadFunction(tile, url) { + const loader = loadFeaturesXhr(url, tile.getFormat(), tile.onLoad.bind(tile), tile.onError.bind(tile)); + tile.setLoader(loader); +} diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index d4c1e56c37..077acea7c2 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -62,9 +62,9 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { class TileClass extends VectorTile { constructor() { super(...arguments); - this.setState(TileState.LOADED); this.setFeatures([feature1, feature2, feature3]); this.setProjection(getProjection('EPSG:4326')); + this.setState(TileState.LOADED); tileCallback(this); } } @@ -73,12 +73,14 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { tileClass: TileClass, tileGrid: createXYZ() }); + source.getSourceTiles = function() { + return [new TileClass([0, 0, 0])]; + }; source.getTile = function() { const tile = VectorTileSource.prototype.getTile.apply(source, arguments); tile.hasContext = function() { return true; }; - tile.sourceTilesLoaded = true; tile.setState(TileState.LOADED); return tile; }; @@ -219,13 +221,10 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { }); map.addLayer(layer2); - const spy1 = sinon.spy(VectorTile.prototype, 'getExecutorGroup'); - const spy2 = sinon.spy(VectorTile.prototype, 'setExecutorGroup'); map.renderSync(); - expect(spy1.callCount).to.be(4); - expect(spy2.callCount).to.be(2); - spy1.restore(); - spy2.restore(); + const tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857')); + expect(Object.keys(tile.executorGroups)[0]).to.be(getUid(layer)); + expect(Object.keys(tile.executorGroups)[1]).to.be(getUid(layer2)); }); }); @@ -244,14 +243,13 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { sourceTile.getImage = function() { return document.createElement('canvas'); }; - const tile = new VectorImageTile([0, 0, 0], 1, [0, 0, 0], createXYZ(), {'0,0,0': sourceTile}); + const tile = new VectorImageTile([0, 0, 0], 1, [0, 0, 0], createXYZ(), + function() { + return sourceTile; + }, + function() {}); tile.transition_ = 0; - tile.tileKeys = ['0,0,0']; - tile.extent = getProjection('EPSG:3857').getExtent(); tile.setState(TileState.LOADED); - tile.getSourceTile = function() { - return sourceTile; - }; layer.getSource().getTile = function() { return tile; }; @@ -286,33 +284,37 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { }); describe('#forEachFeatureAtCoordinate', function() { - let layer, renderer, executorGroup; + let layer, renderer, executorGroup, source; class TileClass extends VectorImageTile { constructor() { super(...arguments); - this.extent = [-Infinity, -Infinity, Infinity, Infinity]; this.setState(TileState.LOADED); - const sourceTile = new VectorTile([0, 0, 0]); - sourceTile.setState(TileState.LOADED); - sourceTile.setProjection(getProjection('EPSG:3857')); - sourceTile.getExecutorGroup = function() { - return executorGroup; - }; - const key = sourceTile.tileCoord.toString(); - this.tileKeys = [key]; - this.sourceTiles_ = {}; - this.sourceTiles_[key] = sourceTile; this.wrappedTileCoord = arguments[0]; } } beforeEach(function() { + const sourceTile = new VectorTile([0, 0, 0]); + sourceTile.setState(TileState.LOADED); + sourceTile.setProjection(getProjection('EPSG:3857')); + source = new VectorTileSource({ + tileClass: TileClass, + tileGrid: createXYZ() + }); + source.sourceTiles_ = { + '0/0/0': sourceTile + }; + source.sourceTilesByTileKey_ = { + '0/0/0': [sourceTile] + }; executorGroup = {}; + source.getTile = function() { + const tile = VectorTileSource.prototype.getTile.apply(source, arguments); + tile.executorGroups[getUid(layer)] = [executorGroup]; + return tile; + }; layer = new VectorTileLayer({ - source: new VectorTileSource({ - tileClass: TileClass, - tileGrid: createXYZ() - }) + source: source }); renderer = new CanvasVectorTileLayerRenderer(layer); executorGroup.forEachFeatureAtCoordinate = function(coordinate, @@ -335,7 +337,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { rotation: 0 } }; - renderer.renderedTiles = [new TileClass([0, 0, -1], undefined, 1)]; + renderer.renderedTiles = [source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'))]; renderer.forEachFeatureAtCoordinate( coordinate, frameState, 0, spy, undefined); expect(spy.callCount).to.be(1); diff --git a/test/spec/ol/source/vectortile.test.js b/test/spec/ol/source/vectortile.test.js index 36d6c80691..2830077aea 100644 --- a/test/spec/ol/source/vectortile.test.js +++ b/test/spec/ol/source/vectortile.test.js @@ -2,12 +2,15 @@ import Map from '../../../../src/ol/Map.js'; import View from '../../../../src/ol/View.js'; import VectorImageTile from '../../../../src/ol/VectorImageTile.js'; import VectorTile from '../../../../src/ol/VectorTile.js'; +import GeoJSON from '../../../../src/ol/format/GeoJSON.js'; import MVT from '../../../../src/ol/format/MVT.js'; import VectorTileLayer from '../../../../src/ol/layer/VectorTile.js'; import {get as getProjection} from '../../../../src/ol/proj.js'; import VectorTileSource from '../../../../src/ol/source/VectorTile.js'; import {createXYZ} from '../../../../src/ol/tilegrid.js'; import TileGrid from '../../../../src/ol/tilegrid/TileGrid.js'; +import {listen, unlistenByKey} from '../../../../src/ol/events.js'; +import TileState from '../../../../src/ol/TileState.js'; describe('ol.source.VectorTile', function() { @@ -47,6 +50,24 @@ describe('ol.source.VectorTile', function() { expect(source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'))) .to.equal(tile); }); + it('loads source tiles', function(done) { + const source = new VectorTileSource({ + format: new GeoJSON(), + url: 'spec/ol/data/point.json' + }); + const tile = source.getTile(0, 0, 0, 1, source.getProjection()); + + tile.load(); + const key = listen(tile, 'change', function(e) { + if (tile.getState() === TileState.LOADED) { + const sourceTile = tile.load()[0]; + expect(sourceTile.getFeatures().length).to.be.greaterThan(0); + unlistenByKey(key); + done(); + } + }); + }); + }); describe('#getTileGridForProjection', function() { diff --git a/test/spec/ol/vectorimagetile.test.js b/test/spec/ol/vectorimagetile.test.js index 6431d8fea5..5d3b924f19 100644 --- a/test/spec/ol/vectorimagetile.test.js +++ b/test/spec/ol/vectorimagetile.test.js @@ -1,33 +1,17 @@ import TileState from '../../../src/ol/TileState.js'; -import {defaultLoadFunction} from '../../../src/ol/VectorImageTile.js'; +import {defaultLoadFunction} from '../../../src/ol/source/VectorTile.js'; import VectorTileSource from '../../../src/ol/source/VectorTile.js'; -import {listen, listenOnce} from '../../../src/ol/events.js'; +import {listen, listenOnce, unlistenByKey} from '../../../src/ol/events.js'; import GeoJSON from '../../../src/ol/format/GeoJSON.js'; import {createXYZ} from '../../../src/ol/tilegrid.js'; import TileGrid from '../../../src/ol/tilegrid/TileGrid.js'; +import {getKey} from '../../../src/ol/tilecoord.js'; +import EventType from '../../../src/ol/events/EventType.js'; describe('ol.VectorImageTile', function() { - it('configures loader that sets features on the source tile', function(done) { - const source = new VectorTileSource({ - format: new GeoJSON(), - url: 'spec/ol/data/point.json' - }); - const tile = source.getTile(0, 0, 0, 1, source.getProjection()); - - tile.load(); - const sourceTile = tile.getTile(tile.tileKeys[0]); - const loader = sourceTile.loader_; - expect(typeof loader).to.be('function'); - - listen(sourceTile, 'change', function(e) { - expect(sourceTile.getFeatures().length).to.be.greaterThan(0); - done(); - }); - }); - - it('sets sourceTilesLoaded when previously failed source tiles are loaded', function(done) { + it('triggers "change" when previously failed source tiles are loaded', function(done) { let sourceTile; const source = new VectorTileSource({ format: new GeoJSON(), @@ -44,16 +28,12 @@ describe('ol.VectorImageTile', function() { listen(tile, 'change', function(e) { ++calls; if (calls === 1) { - expect(tile.sourceTilesLoaded).to.be(false); - } else if (calls === 2) { - expect(tile.sourceTilesLoaded).to.be(true); - } - if (calls == 2) { - done(); - } else { + expect(tile.getState()).to.be(TileState.ERROR); setTimeout(function() { sourceTile.setState(TileState.LOADED); }, 0); + } else if (calls === 2) { + done(); } }); }); @@ -73,7 +53,7 @@ describe('ol.VectorImageTile', function() { }); }); - it('sets EMPTY state when tile has only empty source tiles', function(done) { + it('sets EMPTY state when tile has only empty source tiles', function() { const source = new VectorTileSource({ format: new GeoJSON(), url: '' @@ -81,14 +61,10 @@ describe('ol.VectorImageTile', function() { const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); - - listen(tile, 'change', function() { - expect(tile.getState()).to.be(TileState.EMPTY); - done(); - }); + expect(tile.getState()).to.be(TileState.EMPTY); }); - it('only loads tiles within the source tileGrid\'s extent', function() { + it('only loads tiles within the source tileGrid\'s extent', function(done) { const url = 'spec/ol/data/point.json'; const source = new VectorTileSource({ projection: 'EPSG:4326', @@ -106,8 +82,15 @@ describe('ol.VectorImageTile', function() { const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); - expect(tile.tileKeys.length).to.be(1); - expect(tile.getTile(tile.tileKeys[0]).tileCoord).to.eql([0, 16, 9]); + const key = listen(tile, EventType.CHANGE, function() { + if (tile.getState() === TileState.LOADED) { + unlistenByKey(key); + const sourceTiles = tile.load(); + expect(sourceTiles.length).to.be(1); + expect(sourceTiles[0].tileCoord).to.eql([0, 16, 9]); + done(); + } + }); }); it('#dispose() while loading', function() { @@ -122,13 +105,9 @@ describe('ol.VectorImageTile', function() { const tile = source.getTile(0, 0, 0, 1, source.getProjection()); tile.load(); - expect(tile.loadListenerKeys_.length).to.be(4); - expect(tile.tileKeys.length).to.be(4); expect(tile.getState()).to.be(TileState.LOADING); tile.dispose(); - expect(tile.loadListenerKeys_.length).to.be(0); - expect(tile.tileKeys.length).to.be(0); - expect(tile.sourceTiles_).to.be(null); + expect(source.sourceTilesByTileKey_[getKey(tile)]).to.be(undefined); expect(tile.getState()).to.be(TileState.ABORT); }); @@ -145,14 +124,19 @@ describe('ol.VectorImageTile', function() { tile.load(); listenOnce(tile, 'change', function() { - expect(tile.getState()).to.be(TileState.LOADING); - expect(tile.sourceTilesLoaded).to.be.ok(); - expect(tile.loadListenerKeys_.length).to.be(0); - expect(tile.tileKeys.length).to.be(4); + expect(tile.getState()).to.be(TileState.LOADED); + expect(tile.loadingSourceTiles).to.be(0); + const sourceTiles = tile.load(); + expect(sourceTiles.length).to.be(4); + for (let i = 0, ii = sourceTiles.length; i < ii; ++i) { + expect(sourceTiles[i].consumers).to.be(1); + } tile.dispose(); - expect(tile.tileKeys.length).to.be(0); - expect(tile.sourceTiles_).to.be(null); expect(tile.getState()).to.be(TileState.ABORT); + for (let i = 0, ii = sourceTiles.length; i < ii; ++i) { + expect(sourceTiles[i].consumers).to.be(0); + expect(sourceTiles[i].getState()).to.be(TileState.ABORT); + } done(); }); }); diff --git a/test/spec/ol/vectortile.test.js b/test/spec/ol/vectortile.test.js index b7a7d47a5f..485d397c7a 100644 --- a/test/spec/ol/vectortile.test.js +++ b/test/spec/ol/vectortile.test.js @@ -1,5 +1,5 @@ import Feature from '../../../src/ol/Feature.js'; -import {defaultLoadFunction} from '../../../src/ol/VectorImageTile.js'; +import {defaultLoadFunction} from '../../../src/ol/source/VectorTile.js'; import VectorTile from '../../../src/ol/VectorTile.js'; import {listen} from '../../../src/ol/events.js'; import TextFeature from '../../../src/ol/format/TextFeature.js'; From 0f3c0d1af4a5fde4a3e2e4f06aadd1cc58ba3925 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 6 Jan 2019 13:15:39 +0100 Subject: [PATCH 4/5] Rename VectorImageTile to VectorRenderTile --- .../{VectorImageTile.js => VectorRenderTile.js} | 14 +++++++------- src/ol/index.js | 2 +- src/ol/renderer/canvas/VectorTileLayer.js | 16 ++++++++-------- src/ol/source/VectorTile.js | 10 +++++----- .../ol/renderer/canvas/vectortilelayer.test.js | 6 +++--- test/spec/ol/source/vectortile.test.js | 4 ++-- test/spec/ol/vectorimagetile.test.js | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) rename src/ol/{VectorImageTile.js => VectorRenderTile.js} (89%) diff --git a/src/ol/VectorImageTile.js b/src/ol/VectorRenderTile.js similarity index 89% rename from src/ol/VectorImageTile.js rename to src/ol/VectorRenderTile.js index e13d353d86..025e76c418 100644 --- a/src/ol/VectorImageTile.js +++ b/src/ol/VectorRenderTile.js @@ -1,5 +1,5 @@ /** - * @module ol/VectorImageTile + * @module ol/VectorRenderTile */ import {getUid} from './util.js'; import Tile from './Tile.js'; @@ -18,16 +18,16 @@ import {createCanvasContext2D} from './dom.js'; */ -class VectorImageTile extends Tile { +class VectorRenderTile extends Tile { /** * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {TileState} state State. * @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls. * @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source. - * @param {function(VectorImageTile):Array} getSourceTiles Function + * @param {function(VectorRenderTile):Array} getSourceTiles Function * to get an source tiles for this tile. - * @param {function(VectorImageTile):void} removeSourceTiles Function to remove this tile from its + * @param {function(VectorRenderTile):void} removeSourceTiles Function to remove this tile from its * source tiles's consumer count. */ constructor(tileCoord, state, urlTileCoord, sourceTileGrid, getSourceTiles, removeSourceTiles) { @@ -65,12 +65,12 @@ class VectorImageTile extends Tile { this.replayState_ = {}; /** - * @type {!function(import("./VectorImageTile.js").default):Array} + * @type {!function(import("./VectorRenderTile.js").default):Array} */ this.getSourceTiles_ = getSourceTiles; /** - * @type {!function(import("./VectorImageTile.js").default):void} + * @type {!function(import("./VectorRenderTile.js").default):void} */ this.removeSourceTiles_ = removeSourceTiles; @@ -165,4 +165,4 @@ class VectorImageTile extends Tile { } -export default VectorImageTile; +export default VectorRenderTile; diff --git a/src/ol/index.js b/src/ol/index.js index 2566564f17..e3e3d8607e 100644 --- a/src/ol/index.js +++ b/src/ol/index.js @@ -26,7 +26,7 @@ export {default as Tile} from './Tile.js'; export {default as TileCache} from './TileCache.js'; export {default as TileQueue} from './TileQueue.js'; export {default as TileRange} from './TileRange.js'; -export {default as VectorImageTile} from './VectorImageTile.js'; +export {default as VectorRenderTile} from './VectorRenderTile.js'; export {default as VectorTile} from './VectorTile.js'; export {default as View} from './View.js'; diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index feef1f5178..4bee342199 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -125,7 +125,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** * @private - * @type {!Object} + * @type {!Object} */ this.renderTileImageQueue_ = {}; @@ -156,7 +156,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } /** - * @param {import("../../VectorImageTile.js").default} tile Tile. + * @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. * @param {import("../../proj/Projection").default} projection Projection. */ @@ -176,7 +176,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { * @inheritDoc */ getTile(z, x, y, pixelRatio, projection) { - const tile = /** @type {import("../../VectorImageTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection)); + const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection)); this.prepareTile(tile, pixelRatio, projection); if (tile.getState() < TileState.LOADED) { const tileUid = getUid(tile); @@ -217,7 +217,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } /** - * @param {import("../../VectorImageTile.js").default} tile Tile. + * @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. * @param {import("../../proj/Projection.js").default} projection Projection. * @private @@ -323,7 +323,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { /** @type {!Object} */ const features = {}; - const renderedTiles = /** @type {Array} */ (this.renderedTiles); + const renderedTiles = /** @type {Array} */ (this.renderedTiles); let bufferedExtent, found; let i, ii; @@ -452,7 +452,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { const clips = []; const zs = []; for (let i = tiles.length - 1; i >= 0; --i) { - const tile = /** @type {import("../../VectorImageTile.js").default} */ (tiles[i]); + const tile = /** @type {import("../../VectorRenderTile.js").default} */ (tiles[i]); if (tile.getState() == TileState.ABORT) { continue; } @@ -564,7 +564,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } /** - * @param {import("../../VectorImageTile.js").default} tile Tile. + * @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. * @param {import("../../proj/Projection.js").default} projection Projection. * @return {boolean} A new tile image was rendered. @@ -579,7 +579,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer { } /** - * @param {import("../../VectorImageTile.js").default} tile Tile. + * @param {import("../../VectorRenderTile.js").default} tile Tile. * @param {number} pixelRatio Pixel ratio. * @param {import("../../proj/Projection.js").default} projection Projection. * @private diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 9c61cb0793..d0d181336e 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -3,7 +3,7 @@ */ import TileState from '../TileState.js'; -import VectorImageTile from '../VectorImageTile.js'; +import VectorRenderTile from '../VectorRenderTile.js'; import Tile from '../VectorTile.js'; import {toSize} from '../size.js'; import UrlTile from './UrlTile.js'; @@ -172,7 +172,7 @@ class VectorTile extends UrlTile { /** * @param {number} pixelRatio Pixel ratio. * @param {import("../proj/Projection").default} projection Projection. - * @param {VectorImageTile} tile Vector image tile. + * @param {VectorRenderTile} tile Vector image tile. * @return {Array} Tile keys. */ getSourceTiles(pixelRatio, projection, tile) { @@ -272,7 +272,7 @@ class VectorTile extends UrlTile { } /** - * @param {VectorImageTile} tile Tile. + * @param {VectorRenderTile} tile Tile. * @param {Array} sourceTiles Source tiles. */ addSourceTiles(tile, sourceTiles) { @@ -283,7 +283,7 @@ class VectorTile extends UrlTile { } /** - * @param {VectorImageTile} tile Tile. + * @param {VectorRenderTile} tile Tile. */ removeSourceTiles(tile) { const tileKey = getKey(tile.tileCoord); @@ -314,7 +314,7 @@ class VectorTile extends UrlTile { const tileCoord = [z, x, y]; const urlTileCoord = this.getTileCoordForTileUrlFunction( tileCoord, projection); - const tile = new VectorImageTile( + const tile = new VectorRenderTile( tileCoord, urlTileCoord !== null ? TileState.IDLE : TileState.EMPTY, urlTileCoord, diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 077acea7c2..691940ae94 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -2,7 +2,7 @@ import {clear} from '../../../../../src/ol/obj.js'; import Feature from '../../../../../src/ol/Feature.js'; import Map from '../../../../../src/ol/Map.js'; import TileState from '../../../../../src/ol/TileState.js'; -import VectorImageTile from '../../../../../src/ol/VectorImageTile.js'; +import VectorRenderTile from '../../../../../src/ol/VectorRenderTile.js'; import VectorTile from '../../../../../src/ol/VectorTile.js'; import View from '../../../../../src/ol/View.js'; import {getCenter} from '../../../../../src/ol/extent.js'; @@ -243,7 +243,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { sourceTile.getImage = function() { return document.createElement('canvas'); }; - const tile = new VectorImageTile([0, 0, 0], 1, [0, 0, 0], createXYZ(), + const tile = new VectorRenderTile([0, 0, 0], 1, [0, 0, 0], createXYZ(), function() { return sourceTile; }, @@ -285,7 +285,7 @@ describe('ol.renderer.canvas.VectorTileLayer', function() { describe('#forEachFeatureAtCoordinate', function() { let layer, renderer, executorGroup, source; - class TileClass extends VectorImageTile { + class TileClass extends VectorRenderTile { constructor() { super(...arguments); this.setState(TileState.LOADED); diff --git a/test/spec/ol/source/vectortile.test.js b/test/spec/ol/source/vectortile.test.js index 2830077aea..cfc0462ca4 100644 --- a/test/spec/ol/source/vectortile.test.js +++ b/test/spec/ol/source/vectortile.test.js @@ -1,6 +1,6 @@ import Map from '../../../../src/ol/Map.js'; import View from '../../../../src/ol/View.js'; -import VectorImageTile from '../../../../src/ol/VectorImageTile.js'; +import VectorRenderTile from '../../../../src/ol/VectorRenderTile.js'; import VectorTile from '../../../../src/ol/VectorTile.js'; import GeoJSON from '../../../../src/ol/format/GeoJSON.js'; import MVT from '../../../../src/ol/format/MVT.js'; @@ -41,7 +41,7 @@ describe('ol.source.VectorTile', function() { describe('#getTile()', function() { it('creates a tile with the correct tile class', function() { tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857')); - expect(tile).to.be.a(VectorImageTile); + expect(tile).to.be.a(VectorRenderTile); }); it('sets the correct tileCoord on the created tile', function() { expect(tile.getTileCoord()).to.eql([0, 0, 0]); diff --git a/test/spec/ol/vectorimagetile.test.js b/test/spec/ol/vectorimagetile.test.js index 5d3b924f19..56621a1e8f 100644 --- a/test/spec/ol/vectorimagetile.test.js +++ b/test/spec/ol/vectorimagetile.test.js @@ -9,7 +9,7 @@ import {getKey} from '../../../src/ol/tilecoord.js'; import EventType from '../../../src/ol/events/EventType.js'; -describe('ol.VectorImageTile', function() { +describe('ol.VectorRenderTile', function() { it('triggers "change" when previously failed source tiles are loaded', function(done) { let sourceTile; From 54f48e9c03448364069068439554c2028f365d34 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 8 Jan 2019 15:20:12 +0100 Subject: [PATCH 5/5] Re-add missing import after rebase --- test/spec/ol/renderer/canvas/vectortilelayer.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/spec/ol/renderer/canvas/vectortilelayer.test.js b/test/spec/ol/renderer/canvas/vectortilelayer.test.js index 691940ae94..4ffb3365d8 100644 --- a/test/spec/ol/renderer/canvas/vectortilelayer.test.js +++ b/test/spec/ol/renderer/canvas/vectortilelayer.test.js @@ -19,6 +19,7 @@ import Style from '../../../../../src/ol/style/Style.js'; import Text from '../../../../../src/ol/style/Text.js'; import {createXYZ} from '../../../../../src/ol/tilegrid.js'; import VectorTileRenderType from '../../../../../src/ol/layer/VectorTileRenderType.js'; +import {getUid} from '../../../../../src/ol/util.js'; describe('ol.renderer.canvas.VectorTileLayer', function() {