import react, { memo, useRef } from 'react'; import { range, isEqual } from 'lodash'; import { Rectangle, TileFactory, TileKeyObject } from './types'; import tileUri from './uris'; /** * @hidden */ export const thisIsAModule = true; /** * */ declare global { var cacheForTiledLayer: any; } //export {}; globalThis.cacheForTiledLayer = new Map(); export interface TiledLayerProperties { /** The key of the first (ie top/left) tile */ keyObject: TileKeyObject; /** The current viewport expressed in tiles coordinates */ viewPort?: Rectangle; /** The factory to create tiles */ tileFactory: TileFactory; } /** * A lazily loaded layer 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 * * TODO: test tiles'X and Y boundaries. * * TODO: remove tileFactory to facilitate memoisation. * */ export const TiledLayer: react.FC = ( props: TiledLayerProperties ) => { // console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); const key = tileUri({ provider: props.keyObject.provider, zoomLevel: props.keyObject.zoomLevel, }); const tiles: any = globalThis.cacheForTiledLayer.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, {props.tileFactory(keyObject)} ); } }); } ); globalThis.cacheForTiledLayer.set(key, tiles); } return <>{Array.from(tiles.values())}; }; export default TiledLayer;