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 = {
|
||||
type: 'line',
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
import React from 'react'
|
||||
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'
|
||||
|
||||
const BASEMAP_STYLE_OPTIONS = [
|
||||
{key: 'positron', value: 'positron', text: 'Positron'},
|
||||
{key: 'bright', value: 'bright', text: 'OSM Bright'},
|
||||
{value: 'positron', key: 'positron', text: 'Positron'},
|
||||
{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 (
|
||||
<div>
|
||||
<List>
|
||||
|
@ -18,7 +28,22 @@ function LayerSidebar({mapConfig, setBasemapStyle}) {
|
|||
<Select
|
||||
options={BASEMAP_STYLE_OPTIONS}
|
||||
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>
|
||||
|
|
|
@ -1,18 +1,72 @@
|
|||
import React, {useState, useCallback} from 'react'
|
||||
import React, {useState, useCallback, useMemo} from 'react'
|
||||
import _ from 'lodash'
|
||||
import {Sidebar, Button} from 'semantic-ui-react'
|
||||
import {Button} from 'semantic-ui-react'
|
||||
import {Layer, Source} from 'react-map-gl'
|
||||
import produce from 'immer'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {Page, Map} from 'components'
|
||||
import {useConfig} from 'config'
|
||||
|
||||
import {roadsLayer} from '../../mapstyles'
|
||||
import {colorByDistance} from 'mapstyles'
|
||||
|
||||
import RoadInfo from './RoadInfo'
|
||||
import LayerSidebar from './LayerSidebar'
|
||||
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 [clickLocation, setClickLocation] = useState<{longitude: number; latitude: number} | null>(null)
|
||||
|
||||
|
@ -33,6 +87,10 @@ export default function MapPage() {
|
|||
|
||||
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) {
|
||||
return null
|
||||
}
|
||||
|
@ -55,7 +113,8 @@ export default function MapPage() {
|
|||
onClick={() => setLayerSidebar(layerSidebar ? false : true)}
|
||||
/>
|
||||
<Source id="obs" {...obsMapSource}>
|
||||
<Layer {...roadsLayer} />
|
||||
{showUntagged && <Layer key={untaggedRoadsLayer.id} {...untaggedRoadsLayer} />}
|
||||
<Layer key={roadsLayer.id} {...roadsLayer} />
|
||||
</Source>
|
||||
|
||||
<RoadInfo {...{clickLocation}} />
|
||||
|
@ -65,3 +124,5 @@ export default function MapPage() {
|
|||
</Page>
|
||||
)
|
||||
}
|
||||
|
||||
export default connect((state) => ({mapConfig: state.mapConfig}))(MapPage)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import produce from 'immer'
|
||||
import _ from 'lodash'
|
||||
|
||||
type BaseMapStyle = 'positron' | 'bright'
|
||||
type MapConfigState = {
|
||||
|
@ -13,18 +14,15 @@ const initialState: MapConfigState = {
|
|||
},
|
||||
}
|
||||
|
||||
export function setBasemapStyle(style: BaseMapStyle) {
|
||||
return {type: 'MAPCONFIG.SET_BASEMAP_STYLE', payload: {style}}
|
||||
export function setMapConfigFlag(flag: string, value: unknown) {
|
||||
return {type: 'MAPCONFIG.SET_FLAG', payload: {flag, value}}
|
||||
}
|
||||
|
||||
export default function mapConfigReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case 'MAPCONFIG.SET_BASEMAP_STYLE':
|
||||
case 'MAPCONFIG.SET_FLAG':
|
||||
return produce(state, draft => {
|
||||
if (!draft.baseMap) {
|
||||
draft.baseMap = {}
|
||||
}
|
||||
draft.baseMap.style = action.payload.style
|
||||
_.set(draft, action.payload.flag, action.payload.value)
|
||||
})
|
||||
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue