UNPKG

@allmaps/render

Version:

Render functions for WebGL and image buffers

444 lines (443 loc) 13.2 kB
import { equalSet } from "@allmaps/stdlib"; import { WarpedMapEventType, WarpedMapEvent } from "../shared/events.js"; const PRUNE_MAX_HIGHER_LOG2_SCALE_FACTOR_DIFF = 4; const PRUNE_MAX_LOWER_LOG2_SCALE_FACTOR_DIFF = 2; class TileCache extends EventTarget { cacheableTileFactory; fetchFn; tileCacheForTilesFromSprites; tilesByTileUrl = /* @__PURE__ */ new Map(); mapIdsByTileUrl = /* @__PURE__ */ new Map(); tileUrlsByMapId = /* @__PURE__ */ new Map(); tilesFetchingCount = 0; fetchableTiles = []; constructor(cacheableTileFactory, partialTileCacheOptions) { super(); this.setOptions(partialTileCacheOptions); this.cacheableTileFactory = cacheableTileFactory; } /** * 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 = []; const tileUrls = this.tileUrlsByMapId.get(mapId); if (tileUrls) { for (const tileUrl of tileUrls) { const cacheableTile = this.tilesByTileUrl.get(tileUrl); if (cacheableTile) { 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 = []; const tileUrls = this.tileUrlsByMapId.get(mapId); if (tileUrls) { for (const tileUrl of tileUrls) { const cacheableTile = this.tilesByTileUrl.get(tileUrl); if (cacheableTile && cacheableTile.isCachedTile()) { 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; this.tileCacheForTilesFromSprites = partialTileCacheOptions?.tileCacheForSprites; } // 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 cacheableTile = this.tilesByTileUrl.get(tileUrl); if (cacheableTile) { if (cacheableTile.shouldPrune(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.cacheableTileFactory( 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, { mapIds: [mapId], tileUrl }) ); } } this.addTileUrlForMapId(tileUrl, mapId); this.addMapIdForTileUrl(mapId, tileUrl); } addCacheableTile(cacheableTile) { this.tilesByTileUrl.set(cacheableTile.fetchableTile.tileUrl, cacheableTile); cacheableTile.fetch(); this.updateTilesFetchingCount(1); } // Directly add cached tiles created from sprites addCachedTile(cachedTile) { const mapId = cachedTile.fetchableTile.mapId; const tileUrl = cachedTile.fetchableTile.tileUrl; if (this.tilesByTileUrl.has(tileUrl)) { return; } this.tilesByTileUrl.set(tileUrl, cachedTile); this.addTileUrlForMapId(tileUrl, mapId); this.addMapIdForTileUrl(mapId, tileUrl); this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILELOADED, { mapIds: [mapId], tileUrl }) ); } 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.MAPTILEDELETED, { mapIds: [mapId], tileUrl }) ); } tileFetched(event) { if (event instanceof WarpedMapEvent) { if (!event.data?.tileUrl) { throw new Error("Event data missing"); } const { tileUrl } = event.data; this.updateTilesFetchingCount(-1); for (const mapId of this.mapIdsByTileUrl.get(tileUrl) || []) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILELOADED, { mapIds: [mapId], tileUrl }) ); const tileUrls = this.tileUrlsByMapId.get(mapId); const firstTileUrl = tileUrls?.values().next().value; if (firstTileUrl === tileUrl) { this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.FIRSTMAPTILELOADED, { mapIds: [mapId], tileUrl }) ); } } if (this.tileCacheForTilesFromSprites) { this.tilesByTileUrl.get(tileUrl)?.applySprites(); } } } tileFetchError(event) { if (event instanceof WarpedMapEvent) { if (!event.data?.tileUrl) { throw new Error("Event data missing"); } const { tileUrl } = event.data; if (!this.tilesByTileUrl.has(tileUrl)) { this.updateTilesFetchingCount(-1); } } } // If this is a spritesTileChache tilesFromSpriteTile(event) { if (event instanceof WarpedMapEvent) { if (!event.data?.tileUrl) { throw new Error("Event data missing"); } const { tileUrl } = event.data; this.passTilesFromSprites(tileUrl); } } // If this is a spritesTileChache passTilesFromSprites(tileUrl) { let cacheableSpritesTiles; if (!tileUrl) { cacheableSpritesTiles = Array.from(this.getCacheableTiles()); } else { const cacheableSpritesTile = this.tilesByTileUrl.get(tileUrl); cacheableSpritesTiles = cacheableSpritesTile ? [cacheableSpritesTile] : []; } for (const cacheableSpritesTile of cacheableSpritesTiles) { const cachedTilesFromSprites = cacheableSpritesTile.getCachedTilesFromSprites(); if (!cachedTilesFromSprites) { throw new Error("Cached tiles from sprites not found"); } for (const cachedTile of cachedTilesFromSprites) { if (!cachedTile.isCachedTile()) { break; } this.tileCacheForTilesFromSprites?.addCachedTile(cachedTile); } const mapIds = cachedTilesFromSprites.map( (cachedTile) => cachedTile.fetchableTile.mapId ); this.dispatchEvent( new WarpedMapEvent(WarpedMapEventType.MAPTILESLOADEDFROMSPRITES, { mapIds, spritesInfo: cacheableSpritesTile.fetchableTile.options?.spritesInfo }) ); } } 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) ); cacheableTile.addEventListener( WarpedMapEventType.TILESFROMSPRITETILE, this.tilesFromSpriteTile.bind(this) ); } removeEventListenersFromCacheableTile(cacheableTile) { cacheableTile.removeEventListener( WarpedMapEventType.TILEFETCHED, this.tileFetched.bind(this) ); cacheableTile.removeEventListener( WarpedMapEventType.TILEFETCHERROR, this.tileFetchError.bind(this) ); cacheableTile.removeEventListener( WarpedMapEventType.TILESFROMSPRITETILE, this.tilesFromSpriteTile.bind(this) ); } } export { TileCache }; //# sourceMappingURL=TileCache.js.map