Re-pluging handlers (on LiveMap).

This commit is contained in:
Eric van der Vlist 2022-10-31 18:05:58 +01:00
parent 8341da64a3
commit eb1618d009
10 changed files with 158 additions and 96 deletions

View File

@ -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 = () => (
<IonReactRouter>
<IonRouterOutlet>
<Route exact path='/home'>
<Map
<LiveMap
scope={{
center: { lat: -37.8403508, lon: 77.5539501 },
zoom: 13,
tileProvider: 'osm',
}}
numberOfTiledLayers={5}
/>
</Route>
<Route exact path='/'>

View File

@ -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(<Handlers />);
const transformMap = (t: Transformation) => {};
render(<Handlers transformMap={transformMap} />);
// 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,

View File

@ -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<HandlersProperties> = (
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<HandlersProperties> = (
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<HandlersProperties> = (
const doubleClickHandler = (event: any) => {
genericHandler(event);
transformMap({
props.transformMap({
deltaShift: null,
deltaZoom: Math.SQRT2,
zoomCenter: {
@ -142,7 +145,7 @@ export const Handlers: react.FC<HandlersProperties> = (
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<HandlersProperties> = (
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<HandlersProperties> = (
) {
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,

View File

@ -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= {

View File

@ -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';

View File

@ -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', () => {

View File

@ -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<LiveMapProperties> = (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<LiveMapProperties> = (
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 (
<>
<Handlers transformMap={transform} />
<Map scope={scope} numberOfTiledLayers={props.numberOfTiledLayers} />
</>
);
};
export default LiveMap;

View File

@ -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 `<Map>` component

View File

@ -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', () => {

View File

@ -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 {
/**