diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index 20ed805..5391b9e 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -2,6 +2,13 @@ import react, { useMemo, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import _ from 'lodash'; import { MapState, mapActions } from '../../store/map'; +import { slippyActions } from '../../store/slippy'; +import { lat2tile, lon2tile } from '../../lib/geo'; +import Tile from './tile'; + +import '../../theme/map.css'; + +export const tileSize = 256; const Map: react.FC<{}> = (props: {}) => { const dispatch = useDispatch(); @@ -22,86 +29,43 @@ const Map: react.FC<{}> = (props: {}) => { window.addEventListener('resize', debouncedResizeHandler); }, []); + const nbTilesY = _.ceil(mapState.viewport.height / tileSize) + 3; + const nbTilesX = _.ceil(mapState.viewport.width / tileSize) + 3; + const [tileCenterY, reminderY] = lat2tile( + mapState.scope.center.lat, + mapState.scope.zoom + ); + const [tileCenterX, reminderX] = lon2tile( + mapState.scope.center.lon, + mapState.scope.zoom + ); + const firstTileY = tileCenterY - _.round(nbTilesY / 2); + const firstTileX = tileCenterX - _.round(nbTilesX / 2); + const locationY = (tileCenterY + reminderY - firstTileY) * tileSize; + const locationX = (tileCenterX + reminderX - firstTileX) * tileSize; + const targetLocationY = mapState.viewport.height / 2; + const targetLocationX = mapState.viewport.width / 2; + const deltaY = targetLocationY - locationY; + const deltaX = targetLocationX - locationX; + + dispatch(slippyActions.set({ scale: 1, translation: { x: deltaX, y: deltaY } })); + return ( -
-
- - - - -
-
- - - - -
-
- - - - -
+
+ {_.range(nbTilesY).map((iy) => ( +
+ {_.range(nbTilesX).map((ix) => ( + + ))} +
+ ))}
); }; diff --git a/src/components/map/tile.tsx b/src/components/map/tile.tsx new file mode 100644 index 0000000..bf7124d --- /dev/null +++ b/src/components/map/tile.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { tileSize } from './map'; + +const tileProvider = (zoom: number, x: number, y: number) => + 'https://tile.openstreetmap.org/' + zoom + '/' + x + '/' + y + '.png'; + +const Tile: React.FC<{ + ix: number; + iy: number; + x: number; + y: number; + zoom: number; +}> = (props: { + ix: number; + iy: number; + x: number; + y: number; + zoom: number; +}) => { + return ( + + ); +}; + +export default Tile; diff --git a/src/components/slippy/slippy.tsx b/src/components/slippy/slippy.tsx index 4507345..28a6849 100644 --- a/src/components/slippy/slippy.tsx +++ b/src/components/slippy/slippy.tsx @@ -9,21 +9,6 @@ import WheelHandler from './wheel-handler'; import { useSelector } from 'react-redux'; import { MapState } from '../../store/map'; -export interface Point { - x: number; - y: number; -} - -export interface Translation { - translate: Point; -} - -export interface Scale { - scale: { - center: Point; - factor: number; - }; -} const Slippy: react.FC<{}> = () => { //console.log(`--- Rendering viewport, props: ${JSON.stringify(props)} ---`); diff --git a/src/lib/geo.ts b/src/lib/geo.ts new file mode 100644 index 0000000..12fad71 --- /dev/null +++ b/src/lib/geo.ts @@ -0,0 +1,38 @@ + +export interface Point { + x: number; + y: number; +} + +export interface geoPoint { + lon: number; + lat: number; +} + +// cf https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_(JavaScript/ActionScript,_etc.) +export const lon2tile = (lon: number, zoom: number) => { + const real = ((lon + 180) / 360) * Math.pow(2, zoom); + const floor = Math.floor(real); + return [floor, real - floor]; +}; +export const lat2tile = (lat: number, zoom: number) => { + const real = + ((1 - + Math.log( + Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180) + ) / + Math.PI) / + 2) * + Math.pow(2, zoom); + const floor = Math.floor(real); + return [floor, real - floor]; +}; + +export function tile2long(x: number, z: number) { + return (x / Math.pow(2, z)) * 360 - 180; +} + +export function tile2lat(y: number, z: number) { + var n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z); + return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))); +} diff --git a/src/store/map.ts b/src/store/map.ts index bcf4a84..c67c0df 100644 --- a/src/store/map.ts +++ b/src/store/map.ts @@ -1,10 +1,15 @@ import { createSlice } from '@reduxjs/toolkit'; +import { geoPoint } from '../lib/geo'; export interface MapState { viewport: { width: number; height: number; }; + scope: { + center: geoPoint; + zoom: number; + }; } const initialMapState: MapState = { @@ -12,6 +17,10 @@ const initialMapState: MapState = { width: window.innerWidth, height: window.innerHeight, }, + scope: { + center: { lat: -37.8372, lon: 77.5513 }, + zoom: 13, + }, }; const mapSlice = createSlice({ @@ -19,8 +28,8 @@ const mapSlice = createSlice({ initialState: initialMapState, reducers: { resize: (state) => { - state.viewport.height = window.innerHeight; - state.viewport.width = window.innerWidth; + state.viewport.height = window.innerHeight; + state.viewport.width = window.innerWidth; }, }, }); diff --git a/src/store/slippy.ts b/src/store/slippy.ts index 2aee334..e4c6621 100644 --- a/src/store/slippy.ts +++ b/src/store/slippy.ts @@ -1,6 +1,7 @@ +import { actionSheetController } from '@ionic/core'; import { createSlice } from '@reduxjs/toolkit'; -import { Point } from '../components/slippy/slippy'; +import { Point } from '../lib/geo'; export interface SlippyState { scale: number; @@ -16,7 +17,7 @@ const slippySlice = createSlice({ name: 'slippy', initialState: initialSlippyState, reducers: { - scale(state, action) { + scale: (state, action) => { console.log(`redux scale: ${JSON.stringify(action.payload)}`); state.scale = state.scale * action.payload.factor; state.translation.x = @@ -28,11 +29,14 @@ const slippySlice = createSlice({ (state.translation.y - action.payload.center.y) * (action.payload.factor - 1); }, - translate(state, action) { + translate: (state, action) => { console.log(`redux translate: action=${JSON.stringify(action)}`); state.translation.x = state.translation.x + action.payload.x; state.translation.y = state.translation.y + action.payload.y; }, + set: (state, action) => { + return action.payload; + }, }, }); diff --git a/src/theme/map.css b/src/theme/map.css new file mode 100644 index 0000000..982fe4c --- /dev/null +++ b/src/theme/map.css @@ -0,0 +1,8 @@ +.tilesRow { + height: 256px; +} + +.tile { + height: 256px; + width: 256px; +}