@allmaps/render
Version:
Render functions for WebGL and image buffers
360 lines (359 loc) • 10.4 kB
JavaScript
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