feat: fit to bounds when loading track page
This commit is contained in:
parent
38b1b92210
commit
fe3aa7a8f6
54
frontend/package-lock.json
generated
54
frontend/package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.16.3",
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@turf/bbox": "^6.5.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"fomantic-ui-less": "^2.8.8",
|
"fomantic-ui-less": "^2.8.8",
|
||||||
|
@ -2201,6 +2202,37 @@
|
||||||
"react-dom": "^16.0.0 || ^17.0.0"
|
"react-dom": "^16.0.0 || ^17.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@turf/bbox": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@turf/helpers": "^6.5.0",
|
||||||
|
"@turf/meta": "^6.5.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/turf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@turf/helpers": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/turf"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@turf/meta": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@turf/helpers": "^6.5.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/turf"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz",
|
||||||
|
@ -10645,6 +10677,28 @@
|
||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@turf/bbox": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==",
|
||||||
|
"requires": {
|
||||||
|
"@turf/helpers": "^6.5.0",
|
||||||
|
"@turf/meta": "^6.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@turf/helpers": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="
|
||||||
|
},
|
||||||
|
"@turf/meta": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
|
||||||
|
"requires": {
|
||||||
|
"@turf/helpers": "^6.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.0.tgz",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.16.3",
|
"@babel/runtime": "^7.16.3",
|
||||||
|
"@turf/bbox": "^6.5.0",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "^1.4.7",
|
||||||
"fomantic-ui-less": "^2.8.8",
|
"fomantic-ui-less": "^2.8.8",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import ReactMapGl, {AttributionControl, NavigationControl, Layer, Source} from 'react-map-gl'
|
import ReactMapGl, {WebMercatorViewport, AttributionControl, NavigationControl, Layer, Source} from 'react-map-gl'
|
||||||
|
import turfBbox from '@turf/bbox'
|
||||||
|
|
||||||
import {Page} from 'components'
|
import {Page} from 'components'
|
||||||
import {useConfig, Config} from 'config'
|
import {useConfig, Config} from 'config'
|
||||||
|
@ -30,36 +31,66 @@ function useViewportFromUrl() {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const value = React.useMemo(() => parseHash(location.hash), [location.hash])
|
const value = React.useMemo(() => parseHash(location.hash), [location.hash])
|
||||||
const setter = React.useCallback((v) => {
|
const setter = React.useCallback(
|
||||||
|
(v) => {
|
||||||
history.replace({
|
history.replace({
|
||||||
hash: buildHash(v)
|
hash: buildHash(v),
|
||||||
})
|
})
|
||||||
}, [history])
|
},
|
||||||
|
[history]
|
||||||
|
)
|
||||||
return [value || EMPTY_VIEWPORT, setter]
|
return [value || EMPTY_VIEWPORT, setter]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomMap({viewportFromUrl, children, boundsFromJson}: {viewportFromUrl?: boolean, children: React.ReactNode}) {
|
export function CustomMap({
|
||||||
|
viewportFromUrl,
|
||||||
|
children,
|
||||||
|
boundsFromJson,
|
||||||
|
}: {
|
||||||
|
viewportFromUrl?: boolean
|
||||||
|
children: React.ReactNode
|
||||||
|
boundsFromJson: GeoJSON.Geometry
|
||||||
|
}) {
|
||||||
const [viewportState, setViewportState] = React.useState(EMPTY_VIEWPORT)
|
const [viewportState, setViewportState] = React.useState(EMPTY_VIEWPORT)
|
||||||
const [viewportUrl, setViewportUrl] = useViewportFromUrl()
|
const [viewportUrl, setViewportUrl] = useViewportFromUrl()
|
||||||
|
|
||||||
const [viewport, setViewport] = viewportFromUrl ? [viewportUrl, setViewportUrl] : [viewportState, setViewportState]
|
const [viewport, setViewport] = viewportFromUrl ? [viewportUrl, setViewportUrl] : [viewportState, setViewportState]
|
||||||
|
|
||||||
|
|
||||||
const config = useConfig()
|
const config = useConfig()
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (config?.mapHome && viewport.zoom === 0) {
|
if (config?.mapHome && viewport.zoom === 0 && !boundsFromJson) {
|
||||||
setViewport(config.mapHome)
|
setViewport(config.mapHome)
|
||||||
}
|
}
|
||||||
}, [config])
|
}, [config, boundsFromJson])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (boundsFromJson) {
|
||||||
|
const [minX, minY, maxX, maxY] = turfBbox(boundsFromJson)
|
||||||
|
const vp = new WebMercatorViewport({width: 1000, height: 800}).fitBounds(
|
||||||
|
[
|
||||||
|
[minX, minY],
|
||||||
|
[maxX, maxY],
|
||||||
|
],
|
||||||
|
{
|
||||||
|
padding: 20,
|
||||||
|
offset: [0, -100],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
setViewport(_.pick(vp, ['zoom', 'latitude', 'longitude']))
|
||||||
|
}
|
||||||
|
}, [boundsFromJson])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMapGl mapStyle={basemap} width="100%" height="100%" onViewportChange={setViewport} {...viewport}>
|
<ReactMapGl mapStyle={basemap} width="100%" height="100%" onViewportChange={setViewport} {...viewport}>
|
||||||
<AttributionControl style={{right: 0, bottom: 0}} customAttribution={[
|
<AttributionControl
|
||||||
|
style={{right: 0, bottom: 0}}
|
||||||
|
customAttribution={[
|
||||||
'<a href="https://openstreetmap.org/copyright" target="_blank" rel="nofollow noopener noreferrer">© OpenStreetMap contributors</a>',
|
'<a href="https://openstreetmap.org/copyright" target="_blank" rel="nofollow noopener noreferrer">© OpenStreetMap contributors</a>',
|
||||||
'<a href="https://openmaptiles.org/" target="_blank" rel="nofollow noopener noreferrer">© OpenMapTiles</a>',
|
'<a href="https://openmaptiles.org/" target="_blank" rel="nofollow noopener noreferrer">© OpenMapTiles</a>',
|
||||||
'<a href="https://openbikesensor.org/" target="_blank" rel="nofollow noopener noreferrer">© OpenBikeSensor</a>',
|
'<a href="https://openbikesensor.org/" target="_blank" rel="nofollow noopener noreferrer">© OpenBikeSensor</a>',
|
||||||
]} />
|
]}
|
||||||
<NavigationControl style={{left: 0, top: 0}} />
|
/>
|
||||||
|
<NavigationControl style={{left: 10, top: 10}} />
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</ReactMapGl>
|
</ReactMapGl>
|
||||||
|
@ -70,7 +101,7 @@ export function RoadsMap() {
|
||||||
const {obsMapSource} = useConfig() || {}
|
const {obsMapSource} = useConfig() || {}
|
||||||
|
|
||||||
if (!obsMapSource) {
|
if (!obsMapSource) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default function TrackMap({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={props.style}>
|
<div style={props.style}>
|
||||||
<CustomMap>
|
<CustomMap boundsFromJson={trackData.track}>
|
||||||
{showTrack && (
|
{showTrack && (
|
||||||
<Source id="route" type="geojson" data={trackData.track}>
|
<Source id="route" type="geojson" data={trackData.track}>
|
||||||
<Layer id="route" {...trackLayer} />
|
<Layer id="route" {...trackLayer} />
|
||||||
|
|
Loading…
Reference in a new issue