2022-10-18 14:52:24 +00:00
|
|
|
import react, { memo, useRef } from 'react';
|
2022-10-17 18:52:10 +00:00
|
|
|
import { range, isEqual } from 'lodash';
|
2022-10-17 10:04:25 +00:00
|
|
|
|
2022-10-17 16:17:13 +00:00
|
|
|
import { Rectangle, TileFactory, TileKeyObject } from './types';
|
2022-10-17 15:15:24 +00:00
|
|
|
import tileUri from './uris';
|
2022-10-17 10:04:25 +00:00
|
|
|
|
|
|
|
export interface TiledLayerProperties {
|
|
|
|
/** The key of the first (ie top/left) tile */
|
2022-10-17 11:47:35 +00:00
|
|
|
keyObject: TileKeyObject;
|
|
|
|
/** The current viewport expressed in tiles coordinates */
|
|
|
|
viewPort: Rectangle;
|
2022-10-17 16:17:13 +00:00
|
|
|
/** The factory to create tiles */
|
|
|
|
tileFactory: TileFactory;
|
2022-10-17 10:04:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-10-17 15:48:30 +00:00
|
|
|
* A lazily loaded layer of tiles.
|
2022-10-17 11:47:35 +00:00
|
|
|
*
|
|
|
|
* This component is rather dumb and is mainly a sparse array of tiles.
|
2022-10-17 15:48:30 +00:00
|
|
|
*
|
2022-10-17 11:47:35 +00:00
|
|
|
* 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.
|
2022-10-17 10:04:25 +00:00
|
|
|
*
|
2022-10-17 15:48:30 +00:00
|
|
|
* 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)
|
2022-10-18 14:52:24 +00:00
|
|
|
*
|
2022-10-17 21:04:58 +00:00
|
|
|
* TODO: test tiles'X and Y boundaries.
|
2022-10-17 15:48:30 +00:00
|
|
|
*
|
2022-10-17 10:04:25 +00:00
|
|
|
*/
|
2022-10-18 14:52:24 +00:00
|
|
|
export const TiledLayer: react.FC<TiledLayerProperties> = memo(
|
|
|
|
(props: TiledLayerProperties) => {
|
|
|
|
console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
|
|
|
|
const tiles = useRef<any>({});
|
|
|
|
const previousKeyObject = useRef<TileKeyObject>(props.keyObject);
|
|
|
|
if (!isEqual(props.keyObject, previousKeyObject.current)) {
|
|
|
|
previousKeyObject.current = props.keyObject;
|
|
|
|
tiles.current = {};
|
|
|
|
}
|
2022-10-17 15:15:24 +00:00
|
|
|
|
2022-10-18 14:52:24 +00:00
|
|
|
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 keyObject = {
|
|
|
|
provider: props.keyObject.provider,
|
|
|
|
zoomLevel: props.keyObject.zoomLevel,
|
|
|
|
x: props.keyObject.x + col,
|
|
|
|
y: props.keyObject.y + row,
|
|
|
|
};
|
|
|
|
const key = tileUri(keyObject);
|
|
|
|
if (!Object.hasOwn(tiles.current, key)) {
|
|
|
|
tiles.current[key] = (
|
|
|
|
<g key={key} transform={`translate(${col}, ${row})`}>
|
|
|
|
{props.tileFactory(keyObject)}
|
|
|
|
</g>
|
|
|
|
);
|
|
|
|
}
|
2022-10-17 15:15:24 +00:00
|
|
|
}
|
2022-10-18 14:52:24 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2022-10-17 15:15:24 +00:00
|
|
|
|
2022-10-18 14:52:24 +00:00
|
|
|
return <>{Object.values(tiles.current)}</>;
|
|
|
|
},
|
|
|
|
isEqual
|
|
|
|
);
|
2022-10-17 10:04:25 +00:00
|
|
|
|
|
|
|
export default TiledLayer;
|