UNPKG

@allmaps/render

Version:

Render functions for WebGL and image buffers

360 lines (359 loc) 10.4 kB
import { equalSet } from "@allmaps/stdlib"; import { WarpedMapEventType, WarpedMapEvent } from "../shared/events.js"; import { shouldPruneTile } from "../shared/tiles.js"; const PRUNE_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF = 4; const PRUNE_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF = 2; class TileCache extends EventTarget { cachableTileFactory; fetchFn; tilesByTileUrl = /* @__PURE__ */ new Map(); mapIdsByTileUrl = /* @__PURE__ */ new Map(); tileUrlsByMapId = /* @__PURE__ */ new Map(); tilesFetchingCount = 0; fetchableTiles = []; constructor(cachableTileFactory, partialTileCacheOptions) { super(); this.setOptions(partialTileCacheOptions); this.cachableTileFactory = cachableTileFactory; } /** * Get the tiles in this cache * * @returns */ getCacheableTiles() { return this.tilesByTileUrl.values(); } /** * Get a specific tile in this cache * * @param tileUrl - the URL of the requested tile * @returns */ getCacheableTile(tileUrl) { return this.tilesByTileUrl.get(tileUrl); } /** * Get the tiles in this cache, corresponding to a specific map * * @param mapId - ID of the map * @returns */ getMapCacheableTiles(mapId) { const cacheableTiles = []; for (const cacheableTile of this.tilesByTileUrl.values()) { if (this.tileUrlsByMapId.get(mapId)?.has(cacheableTile.tileUrl)) { cacheableTiles.push(cacheableTile); } } return cacheableTiles; } /** * Get the tiles in this cache that have been fetched * * @returns */ getCachedTiles() { const cachedTiles = []; for (const cacheableTile of this.tilesByTileUrl.values()) { if (cacheableTile.isCachedTile()) { cachedTiles.push(cacheableTile); } } return cachedTiles; } /** * Get a specific cached tile in this cache that has been fetched * * @param tileUrl - the URL of the requested tile * @returns */ getCachedTile(tileUrl) { const cacheableTile = this.tilesByTileUrl.get(tileUrl); if (cacheableTile && cacheableTile.isCachedTile()) { return cacheableTile; } } /** * Get the tiles in this cache, corresponding to a specific map, that have been fetched * * @param mapId - ID of the map * @returns */ getMapCachedTiles(mapId) { const cachedTiles = []; for (const cacheableTile of this.tilesByTileUrl.values()) { if (cacheableTile.isCachedTile() && this.tileUrlsByMapId.get(mapId)?.has(cacheableTile.tileUrl)) { cachedTiles.push(cacheableTile); } } return cachedTiles; } /** * Get the URLs of tiles in this cache * * @returns */ getTileUrls() { return this.tilesByTileUrl.keys(); } /** * Get the URLs of tiles in this cache, corresponding to a specific map * * @param mapId - ID of the map * @returns */ getMapTileUrls(mapId) { return this.tileUrlsByMapId.get(mapId) || /* @__PURE__ */ new Set(); } /** * Get the Tile Cache options * * @param partialTileCacheOptions - Options */ setOptions(partialTileCacheOptions) { this.fetchFn = partialTileCacheOptions?.fetchFn; } // TODO: this function needs a new name! /** * Process the request for new tiles to be added to this cache * * @param fetchableTiles */ requestFetchableTiles(fetchableTiles) { const previousKeys = new Set( this.fetchableTiles.map((fetchableTile) => fetchableTile.fetchableTileKey) ); const keys = new Set( fetchableTiles.map((fetchableTile) => fetchableTile.fetchableTileKey) ); if (equalSet(previousKeys, keys)) { return; } for (const fetchableTile of fetchableTiles) { this.requestFetchableTile(fetchableTile); } this.fetchableTiles = fetchableTiles; } /** * Returns a promise that resolves when all requested tiles are loaded. * This could happen immidiately, in case there are no ongoing requests and the tilesFetchingCount is zero, * or in a while, when the count reaches zero and the ALLREQUESTEDTILESLOADED event is fired. */ async allRequestedTilesLoaded() { return new Promise((resolve) => { if (this.finished) { resolve(); } else { const listener = () => { this.removeEventListener( WarpedMapEventType.ALLREQUESTEDTILESLOADED, listener ); resolve(); }; this.addEventListener( WarpedMapEventType.ALLREQUESTEDTILESLOADED, listener ); } }); } /** * Prune tiles in this cache using the provided prune info */ prune(pruneInfoByMapId) { for (const [tileUrl, mapIds] of this.mapIdsByTileUrl.entries()) { for (const mapId of mapIds) { const pruneInfo = pruneInfoByMapId.get(mapId); const tile = this.tilesByTileUrl.get(tileUrl)?.tile; if (tile) { if (!pruneInfo || shouldPruneTile(tile, pruneInfo, { maxHigherLog2ScaleFactorDiff: PRUNE_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF, maxLowerLog2ScaleFactorDiff: PRUNE_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF })) { this.removeCacheableTileForMapId(tileUrl, mapId); } } } } } clear() { for (const cacheableTile of this.getCacheableTiles()) { cacheableTile.abort(); this.removeEventListenersFromCacheableTile(cacheableTile); } this.tilesByTileUrl = /* @__PURE__ */ new Map(); this.mapIdsByTileUrl = /* @__PURE__ */ new Map(); this.tileUrlsByMapId = /* @__PURE__ */ new Map(); this.tilesFetchingCount = 0; } destroy() { this.clear(); } requestFetchableTile(fetchableTile) { const mapId = fetchableTile.mapId; const tileUrl = fetchableTile.tileUrl; if (!this.tilesByTileUrl.has(tileUrl)) { const cacheableTile = this.cachableTileFactory( fetchableTile, this.fetchFn ); this.addEventListenersToCacheableTile(cacheableTile); this.addCacheableTile(cacheableTile); } else { const cachedTile = this.tilesByTileUrl.get(tileUrl); if (cachedTile?.isCachedTile()) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILELOADED, { mapId, tileUrl }) ); } } this.addTileUrlForMapId(tileUrl, mapId); this.addMapIdForTileUrl(mapId, tileUrl); } addCacheableTile(cacheableTile) { this.tilesByTileUrl.set(cacheableTile.tileUrl, cacheableTile); cacheableTile.fetch(); this.updateTilesFetchingCount(1); } removeCacheableTileForMapId(tileUrl, mapId) { const cacheableTile = this.tilesByTileUrl.get(tileUrl); if (!cacheableTile) { return; } const mapIds = this.removeMapIdForTileUrl(mapId, tileUrl); this.removeTileUrlForMapId(tileUrl, mapId); if (!mapIds.size) { if (!cacheableTile.isCachedTile()) { cacheableTile.abort(); this.updateTilesFetchingCount(-1); } this.tilesByTileUrl.delete(tileUrl); } this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILEREMOVED, { mapId, tileUrl }) ); } tileFetched(event) { if (event instanceof WarpedMapEvent) { const tileUrl = event.data; this.updateTilesFetchingCount(-1); for (const mapId of this.mapIdsByTileUrl.get(tileUrl) || []) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILELOADED, { mapId, tileUrl }) ); const tileUrls = this.tileUrlsByMapId.get(mapId); const firstTileUrl = tileUrls?.values().next().value; if (firstTileUrl === tileUrl) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.FIRSTMAPTILELOADED, { mapId, tileUrl }) ); } } } } tileFetchError(event) { if (event instanceof WarpedMapEvent) { const tileUrl = event.data; if (!this.tilesByTileUrl.has(tileUrl)) { this.updateTilesFetchingCount(-1); } } } addMapIdForTileUrl(mapId, tileUrl) { let mapIds = this.mapIdsByTileUrl.get(tileUrl); if (!mapIds) { mapIds = /* @__PURE__ */ new Set([mapId]); } else { mapIds.add(mapId); } this.mapIdsByTileUrl.set(tileUrl, mapIds); return mapIds; } removeMapIdForTileUrl(mapId, tileUrl) { const mapIds = this.mapIdsByTileUrl.get(tileUrl); if (!mapIds) { return /* @__PURE__ */ new Set(); } else { mapIds.delete(mapId); } if (!mapIds.size) { this.mapIdsByTileUrl.delete(tileUrl); } else { this.mapIdsByTileUrl.set(tileUrl, mapIds); } return mapIds; } addTileUrlForMapId(tileUrl, mapId) { let tileUrls = this.tileUrlsByMapId.get(mapId); if (!tileUrls) { tileUrls = /* @__PURE__ */ new Set([tileUrl]); } else { tileUrls.add(tileUrl); } this.tileUrlsByMapId.set(mapId, tileUrls); return tileUrls; } removeTileUrlForMapId(tileUrl, mapId) { const tileUrls = this.tileUrlsByMapId.get(mapId); if (!tileUrls) { return /* @__PURE__ */ new Set(); } else { tileUrls.delete(tileUrl); if (!tileUrls.size) { this.tileUrlsByMapId.delete(mapId); } else { this.tileUrlsByMapId.set(mapId, tileUrls); } return tileUrls; } } get finished() { return this.tilesFetchingCount === 0; } updateTilesFetchingCount(delta) { this.tilesFetchingCount += delta; if (this.tilesFetchingCount === 0) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.ALLREQUESTEDTILESLOADED) ); } } addEventListenersToCacheableTile(cacheableTile) { cacheableTile.addEventListener( WarpedMapEventType.TILEFETCHED, this.tileFetched.bind(this) ); cacheableTile.addEventListener( WarpedMapEventType.TILEFETCHERROR, this.tileFetchError.bind(this) ); } removeEventListenersFromCacheableTile(cacheableTile) { cacheableTile.removeEventListener( WarpedMapEventType.TILEFETCHED, this.tileFetched.bind(this) ); cacheableTile.removeEventListener( WarpedMapEventType.TILEFETCHERROR, this.tileFetchError.bind(this) ); } } export { TileCache }; //# sourceMappingURL=TileCache.js.map