Commit e9efc4f6 authored by alain's avatar alain 🐙
Browse files

support multiple units for station layer

parent d8af7e43
import React from 'react'
import DeckGL from '@deck.gl/react';
import { connect } from 'react-redux'
import memoize from "memoize-one";
import StationLayer from "./StationLayer"
......@@ -28,7 +29,7 @@ class DeckLayers extends React.Component {
layers = memoize(
(deckLayers, setModal, zoom, sources) => {
(deckLayers, setModal, zoom, unitStatus, sources) => {
const layers = []
const count = {}
......@@ -51,13 +52,22 @@ class DeckLayers extends React.Component {
switch(layerDefinition.type) {
case "StationLayer":
const unit = (layerDefinition.units ? layerDefinition.units.find(x => x.id === unitStatus[layerDefinition.id]) : null)
const layerData = (unit.conversion ? data.map(d => {
d.mean = unit.conversion(d.mean)
return d
}) : data)
layers.push(
new StationLayer({
id: `${deckLayer}-${sourceName}-layer`,
count: { current: count[sourceName].current, total: count[sourceName].total },
data,
range: layerDefinition.range,
legend: layerDefinition.legend,
data: layerData,
range: unit.range,
legend: unit.legend,
conversion: unit.conversion,
sides: source.sides,
angle: source.angle,
factor: source.factor,
......@@ -66,7 +76,7 @@ class DeckLayers extends React.Component {
extruded: true,
onHover: e => this.setState({
hover: e.object,
hoverContent: <StationTooltip d={e.object} label={layerDefinition.label} unit={ layerDefinition.unit} />,
hoverContent: <StationTooltip d={e.object} label={layerDefinition.label} unit={unit.label} />,
mousePosition: [e.x, e.y]
}),
onClick: e => setModal(<StationInfo clickedObject={e.object} activeLayer={layerDefinition} />)
......@@ -141,7 +151,7 @@ class DeckLayers extends React.Component {
render() {
const { viewport, deckLayers, setModal } = this.props
const { viewport, deckLayers, setModal, unitStatus } = this.props
const { hover, hoverContent, mousePosition } = this.state
// add sources count to memoize function as it doesn't detect changes in deckLayers reliably
......@@ -150,7 +160,7 @@ class DeckLayers extends React.Component {
sources += Object.keys(deckLayers[layer]).length
})
const layers = this.layers(deckLayers, setModal, viewport.zoom, sources)
const layers = this.layers(deckLayers, setModal, viewport.zoom, unitStatus, sources)
return (
<div>
......@@ -161,4 +171,11 @@ class DeckLayers extends React.Component {
}
}
const mapStateToProps = (state) => ({
unitStatus: state.unitStatus
})
DeckLayers = connect(mapStateToProps)(DeckLayers)
export default DeckLayers
\ No newline at end of file
import React from 'react'
const Legend = (props) => {
const { parameter } = props
const { unit } = props
return (
<div className="legend">
<div className="legend-labels">
<span>{parameter.range[0]} {parameter.unit}</span>
<span>{parameter.range[1]} {parameter.unit} </span>
<span>{unit.range[0]} {unit.label}</span>
<span>{unit.range[1]} {unit.label} </span>
</div>
<svg width="236" height="8">
<defs>
<linearGradient id={`legend-${parameter.id}`} x1="0" y1="0" x2="236" y2="8" gradientUnits="userSpaceOnUse">
{ Object.keys(parameter.legend).sort((a,b) => a-b).map(s => {
return <stop key={s} offset={`${(s-parameter.range[0])/(parameter.range[1]-parameter.range[0]) * 100}%`} stopColor={parameter.legend[s].color} />
<linearGradient id={`legend-${unit.id}`} x1="0" y1="0" x2="236" y2="8" gradientUnits="userSpaceOnUse">
{ Object.keys(unit.legend).sort((a,b) => a-b).map(s => {
return <stop key={s} offset={`${(s-unit.range[0])/(unit.range[1]-unit.range[0]) * 100}%`} stopColor={unit.legend[s].color} />
}) }
</linearGradient>
</defs>
<rect x="0" y="0" width="236" height="8" fill={`url(#legend-${parameter.id})`} />
<rect x="0" y="0" width="236" height="8" fill={`url(#legend-${unit.id})`} />
</svg>
<div className="legend-labels">
<span>{ parameter.legend[parameter.range[0]].label }</span>
<span>{ parameter.legend[parameter.range[1]].label }</span>
<span>{ unit.legend[unit.range[0]].label }</span>
<span>{ unit.legend[unit.range[1]].label }</span>
</div>
</div>
)
......
import React from 'react'
import { connect } from 'react-redux'
import { setPanelStatus } from "../actions"
import { setPanelStatus, setUnitStatus } from "../actions"
import { inArray } from "../util/data.js"
......@@ -20,14 +20,24 @@ class PanelLayers extends React.Component {
}
componentDidMount() {
const change = {}
this.props.dataGroups.forEach((dataGroup, i) => {
change[dataGroup.title] = (i === 0 && window.innerHeight > 700 ? true : false)
const { dataGroups } = this.props
const panelStatus = {},
unitStatus = {}
dataGroups.forEach((dataGroup, i) => {
panelStatus[dataGroup.title] = (i === 0 && window.innerHeight > 700 ? true : false)
Object.keys(dataGroup.parameters).forEach(parameter => {
if(dataGroup.parameters[parameter].units) {
unitStatus[parameter] = dataGroup.parameters[parameter].units[0].id
}
})
})
this.props.dispatch(setUnitStatus(unitStatus, false))
setTimeout(() => {
this.props.dispatch(setPanelStatus(change, false))
this.props.dispatch(setPanelStatus(panelStatus, false))
}, 400)
}
......@@ -39,9 +49,16 @@ class PanelLayers extends React.Component {
this.props.dispatch(setPanelStatus(change, change[group]))
}
toggleUnit(parameter, unit) {
const change = {}
change[parameter] = unit
this.props.dispatch(setUnitStatus(change))
}
render() {
const { layers, dataGroups, viewportHeight, panelStatus } = this.props
const { layers, dataGroups, viewportHeight, panelStatus, unitStatus } = this.props
const dataGroupsJSX = dataGroups.map((dataGroup, i) => {
let classes = ["panel-group"]
......@@ -57,6 +74,7 @@ class PanelLayers extends React.Component {
{Object.keys(dataGroup.parameters).map((option) => {
const parameter = dataGroup.parameters[option]
const active = inArray(layers, option)
const unit = (parameter.units ? parameter.units.find(x => x.id === unitStatus[option]) : null)
return (
<li key={option} className={(active ? "active" : "")}>
......@@ -68,9 +86,14 @@ class PanelLayers extends React.Component {
}
this.props.toggleLayer(option)
}}>{parameter.label}</span>
}}><span className="switch"></span>{parameter.label}</span>
{ parameter.units && parameter.units.length > 1 ?
<span className="switch-labeled">{ parameter.units.map(unit => <span key={unit.id} className={(unitStatus[parameter.id] === unit.id ? "active" : null)} onClick={() => {
this.toggleUnit(parameter.id, unit.id)
}}>{ unit.label }</span>) }</span>
: null }
{ parameter.tooltip ? <Tooltip parameter={parameter} /> : null }
{ parameter.legend ? <Legend parameter={parameter} /> : null }
{ unit ? <Legend unit={unit} /> : null }
</li>)
})
}
......@@ -93,7 +116,8 @@ class PanelLayers extends React.Component {
const mapStateToProps = (state) => ({
panelStatus: state.panelStatus
panelStatus: state.panelStatus,
unitStatus: state.unitStatus
})
PanelLayers = connect(mapStateToProps)(PanelLayers)
......
......@@ -5,6 +5,11 @@ export const setPanelStatus = (change, closeOthers) => ({
closeOthers
})
export const setUnitStatus = (change) => ({
type: 'SET_UNIT_STATUS',
change
})
export const setPlaces = places => ({
type: 'SET_PLACES',
places
......
......@@ -59,18 +59,19 @@
// panel layer
.layer-settings-options li {
line-height: 2;
cursor: pointer;
margin: 0.5rem 0;
.parameter-title {
margin-bottom: 0.25rem;
user-select: none;
cursor: pointer;
}
.legend {
max-height: 0;
overflow: hidden;
transition: all 100ms ease;
transition-delay: 100ms;
user-select: none;
}
......@@ -83,6 +84,7 @@
.legend {
max-height: 80px;
transition: all 200ms ease;
transition-delay: 100ms;
}
}
}
......@@ -91,5 +93,5 @@
display: flex;
justify-content: space-between;
font-size: 13px;
text-transform: lowercase;
//text-transform: lowercase;
}
\ No newline at end of file
......@@ -188,3 +188,69 @@ button.button-text {
transform: rotate(180deg) translateY(-2px);
}
}
$switch-size: 0.9em;
.switch {
display: inline-block;
position: relative;
background-color: #dedede;
width: 1.6 * $switch-size;
height: 1 * $switch-size;
vertical-align: middle;
margin-right: 0.4 * $switch-size;
border-radius: 0.5 * $switch-size;
//box-shadow: 0 0 0.25em 0 rgba(0, 0, 0, 0.25);
&:after {
content: "";
position: absolute;
left: 0.15 * $switch-size;
top: 0.15 * $switch-size;
width: 0.7 * $switch-size;
height: 0.7 * $switch-size;
background-color: #777;
border-radius: 0.5 * $switch-size;
transition: all 100ms ease;
}
&:hover:after {
transform: scale(1.1);
}
}
.active .switch {
&:after {
left: 0.75 * $switch-size;
background-color: var(--color-true, #ccc);
}
}
.switch-labeled {
float: right;
font-size: 0.65em;
margin-top: 0.6em;
& > span {
display: inline-block;
background-color: #dedede;
line-height: 1.2em;
padding: 0.3em 0.3em 0.1em;
cursor: pointer;
&.active {
background-color: var(--color-true, #ccc);
color: #FFF;
}
&:first-child {
border-radius: 0.8em 0 0 0.8em;
padding-left: 0.5em;
}
&:last-child {
border-radius: 0 0.8em 0.8em 0;
padding-right: 0.5em;
}
}
}
import { combineReducers } from 'redux'
//import { deepCopy } from 'util/data'
const panelStatus = (state = {}, action) => {
switch (action.type) {
......@@ -17,6 +18,18 @@ const panelStatus = (state = {}, action) => {
}
}
const unitStatus = (state = {}, action) => {
switch (action.type) {
case 'SET_UNIT_STATUS':
//return deepCopy({...state, ...action.change})
return {...state, ...action.change}
default:
return state;
}
}
const location = (state = null, action) => {
switch (action.type) {
case 'SET_PLACES':
......@@ -29,6 +42,7 @@ const location = (state = null, action) => {
const combinedReducer = combineReducers({
panelStatus,
unitStatus,
location
})
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment