Re-pluging handlers (on LiveMap).
This commit is contained in:
parent
8341da64a3
commit
eb1618d009
|
@ -20,7 +20,7 @@ import '@ionic/react/css/display.css';
|
||||||
|
|
||||||
/* Theme variables */
|
/* Theme variables */
|
||||||
import './theme/variables.css';
|
import './theme/variables.css';
|
||||||
import Map from './components/map/Map';
|
import LiveMap from './components/map/LiveMap';
|
||||||
|
|
||||||
setupIonicReact();
|
setupIonicReact();
|
||||||
|
|
||||||
|
@ -33,12 +33,13 @@ const App: React.FC = () => (
|
||||||
<IonReactRouter>
|
<IonReactRouter>
|
||||||
<IonRouterOutlet>
|
<IonRouterOutlet>
|
||||||
<Route exact path='/home'>
|
<Route exact path='/home'>
|
||||||
<Map
|
<LiveMap
|
||||||
scope={{
|
scope={{
|
||||||
center: { lat: -37.8403508, lon: 77.5539501 },
|
center: { lat: -37.8403508, lon: 77.5539501 },
|
||||||
zoom: 13,
|
zoom: 13,
|
||||||
tileProvider: 'osm',
|
tileProvider: 'osm',
|
||||||
}}
|
}}
|
||||||
|
numberOfTiledLayers={5}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path='/'>
|
<Route exact path='/'>
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import { createEvent, fireEvent, render, screen } from '@testing-library/react';
|
import { createEvent, fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { Handlers } from './Handlers';
|
import { Handlers } from './Handlers';
|
||||||
|
import { Transformation } from './LiveMap';
|
||||||
import { Point } from './types';
|
import { Point } from './types';
|
||||||
describe('The Handlers component ', () => {
|
describe('The Handlers component ', () => {
|
||||||
test('is just an empty div', () => {
|
test('is just an empty div', () => {
|
||||||
const transformMap = (
|
const transformMap = (t: Transformation) => {};
|
||||||
deltaShift: Point | null,
|
render(<Handlers transformMap={transformMap} />);
|
||||||
deltaZoom: number | null,
|
|
||||||
zoomCenter: Point | null
|
|
||||||
) => {};
|
|
||||||
render(<Handlers />);
|
|
||||||
// screen.debug();
|
// screen.debug();
|
||||||
const handlers = screen.getByRole('presentation');
|
const handlers = screen.getByRole('presentation');
|
||||||
// screen.debug();
|
// screen.debug();
|
||||||
|
|
|
@ -4,14 +4,16 @@ import { useAtom } from 'jotai';
|
||||||
import { Point } from './types';
|
import { Point } from './types';
|
||||||
import './Handler.css';
|
import './Handler.css';
|
||||||
import { handlersConfig } from './config';
|
import { handlersConfig } from './config';
|
||||||
import { relativeCoordinateSystemAtom } from './Map';
|
import { relativeCoordinateSystemAtom, Transformation } from './LiveMap';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* See also {@link components/map/Map!relativeCoordinateSystemAtom}.
|
* See also {@link components/map/Map!relativeCoordinateSystemAtom}.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface HandlersProperties {}
|
export interface HandlersProperties {
|
||||||
|
transformMap: (t: Transformation) => void;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param props
|
* @param props
|
||||||
|
@ -28,8 +30,6 @@ export interface HandlersProperties {}
|
||||||
export const Handlers: react.FC<HandlersProperties> = (
|
export const Handlers: react.FC<HandlersProperties> = (
|
||||||
props: HandlersProperties
|
props: HandlersProperties
|
||||||
) => {
|
) => {
|
||||||
const [, transformMap] = useAtom(relativeCoordinateSystemAtom);
|
|
||||||
|
|
||||||
const genericHandler = (event: any) => {
|
const genericHandler = (event: any) => {
|
||||||
// console.log(`Log - Event: ${event.type}`);
|
// console.log(`Log - Event: ${event.type}`);
|
||||||
// if (event.clientX !== undefined) {
|
// if (event.clientX !== undefined) {
|
||||||
|
@ -81,11 +81,14 @@ export const Handlers: react.FC<HandlersProperties> = (
|
||||||
if (
|
if (
|
||||||
mouseState.current.down &&
|
mouseState.current.down &&
|
||||||
Date.now() - mouseState.current.timestamp >
|
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);
|
genericHandler(event);
|
||||||
if (mouseState.current.down) {
|
if (mouseState.current.down) {
|
||||||
transformMap({
|
props.transformMap({
|
||||||
deltaShift: {
|
deltaShift: {
|
||||||
x: event.clientX - mouseState.current.starting.x,
|
x: event.clientX - mouseState.current.starting.x,
|
||||||
y: event.clientY - mouseState.current.starting.y,
|
y: event.clientY - mouseState.current.starting.y,
|
||||||
|
@ -113,7 +116,7 @@ export const Handlers: react.FC<HandlersProperties> = (
|
||||||
|
|
||||||
const doubleClickHandler = (event: any) => {
|
const doubleClickHandler = (event: any) => {
|
||||||
genericHandler(event);
|
genericHandler(event);
|
||||||
transformMap({
|
props.transformMap({
|
||||||
deltaShift: null,
|
deltaShift: null,
|
||||||
deltaZoom: Math.SQRT2,
|
deltaZoom: Math.SQRT2,
|
||||||
zoomCenter: {
|
zoomCenter: {
|
||||||
|
@ -142,7 +145,7 @@ export const Handlers: react.FC<HandlersProperties> = (
|
||||||
Date.now() - wheelState.current.timestamp >
|
Date.now() - wheelState.current.timestamp >
|
||||||
handlersConfig.wheelThrottleDelay
|
handlersConfig.wheelThrottleDelay
|
||||||
) {
|
) {
|
||||||
transformMap({
|
props.transformMap({
|
||||||
deltaShift: null,
|
deltaShift: null,
|
||||||
deltaZoom: event.deltaY < 0 ? Math.SQRT2 : Math.SQRT1_2,
|
deltaZoom: event.deltaY < 0 ? Math.SQRT2 : Math.SQRT1_2,
|
||||||
zoomCenter: {
|
zoomCenter: {
|
||||||
|
@ -247,7 +250,7 @@ export const Handlers: react.FC<HandlersProperties> = (
|
||||||
x: (event.touches[0].screenX + event.touches[1].screenX) / 2,
|
x: (event.touches[0].screenX + event.touches[1].screenX) / 2,
|
||||||
y: (event.touches[0].screenY + event.touches[1].screenY) / 2,
|
y: (event.touches[0].screenY + event.touches[1].screenY) / 2,
|
||||||
};
|
};
|
||||||
transformMap({
|
props.transformMap({
|
||||||
deltaShift: {
|
deltaShift: {
|
||||||
x: currentCenter.x - previousCenter.x,
|
x: currentCenter.x - previousCenter.x,
|
||||||
y: currentCenter.y - previousCenter.y,
|
y: currentCenter.y - previousCenter.y,
|
||||||
|
@ -266,7 +269,7 @@ export const Handlers: react.FC<HandlersProperties> = (
|
||||||
) {
|
) {
|
||||||
if (event.touches.length === 1) {
|
if (event.touches.length === 1) {
|
||||||
genericHandler(event);
|
genericHandler(event);
|
||||||
transformMap({
|
props.transformMap({
|
||||||
deltaShift: {
|
deltaShift: {
|
||||||
x: event.touches[0].screenX - touchState.current.touches[0].x,
|
x: event.touches[0].screenX - touchState.current.touches[0].x,
|
||||||
y: event.touches[0].screenY - touchState.current.touches[0].y,
|
y: event.touches[0].screenY - touchState.current.touches[0].y,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { renderHook, act, render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { useAtom } from 'jotai';
|
|
||||||
import LayerStack from './LayerStack';
|
import LayerStack from './LayerStack';
|
||||||
import { CoordinateSystem } from './Map';
|
import { CoordinateSystem } from './LiveMap';
|
||||||
|
|
||||||
describe('The LayerStack component', () => {
|
describe('The LayerStack component', () => {
|
||||||
const coordinateSystem:CoordinateSystem= {
|
const coordinateSystem:CoordinateSystem= {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import react from 'react';
|
||||||
|
|
||||||
import { TileKeyObject } from './types';
|
import { TileKeyObject } from './types';
|
||||||
|
|
||||||
import { CoordinateSystem } from './Map';
|
import { CoordinateSystem } from './LiveMap';
|
||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
import tileUri from './uris';
|
import tileUri from './uris';
|
||||||
import TiledLayer from './TiledLayer';
|
import TiledLayer from './TiledLayer';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { renderHook, act } from '@testing-library/react';
|
import { renderHook, act } from '@testing-library/react';
|
||||||
import { useAtom } from 'jotai';
|
import { useAtom } from 'jotai';
|
||||||
import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './Map';
|
import { coordinateSystemAtom, relativeCoordinateSystemAtom } from './LiveMap';
|
||||||
|
|
||||||
describe('The Map component coordinate system', () => {
|
describe('The Map component coordinate system', () => {
|
||||||
test('is initialized', () => {
|
test('is initialized', () => {
|
|
@ -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) => {
|
const initialCoordinateSystem: CoordinateSystem = {
|
||||||
return <></>;
|
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;
|
export default LiveMap;
|
||||||
|
|
|
@ -11,70 +11,6 @@ export interface MapProperties {
|
||||||
numberOfTiledLayers?: number;
|
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
|
* @returns A `<Map>` component
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { CoordinateSystem } from './Map';
|
import { CoordinateSystem } from './LiveMap';
|
||||||
import TiledLayer from './TiledLayer';
|
import TiledLayer from './TiledLayer';
|
||||||
|
|
||||||
describe('The TiledLayer component', () => {
|
describe('The TiledLayer component', () => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useAtom } from 'jotai';
|
||||||
import react from 'react';
|
import react from 'react';
|
||||||
import TileSet from './TileSet';
|
import TileSet from './TileSet';
|
||||||
import { Point, TileKeyObject } from './types';
|
import { Point, TileKeyObject } from './types';
|
||||||
import { CoordinateSystem, coordinateSystemAtom } from './Map';
|
import { CoordinateSystem } from './LiveMap';
|
||||||
|
|
||||||
export interface TiledLayerProperties {
|
export interface TiledLayerProperties {
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue