diff --git a/mapper/src/App.js b/mapper/src/App.js index 645870f..a93dad9 100644 --- a/mapper/src/App.js +++ b/mapper/src/App.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import * as leaflet from 'leaflet'; -import { MapContainer, Polyline, TileLayer, useMap } from 'react-leaflet'; +import { MapContainer, Polyline, TileLayer, useMap, useMapEvents } from 'react-leaflet'; import Datetime from 'react-datetime'; import 'react-datetime/css/react-datetime.css'; import axios from 'axios'; @@ -71,14 +71,39 @@ function useSensor(measurement, name, end, duration) { }; +function ChangeView({ center, zoom }) { + const map = useMap(); + map.setView(center, zoom); + return null; +} -function Map({end, duration, slider}) { +function MapEvents({ onMapChange }) { + const map = useMapEvents({ + moveend: () => { + const center = map.getCenter(); + onMapChange({ + zoom: map.getZoom(), + center: [center.lat, center.lng], + }); + }, + }); + return null; +} + +function Map({end, duration, slider, mapState, setMapState}) { const [data, loading] = useSensor('owntracks', 'OwnTracks', end, duration); const range = parseSlider(end, duration, slider); const coords = data.length ? data.filter(x => !range || (x.time >= range[0] && x.time <= range[1])).map(({ lat, lon }) => [lat, lon]).filter(([lat, lon]) => lat !== null || lon !== null) : []; + useEffect(() => { + // If URL provides no center, set it from the data once it's loaded. + if (mapState.center === null && coords.length > 0) { + setMapState(prev => ({ ...prev, center: coords[coords.length - 1] })); + } + }, [coords, mapState.center, setMapState]); + const handleSubmit = (e) => { e.preventDefault(); const api_key = e.target[0].value; @@ -91,13 +116,17 @@ function Map({end, duration, slider}) {
Loading...
: coords.length ? -No data
@@ -284,6 +313,9 @@ function App() { const initialDurationId = params.get('duration'); const initialEndTimestamp = params.get('end'); const initialSliderValue = params.get('slider'); + const initialLat = params.get('lat'); + const initialLng = params.get('lng'); + const initialZoom = params.get('zoom'); const initialDuration = (initialDurationId && durations[parseInt(initialDurationId, 10)]) ? durations[parseInt(initialDurationId, 10)] : durations[0]; const initialEnd = initialEndTimestamp ? moment.unix(initialEndTimestamp) : moment(); @@ -292,6 +324,10 @@ function App() { const [duration, setDuration] = useState(initialDuration); const [end, setEnd] = useState(initialEnd); const [slider, setSlider] = useState(initialSlider); + const [mapState, setMapState] = useState({ + center: (initialLat && initialLng) ? [parseFloat(initialLat), parseFloat(initialLng)] : null, + zoom: initialZoom ? parseInt(initialZoom, 10) : 13, + }); useEffect(() => { const handler = setTimeout(() => { @@ -299,13 +335,18 @@ function App() { params.set('duration', duration.id); params.set('end', end.unix()); params.set('slider', slider.join(',')); + if (mapState.center) { + params.set('lat', mapState.center[0].toFixed(5)); + params.set('lng', mapState.center[1].toFixed(5)); + params.set('zoom', mapState.zoom); + } window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`); }, 500); return () => { clearTimeout(handler); }; - }, [duration, end, slider]); + }, [duration, end, slider, mapState]); return (