2022-10-19 11:55:44 +00:00
|
|
|
import react, { useCallback } from 'react';
|
2022-10-19 09:17:24 +00:00
|
|
|
import { atom, useAtom } from 'jotai';
|
|
|
|
|
2022-10-18 11:45:16 +00:00
|
|
|
import Handlers from './Handlers';
|
2022-10-17 20:37:07 +00:00
|
|
|
import Tile from './Tile';
|
|
|
|
import TiledLayer from './TiledLayer';
|
2022-10-18 11:45:16 +00:00
|
|
|
import { Point, TileFactory } from './types';
|
2022-10-17 08:37:26 +00:00
|
|
|
|
2022-10-17 10:04:25 +00:00
|
|
|
export interface MapProperties {}
|
2022-10-17 08:37:26 +00:00
|
|
|
|
2022-10-19 11:55:44 +00:00
|
|
|
/**
|
|
|
|
* Definition of a coordinate system
|
|
|
|
*
|
|
|
|
* The coordinate system is shifted and zoomed (from the viewport origin)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
export interface CoordinateSystem {
|
|
|
|
/** Zoom relative to the origin */
|
|
|
|
zoom: number;
|
|
|
|
/** Origin's shift (in pixels) */
|
|
|
|
shift: Point;
|
|
|
|
}
|
|
|
|
|
|
|
|
const initialCoordinateSystem: CoordinateSystem = {
|
2022-10-19 09:17:24 +00:00
|
|
|
zoom: 1,
|
|
|
|
shift: { x: 0, y: 0 },
|
|
|
|
};
|
2022-10-18 11:45:16 +00:00
|
|
|
|
2022-10-19 11:55:44 +00:00
|
|
|
/** An atom to store the map coordinates system */
|
|
|
|
export const coordinateSystemAtom = atom(initialCoordinateSystem);
|
2022-10-19 09:17:24 +00:00
|
|
|
|
2022-10-19 11:04:56 +00:00
|
|
|
/**
|
|
|
|
* Description of coordinates system transformation
|
|
|
|
*/
|
2022-10-19 09:17:24 +00:00
|
|
|
export interface Transformation {
|
2022-10-19 11:04:56 +00:00
|
|
|
/** New translation to apply */
|
2022-10-19 09:17:24 +00:00
|
|
|
deltaShift: Point | null;
|
2022-10-19 11:04:56 +00:00
|
|
|
/** Zoom factor to apply */
|
2022-10-19 09:17:24 +00:00
|
|
|
deltaZoom: number | null;
|
2022-10-19 11:04:56 +00:00
|
|
|
/** Center of the new zoom to apply */
|
2022-10-19 09:17:24 +00:00
|
|
|
zoomCenter: Point | null;
|
|
|
|
}
|
2022-10-17 20:37:07 +00:00
|
|
|
|
2022-10-19 11:04:56 +00:00
|
|
|
/**
|
|
|
|
* A write only atom to translate and zoom the coordinate system
|
|
|
|
*/
|
2022-10-19 09:17:24 +00:00
|
|
|
export const relativeCoordinateSystemAtom = atom(
|
|
|
|
null,
|
|
|
|
(get, set, t: Transformation) => {
|
|
|
|
const actualDeltaShift =
|
|
|
|
t.deltaShift === null ? { x: 0, y: 0 } : t.deltaShift;
|
|
|
|
const actualDeltaZoom = t.deltaZoom === null ? 1 : t.deltaZoom;
|
|
|
|
const actualZoomCenter =
|
|
|
|
t.zoomCenter === null ? { x: 0, y: 0 } : t.zoomCenter;
|
|
|
|
const coordinateSystem = get(coordinateSystemAtom);
|
2022-10-18 11:45:16 +00:00
|
|
|
var newCoordinateSystem = {
|
|
|
|
shift: {
|
|
|
|
x:
|
|
|
|
coordinateSystem.shift.x +
|
|
|
|
actualDeltaShift.x +
|
|
|
|
(coordinateSystem.shift.x - actualZoomCenter.x) *
|
|
|
|
(actualDeltaZoom - 1),
|
|
|
|
y:
|
|
|
|
coordinateSystem.shift.y +
|
|
|
|
actualDeltaShift.y +
|
|
|
|
(coordinateSystem.shift.y - actualZoomCenter.y) *
|
|
|
|
(actualDeltaZoom - 1),
|
|
|
|
},
|
|
|
|
zoom: coordinateSystem.zoom * actualDeltaZoom,
|
|
|
|
};
|
2022-10-19 09:17:24 +00:00
|
|
|
set(coordinateSystemAtom, newCoordinateSystem);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @returns A Map component
|
|
|
|
*/
|
|
|
|
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
|
|
|
|
const [coordinateSystem, setCoordinateSystem] = useAtom(coordinateSystemAtom);
|
|
|
|
|
|
|
|
const simpleTileFactory: TileFactory = useCallback(
|
|
|
|
(keyObject) => (
|
|
|
|
<Tile
|
|
|
|
href={`https://tile.openstreetmap.org/${keyObject.zoomLevel}/${keyObject.x}/${keyObject.y}.png`}
|
|
|
|
/>
|
|
|
|
),
|
|
|
|
[]
|
|
|
|
);
|
2022-10-18 11:45:16 +00:00
|
|
|
|
2022-10-18 11:57:46 +00:00
|
|
|
const viewPort = {
|
|
|
|
topLeft: {
|
|
|
|
x: Math.floor(-coordinateSystem.shift.x / coordinateSystem.zoom / 256),
|
|
|
|
y: Math.floor(-coordinateSystem.shift.y / coordinateSystem.zoom / 256),
|
|
|
|
},
|
|
|
|
bottomRight: {
|
|
|
|
x: Math.ceil(
|
|
|
|
(-coordinateSystem.shift.x + window.innerWidth) /
|
|
|
|
coordinateSystem.zoom /
|
|
|
|
256
|
|
|
|
),
|
|
|
|
y: Math.ceil(
|
|
|
|
(-coordinateSystem.shift.y + window.innerHeight) /
|
|
|
|
coordinateSystem.zoom /
|
|
|
|
256
|
|
|
|
),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-10-17 20:37:07 +00:00
|
|
|
return (
|
2022-10-18 11:45:16 +00:00
|
|
|
<>
|
2022-10-19 09:17:24 +00:00
|
|
|
<Handlers />
|
2022-10-18 11:45:16 +00:00
|
|
|
<svg width={window.innerWidth} height={window.innerHeight}>
|
|
|
|
<g
|
|
|
|
transform={`translate(${coordinateSystem.shift.x}, ${coordinateSystem.shift.y}) scale(${coordinateSystem.zoom})`}
|
|
|
|
>
|
|
|
|
<g transform='scale(256)'>
|
|
|
|
<TiledLayer
|
|
|
|
keyObject={{ provider: 'osm', zoomLevel: 16, x: 33485, y: 23936 }}
|
2022-10-18 11:57:46 +00:00
|
|
|
viewPort={viewPort}
|
2022-10-18 11:45:16 +00:00
|
|
|
tileFactory={simpleTileFactory}
|
|
|
|
/>
|
|
|
|
</g>
|
|
|
|
</g>
|
|
|
|
</svg>
|
|
|
|
</>
|
2022-10-17 20:37:07 +00:00
|
|
|
);
|
2022-10-17 08:37:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default Map;
|