Make road colors and untagged roads display configurable
This commit is contained in:
parent
f0c715bcbc
commit
6b38540586
|
@ -27,51 +27,6 @@ export function colorByDistance(attribute = 'distance_overtaker_mean', fallback
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const roadsLayer = {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const trackLayer = {
|
export const trackLayer = {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import {List, Select} from 'semantic-ui-react'
|
import {List, Select, Header, Checkbox} from 'semantic-ui-react'
|
||||||
|
|
||||||
import * as mapConfigActions from 'reducers/mapConfig'
|
import * as mapConfigActions from 'reducers/mapConfig'
|
||||||
|
|
||||||
const BASEMAP_STYLE_OPTIONS = [
|
const BASEMAP_STYLE_OPTIONS = [
|
||||||
{key: 'positron', value: 'positron', text: 'Positron'},
|
{value: 'positron', key: 'positron', text: 'Positron'},
|
||||||
{key: 'bright', value: 'bright', text: 'OSM Bright'},
|
{value: 'bright', key: 'bright', text: 'OSM Bright'},
|
||||||
]
|
]
|
||||||
|
|
||||||
function LayerSidebar({mapConfig, setBasemapStyle}) {
|
const ROAD_ATTRIBUTE_OPTIONS = [
|
||||||
|
{value: 'distance_overtaker_mean', key: 'distance_overtaker_mean', text: 'Overtaker distance mean'},
|
||||||
|
{value: 'distance_overtaker_min', key: 'distance_overtaker_min', text: 'Overtaker distance minimum'},
|
||||||
|
{value: 'distance_overtaker_max', key: 'distance_overtaker_max', text: 'Overtaker distance maximum'},
|
||||||
|
{value: 'distance_overtaker_median', key: 'distance_overtaker_median', text: 'Overtaker distance median'},
|
||||||
|
{value: 'overtaking_event_count', key: 'overtaking_event_count', text: 'Event count'},
|
||||||
|
]
|
||||||
|
|
||||||
|
function LayerSidebar({mapConfig, setMapConfigFlag}) {
|
||||||
|
const showUntagged = mapConfig?.obsRoads?.showUntagged ?? true
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<List>
|
<List>
|
||||||
|
@ -18,7 +28,22 @@ function LayerSidebar({mapConfig, setBasemapStyle}) {
|
||||||
<Select
|
<Select
|
||||||
options={BASEMAP_STYLE_OPTIONS}
|
options={BASEMAP_STYLE_OPTIONS}
|
||||||
value={mapConfig?.baseMap?.style ?? 'positron'}
|
value={mapConfig?.baseMap?.style ?? 'positron'}
|
||||||
onChange={(_e, {value}) => setBasemapStyle(value)}
|
onChange={(_e, {value}) => setMapConfigFlag('baseMap.style', value)}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
<Header as='h4' dividing>OBS Roads</Header>
|
||||||
|
<List.Item>
|
||||||
|
<Checkbox label='Show untagged roads' checked={showUntagged}
|
||||||
|
onChange={() => setMapConfigFlag('obsRoads.showUntagged', !showUntagged)}
|
||||||
|
/>
|
||||||
|
</List.Item>
|
||||||
|
<List.Item>
|
||||||
|
<List.Header style={{marginBlock: 8}}>Color based on</List.Header>
|
||||||
|
<Select
|
||||||
|
fluid
|
||||||
|
options={ROAD_ATTRIBUTE_OPTIONS}
|
||||||
|
value={mapConfig?.obsRoads?.attribute ?? 'distance_overtaker_mean'}
|
||||||
|
onChange={(_e, {value}) => setMapConfigFlag('obsRoads.attribute', value)}
|
||||||
/>
|
/>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
|
|
|
@ -1,18 +1,72 @@
|
||||||
import React, {useState, useCallback} from 'react'
|
import React, {useState, useCallback, useMemo} from 'react'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import {Sidebar, Button} from 'semantic-ui-react'
|
import {Button} from 'semantic-ui-react'
|
||||||
import {Layer, Source} from 'react-map-gl'
|
import {Layer, Source} from 'react-map-gl'
|
||||||
|
import produce from 'immer'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
|
||||||
import {Page, Map} from 'components'
|
import {Page, Map} from 'components'
|
||||||
import {useConfig} from 'config'
|
import {useConfig} from 'config'
|
||||||
|
import {colorByDistance} from 'mapstyles'
|
||||||
import {roadsLayer} from '../../mapstyles'
|
|
||||||
|
|
||||||
import RoadInfo from './RoadInfo'
|
import RoadInfo from './RoadInfo'
|
||||||
import LayerSidebar from './LayerSidebar'
|
import LayerSidebar from './LayerSidebar'
|
||||||
import styles from './styles.module.less'
|
import styles from './styles.module.less'
|
||||||
|
|
||||||
export default function MapPage() {
|
|
||||||
|
const untaggedRoadsLayer = {
|
||||||
|
id: 'obs_roads_untagged',
|
||||||
|
type: 'line',
|
||||||
|
source: 'obs',
|
||||||
|
'source-layer': 'obs_roads',
|
||||||
|
filter: ['!', ['to-boolean', ['get', 'distance_overtaker_mean']]],
|
||||||
|
layout: {
|
||||||
|
'line-cap': 'round',
|
||||||
|
'line-join': 'round',
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'line-width': [
|
||||||
|
'interpolate',
|
||||||
|
['exponential', 1.5],
|
||||||
|
['zoom'],
|
||||||
|
12,
|
||||||
|
2,
|
||||||
|
17,
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
'line-color': "#ABC",
|
||||||
|
'line-opacity': [
|
||||||
|
'interpolate',
|
||||||
|
['linear'],
|
||||||
|
['zoom'],
|
||||||
|
14,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
'line-offset': [
|
||||||
|
'interpolate',
|
||||||
|
['exponential', 1.5],
|
||||||
|
['zoom'],
|
||||||
|
12,
|
||||||
|
['get', 'offset_direction'],
|
||||||
|
19,
|
||||||
|
['*', ['get', 'offset_direction'], 8],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
minzoom: 12,
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRoadsLayer = attribute => produce(untaggedRoadsLayer, draft => {
|
||||||
|
draft.id = 'obs_roads_normal'
|
||||||
|
draft.filter = draft.filter[1] // remove '!'
|
||||||
|
draft.paint['line-width'][6] = 6
|
||||||
|
draft.paint['line-color'] = colorByDistance(attribute)
|
||||||
|
draft.paint['line-opacity'][3] = 12
|
||||||
|
draft.paint['line-opacity'][5] = 13
|
||||||
|
})
|
||||||
|
|
||||||
|
function MapPage({mapConfig}) {
|
||||||
const {obsMapSource} = useConfig() || {}
|
const {obsMapSource} = useConfig() || {}
|
||||||
const [clickLocation, setClickLocation] = useState<{longitude: number; latitude: number} | null>(null)
|
const [clickLocation, setClickLocation] = useState<{longitude: number; latitude: number} | null>(null)
|
||||||
|
|
||||||
|
@ -33,6 +87,10 @@ export default function MapPage() {
|
||||||
|
|
||||||
const [layerSidebar, setLayerSidebar] = useState(true)
|
const [layerSidebar, setLayerSidebar] = useState(true)
|
||||||
|
|
||||||
|
const showUntagged = mapConfig?.obsRoads?.showUntagged ?? true
|
||||||
|
const roadsLayerColorAttribute = mapConfig?.obsRoads?.attribute ?? 'distance_overtaker_mean'
|
||||||
|
const roadsLayer = useMemo(() => getRoadsLayer(roadsLayerColorAttribute ), [roadsLayerColorAttribute ])
|
||||||
|
|
||||||
if (!obsMapSource) {
|
if (!obsMapSource) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -55,7 +113,8 @@ export default function MapPage() {
|
||||||
onClick={() => setLayerSidebar(layerSidebar ? false : true)}
|
onClick={() => setLayerSidebar(layerSidebar ? false : true)}
|
||||||
/>
|
/>
|
||||||
<Source id="obs" {...obsMapSource}>
|
<Source id="obs" {...obsMapSource}>
|
||||||
<Layer {...roadsLayer} />
|
{showUntagged && <Layer key={untaggedRoadsLayer.id} {...untaggedRoadsLayer} />}
|
||||||
|
<Layer key={roadsLayer.id} {...roadsLayer} />
|
||||||
</Source>
|
</Source>
|
||||||
|
|
||||||
<RoadInfo {...{clickLocation}} />
|
<RoadInfo {...{clickLocation}} />
|
||||||
|
@ -65,3 +124,5 @@ export default function MapPage() {
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect((state) => ({mapConfig: state.mapConfig}))(MapPage)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
type BaseMapStyle = 'positron' | 'bright'
|
type BaseMapStyle = 'positron' | 'bright'
|
||||||
type MapConfigState = {
|
type MapConfigState = {
|
||||||
|
@ -13,18 +14,15 @@ const initialState: MapConfigState = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setBasemapStyle(style: BaseMapStyle) {
|
export function setMapConfigFlag(flag: string, value: unknown) {
|
||||||
return {type: 'MAPCONFIG.SET_BASEMAP_STYLE', payload: {style}}
|
return {type: 'MAPCONFIG.SET_FLAG', payload: {flag, value}}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function mapConfigReducer(state = initialState, action) {
|
export default function mapConfigReducer(state = initialState, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'MAPCONFIG.SET_BASEMAP_STYLE':
|
case 'MAPCONFIG.SET_FLAG':
|
||||||
return produce(state, draft => {
|
return produce(state, draft => {
|
||||||
if (!draft.baseMap) {
|
_.set(draft, action.payload.flag, action.payload.value)
|
||||||
draft.baseMap = {}
|
|
||||||
}
|
|
||||||
draft.baseMap.style = action.payload.style
|
|
||||||
})
|
})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue