Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error bar #975

Merged
merged 23 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/client/app/actions/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export function changeChartToRender(chartType: t.ChartTypes): t.ChangeChartToRen
export function toggleAreaNormalization(): t.ToggleAreaNormalizationAction {
return { type: ActionType.ToggleAreaNormalization };
}
export function toggleShowMinMax(): t.ToggleShowMinMaxAction {
return { type: ActionType.ToggleShowMinMax }
}

export function changeBarStacking(): t.ChangeBarStackingAction {
return { type: ActionType.ChangeBarStacking };
Expand Down Expand Up @@ -213,6 +216,7 @@ export interface LinkOptions {
sliderRange?: TimeInterval;
toggleAreaNormalization?: boolean;
areaUnit?: string;
toggleMinMax?: boolean;
toggleBarStacking?: boolean;
comparePeriod?: ComparePeriod;
compareSortingOrder?: SortingOrder;
Expand All @@ -231,7 +235,7 @@ export function changeOptionsFromLink(options: LinkOptions) {
const dispatchSecond: Array<Thunk | t.ChangeChartToRenderAction | t.ChangeBarStackingAction |
t.ChangeGraphZoomAction | t.ChangeCompareSortingOrderAction | t.ToggleOptionsVisibility |
m.UpdateSelectedMapAction | t.UpdateLineGraphRate | t.ToggleAreaNormalizationAction |
t.UpdateSelectedAreaUnitAction> = [];
t.UpdateSelectedAreaUnitAction | t.ToggleShowMinMaxAction> = [];
/* eslint-enable @typescript-eslint/indent */

if (options.meterIDs) {
Expand Down Expand Up @@ -266,7 +270,9 @@ export function changeOptionsFromLink(options: LinkOptions) {
}
if (options.areaUnit) {
dispatchSecond.push(updateSelectedAreaUnit(options.areaUnit as AreaUnitType));

}
if (options.toggleMinMax) {
dispatchSecond.push(toggleShowMinMax());
}
if (options.toggleBarStacking) {
dispatchSecond.push(changeBarStacking());
Expand Down
42 changes: 42 additions & 0 deletions src/client/app/components/ErrorBarComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import * as React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { State } from '../types/redux/state';
import { toggleShowMinMax } from '../actions/graph';
import translate from '../utils/translate';
import TooltipMarkerComponent from './TooltipMarkerComponent';

/**
* React Component rendering an Error Bar checkbox for toggle operation.
* @returns Error Bar checkbox with tooltip and label
*/
export default function ErrorBarComponent() {
const dispatch = useDispatch();
const graphState = useSelector((state: State) => state.graph);

/**
* Dispatches an action to toggle visibility of min/max lines on checkbox interaction
*/
const handleToggleShowMinMax = () => {
dispatch(toggleShowMinMax());
}

return (
<div className='checkbox'>
<input
type='checkbox'
style={{ marginRight: '10px' }}
onChange={() => handleToggleShowMinMax()}
checked={graphState.showMinMax}
id='errorBar'
/>
<label htmlFor='errorBar'>
{translate('error.bar')}
</label>
<TooltipMarkerComponent page='home' helpTextId='help.home.error.bar' />
</div>
);
}
6 changes: 4 additions & 2 deletions src/client/app/components/ExportComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export default function ExportComponent() {
const adminState = useSelector((state: State) => state.admin);
// readings state
const readingsState = useSelector((state: State) => state.readings);
// error bar state
const errorBarState = useSelector((state: State) => state.graph.showMinMax);
// Time range of graphic
const timeInterval = graphState.timeInterval;

Expand Down Expand Up @@ -84,7 +86,7 @@ export default function ExportComponent() {
const sortedReadings = _.sortBy(readings, item => item.startTimestamp, 'asc');
// Identifier for current meter.
const meterIdentifier = metersState[meterId].identifier;
graphExport(sortedReadings, meterIdentifier, unitLabel, unitIdentifier, chartName, scaling);
graphExport(sortedReadings, meterIdentifier, unitLabel, unitIdentifier, chartName, scaling, errorBarState);
}
}
}
Expand Down Expand Up @@ -324,4 +326,4 @@ export default function ExportComponent() {
</div> : ''}
</>
);
}
}
6 changes: 6 additions & 0 deletions src/client/app/components/RouteComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface RouteProps {
role: UserRole;
renderOnce: boolean;
areaNormalization: boolean;
minMax: boolean;
changeOptionsFromLink(options: LinkOptions): Promise<any[]>;
clearCurrentUser(): any;
changeRenderOnce(): any;
Expand Down Expand Up @@ -214,6 +215,11 @@ export default class RouteComponent extends React.Component<RouteProps> {
case 'areaUnit':
options.areaUnit = info;
break;
case 'minMax':
if (this.props.minMax.toString() !== info) {
options.toggleMinMax = true;
}
break;
case 'comparePeriod':
options.comparePeriod = validateComparePeriod(info);
break;
Expand Down
1 change: 1 addition & 0 deletions src/client/app/components/TooltipHelpComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export default class TooltipHelpComponent extends React.Component<TooltipHelpPro
'help.home.chart.select': { link: `${HELP_URL}/graphType.html` },
'help.home.compare.interval.tip': { link: `${HELP_URL}/compareGraphic.html#usage` },
'help.home.compare.sort.tip': { link: `${HELP_URL}/compareGraphic.html#usage` },
'help.home.error.bar': { link: `${HELP_URL}/errorBar.html#usage` },
'help.home.export.graph.data': { link: `${HELP_URL}/export.html` },
'help.home.hide.or.show.options': { link: `${HELP_URL}/hideOptions.html` },
'help.home.map.interval.tip': { link: `${HELP_URL}/mapGraphic.html#usage` },
Expand Down
10 changes: 7 additions & 3 deletions src/client/app/components/UIOptionsComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import MapChartSelectComponent from './MapChartSelectComponent';
import ReactTooltip from 'react-tooltip';
import GraphicRateMenuComponent from './GraphicRateMenuComponent';
import AreaUnitSelectComponent from './AreaUnitSelectComponent';
import ErrorBarComponent from './ErrorBarComponent';

const Slider = createSliderWithTooltip(sliderWithoutTooltips);

Expand Down Expand Up @@ -88,8 +89,11 @@ class UIOptionsComponent extends React.Component<UIOptionsPropsWithIntl, UIOptio
<ChartSelectComponent />
<ChartDataSelectComponent />
<GraphicRateMenuComponent />
<AreaUnitSelectComponent/>

<AreaUnitSelectComponent />
{/* Controls error bar, specifically for the line chart. */}
{this.props.chartToRender === ChartTypes.line &&
<ErrorBarComponent />
}
{/* Controls specific to the bar chart. */}
{this.props.chartToRender === ChartTypes.bar &&
<div>
Expand Down Expand Up @@ -327,4 +331,4 @@ class UIOptionsComponent extends React.Component<UIOptionsPropsWithIntl, UIOptio
}
}

export default injectIntl(UIOptionsComponent);
export default injectIntl(UIOptionsComponent);
1 change: 1 addition & 0 deletions src/client/app/containers/ChartLinkContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function mapStateToProps(state: State) {
linkText += `&unitID=${unitID.toString()}`;
linkText += `&rate=${state.graph.lineGraphRate.label.toString()},${state.graph.lineGraphRate.rate.toString()}`;
linkText += `&areaUnit=${state.graph.selectedAreaUnit}&areaNormalization=${state.graph.areaNormalization}`;
linkText += `&minMax=${state.graph.showMinMax}`;
return {
linkText,
chartType
Expand Down
26 changes: 24 additions & 2 deletions src/client/app/containers/LineChartContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ function mapStateToProps(state: State) {
// Create two arrays for the x and y values. Fill the array with the data from the line readings
const xData: string[] = [];
const yData: number[] = [];
// Create two arrays to store the min and max values of y-axis data points
const yMinData: number[] = [];
const yMaxData: number[] = [];
const hoverText: string[] = [];
const readings = _.values(readingsData.readings);
// The scaling is the factor to change the reading by. It divides by the area while will be 1 if no scaling by area.
// const scaling = currentSelectedRate.rate / meterArea;
readings.forEach(reading => {
// As usual, we want to interpret the readings in UTC. We lose the timezone as this as the start/endTimestamp
// are equivalent to Unix timestamp in milliseconds.
Expand All @@ -81,7 +83,20 @@ function mapStateToProps(state: State) {
xData.push(timeReading.format('YYYY-MM-DD HH:mm:ss'));
const readingValue = reading.reading * scaling;
yData.push(readingValue);
hoverText.push(`<b> ${timeReading.format('ddd, ll LTS')} </b> <br> ${label}: ${readingValue.toPrecision(6)} ${unitLabel}`);
// All hover have the date, meter name and value.
const hoverStart = `<b> ${timeReading.format('ddd, ll LTS')} </b> <br> ${label}: ${readingValue.toPrecision(6)} ${unitLabel}`;
if (state.graph.showMinMax && reading.max != null) {
// We want to show min/max. Note if the data is raw for this meter then all the min/max values are null.
// In this case we still push the min/max but plotly will not show them. This is a little extra work
// but makes the code cleaner.
const minValue = reading.min * scaling;
yMinData.push(minValue);
const maxValue = reading.max * scaling;
yMaxData.push(maxValue);
hoverText.push(`${hoverStart} <br> min: ${minValue.toPrecision(6)} <br> max: ${maxValue.toPrecision(6)}`);
Elias0127 marked this conversation as resolved.
Show resolved Hide resolved
} else {
hoverText.push(hoverStart);
}
});

/*
Expand All @@ -103,6 +118,13 @@ function mapStateToProps(state: State) {
name: label,
x: xData,
y: yData,
// only show error bars if enabled and there is data
error_y: state.graph.showMinMax && yMaxData.length > 0 ? {
type: 'data',
symmetric: false,
array: yMaxData.map((maxValue, index) => (maxValue - yData[index])),
arrayminus: yData.map((value, index) => (value - yMinData[index]))
} : undefined,
text: hoverText,
hoverinfo: 'text',
type: 'scatter',
Expand Down
3 changes: 2 additions & 1 deletion src/client/app/containers/RouteContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ function mapStateToProps(state: State) {
role,
// true if the chartlink rendering has been done.
renderOnce: state.graph.renderOnce,
areaNormalization: state.graph.areaNormalization
areaNormalization: state.graph.areaNormalization,
minMax: state.graph.showMinMax
};
}

Expand Down
11 changes: 9 additions & 2 deletions src/client/app/reducers/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ const defaultState: GraphState = {
areaNormalization: false,
hotlinked: false,
optionsVisibility: true,
lineGraphRate: {label: 'hour', rate: 1},
renderOnce: false
lineGraphRate: { label: 'hour', rate: 1 },
renderOnce: false,
showMinMax: false
};

export default function graph(state = defaultState, action: GraphAction) {
Expand Down Expand Up @@ -93,6 +94,12 @@ export default function graph(state = defaultState, action: GraphAction) {
...state,
areaNormalization: !state.areaNormalization
};
case ActionType.ToggleShowMinMax:
return {
...state,
showMinMax: !state.showMinMax
};

Elias0127 marked this conversation as resolved.
Show resolved Hide resolved
case ActionType.ChangeBarStacking:
return {
...state,
Expand Down
Loading