From 0167c2760ecde1ba000632cb225e97dca087637c Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sat, 11 Jul 2020 11:30:01 +0200 Subject: [PATCH 1/2] Grow cache size dynamically --- src/ol/renderer/canvas/TileLayer.js | 3 +++ src/ol/source/BingMaps.js | 2 +- src/ol/source/CartoDB.js | 2 +- src/ol/source/OSM.js | 2 +- src/ol/source/Stamen.js | 2 +- src/ol/source/Tile.js | 21 +++++++++++++-------- src/ol/source/TileArcGISRest.js | 2 +- src/ol/source/TileImage.js | 2 +- src/ol/source/TileJSON.js | 2 +- src/ol/source/TileWMS.js | 2 +- src/ol/source/VectorTile.js | 11 ++++++++++- src/ol/source/WMTS.js | 2 +- src/ol/source/XYZ.js | 2 +- src/ol/source/Zoomify.js | 2 +- src/ol/structs/LRUCache.js | 4 +++- 15 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index b49bd78b7f..aa968c9506 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -597,6 +597,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { const wantedTiles = frameState.wantedTiles[tileSourceKey]; const tileQueue = frameState.tileQueue; const minZoom = tileGrid.getMinZoom(); + let tileCount = 0; let tile, tileRange, tileResolution, x, y, z; for (z = minZoom; z <= currentZ; ++z) { tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); @@ -604,6 +605,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { if (currentZ - z <= preload) { + ++tileCount; tile = tileSource.getTile(z, x, y, pixelRatio, projection); if (tile.getState() == TileState.IDLE) { wantedTiles[tile.getKey()] = true; @@ -625,6 +627,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { } } } + tileSource.updateCacheSize(tileCount, projection); } } diff --git a/src/ol/source/BingMaps.js b/src/ol/source/BingMaps.js index 61f2584dae..ef0836f7a1 100644 --- a/src/ol/source/BingMaps.js +++ b/src/ol/source/BingMaps.js @@ -48,7 +48,7 @@ const TOS_ATTRIBUTION = /** * @typedef {Object} Options - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {boolean} [hidpi=false] If `true` hidpi tiles will be requested. * @property {string} [culture='en-us'] Culture code. * @property {string} key Bing Maps API key. Get yours at http://www.bingmapsportal.com/. diff --git a/src/ol/source/CartoDB.js b/src/ol/source/CartoDB.js index b6480d2651..76083b104d 100644 --- a/src/ol/source/CartoDB.js +++ b/src/ol/source/CartoDB.js @@ -9,7 +9,7 @@ import {assign} from '../obj.js'; /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/OSM.js b/src/ol/source/OSM.js index 38dede1ba4..0531940667 100644 --- a/src/ol/source/OSM.js +++ b/src/ol/source/OSM.js @@ -19,7 +19,7 @@ export const ATTRIBUTION = /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin='anonymous'] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/Stamen.js b/src/ol/source/Stamen.js index 5e4b95a594..1a744abb04 100644 --- a/src/ol/source/Stamen.js +++ b/src/ol/source/Stamen.js @@ -86,7 +86,7 @@ const ProviderConfig = { /** * @typedef {Object} Options - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {boolean} [imageSmoothing=true] Enable image smoothing. * @property {string} layer Layer name. * @property {number} [minZoom] Minimum zoom. diff --git a/src/ol/source/Tile.js b/src/ol/source/Tile.js index 01f655c1e0..6f21a948cb 100644 --- a/src/ol/source/Tile.js +++ b/src/ol/source/Tile.js @@ -75,19 +75,12 @@ class TileSource extends Source { if (tileGrid) { toSize(tileGrid.getTileSize(tileGrid.getMinZoom()), tileSize); } - const canUseScreen = typeof screen !== 'undefined'; - const width = canUseScreen ? screen.availWidth || screen.width : 1920; - const height = canUseScreen ? screen.availHeight || screen.height : 1080; - const minCacheSize = - 4 * Math.ceil(width / tileSize[0]) * Math.ceil(height / tileSize[1]); /** * @protected * @type {import("../TileCache.js").default} */ - this.tileCache = new TileCache( - Math.max(minCacheSize, options.cacheSize || 0) - ); + this.tileCache = new TileCache(options.cacheSize || 0); /** * @protected @@ -325,6 +318,18 @@ class TileSource extends Source { super.refresh(); } + /** + * Increases the cache size if needed + * @param {number} tileCount Minimum number of tiles needed. + * @param {import("../proj/Projection.js").default} projection Projection. + */ + updateCacheSize(tileCount, projection) { + const tileCache = this.getTileCacheForProjection(projection); + if (tileCount > tileCache.highWaterMark) { + tileCache.highWaterMark = tileCount; + } + } + /** * Marks a tile coord as being used, without triggering a load. * @abstract diff --git a/src/ol/source/TileArcGISRest.js b/src/ol/source/TileArcGISRest.js index c85cf4c6f1..83ba9f803c 100644 --- a/src/ol/source/TileArcGISRest.js +++ b/src/ol/source/TileArcGISRest.js @@ -13,7 +13,7 @@ import {hash as tileCoordHash} from '../tilecoord.js'; /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/TileImage.js b/src/ol/source/TileImage.js index ff60cab9f8..e345aac0ea 100644 --- a/src/ol/source/TileImage.js +++ b/src/ol/source/TileImage.js @@ -18,7 +18,7 @@ import {getUid} from '../util.js'; * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/TileJSON.js b/src/ol/source/TileJSON.js index 4a5dbe385e..c61ab6ea60 100644 --- a/src/ol/source/TileJSON.js +++ b/src/ol/source/TileJSON.js @@ -36,7 +36,7 @@ import {jsonp as requestJSONP} from '../net.js'; /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/TileWMS.js b/src/ol/source/TileWMS.js index 7647b8bb8d..8a880687fc 100644 --- a/src/ol/source/TileWMS.js +++ b/src/ol/source/TileWMS.js @@ -20,7 +20,7 @@ import {hash as tileCoordHash} from '../tilecoord.js'; /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/VectorTile.js b/src/ol/source/VectorTile.js index 49df88a1ad..9b1047ceed 100644 --- a/src/ol/source/VectorTile.js +++ b/src/ol/source/VectorTile.js @@ -27,7 +27,7 @@ import {toSize} from '../size.js'; * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible. - * @property {number} [cacheSize=128] Cache size. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least twice the number of tiles in the viewport. * @property {import("../extent.js").Extent} [extent] * @property {import("../format/Feature.js").default} [format] Feature format for tiles. Used and required by the default. * @property {boolean} [overlaps=true] This source may have overlapping geometries. Setting this @@ -509,6 +509,15 @@ class VectorTile extends UrlTile { Math.round(tileSize[1] * pixelRatio), ]; } + + /** + * Increases the cache size if needed + * @param {number} tileCount Minimum number of tiles needed. + * @param {import("../proj/Projection.js").default} projection Projection. + */ + updateCacheSize(tileCount, projection) { + super.updateCacheSize(tileCount * 2, projection); + } } export default VectorTile; diff --git a/src/ol/source/WMTS.js b/src/ol/source/WMTS.js index 94071eda0e..b61af5eee2 100644 --- a/src/ol/source/WMTS.js +++ b/src/ol/source/WMTS.js @@ -14,7 +14,7 @@ import {find, findIndex, includes} from '../array.js'; /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index 80167f7c24..000ed7c445 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -9,7 +9,7 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value if you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/source/Zoomify.js b/src/ol/source/Zoomify.js index f2511fe24b..0531e9634c 100644 --- a/src/ol/source/Zoomify.js +++ b/src/ol/source/Zoomify.js @@ -83,7 +83,7 @@ export class CustomTile extends ImageTile { /** * @typedef {Object} Options * @property {import("./Source.js").AttributionLike} [attributions] Attributions. - * @property {number} [cacheSize] Tile cache size. The default depends on the screen size. Will be ignored if too small. + * @property {number} [cacheSize] Initial tile cache size. Will auto-grow to hold at least the number of tiles in the viewport. * @property {null|string} [crossOrigin] The `crossOrigin` attribute for loaded images. Note that * you must provide a `crossOrigin` value you want to access pixel data with the Canvas renderer. * See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more detail. diff --git a/src/ol/structs/LRUCache.js b/src/ol/structs/LRUCache.js index 2850c30479..fbae5b6d29 100644 --- a/src/ol/structs/LRUCache.js +++ b/src/ol/structs/LRUCache.js @@ -27,6 +27,8 @@ class LRUCache { */ constructor(opt_highWaterMark) { /** + * Desired max cache size after expireCache(). If set to 0, no cache entries + * will be pruned at all. * @type {number} */ this.highWaterMark = @@ -61,7 +63,7 @@ class LRUCache { * @return {boolean} Can expire cache. */ canExpireCache() { - return this.getCount() > this.highWaterMark; + return this.highWaterMark > 0 && this.getCount() > this.highWaterMark; } /** From 18d96a220561ddb4c71fe9e110ebd02fa4961391 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sat, 11 Jul 2020 20:32:54 +0200 Subject: [PATCH 2/2] Auto-grow tile cache from zero --- test/spec/ol/source/tile.test.js | 43 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/test/spec/ol/source/tile.test.js b/test/spec/ol/source/tile.test.js index 3c3f92f69c..435b891a24 100644 --- a/test/spec/ol/source/tile.test.js +++ b/test/spec/ol/source/tile.test.js @@ -1,9 +1,13 @@ +import Map from '../../../../src/ol/Map.js'; import Projection from '../../../../src/ol/proj/Projection.js'; import Source from '../../../../src/ol/source/Source.js'; import Tile from '../../../../src/ol/Tile.js'; +import TileDebugSource from '../../../../src/ol/source/TileDebug.js'; import TileGrid from '../../../../src/ol/tilegrid/TileGrid.js'; +import TileLayer from '../../../../src/ol/layer/Tile.js'; import TileRange from '../../../../src/ol/TileRange.js'; import TileSource from '../../../../src/ol/source/Tile.js'; +import View from '../../../../src/ol/View.js'; import {getKeyZXY} from '../../../../src/ol/tilecoord.js'; import {get as getProjection} from '../../../../src/ol/proj.js'; @@ -53,23 +57,34 @@ describe('ol.source.Tile', function () { expect(source).to.be.a(Source); expect(source).to.be.a(TileSource); }); - it('sets a screen dependent cache size', function () { + it('sets 0 as initial cache size', function () { const source = new TileSource({}); - expect(source.tileCache.highWaterMark).to.be( - 4 * - Math.ceil(screen.availWidth / 256) * - Math.ceil(screen.availHeight / 256) - ); + expect(source.tileCache.highWaterMark).to.be(0); }); - it('ignores a cache size that is too small', function () { - const source = new TileSource({ - cacheSize: 1, + it('grows the cache', function () { + const source = new TileDebugSource(); + const layer = new TileLayer({ + source: source, }); - expect(source.tileCache.highWaterMark).to.be( - 4 * - Math.ceil(screen.availWidth / 256) * - Math.ceil(screen.availHeight / 256) - ); + const target = document.createElement('div'); + target.style.width = '100px'; + target.style.height = '100px'; + document.body.appendChild(target); + const map = new Map({ + layers: [layer], + view: new View({ + center: [0, 0], + zoom: 2, + }), + target: target, + }); + map.renderSync(); + expect( + source.getTileCacheForProjection(map.getView().getProjection()) + .highWaterMark + ).to.be(4); + map.setTarget(null); + document.body.removeChild(target); }); it('sets a custom cache size', function () { const projection = getProjection('EPSG:4326');