Moving the map center to the current location when required.

This commit is contained in:
Eric van der Vlist 2022-11-22 22:10:08 +01:00
parent 0ba385c476
commit 1555c00fe8
5 changed files with 219 additions and 39 deletions

118
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "dyomedeaOl", "name": "dyomedeaOl",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@awesome-cordova-plugins/geolocation": "^6.2.0",
"@capacitor/android": "4.5.0", "@capacitor/android": "4.5.0",
"@capacitor/app": "4.1.1", "@capacitor/app": "4.1.1",
"@capacitor/core": "4.5.0", "@capacitor/core": "4.5.0",
@ -27,6 +28,10 @@
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"history": "^4.9.0", "history": "^4.9.0",
"ionicons": "^6.0.3", "ionicons": "^6.0.3",
"jotai": "^1.10.0",
"jotai-location": "^0.2.0",
"jotai-utils": "^0.0.0",
"lodash": "^4.17.21",
"ol": "^7.1.0", "ol": "^7.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -48,7 +53,8 @@
"workbox-streams": "^5.1.4" "workbox-streams": "^5.1.4"
}, },
"devDependencies": { "devDependencies": {
"@capacitor/cli": "4.5.0" "@capacitor/cli": "4.5.0",
"@types/lodash": "^4.14.189"
} }
}, },
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
@ -68,6 +74,30 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@awesome-cordova-plugins/core": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/core/-/core-6.2.0.tgz",
"integrity": "sha512-z5rKlTuQpLLPEIgzOmomQdoiRw5Sal2blh5CJRcjqh8ktspefbNC81Ni0MykPdV78/UlRORTHEzxugpcEcl1RQ==",
"peer": true,
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@awesome-cordova-plugins/geolocation": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/geolocation/-/geolocation-6.2.0.tgz",
"integrity": "sha512-EGUwTOrH4GjFHH4Zt0pzExg2TasJqQdYDcV4qpW8lY41fJBEFJCAB8KmSsD60hi25S5DxHQSJjsTuGBq2bq6DA==",
"dependencies": {
"@types/cordova": "latest"
},
"peerDependencies": {
"@awesome-cordova-plugins/core": "^6.0.1",
"rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
}
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
@ -3669,6 +3699,11 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/cordova": {
"version": "0.0.34",
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
"integrity": "sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA=="
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.4.10", "version": "8.4.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz",
@ -3825,6 +3860,12 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
}, },
"node_modules/@types/lodash": {
"version": "4.14.189",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz",
"integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA==",
"dev": true
},
"node_modules/@types/mime": { "node_modules/@types/mime": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -10277,6 +10318,72 @@
"url": "https://github.com/chalk/supports-color?sponsor=1" "url": "https://github.com/chalk/supports-color?sponsor=1"
} }
}, },
"node_modules/jotai": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/jotai/-/jotai-1.10.0.tgz",
"integrity": "sha512-3Q8kQU3Ktds+80Ku4dVcvnwSXEcK0Fg0b6mC1+4wz3rmF64lOGNUySKXQ4njvYCWodR8bw2HygOKYSYkHxQQmA==",
"engines": {
"node": ">=12.7.0"
},
"peerDependencies": {
"@babel/core": "*",
"@babel/template": "*",
"jotai-immer": "*",
"jotai-optics": "*",
"jotai-redux": "*",
"jotai-tanstack-query": "*",
"jotai-urql": "*",
"jotai-valtio": "*",
"jotai-xstate": "*",
"jotai-zustand": "*",
"react": ">=16.8"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"@babel/template": {
"optional": true
},
"jotai-immer": {
"optional": true
},
"jotai-optics": {
"optional": true
},
"jotai-redux": {
"optional": true
},
"jotai-tanstack-query": {
"optional": true
},
"jotai-urql": {
"optional": true
},
"jotai-valtio": {
"optional": true
},
"jotai-xstate": {
"optional": true
},
"jotai-zustand": {
"optional": true
}
}
},
"node_modules/jotai-location": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/jotai-location/-/jotai-location-0.2.0.tgz",
"integrity": "sha512-SS4HyCf+PUKtyUZ7ge820ti92NUDRx4AvSuINTFx8kuAxbusMYjNSntcTJy2Fg+R0YPO2RtvV5INzzlUQ+1oGw==",
"peerDependencies": {
"jotai": "*"
}
},
"node_modules/jotai-utils": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/jotai-utils/-/jotai-utils-0.0.0.tgz",
"integrity": "sha512-z/gS7iuWWwXY/JnvsYmcDpcFLnDsJVqkIFJ66h060LY5+cte8S7SDKEVEFPcoM6UQjyy3mj6eEKjcxb1+7OqZw=="
},
"node_modules/js-sdsl": { "node_modules/js-sdsl": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
@ -13700,6 +13807,15 @@
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
}, },
"node_modules/rxjs": {
"version": "7.5.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz",
"integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",

View File

@ -3,6 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@awesome-cordova-plugins/geolocation": "^6.2.0",
"@capacitor/android": "4.5.0", "@capacitor/android": "4.5.0",
"@capacitor/app": "4.1.1", "@capacitor/app": "4.1.1",
"@capacitor/core": "4.5.0", "@capacitor/core": "4.5.0",
@ -22,6 +23,10 @@
"@types/react-router-dom": "^5.1.7", "@types/react-router-dom": "^5.1.7",
"history": "^4.9.0", "history": "^4.9.0",
"ionicons": "^6.0.3", "ionicons": "^6.0.3",
"jotai": "^1.10.0",
"jotai-location": "^0.2.0",
"jotai-utils": "^0.0.0",
"lodash": "^4.17.21",
"ol": "^7.1.0", "ol": "^7.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -67,7 +72,8 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@capacitor/cli": "4.5.0" "@capacitor/cli": "4.5.0",
"@types/lodash": "^4.14.189"
}, },
"description": "An Ionic project" "description": "An Ionic project"
} }

