import react, { memo } from 'react'; import { isEqual, range } from 'lodash'; import { Rectangle, TileKeyObject } from './types'; import tileUri from './uris'; import Tile from './Tile'; /** * @hidden */ export const thisIsAModule = true; /** * */ declare global { var cacheForTileSet: any; } //export {}; globalThis.cacheForTileSet = new Map(); export interface TileSetProperties { /** A partial Tile key object specifying the provider and zoom level */ keyObject: TileKeyObject; /** The current viewport expressed in tiles coordinates */ viewPort?: Rectangle; } /** * A lazily loaded set of tiles. * * This component is rather dumb and is mainly a sparse array of tiles. * * New tiles are added to the array when the viewport is updated and they stay in the array until * the component is destroyed. * * This component has no need to know the number nor the size of its tiles: tiles can be added when needed and * its unit is the tile size (the parent component needs to transform its enclosing SVG group to adapt its units) * * The `globalThis.cacheForTiledLayer` global variable is used as a cache to store tiles without being subject * to re-initialization when components are unmounted/remounted. * * This cache is a map of map, the first key identifying the ``s. * * Idea stolen [on the web](https://dev.to/tiagof/react-re-mounting-vs-re-rendering-lnh) * * TODO: cache housekeeping * * */ export const TileSet: react.FC = memo( (props: TileSetProperties) => { // console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); const key = tileUri({ provider: props.keyObject.provider, zoomLevel: props.keyObject.zoomLevel, }); const tiles: any = globalThis.cacheForTileSet.get(key) ?? new Map(); if (props.viewPort !== undefined) { range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).forEach( (row) => { range( props.viewPort!.topLeft.x, props.viewPort!.bottomRight.x + 1 ).forEach((col) => { const keyObject = { provider: props.keyObject.provider, zoomLevel: props.keyObject.zoomLevel, x: props.keyObject.x + col, y: props.keyObject.y + row, }; const key = tileUri(keyObject); if (!tiles.has(key)) { tiles.set( key, ); } }); } ); globalThis.cacheForTileSet.set(key, tiles); } return <>{Array.from(tiles.values())}; }, isEqual ); export default TileSet;