make mapstyles simpler
This commit is contained in:
parent
15dfb2dc3b
commit
38b1b92210
|
@ -3,6 +3,8 @@ import _ from 'lodash'
|
||||||
import bright from './bright.json'
|
import bright from './bright.json'
|
||||||
import positron from './positron.json'
|
import positron from './positron.json'
|
||||||
|
|
||||||
|
export {bright, positron}
|
||||||
|
|
||||||
export function colorByDistance(attribute = 'distance_overtaker_mean', fallback = '#ABC') {
|
export function colorByDistance(attribute = 'distance_overtaker_mean', fallback = '#ABC') {
|
||||||
return [
|
return [
|
||||||
'case',
|
'case',
|
||||||
|
@ -24,62 +26,58 @@ export function colorByDistance(attribute = 'distance_overtaker_mean', fallback
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRoadsStyle(style, mapSource) {
|
export const roadsLayer = {
|
||||||
style.sources.obs = mapSource
|
id: 'obs',
|
||||||
|
type: 'line',
|
||||||
|
source: 'obs',
|
||||||
|
'source-layer': 'obs_roads',
|
||||||
|
layout: {
|
||||||
|
'line-cap': 'round',
|
||||||
|
'line-join': 'round',
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-width': [
|
||||||
|
'interpolate',
|
||||||
|
['exponential', 1.5],
|
||||||
|
['zoom'],
|
||||||
|
12,
|
||||||
|
2,
|
||||||
|
17,
|
||||||
|
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 2, 6],
|
||||||
|
],
|
||||||
|
'line-color': colorByDistance(),
|
||||||
|
'line-opacity': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['zoom'],
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
13,
|
||||||
|
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 0, 1],
|
||||||
|
14,
|
||||||
|
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 0, 1],
|
||||||
|
15,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'line-offset': [
|
||||||
|
'interpolate',
|
||||||
|
['exponential', 1.5],
|
||||||
|
['zoom'],
|
||||||
|
12,
|
||||||
|
['get', 'offset_direction'],
|
||||||
|
19,
|
||||||
|
['*', ['get', 'offset_direction'], 8],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
minzoom: 12,
|
||||||
|
}
|
||||||
|
|
||||||
// insert before "road_oneway" layer
|
export const trackLayer = {
|
||||||
let idx = style.layers.findIndex((l) => l.id === 'road_oneway')
|
type: 'line',
|
||||||
if (idx === -1) {
|
paint: {
|
||||||
idx = style.layers.length
|
'line-width': ['interpolate', ['linear'], ['zoom'], 14, 2, 17, 5],
|
||||||
|
'line-color': '#F06292',
|
||||||
}
|
}
|
||||||
style.layers.splice(idx, 0, {
|
|
||||||
id: 'obs',
|
|
||||||
type: 'line',
|
|
||||||
source: 'obs',
|
|
||||||
'source-layer': 'obs_roads',
|
|
||||||
layout: {
|
|
||||||
'line-cap': 'round',
|
|
||||||
'line-join': 'round',
|
|
||||||
},
|
|
||||||
paint: {
|
|
||||||
'line-width': [
|
|
||||||
'interpolate',
|
|
||||||
['exponential', 1.5],
|
|
||||||
['zoom'],
|
|
||||||
12,
|
|
||||||
2,
|
|
||||||
17,
|
|
||||||
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 2, 6],
|
|
||||||
],
|
|
||||||
'line-color': colorByDistance(),
|
|
||||||
'line-opacity': [
|
|
||||||
'interpolate',
|
|
||||||
['linear'],
|
|
||||||
['zoom'],
|
|
||||||
12,
|
|
||||||
0,
|
|
||||||
13,
|
|
||||||
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 0, 1],
|
|
||||||
14,
|
|
||||||
['case', ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]], 0, 1],
|
|
||||||
15,
|
|
||||||
1,
|
|
||||||
],
|
|
||||||
'line-offset': [
|
|
||||||
'interpolate',
|
|
||||||
['exponential', 1.5],
|
|
||||||
['zoom'],
|
|
||||||
12,
|
|
||||||
['get', 'offset_direction'],
|
|
||||||
19,
|
|
||||||
['*', ['get', 'offset_direction'], 8],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
minzoom: 12,
|
|
||||||
})
|
|
||||||
|
|
||||||
return style
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const basemap = positron
|
export const basemap = positron
|
||||||
export const obsRoads = (sourceUrl) => addRoadsStyle(_.cloneDeep(basemap), sourceUrl)
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import ReactMapGl, {AttributionControl, NavigationControl, Layer, Source} from 'react-map-gl'
|
||||||
|
|
||||||
import {Page} from 'components'
|
import {Page} from 'components'
|
||||||
import {useConfig, Config} from 'config'
|
import {useConfig, Config} from 'config'
|
||||||
import {useHistory, useLocation} from 'react-router-dom'
|
import {useHistory, useLocation} from 'react-router-dom'
|
||||||
import ReactMapGl, {AttributionControl } from 'react-map-gl'
|
|
||||||
|
|
||||||
import styles from './MapPage.module.less'
|
import styles from './MapPage.module.less'
|
||||||
|
|
||||||
import {obsRoads, basemap } from '../mapstyles'
|
import {roadsLayer, basemap} from '../mapstyles'
|
||||||
|
|
||||||
function parseHash(v) {
|
function parseHash(v) {
|
||||||
if (!v) return null
|
if (!v) return null
|
||||||
|
@ -37,52 +38,47 @@ function useViewportFromUrl() {
|
||||||
return [value || EMPTY_VIEWPORT, setter]
|
return [value || EMPTY_VIEWPORT, setter]
|
||||||
}
|
}
|
||||||
|
|
||||||
function CustomMapInner({viewportFromUrl, mapSource, config, mode, children}: {viewportFromUrl?: boolean, mapSource: string, config: Config, mode?: 'roads'}) {
|
export function CustomMap({viewportFromUrl, children, boundsFromJson}: {viewportFromUrl?: boolean, children: React.ReactNode}) {
|
||||||
const mapStyle = React.useMemo(() => {
|
|
||||||
if (mode === 'roads') {
|
|
||||||
return mapSource && obsRoads(mapSource)
|
|
||||||
} else {
|
|
||||||
return basemap
|
|
||||||
}
|
|
||||||
}, [mapSource, mode])
|
|
||||||
|
|
||||||
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()
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (config?.mapHome && viewport.zoom === 0) {
|
if (config?.mapHome && viewport.zoom === 0) {
|
||||||
setViewport(config.mapHome)
|
setViewport(config.mapHome)
|
||||||
}
|
}
|
||||||
}, [config])
|
}, [config])
|
||||||
|
|
||||||
if (!mapStyle) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactMapGl mapStyle={mapStyle} 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}} />
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</ReactMapGl>
|
</ReactMapGl>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CustomMap(props) {
|
export function RoadsMap() {
|
||||||
const config = useConfig() || {}
|
const {obsMapSource} = useConfig() || {}
|
||||||
if (!config) return null;
|
|
||||||
const {obsMapSource: mapSource} = config
|
|
||||||
|
|
||||||
if (!mapSource) return null;
|
if (!obsMapSource) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomMapInner {...{mapSource, config}} {...props} />
|
<CustomMap viewportFromUrl>
|
||||||
|
<Source id="obs" {...obsMapSource}>
|
||||||
|
<Layer {...roadsLayer} />
|
||||||
|
</Source>
|
||||||
|
</CustomMap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +86,7 @@ export default function MapPage() {
|
||||||
return (
|
return (
|
||||||
<Page fullScreen>
|
<Page fullScreen>
|
||||||
<div className={styles.mapContainer}>
|
<div className={styles.mapContainer}>
|
||||||
<CustomMap mode='roads' viewportFromUrl />
|
<RoadsMap />
|
||||||
</div>
|
</div>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {Source, Layer} from 'react-map-gl'
|
||||||
import type {TrackData} from 'types'
|
import type {TrackData} from 'types'
|
||||||
import {CustomMap} from '../MapPage'
|
import {CustomMap} from '../MapPage'
|
||||||
|
|
||||||
import {colorByDistance} from '../../mapstyles'
|
import {colorByDistance, trackLayer} from '../../mapstyles'
|
||||||
|
|
||||||
export default function TrackMap({
|
export default function TrackMap({
|
||||||
trackData,
|
trackData,
|
||||||
|
@ -27,14 +27,7 @@ export default function TrackMap({
|
||||||
<CustomMap>
|
<CustomMap>
|
||||||
{showTrack && (
|
{showTrack && (
|
||||||
<Source id="route" type="geojson" data={trackData.track}>
|
<Source id="route" type="geojson" data={trackData.track}>
|
||||||
<Layer
|
<Layer id="route" {...trackLayer} />
|
||||||
id="route"
|
|
||||||
type="line"
|
|
||||||
paint={{
|
|
||||||
'line-width': ['interpolate', ['linear'], ['zoom'], 14, 2, 17, 5],
|
|
||||||
'line-color': '#F06292',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Source>
|
</Source>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue