From eb1618d009c179bb56b9fcc6d36dcd8a4515c4a2 Mon Sep 17 00:00:00 2001 From: evlist Date: Mon, 31 Oct 2022 18:05:58 +0100 Subject: [PATCH] Re-pluging handlers (on LiveMap). --- src/App.tsx | 5 +- src/components/map/Handlers.test.tsx | 11 +- src/components/map/Handlers.tsx | 27 ++-- src/components/map/LayerStack.test.tsx | 5 +- src/components/map/LayerStack.tsx | 2 +- .../map/{Map.test.tsx => LiveMap.test.tsx} | 2 +- src/components/map/LiveMap.tsx | 134 +++++++++++++++++- src/components/map/Map.tsx | 64 --------- src/components/map/TiledLayer.test.tsx | 2 +- src/components/map/TiledLayer.tsx | 2 +- 10 files changed, 158 insertions(+), 96 deletions(-) rename src/components/map/{Map.test.tsx => LiveMap.test.tsx} (99%) diff --git a/src/App.tsx b/src/App.tsx index 2c5e58b..7aede86 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,7 +20,7 @@ import '@ionic/react/css/display.css'; /* Theme variables */ import './theme/variables.css'; -import Map from './components/map/Map'; +import LiveMap from './components/map/LiveMap'; setupIonicReact(); @@ -33,12 +33,13 @@ const App: React.FC = () => ( - diff --git a/src/components/map/Handlers.test.tsx b/src/components/map/Handlers.test.tsx index 99298bb..77d9eb3 100644 --- a/src/components/map/Handlers.test.tsx +++ b/src/components/map/Handlers.test.tsx @@ -1,14 +1,11 @@ import { createEvent, fireEvent, render, screen } from '@testing-library/react'; import { Handlers } from './Handlers'; +import { Transformation } from './LiveMap'; import { Point } from './types'; describe('The Handlers component ', () => { test('is just an empty div', () => { - const transformMap = ( - deltaShift: Point | null, - deltaZoom: number | null, - zoomCenter: Point | null - ) => {}; - render(); + const transformMap = (t: Transformation) => {}; + render(); // screen.debug(); const handlers = screen.getByRole('presentation'); // screen.debug(); @@ -19,7 +16,7 @@ describe('The Handlers component ', () => { /> `); }); -/* test('handle mouseDown / mouseMove events', () => { + /* test('handle mouseDown / mouseMove events', () => { var transformMapParams: any; function transformMap( deltaShift: Point | null, diff --git a/src/components/map/Handlers.tsx b/src/components/map/Handlers.tsx index 8a84441..1d8a449 100644 --- a/src/components/map/Handlers.tsx +++ b/src/components/map/Handlers.tsx @@ -4,14 +4,16 @@ import { useAtom } from 'jotai'; import { Point } from './types'; import './Handler.css'; import { handlersConfig } from './config'; -import { relativeCoordinateSystemAtom } from './Map'; +import { relativeCoordinateSystemAtom, Transformation } from './LiveMap'; /** - * + * * See also {@link components/map/Map!relativeCoordinateSystemAtom}. - * + * */ -export interface HandlersProperties {} +export interface HandlersProperties { + transformMap: (t: Transformation) => void; +} /** * * @param props @@ -28,8 +30,6 @@ export interface HandlersProperties {} export const Handlers: react.FC = ( props: HandlersProperties ) => { - const [, transformMap] = useAtom(relativeCoordinateSystemAtom); - const genericHandler = (event: any) => { // console.log(`Log - Event: ${event.type}`); // if (event.clientX !== undefined) { @@ -81,11 +81,14 @@ export const Handlers: react.FC = ( if ( mouseState.current.down && Date.now() - mouseState.current.timestamp > - handlersConfig.mouseMoveThrottleDelay + handlersConfig.mouseMoveThrottleDelay && + (event.clientX - mouseState.current.starting.x) ** 2 + + (event.clientY - mouseState.current.starting.y) ** 2 > + 100 ) { genericHandler(event); if (mouseState.current.down) { - transformMap({ + props.transformMap({ deltaShift: { x: event.clientX - mouseState.current.starting.x, y: event.clientY - mouseState.current.starting.y, @@ -113,7 +116,7 @@ export const Handlers: react.FC = ( const doubleClickHandler = (event: any) => { genericHandler(event); - transformMap({ + props.transformMap({ deltaShift: null, deltaZoom: Math.SQRT2, zoomCenter: { @@ -142,7 +145,7 @@ export const Handlers: react.FC = ( Date.now() - wheelState.current.timestamp > handlersConfig.wheelThrottleDelay ) { - transformMap({ + props.transformMap({ deltaShift: null, deltaZoom: event.deltaY < 0 ? Math.SQRT2 : Math.SQRT1_2, zoomCenter: { @@ -247,7 +250,7 @@ export const Handlers: react.FC = ( x: (event.touches[0].screenX + event.touches[1].screenX) / 2, y: (event.touches[0].screenY + event.touches[1].screenY) / 2, }; - transformMap({ + props.transformMap({ deltaShift: { x: currentCenter.x - previousCenter.x, y: currentCenter.y - previousCenter.y, @@ -266,7 +269,7 @@ export const Handlers: react.FC = ( ) { if (event.touches.length === 1) { genericHandler(event); - transformMap({ + props.transformMap({ deltaShift: { x: event.touches[0].screenX - touchState.current.touches[0].x, y: event.touches[0].screenY - touchState.current.touches[0].y, diff --git a/src/components/map/LayerStack.test.tsx b/src/components/map/LayerStack.test.tsx index d618904..29ad14f 100644 --- a/src/components/map/LayerStack.test.tsx +++ b/src/components/map/LayerStack.test.tsx @@ -1,7 +1,6 @@ -import { renderHook, act, render, screen } from '@testing-library/react'; -import { useAtom } from 'jotai'; +import { render, screen } from '@testing-library/react'; import LayerStack from './LayerStack'; -import { CoordinateSystem } from './Map'; +import { CoordinateSystem } from './LiveMap'; describe('The LayerStack component', () => { const coordinateSystem:CoordinateSystem= { diff --git a/src/components/map/LayerStack.tsx b/src/components/map/LayerStack.tsx index 440e897..1e11f19 100644 --- a/src/components/map/LayerStack.tsx +++ b/src/components/map/LayerStack.tsx @@ -2,7 +2,7 @@ import react from 'react'; import { TileKeyObject } from './types'; -import { CoordinateSystem } from './Map'; +import { CoordinateSystem } from './LiveMap'; import { range } from 'lodash'; import tileUri from './uris'; import TiledLayer from './TiledLayer'; diff --git a/src/components/map/Map.test.tsx b/src/components/map/LiveMap.test.tsx similarity index 99% rename from src/components/map/Map.test.tsx rename to src/components/map/LiveMap.test.tsx index e126c0f..44abb43 100644 --- a/src/components/map/Map.test.tsx +++ b/src/components/map/LiveMap.test.tsx @@ -1,6 +1,6 @@ import { renderHook, act } from '@testing-library/react'; import { useAtom } from 'jotai'; -import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './Map'; +import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './LiveMap'; describe('The Map component coordinate system', () => { test('is initialized', () => { diff --git a/src/components/map/LiveMap.tsx b/src/components/map/LiveMap.tsx index 33264c3..5e4247d 100644 --- a/src/components/map/LiveMap.tsx +++ b/src/components/map/LiveMap.tsx @@ -1,9 +1,135 @@ -import react from 'react'; +import react, { useState } from 'react'; +import { MapScope, Point } from './types'; +import Map from './Map'; +import Handlers from './Handlers'; +import { atom } from 'jotai'; +import { tileProviders } from './tile-providers'; +import { lon2tile, lat2tile, tile2lat, tile2long } from '../../lib/geo'; -export interface LiveMapProperties {} +/** + * 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; +} -export const LiveMap: react.FC = (props: LiveMapProperties) => { - return <>; +const initialCoordinateSystem: CoordinateSystem = { + zoom: 1, + shift: { x: 0, y: 0 }, +}; + +/** An atom to store the map coordinates system */ +export const coordinateSystemAtom = atom(initialCoordinateSystem); + +/** + * Description of coordinates system transformation + */ +export interface Transformation { + /** New translation to apply */ + deltaShift: Point | null; + /** Zoom factor to apply */ + deltaZoom: number | null; + /** Center of the new zoom to apply */ + zoomCenter: Point | null; +} + +/** + * A write only atom to translate and zoom the coordinate system + */ +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); + 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, + }; + set(coordinateSystemAtom, newCoordinateSystem); + } +); + +export interface LiveMapProperties { + scope: MapScope; + numberOfTiledLayers?: number; +} + +export const LiveMap: react.FC = ( + props: LiveMapProperties +) => { + const [scope, setScope] = useState(props.scope); + console.log(`LiveMap rendering: ${JSON.stringify(scope)}`); + const transform = (t: Transformation) => { + const deltaZoom = t.deltaZoom === null ? 0 : Math.log2(t.deltaZoom); + + const tileProvider = tileProviders[scope.tileProvider]; + + const tilesZoom = Math.min( + Math.max(Math.round(scope.zoom), tileProvider.minZoom), + tileProvider.maxZoom + ); + const softZoom = scope.zoom - tilesZoom; + const relativeScale = 2 ** softZoom; + const visibleTileSize = tileProvider.tileSize * relativeScale; + + const tilesCenter: Point = { + x: lon2tile(scope.center.lon, tilesZoom), + y: lat2tile(scope.center.lat, tilesZoom), + }; + const newTilesCenter = { + x: + tilesCenter.x - + (t.deltaShift === null ? 0 : t.deltaShift.x) / visibleTileSize, + y: + tilesCenter.y - + (t.deltaShift === null ? 0 : t.deltaShift.y) / visibleTileSize, + }; + + const newScope: MapScope = { + center: { + lat: tile2lat(newTilesCenter.y, tilesZoom), + lon: tile2long(newTilesCenter.x, tilesZoom), + }, + zoom: deltaZoom + scope.zoom, + tileProvider: scope.tileProvider, + }; + console.log( + `LiveMap transform: ${JSON.stringify(t)}, ${JSON.stringify( + scope + )} -> ${JSON.stringify(newScope)}, delta lat: ${ + newScope.center.lat - scope.center.lat + }, delta lon: ${newScope.center.lon - scope.center.lon}` + ); + setScope(newScope); + }; + + return ( + <> + + + + ); }; export default LiveMap; diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx index e361775..5f6140e 100644 --- a/src/components/map/Map.tsx +++ b/src/components/map/Map.tsx @@ -11,70 +11,6 @@ export interface MapProperties { numberOfTiledLayers?: number; } -/** - * 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 = { - zoom: 1, - shift: { x: 0, y: 0 }, -}; - -/** An atom to store the map coordinates system */ -export const coordinateSystemAtom = atom(initialCoordinateSystem); - -/** - * Description of coordinates system transformation - */ -export interface Transformation { - /** New translation to apply */ - deltaShift: Point | null; - /** Zoom factor to apply */ - deltaZoom: number | null; - /** Center of the new zoom to apply */ - zoomCenter: Point | null; -} - -/** - * A write only atom to translate and zoom the coordinate system - */ -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); - 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, - }; - set(coordinateSystemAtom, newCoordinateSystem); - } -); - /** * * @returns A `` component diff --git a/src/components/map/TiledLayer.test.tsx b/src/components/map/TiledLayer.test.tsx index 0dd5b9c..af79eb9 100644 --- a/src/components/map/TiledLayer.test.tsx +++ b/src/components/map/TiledLayer.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react'; -import { CoordinateSystem } from './Map'; +import { CoordinateSystem } from './LiveMap'; import TiledLayer from './TiledLayer'; describe('The TiledLayer component', () => { diff --git a/src/components/map/TiledLayer.tsx b/src/components/map/TiledLayer.tsx index e507f5a..9c47412 100644 --- a/src/components/map/TiledLayer.tsx +++ b/src/components/map/TiledLayer.tsx @@ -2,7 +2,7 @@ import { useAtom } from 'jotai'; import react from 'react'; import TileSet from './TileSet'; import { Point, TileKeyObject } from './types'; -import { CoordinateSystem, coordinateSystemAtom } from './Map'; +import { CoordinateSystem } from './LiveMap'; export interface TiledLayerProperties { /**