diff --git a/src/App.tsx b/src/App.tsx index f3c47eb7..2f539c80 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,6 +11,7 @@ import { getQueryStore, getRouteStore, getSettingsStore, + getCurrentLocationStore, } from '@/stores/Stores' import MapComponent from '@/map/MapComponent' import MapOptions from '@/map/MapOptions' @@ -22,6 +23,7 @@ import { QueryStoreState, RequestState } from '@/stores/QueryStore' import { RouteStoreState } from '@/stores/RouteStore' import { MapOptionsStoreState } from '@/stores/MapOptionsStore' import { ErrorStoreState } from '@/stores/ErrorStore' +import { CurrentLocationStoreState } from '@/stores/CurrentLocationStore' import Search from '@/sidebar/search/Search' import ErrorMessage from '@/sidebar/ErrorMessage' import useBackgroundLayer from '@/layers/UseBackgroundLayer' @@ -45,6 +47,7 @@ import useExternalMVTLayer from '@/layers/UseExternalMVTLayer' import LocationButton from '@/map/LocationButton' import { SettingsContext } from '@/contexts/SettingsContext' import usePOIsLayer from '@/layers/UsePOIsLayer' +import useCurrentLocationLayer from '@/layers/UseCurrentLocationLayer' export const POPUP_CONTAINER_ID = 'popup-container' export const SIDEBAR_CONTENT_ID = 'sidebar-content' @@ -59,6 +62,7 @@ export default function App() { const [pathDetails, setPathDetails] = useState(getPathDetailsStore().state) const [mapFeatures, setMapFeatures] = useState(getMapFeatureStore().state) const [pois, setPOIs] = useState(getPOIsStore().state) + const [currentLocation, setCurrentLocation] = useState(getCurrentLocationStore().state) const map = getMap() @@ -72,6 +76,7 @@ export default function App() { const onPathDetailsChanged = () => setPathDetails(getPathDetailsStore().state) const onMapFeaturesChanged = () => setMapFeatures(getMapFeatureStore().state) const onPOIsChanged = () => setPOIs(getPOIsStore().state) + const onCurrentLocationChanged = () => setCurrentLocation(getCurrentLocationStore().state) getSettingsStore().register(onSettingsChanged) getQueryStore().register(onQueryChanged) @@ -82,6 +87,7 @@ export default function App() { getPathDetailsStore().register(onPathDetailsChanged) getMapFeatureStore().register(onMapFeaturesChanged) getPOIsStore().register(onPOIsChanged) + getCurrentLocationStore().register(onCurrentLocationChanged) onQueryChanged() onInfoChanged() @@ -91,6 +97,7 @@ export default function App() { onPathDetailsChanged() onMapFeaturesChanged() onPOIsChanged() + onCurrentLocationChanged() return () => { getSettingsStore().deregister(onSettingsChanged) @@ -102,6 +109,7 @@ export default function App() { getPathDetailsStore().deregister(onPathDetailsChanged) getMapFeatureStore().deregister(onMapFeaturesChanged) getPOIsStore().deregister(onPOIsChanged) + getCurrentLocationStore().deregister(onCurrentLocationChanged) } }, []) @@ -116,6 +124,7 @@ export default function App() { useQueryPointsLayer(map, query.queryPoints) usePathDetailsLayer(map, pathDetails) usePOIsLayer(map, pois) + useCurrentLocationLayer(map, currentLocation) const isSmallScreen = useMediaQuery({ query: '(max-width: 44rem)' }) return ( @@ -138,6 +147,7 @@ export default function App() { error={error} encodedValues={info.encoded_values} drawAreas={settings.drawAreasEnabled} + currentLocation={currentLocation} /> ) : ( )} @@ -160,12 +171,22 @@ interface LayoutProps { route: RouteStoreState map: Map mapOptions: MapOptionsStoreState + currentLocation: CurrentLocationStoreState error: ErrorStoreState encodedValues: object[] drawAreas: boolean } -function LargeScreenLayout({ query, route, map, error, mapOptions, encodedValues, drawAreas }: LayoutProps) { +function LargeScreenLayout({ + query, + route, + map, + error, + mapOptions, + encodedValues, + drawAreas, + currentLocation, +}: LayoutProps) { const [showSidebar, setShowSidebar] = useState(true) const [showCustomModelBox, setShowCustomModelBox] = useState(false) return ( @@ -216,7 +237,7 @@ function LargeScreenLayout({ query, route, map, error, mapOptions, encodedValues
- +
@@ -229,7 +250,16 @@ function LargeScreenLayout({ query, route, map, error, mapOptions, encodedValues ) } -function SmallScreenLayout({ query, route, map, error, mapOptions, encodedValues, drawAreas }: LayoutProps) { +function SmallScreenLayout({ + query, + route, + map, + error, + mapOptions, + encodedValues, + drawAreas, + currentLocation, +}: LayoutProps) { return ( <>
@@ -248,7 +278,7 @@ function SmallScreenLayout({ query, route, map, error, mapOptions, encodedValues
- +
diff --git a/src/actions/Actions.ts b/src/actions/Actions.ts index 466b1282..bc20514a 100644 --- a/src/actions/Actions.ts +++ b/src/actions/Actions.ts @@ -189,7 +189,7 @@ export class ToggleExternalMVTLayer implements Action { export class MapIsLoaded implements Action {} -export class ZoomMapToPoint implements Action { +export class MoveMapToPoint implements Action { readonly coordinate: Coordinate constructor(coordinate: Coordinate) { @@ -272,3 +272,35 @@ export class SetPOIs implements Action { this.pois = pois } } + +/** + * Start watching the location and synchronizing the view. + */ +export class StartWatchCurrentLocation implements Action {} +export class StopWatchCurrentLocation implements Action {} + +/** + * Start synchronizing the view again. + */ +export class StartSyncCurrentLocation implements Action {} +export class StopSyncCurrentLocation implements Action {} + +export class CurrentLocationError implements Action { + readonly error: string + + constructor(error: string) { + this.error = error + } +} + +export class CurrentLocation implements Action { + readonly coordinate: Coordinate + readonly accuracy: number + readonly heading: number | null + + constructor(coordinate: Coordinate, accuracy: number, heading: number | null) { + this.coordinate = coordinate + this.accuracy = accuracy + this.heading = heading + } +} diff --git a/src/index.tsx b/src/index.tsx index ee8937a4..81842587 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -13,6 +13,7 @@ import { getQueryStore, getRouteStore, getSettingsStore, + getCurrentLocationStore, setStores, } from '@/stores/Stores' import Dispatcher from '@/stores/Dispatcher' @@ -31,6 +32,7 @@ import MapFeatureStore from '@/stores/MapFeatureStore' import SettingsStore from '@/stores/SettingsStore' import { ErrorAction, InfoReceived } from '@/actions/Actions' import POIsStore from '@/stores/POIsStore' +import CurrentLocationStore from '@/stores/CurrentLocationStore' import { setDistanceFormat } from '@/Converters' import { AddressParseResult } from '@/pois/AddressParseResult' @@ -61,6 +63,7 @@ setStores({ pathDetailsStore: new PathDetailsStore(), mapFeatureStore: new MapFeatureStore(), poisStore: new POIsStore(), + currentLocationStore: new CurrentLocationStore(), }) setMap(createMap()) @@ -75,6 +78,7 @@ Dispatcher.register(getMapOptionsStore()) Dispatcher.register(getPathDetailsStore()) Dispatcher.register(getMapFeatureStore()) Dispatcher.register(getPOIsStore()) +Dispatcher.register(getCurrentLocationStore()) // register map action receiver const smallScreenMediaQuery = window.matchMedia('(max-width: 44rem)') diff --git a/src/layers/UseCurrentLocationLayer.tsx b/src/layers/UseCurrentLocationLayer.tsx new file mode 100644 index 00000000..bd0d5f8c --- /dev/null +++ b/src/layers/UseCurrentLocationLayer.tsx @@ -0,0 +1,129 @@ +import { Feature, Map } from 'ol' +import { useEffect, useRef } from 'react' +import VectorLayer from 'ol/layer/Vector' +import VectorSource from 'ol/source/Vector' +import { Circle, Circle as CircleGeom, Point } from 'ol/geom' +import { Circle as CircleStyle, Fill, RegularShape, Stroke, Style } from 'ol/style' +import { CurrentLocationStoreState } from '@/stores/CurrentLocationStore' +import { fromLonLat } from 'ol/proj' + +export default function useCurrentLocationLayer(map: Map, locationState: CurrentLocationStoreState) { + const layerRef = useRef | null>(null) + const positionFeatureRef = useRef(null) + const accuracyFeatureRef = useRef(null) + const headingFeatureRef = useRef(null) + + useEffect(() => { + if (!locationState.enabled) { + if (layerRef.current) { + map.removeLayer(layerRef.current) + layerRef.current = null + } + return + } else if (!layerRef.current) { + const layer = createLocationLayer() + layer.getSource()?.addFeature((positionFeatureRef.current = new Feature())) + layer.getSource()?.addFeature((accuracyFeatureRef.current = new Feature())) + layer.getSource()?.addFeature((headingFeatureRef.current = new Feature())) + map.addLayer(layer) + + layerRef.current = layer + } + + return () => { + if (layerRef.current) { + map.removeLayer(layerRef.current) + layerRef.current = null + } + } + }, [locationState.enabled]) + + useEffect(() => { + if ( + !locationState.enabled || + !locationState.coordinate || + !layerRef.current || + // typescript complaints without the following + !positionFeatureRef.current || + !accuracyFeatureRef.current || + !headingFeatureRef.current + ) + return + + const coord = fromLonLat([locationState.coordinate.lng, locationState.coordinate.lat]) + positionFeatureRef.current.setGeometry(new Point(coord)) + accuracyFeatureRef.current.setGeometry(new Circle(coord, locationState.accuracy)) + + // set heading feature position (style will handle the triangle and rotation) + if (locationState.heading != null) { + headingFeatureRef.current.setGeometry(new Point(coord)) + headingFeatureRef.current.set('heading', locationState.heading) + } else { + headingFeatureRef.current.setGeometry(undefined) + headingFeatureRef.current.unset('heading') // not strictly necessary + } + + if (locationState.syncView) { + const currentZoom = map.getView().getZoom() + const targetZoom = currentZoom == undefined || currentZoom < 16 ? 16 : currentZoom + const zoomDifference = Math.abs(targetZoom - (currentZoom || 0)) + if (zoomDifference > 0.1) { + map.getView().animate({ zoom: targetZoom, center: coord, duration: 400 }) + } else { + // for smaller zoom changes set center without animation to avoid pulsing of map + map.getView().setCenter(coord) + } + } + }, [ + locationState.coordinate, + locationState.accuracy, + locationState.heading, + locationState.syncView, + locationState.enabled, + ]) +} + +function createLocationLayer(): VectorLayer { + return new VectorLayer({ + source: new VectorSource(), + style: feature => { + const geometry = feature.getGeometry() + if (geometry instanceof Point) { + const heading = feature.get('heading') + if (heading !== undefined) { + // triangle style for heading direction + return new Style({ + image: new RegularShape({ + points: 3, + radius: 8, + displacement: [0, 9], + rotation: (heading * Math.PI) / 180, // convert degrees to radians + fill: new Fill({ color: '#368fe8' }), + stroke: new Stroke({ color: '#FFFFFF', width: 1 }), + }), + zIndex: 1, + }) + } else { + // blue dot style for position + return new Style({ + image: new CircleStyle({ + radius: 8, + fill: new Fill({ color: '#368fe8' }), + stroke: new Stroke({ color: '#FFFFFF', width: 2 }), + }), + zIndex: 2, // above the others + }) + } + } else if (geometry instanceof CircleGeom) { + // accuracy circle style + return new Style({ + fill: new Fill({ color: 'rgba(66, 133, 244, 0.1)' }), + stroke: new Stroke({ color: 'rgba(66, 133, 244, 0.3)', width: 1 }), + zIndex: 0, // behind the others + }) + } + return [] + }, + zIndex: 4, // layer itself should be above paths and query points + }) +} diff --git a/src/layers/UseQueryPointsLayer.tsx b/src/layers/UseQueryPointsLayer.tsx index 48973ea8..d21e4f1a 100644 --- a/src/layers/UseQueryPointsLayer.tsx +++ b/src/layers/UseQueryPointsLayer.tsx @@ -85,7 +85,7 @@ function removeDragInteractions(map: Map) { .forEach(i => map.removeInteraction(i)) } -function addDragInteractions(map: Map, queryPointsLayer: VectorLayer>>) { +function addDragInteractions(map: Map, queryPointsLayer: VectorLayer) { let tmp = queryPointsLayer.getSource() if (tmp == null) throw new Error('source must not be null') // typescript requires this const modify = new Modify({ diff --git a/src/map/ContextMenuContent.tsx b/src/map/ContextMenuContent.tsx index 9c142725..4fdcc011 100644 --- a/src/map/ContextMenuContent.tsx +++ b/src/map/ContextMenuContent.tsx @@ -3,7 +3,7 @@ import { coordinateToText } from '@/Converters' import styles from './ContextMenuContent.module.css' import QueryStore, { QueryPoint, QueryPointType } from '@/stores/QueryStore' import Dispatcher from '@/stores/Dispatcher' -import { AddPoint, SetPoint, ZoomMapToPoint } from '@/actions/Actions' +import { AddPoint, SetPoint, MoveMapToPoint } from '@/actions/Actions' import { RouteStoreState } from '@/stores/RouteStore' import { findNextWayPoint } from '@/map/findNextWayPoint' import { tr } from '@/translation/Translation' @@ -143,7 +143,7 @@ export function ContextMenuContent({ className={styles.entry} onClick={() => { onSelect() - Dispatcher.dispatch(new ZoomMapToPoint(coordinate)) + Dispatcher.dispatch(new MoveMapToPoint(coordinate)) }} > {tr('center_map')} diff --git a/src/map/LocationButton.tsx b/src/map/LocationButton.tsx index 4bbdd108..fa9ddd72 100644 --- a/src/map/LocationButton.tsx +++ b/src/map/LocationButton.tsx @@ -1,48 +1,36 @@ import styles from './LocationButton.module.css' -import { onCurrentLocationButtonClicked } from '@/map/MapComponent' import Dispatcher from '@/stores/Dispatcher' -import { SetBBox, SetPoint, ZoomMapToPoint } from '@/actions/Actions' -import { QueryPoint, QueryPointType } from '@/stores/QueryStore' +import { StartSyncCurrentLocation, StartWatchCurrentLocation, StopWatchCurrentLocation } from '@/actions/Actions' import LocationError from '@/map/location_error.svg' import LocationSearching from '@/map/location_searching.svg' import LocationOn from '@/map/location_on.svg' -import { useState } from 'react' -import { tr } from '@/translation/Translation' -import { getBBoxFromCoord } from '@/utils' +import Location from '@/map/location.svg' +import LocationNotInSync from '@/map/location_not_in_sync.svg' +import { CurrentLocationStoreState } from '@/stores/CurrentLocationStore' -export default function LocationButton(props: { queryPoints: QueryPoint[] }) { - const [locationSearch, setLocationSearch] = useState('synched_map_or_initial') +export default function LocationButton(props: { currentLocation: CurrentLocationStoreState }) { return (
{ - setLocationSearch('search') - onCurrentLocationButtonClicked(coordinate => { - if (coordinate) { - if (props.queryPoints[0] && !props.queryPoints[0].isInitialized) - Dispatcher.dispatch( - new SetPoint( - { - ...props.queryPoints[0], - coordinate, - queryText: tr('current_location'), - isInitialized: true, - type: QueryPointType.From, - }, - false - ) - ) - Dispatcher.dispatch(new ZoomMapToPoint(coordinate)) - // We do not reset state of this button when map is moved, so we do not know if - // the map is currently showing the location. - setLocationSearch('synched_map_or_initial') - } else setLocationSearch('error') - }) + if (props.currentLocation.enabled && !props.currentLocation.syncView && !props.currentLocation.error) { + Dispatcher.dispatch(new StartSyncCurrentLocation()) + } else { + if (props.currentLocation.enabled) { + Dispatcher.dispatch(new StopWatchCurrentLocation()) + } else { + Dispatcher.dispatch(new StartWatchCurrentLocation()) + } + } }} > - {locationSearch == 'error' && } - {locationSearch == 'search' && } - {locationSearch == 'synched_map_or_initial' && } + {(() => { + if (props.currentLocation.error) return + if (!props.currentLocation.enabled) return + if (!props.currentLocation.syncView) return + if (props.currentLocation.coordinate != null) return + return + })()}
) } diff --git a/src/map/MapComponent.tsx b/src/map/MapComponent.tsx index ac2d39b3..18f5dab7 100644 --- a/src/map/MapComponent.tsx +++ b/src/map/MapComponent.tsx @@ -43,23 +43,3 @@ export function onCurrentLocationSelected( { timeout: 300_000, enableHighAccuracy: true } ) } - -export function onCurrentLocationButtonClicked(onSelect: (coordinate: Coordinate | undefined) => void) { - if (!navigator.geolocation) { - Dispatcher.dispatch(new ErrorAction('Geolocation is not supported in this browser')) - onSelect(undefined) - return - } - - navigator.geolocation.getCurrentPosition( - position => { - onSelect({ lat: position.coords.latitude, lng: position.coords.longitude }) - }, - error => { - Dispatcher.dispatch(new ErrorAction(tr('searching_location_failed') + ': ' + error.message)) - onSelect(undefined) - }, - // DO NOT use e.g. maximumAge: 5_000 -> getCurrentPosition will then never return on mobile firefox!? - { timeout: 300_000, enableHighAccuracy: true } - ) -} diff --git a/src/map/location.svg b/src/map/location.svg new file mode 100644 index 00000000..0a1871ad --- /dev/null +++ b/src/map/location.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/map/location_not_in_sync.svg b/src/map/location_not_in_sync.svg new file mode 100644 index 00000000..6534e601 --- /dev/null +++ b/src/map/location_not_in_sync.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/map/location_on.svg b/src/map/location_on.svg index 0a1871ad..643c3be1 100644 --- a/src/map/location_on.svg +++ b/src/map/location_on.svg @@ -1,6 +1,6 @@ - - + \ No newline at end of file diff --git a/src/map/map.ts b/src/map/map.ts index b0aefc25..8cc35182 100644 --- a/src/map/map.ts +++ b/src/map/map.ts @@ -1,7 +1,7 @@ import Dispatcher from '@/stores/Dispatcher' import { Map, View } from 'ol' import { fromLonLat } from 'ol/proj' -import { MapIsLoaded } from '@/actions/Actions' +import { MapIsLoaded, StopSyncCurrentLocation } from '@/actions/Actions' import { defaults as defaultControls } from 'ol/control' import styles from '@/map/Map.module.css' @@ -31,12 +31,18 @@ export function createMap(): Map { map.once('postrender', () => { Dispatcher.dispatch(new MapIsLoaded()) }) + + map.on('pointerdrag', () => { + if (!getMap().getView().getAnimating()) Dispatcher.dispatch(new StopSyncCurrentLocation()) + }) + return map } export function setMap(m: Map) { map = m } + export function getMap(): Map { if (!map) throw Error('Map must be initialized before it can be used. Use "createMap" when starting the app') return map diff --git a/src/sidebar/search/Search.tsx b/src/sidebar/search/Search.tsx index fd4d8649..17faeb8f 100644 --- a/src/sidebar/search/Search.tsx +++ b/src/sidebar/search/Search.tsx @@ -2,7 +2,16 @@ import { useState } from 'react' import Dispatcher from '@/stores/Dispatcher' import styles from '@/sidebar/search/Search.module.css' import { QueryPoint } from '@/stores/QueryStore' -import { AddPoint, ClearRoute, InvalidatePoint, MovePoint, RemovePoint, SetBBox, SetPoint } from '@/actions/Actions' +import { + AddPoint, + ClearRoute, + InvalidatePoint, + MovePoint, + RemovePoint, + SetBBox, + SetPoint, + StopSyncCurrentLocation, +} from '@/actions/Actions' import RemoveIcon from './minus-circle-solid.svg' import AddIcon from './plus-circle-solid.svg' import TargetIcon from './send.svg' @@ -34,6 +43,7 @@ export default function Search({ points, profile, map }: { points: QueryPoint[]; onChange={() => { Dispatcher.dispatch(new ClearRoute()) Dispatcher.dispatch(new InvalidatePoint(point)) + Dispatcher.dispatch(new StopSyncCurrentLocation()) }} showTargetIcons={showTargetIcons} moveStartIndex={moveStartIndex} diff --git a/src/stores/CurrentLocationStore.ts b/src/stores/CurrentLocationStore.ts new file mode 100644 index 00000000..a95b7639 --- /dev/null +++ b/src/stores/CurrentLocationStore.ts @@ -0,0 +1,134 @@ +import Store from '@/stores/Store' +import Dispatcher, { Action } from '@/stores/Dispatcher' +import { + CurrentLocation, + CurrentLocationError, + StartSyncCurrentLocation, + StartWatchCurrentLocation, + StopSyncCurrentLocation, + StopWatchCurrentLocation, +} from '@/actions/Actions' +import { tr } from '@/translation/Translation' +import { Coordinate } from '@/utils' + +export interface CurrentLocationStoreState { + error: string | null + enabled: boolean + syncView: boolean + accuracy: number // meters + heading: number | null + coordinate: Coordinate | null +} + +export default class CurrentLocationStore extends Store { + private watchId: number | null = null + + constructor() { + super({ + error: null, + enabled: false, + syncView: false, + accuracy: 0, + heading: null, + coordinate: null, + }) + } + + reduce(state: CurrentLocationStoreState, action: Action): CurrentLocationStoreState { + // console.log('NOW ', action.constructor.name, action) + // console.log('NOW state ', state) + + if (action instanceof StartWatchCurrentLocation) { + if (state.enabled) { + console.log('NOW cannot start as already started. ID = ' + this.watchId) + return state + } + + this.start() + return { + ...state, + error: null, + enabled: true, + syncView: true, + heading: null, + coordinate: null, + } + } else if (action instanceof StopWatchCurrentLocation) { + this.stop() + return { + ...state, + error: null, + enabled: false, + heading: null, + syncView: false, + } + } else if (action instanceof CurrentLocationError) { + return { + ...state, + enabled: false, + syncView: false, + error: action.error, + heading: null, + coordinate: null, + } + } else if (action instanceof CurrentLocation) { + return { + ...state, + heading: action.heading, + accuracy: action.accuracy, + coordinate: action.coordinate, + } + } else if (action instanceof StartSyncCurrentLocation) { + if (!state.enabled) { + console.warn('cannot start synchronizing view as current location not enabled') + return state + } + + return { + ...state, + error: null, + enabled: true, + syncView: true, + } + } else if (action instanceof StopSyncCurrentLocation) { + if (!state.enabled) return state + + return { + ...state, + error: null, + syncView: false, + } + } + return state + } + + start() { + if (!navigator.geolocation) { + Dispatcher.dispatch(new CurrentLocationError('Geolocation is not supported in this browser')) + this.watchId = null + return + } + + this.watchId = navigator.geolocation.watchPosition( + position => { + Dispatcher.dispatch( + new CurrentLocation( + { lng: position.coords.longitude, lat: position.coords.latitude }, + position.coords.accuracy, + // heading is in degrees from north, clockwise + position.coords.heading + ) + ) + }, + error => { + Dispatcher.dispatch(new CurrentLocationError(tr('searching_location_failed') + ': ' + error.message)) + }, + // DO NOT use e.g. maximumAge: 5_000 -> getCurrentPosition will then never return on mobile firefox!? + { timeout: 300_000, enableHighAccuracy: true } + ) + } + + stop() { + if (this.watchId) navigator.geolocation.clearWatch(this.watchId) + } +} diff --git a/src/stores/MapActionReceiver.ts b/src/stores/MapActionReceiver.ts index a2191be7..6361e3c9 100644 --- a/src/stores/MapActionReceiver.ts +++ b/src/stores/MapActionReceiver.ts @@ -7,7 +7,7 @@ import { RouteRequestSuccess, SetBBox, SetSelectedPath, - ZoomMapToPoint, + MoveMapToPoint, } from '@/actions/Actions' import RouteStore from '@/stores/RouteStore' import { Bbox } from '@/api/graphhopper' @@ -30,7 +30,7 @@ export default class MapActionReceiver implements ActionReceiver { // we estimate the map size to be equal to the window size. we don't know better at this point, because // the map has not been rendered for the first time yet fitBounds(this.map, action.bbox, isSmallScreen, [window.innerWidth, window.innerHeight]) - } else if (action instanceof ZoomMapToPoint) { + } else if (action instanceof MoveMapToPoint) { let zoom = this.map.getView().getZoom() if (zoom == undefined || zoom < 8) zoom = 8 this.map.getView().animate({ diff --git a/src/stores/Stores.ts b/src/stores/Stores.ts index b7ba16d3..2660b7ab 100644 --- a/src/stores/Stores.ts +++ b/src/stores/Stores.ts @@ -7,6 +7,7 @@ import PathDetailsStore from '@/stores/PathDetailsStore' import MapFeatureStore from '@/stores/MapFeatureStore' import SettingsStore from '@/stores/SettingsStore' import POIsStore from '@/stores/POIsStore' +import CurrentLocationStore from '@/stores/CurrentLocationStore' let settingsStore: SettingsStore let queryStore: QueryStore @@ -17,6 +18,7 @@ let mapOptionsStore: MapOptionsStore let pathDetailsStore: PathDetailsStore let mapFeatureStore: MapFeatureStore let poisStore: POIsStore +let currentLocationStore: CurrentLocationStore interface StoresInput { settingsStore: SettingsStore @@ -28,6 +30,7 @@ interface StoresInput { pathDetailsStore: PathDetailsStore mapFeatureStore: MapFeatureStore poisStore: POIsStore + currentLocationStore: CurrentLocationStore } export const setStores = function (stores: StoresInput) { @@ -40,6 +43,7 @@ export const setStores = function (stores: StoresInput) { pathDetailsStore = stores.pathDetailsStore mapFeatureStore = stores.mapFeatureStore poisStore = stores.poisStore + currentLocationStore = stores.currentLocationStore } export const getSettingsStore = () => settingsStore @@ -51,3 +55,4 @@ export const getMapOptionsStore = () => mapOptionsStore export const getPathDetailsStore = () => pathDetailsStore export const getMapFeatureStore = () => mapFeatureStore export const getPOIsStore = () => poisStore +export const getCurrentLocationStore = () => currentLocationStore