dyomedea/src/components/map/Map.tsx

72 lines
2.3 KiB
TypeScript

import react, { JSXElementConstructor, ReactElement, ReactNode } from 'react';
import useDimensions from 'react-cool-dimensions';
import { Point, MapScope } from './types';
import Marker from './Marker';
import LayerStack from './LayerStack';
import { tileProviders } from './tile-providers';
import { lon2tile, lat2tile } from '../../lib/geo';
export interface MapProperties {
scope: MapScope;
numberOfTiledLayers?: number;
/** Markers are non scalable SVG snippets tied to geo location */
markers?: ReactElement<any, string | JSXElementConstructor<any>>[];
}
/**
*
* @returns A `<Map>` component
*
* `<Map>` components display the map specified by their {@link MapProperties}'s scope.
*
* They can be driven by {@link components/map/LiveMap!LiveMap} component to react to user's event.
*
*/
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
const { observe, width, height } = useDimensions<HTMLDivElement>();
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;
const nbTilesLeft = width / 2 / visibleTileSize;
const nbTilesTop = height / 2 / visibleTileSize;
const firstTileLeft = Math.floor(tilesCenter.x - nbTilesLeft);
const firstTileTop = Math.floor(tilesCenter.y - nbTilesTop);
return (
<div style={{ width: '100%', height: '100%' }} ref={observe}>
<LayerStack
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,
}}
markers={props.markers}
/>
</div>
);
};
export default Map;