From 25b43f3d57ed3259ea2721702d09a820a8a46423 Mon Sep 17 00:00:00 2001 From: Tim Deubler Date: Tue, 7 Nov 2023 18:48:21 +0100 Subject: [PATCH] added(core): The ["ignoreTileQueryLimit"](https://heremaps.github.io/xyz-maps/docs/core.imlprovideroptions.html#ignoretilequerylimit) option has been introduced to mitigate an excessive number of tile requests. Signed-off-by: Tim Deubler --- .../EditableRemoteTileProvider.ts | 1 + .../FixedLevelTileLoadDelegator.ts | 60 +++++++++++++------ .../RemoteTileProvider/RemoteTileProvider.ts | 1 + .../RemoteTileProviderOptions.ts | 8 +++ 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/packages/core/src/providers/RemoteTileProvider/EditableRemoteTileProvider.ts b/packages/core/src/providers/RemoteTileProvider/EditableRemoteTileProvider.ts index 1e5f5f32e..a77a1f8b1 100644 --- a/packages/core/src/providers/RemoteTileProvider/EditableRemoteTileProvider.ts +++ b/packages/core/src/providers/RemoteTileProvider/EditableRemoteTileProvider.ts @@ -99,6 +99,7 @@ export abstract class EditableRemoteTileProvider extends EditableFeatureProvider provider, loader, level: provider.level, + ignoreTileQueryLimit: options.ignoreTileQueryLimit, preProcessor, processTileResponse: (tile, data, onDone) => { if (tile.error) { diff --git a/packages/core/src/providers/RemoteTileProvider/FixedLevelTileLoadDelegator.ts b/packages/core/src/providers/RemoteTileProvider/FixedLevelTileLoadDelegator.ts index 42f58dacf..1a2cd90e8 100644 --- a/packages/core/src/providers/RemoteTileProvider/FixedLevelTileLoadDelegator.ts +++ b/packages/core/src/providers/RemoteTileProvider/FixedLevelTileLoadDelegator.ts @@ -23,6 +23,7 @@ import {Tile} from '../../tile/Tile'; import TileProvider from '../TileProvider/TileProvider'; import {TileLoadDelegator} from './TileLoadDelegator'; import {tileUtils} from '@here/xyz-maps-core'; +import {add} from '@here/xyz-maps-common/src/Vec3'; let UNDEF; @@ -32,21 +33,23 @@ type TileLoader = any; export class FixedLevelTileLoadDelegator extends TileLoadDelegator { private level: number; private dep: { [quadkey: string]: Tile[] } = {}; + private ignoreTileQueryLimit: boolean; constructor(options: { - level: number, - provider: TileProvider, - loader: TileLoader, - preProcessor?: (input: { - data: any, - ready: (features: any) => void, - tile?: { x: number, y: number, z: number } - }) => (any | Promise), - processTileResponse: (tile: Tile, data: any, onDone: (data: any) => void, xhr: XMLHttpRequest) => any + level: number, + ignoreTileQueryLimit?: boolean, + provider: TileProvider, + loader: TileLoader, + preProcessor?: (input: { + data: any, + ready: (features: any) => void, + tile?: { x: number, y: number, z: number } + }) => (any | Promise), + processTileResponse: (tile: Tile, data: any, onDone: (data: any) => void, xhr: XMLHttpRequest) => any }) { super(options); - this.level = options.level; + this.ignoreTileQueryLimit = options.ignoreTileQueryLimit || false; } cancel(quadkey: string | Tile, cb?: () => void) { @@ -146,6 +149,8 @@ export class FixedLevelTileLoadDelegator extends TileLoadDelegator { return tile; }; + private blockedLevels: { [level: number]: number } = {}; + getTile(quadkey: string, callback: (tile: Tile, error?: any) => void) { const provider = this.provider; const storage = provider.storage; @@ -177,13 +182,35 @@ export class FixedLevelTileLoadDelegator extends TileLoadDelegator { } } - if (quadkey.length != storageLevel) { + const addSimpleOnLoadCallback = (tile, callback) => { + if (!callback) return; + if (tile.onLoaded.indexOf(callback) == -1) { + tile.onLoaded.push(callback); + } + }; + + const requestedLevel = quadkey.length; + + if (requestedLevel != storageLevel) { + tile.loadStartTs = Date.now(); + + console.log('this.ignoreTileQueryLimit', this.ignoreTileQueryLimit); + + if (!this.ignoreTileQueryLimit && storageLevel > requestedLevel) { + if (!this.blockedLevels[requestedLevel]) { + this.blockedLevels[requestedLevel] = 1; + console.warn(`The request for all tiles from level ${requestedLevel} was denied because the TileProvider is set to level ${storageLevel}, leading to an excessive number of tile requests.`); + } + + addSimpleOnLoadCallback(tile, callback); + setTimeout(() => this.completeTile(tile, []), 0); + return tile; + } + const loaderTiles = tileUtils.getTilesOfLevel(quadkey, storageLevel); let loaderTile; let receiver; - tile.loadStartTs = Date.now(); - if (!tile.onLoaded.length) { receiver = new TileReceiver(tile, loaderTiles); @@ -197,7 +224,6 @@ export class FixedLevelTileLoadDelegator extends TileLoadDelegator { for (let l = 0; l < loaderTiles.length; l++) { loaderTile = storage.get(loaderTiles[l]); - if (loaderTile == UNDEF) { loaderTile = provider.getTile(loaderTiles[l], receiver); } else {// if( loaderTile.onLoaded.indexOf(receiver) == -1 ) @@ -210,11 +236,7 @@ export class FixedLevelTileLoadDelegator extends TileLoadDelegator { } } else { // attach the callback - if (callback) { - if (tile.onLoaded.indexOf(callback) == -1) { - tile.onLoaded.push(callback); - } - } + addSimpleOnLoadCallback(tile, callback); if (!tile.loadStartTs) { tile.loadStartTs = Date.now(); diff --git a/packages/core/src/providers/RemoteTileProvider/RemoteTileProvider.ts b/packages/core/src/providers/RemoteTileProvider/RemoteTileProvider.ts index f3f377514..aa13c7edc 100644 --- a/packages/core/src/providers/RemoteTileProvider/RemoteTileProvider.ts +++ b/packages/core/src/providers/RemoteTileProvider/RemoteTileProvider.ts @@ -84,6 +84,7 @@ export class RemoteTileProvider extends FeatureProvider { provider, loader, level: provider.level, + ignoreTileQueryLimit: options.ignoreTileQueryLimit, preProcessor, processTileResponse: (tile, data, onDone) => { if (tile.error) { diff --git a/packages/core/src/providers/RemoteTileProvider/RemoteTileProviderOptions.ts b/packages/core/src/providers/RemoteTileProvider/RemoteTileProviderOptions.ts index 94d58e8e5..ecdedd6aa 100644 --- a/packages/core/src/providers/RemoteTileProvider/RemoteTileProviderOptions.ts +++ b/packages/core/src/providers/RemoteTileProvider/RemoteTileProviderOptions.ts @@ -77,5 +77,13 @@ export interface RemoteTileProviderOptions extends TileProviderOptions { ready: (data) => void }): { put: GeoJSONFeature[], remove: GeoJSONFeature[] } | Promise<{ put: GeoJSONFeature[], remove: GeoJSONFeature[] }>; + /** + * To prevent an overwhelming volume of tile requests, any requests for zoom levels lower than the provider's setting are disregarded. + * Enabling "ignoreTileQueryLimit" will bypass the tile query limit but may risk browser crashes. + * + * @defaultValue false + */ + ignoreTileQueryLimit?: boolean; + loader?: any; }