View File

@ -1,36 +1,29 @@
import React from 'react'; import React from 'react';
// import { Geolocation } from '@awesome-cordova-plugins/geolocation'; import { Geolocation } from '@awesome-cordova-plugins/geolocation';
import style from './GetLocation.module.css'; import style from './GetLocation.module.css';
import { IonButton, IonIcon } from '@ionic/react'; import { IonButton, IonIcon } from '@ionic/react';
import { locateOutline } from 'ionicons/icons'; import { locateOutline } from 'ionicons/icons';
// import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
// import { setCenterAtom } from '../../App'; import { stateAtom } from '../map';
// import { locationAtom } from '../map/CurrentLocation'; // import { locationAtom } from '../map/CurrentLocation';
const GetLocation: React.FC<{}> = () => { const GetLocation: React.FC<{}> = () => {
// const [, setCenter] = useAtom(setCenterAtom); const [state, setState] = useAtom(stateAtom);
// const [, setLocation] = useAtom(locationAtom);
// const onClickHandler = (event: any) => { const onClickHandler = (event: any) => {
// console.log('Click handler'); console.log('Click handler');
// Geolocation.getCurrentPosition().then((position) => { Geolocation.getCurrentPosition().then((position) => {
// console.log( console.log({ caller: 'GetLocation/onClickHandler', position, state });
// `Click handler, position: ${position.coords.latitude}, ${position.coords.longitude}` setState({
// ); zoom: state.zoom,
// setCenter({ rotation: state.rotation,
// lat: position.coords.latitude, center: [position.coords.longitude, position.coords.latitude],
// lon: position.coords.longitude, });
// }); });
// setLocation({ };
// lat: position.coords.latitude,
// lon: position.coords.longitude,
// });
// });
// };
// onClick={onClickHandler}
return ( return (
<IonButton <IonButton
@ -38,6 +31,7 @@ const GetLocation: React.FC<{}> = () => {
shape='round' shape='round'
size='small' size='small'
fill='solid' fill='solid'
onClick={onClickHandler}
> >
<IonIcon slot='icon-only' icon={locateOutline} color='white' /> <IonIcon slot='icon-only' icon={locateOutline} color='white' />
</IonButton> </IonButton>

View File

@ -1,4 +1,8 @@
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { atom, useAtom } from 'jotai';
import { atomWithHash } from 'jotai-location';
import OlMap from 'ol/Map'; import OlMap from 'ol/Map';
import View from 'ol/View'; import View from 'ol/View';
import TileLayer from 'ol/layer/Tile'; import TileLayer from 'ol/layer/Tile';
@ -7,37 +11,95 @@ import Rotate from 'ol/control/Rotate';
import ScaleLine from 'ol/control/ScaleLine'; import ScaleLine from 'ol/control/ScaleLine';
import Control from 'ol/control/Control'; import Control from 'ol/control/Control';
import OSM from 'ol/source/OSM'; import OSM from 'ol/source/OSM';
import { useGeographic as olUseGeographic } from 'ol/proj';
import { isEqual } from 'lodash';
import './Map.css'; import './Map.css';
import Collection from 'ol/Collection'; import Collection from 'ol/Collection';
import { createPortal } from 'react-dom';
import GetLocation from '../get-location'; import GetLocation from '../get-location';
export interface MapProperties {} export interface MapProperties {}
export const Map: React.FC<MapProperties> = (props: MapProperties) => { olUseGeographic();
const target = useRef<HTMLDivElement>(null);
const mapAtom = atom<OlMap | null>(null);
export const stateAtom = atomWithHash<any>('map', null);
//atom(
// (get) => {
// const map = get(mapAtom);
// const view = map?.getView();
// return {
// center: view?.getCenter(),
// zoom: view?.getZoom(),
// rotation: view?.getRotation(),
// };
// },
// (get, set, state: any) => {
// const map = get(mapAtom);
// const view = map?.getView();
// view?.setCenter(state.center);
// view?.setZoom(state.zoom);
// view?.setRotation(state.rotation);
// }
//);
export const Map: React.FC<MapProperties> = (props: MapProperties) => {
const [map, setMap] = useAtom(mapAtom);
const [state, setState] = useAtom(stateAtom);
const target = useRef<HTMLDivElement>(null);
const getLocation = useRef<HTMLDivElement>(null); const getLocation = useRef<HTMLDivElement>(null);
useEffect(() => { const previousState = useRef<any>(null);
// const getLocation = target.current
// ? createPortal(<GetLocation />, target.current)
// : null;
if (state && map) {
if (!isEqual(state, previousState.current)) {
console.log({ caller: 'Map / updateView', state });
previousState.current = state;
map.getView().animate(state);
}
// const view = map.getView();
// view.setCenter(state.center);
// view.setZoom(state.zoom);
// view.setRotation(state.rotation);
// map.renderSync();
}
useEffect(() => {
console.log({ caller: 'Map / useEffect', target, getLocation }); console.log({ caller: 'Map / useEffect', target, getLocation });
const changeListener = (event: any) => {
const view = olMap?.getView();
const newState = {
center: view?.getCenter(),
zoom: view?.getZoom(),
rotation: view?.getRotation(),
};
setState(newState);
console.log({
caller: 'Map / changeListener',
event,
newState,
olMap,
});
};
const controls = new Collection([ const controls = new Collection([
new Attribution({ collapsible: true }), new Attribution({ collapsible: true }),
new Rotate(), new Rotate(),
new ScaleLine({ bar: true }), new ScaleLine({ bar: true }),
new Control({ element: getLocation.current ?? undefined }), new Control({ element: getLocation.current ?? undefined }),
]); ]);
const map = new OlMap({ const olMap = new OlMap({
view: new View({ view: new View(
state ?? {
center: [0, 0], center: [0, 0],
zoom: 1, zoom: 1,
}), }
),
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new OSM(), source: new OSM(),
@ -46,6 +108,8 @@ export const Map: React.FC<MapProperties> = (props: MapProperties) => {
target: target.current ?? undefined, target: target.current ?? undefined,
controls, controls,
}); });
olMap.on(['change', 'moveend'], changeListener);
setMap(olMap);
}, []); }, []);
return ( return (

View File

@ -1 +1 @@
export { default } from './Map'; export { default, stateAtom } from './Map';