import react, { useRef } from 'react'; import { range } from 'lodash'; import { Rectangle, TileFactory, TileKeyObject } from './types'; import tileUri from './uris'; 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 or its number of tiles is updated. * * 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) * */ export const TiledLayer: react.FC = ( props: TiledLayerProperties ) => { console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); const tiles = useRef({}); range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map( (row) => { range(props.viewPort.topLeft.x, props.viewPort.bottomRight.x + 1).map( (col) => { const key = tileUri({ provider: props.keyObject.provider, zoomLevel: props.keyObject.zoomLevel, x: props.keyObject.x + row, y: props.keyObject.x + col, }); if (!Object.hasOwn(tiles.current, key)) { tiles.current[key] = ( ); } } ); } ); return <>{Object.values(tiles.current)}; }; export default TiledLayer;