2022-10-19 16:35:20 +00:00
|
|
|
import react from 'react';
|
2022-11-02 14:14:34 +00:00
|
|
|
import useDimensions from 'react-cool-dimensions';
|
2022-10-19 09:17:24 +00:00
|
|
|
|
2022-10-31 14:02:46 +00:00
|
|
|
import { Point, MapScope } from './types';
|
2022-10-19 16:35:20 +00:00
|
|
|
import LayerStack from './LayerStack';
|
2022-10-31 14:02:46 +00:00
|
|
|
import { tileProviders } from './tile-providers';
|
|
|
|
import { lon2tile, lat2tile } from '../../lib/geo';
|
2022-10-17 08:37:26 +00:00
|
|
|
|
2022-10-31 14:02:46 +00:00
|
|
|
export interface MapProperties {
|
|
|
|
scope: MapScope;
|
2022-10-31 14:08:50 +00:00
|
|
|
numberOfTiledLayers?: number;
|
2022-10-31 14:02:46 +00:00
|
|
|
}
|
2022-10-17 08:37:26 +00:00
|
|
|
|
2022-10-19 09:17:24 +00:00
|
|
|
/**
|
|
|
|
*
|
2022-10-31 14:02:46 +00:00
|
|
|
* @returns A `<Map>` component
|
2022-11-02 14:14:34 +00:00
|
|
|
*
|
2022-11-01 13:37:08 +00:00
|
|
|
* `<Map>` components display the map specified by their {@link MapProperties}'s scope.
|
2022-11-02 14:14:34 +00:00
|
|
|
*
|
|
|
|
* They can be driven by {@link components/map/LiveMap!LiveMap} component to react to user's event.
|
2022-10-19 16:35:20 +00:00
|
|
|
*
|
2022-10-19 09:17:24 +00:00
|
|
|
*/
|
|
|
|
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
|
2022-11-02 14:14:34 +00:00
|
|
|
const { observe, width, height } = useDimensions<HTMLDivElement>();
|
|
|
|
|
2022-10-31 14:02:46 +00:00
|
|
|
const tileProvider = tileProviders[props.scope.tileProvider];
|
|
|
|
|
|
|
|
const tilesZoom = Math.min(
|
|
|
|
Math.max(Math.round(props.scope.zoom), tileProvider.minZoom),
|
|
|
|
tileProvider.maxZoom
|
|
|
|
);
|
|
|
|
const tilesCenter: Point = {
|
|
|
|
x: lon2tile(props.scope.center.lon, tilesZoom),
|
|
|
|
y: lat2tile(props.scope.center.lat, tilesZoom),
|
|
|
|
};
|
|
|
|
const softZoom = props.scope.zoom - tilesZoom;
|
|
|
|
const relativeScale = 2 ** softZoom;
|
|
|
|
const visibleTileSize = tileProvider.tileSize * relativeScale;
|
2022-11-02 14:14:34 +00:00
|
|
|
const nbTilesLeft = width / 2 / visibleTileSize;
|
|
|
|
const nbTilesTop = height / 2 / visibleTileSize;
|
2022-10-31 14:02:46 +00:00
|
|
|
const firstTileLeft = Math.floor(tilesCenter.x - nbTilesLeft);
|
|
|
|
const firstTileTop = Math.floor(tilesCenter.y - nbTilesTop);
|
2022-10-19 09:17:24 +00:00
|
|
|
|
2022-10-17 20:37:07 +00:00
|
|
|
return (
|
2022-11-02 14:14:34 +00:00
|
|
|
<div style={{ width: '100%', height: '100%' }} ref={observe}>
|
2022-10-19 16:35:20 +00:00
|
|
|
<LayerStack
|
2022-10-31 14:02:46 +00:00
|
|
|
numberOfTiledLayers={props.numberOfTiledLayers}
|
|
|
|
keyObject={{
|
|
|
|
provider: props.scope.tileProvider,
|
|
|
|
zoomLevel: tilesZoom,
|
|
|
|
x: firstTileLeft,
|
|
|
|
y: firstTileTop,
|
|
|
|
}}
|
|
|
|
coordinateSystem={{
|
|
|
|
shift: {
|
|
|
|
x: -((tilesCenter.x - nbTilesLeft) % 1) * visibleTileSize,
|
|
|
|
y: -((tilesCenter.y - nbTilesTop) % 1) * visibleTileSize,
|
|
|
|
},
|
|
|
|
zoom: relativeScale,
|
|
|
|
}}
|
2022-10-19 16:35:20 +00:00
|
|
|
/>
|
2022-11-02 14:14:34 +00:00
|
|
|
</div>
|
2022-10-17 20:37:07 +00:00
|
|
|
);
|
2022-10-17 08:37:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default Map;
|