Skip to content

Commit

Permalink
Merge pull request #317 from inowas/dev
Browse files Browse the repository at this point in the history
Feature/1967 monthly ticks on x axis (#316)
  • Loading branch information
rabbl authored Oct 27, 2020
2 parents 80162b7 + b2c546b commit 603a8d7
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 43 deletions.
8 changes: 7 additions & 1 deletion src/scenes/t19/components/HeatTransportController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
} from 'semantic-ui-react';
import {HeatTransportInput, HeatTransportResults} from './index';
import {IHeatTransportRequest, IHeatTransportRequestOptions, IHtm} from '../../../core/model/htm/Htm.type';
import {IRootReducer} from '../../../reducers';
import {includes} from 'lodash';
import {makeHeatTransportRequest} from '../../../services/api';
import {useSelector} from 'react-redux';
import Htm from '../../../core/model/htm/Htm';
import HtmInput from '../../../core/model/htm/HtmInput';
import React, {FormEvent, useEffect, useState} from 'react';
Expand All @@ -25,6 +27,8 @@ const HeatTransportController = (props: IProps) => {
const [activeValue, setActiveValue] = useState<string>('');
const [requestOptions, setRequestOptions] = useState<IHeatTransportRequestOptions>(props.htm.options);

const user = useSelector((state: IRootReducer) => state.user);

useEffect(() => {
setRequestOptions(props.htm.options);
}, [props.htm]);
Expand Down Expand Up @@ -96,6 +100,7 @@ const HeatTransportController = (props: IProps) => {
<Grid.Row>
<Grid.Column width={8}>
<HeatTransportInput
dateTimeFormat={user.settings.dateFormat}
input={props.htm.inputSw}
label="Surface water"
name="sw"
Expand All @@ -105,6 +110,7 @@ const HeatTransportController = (props: IProps) => {
</Grid.Column>
<Grid.Column width={8}>
<HeatTransportInput
dateTimeFormat={user.settings.dateFormat}
input={props.htm.inputGw}
label="Groundwater"
name="gw"
Expand Down Expand Up @@ -152,7 +158,7 @@ const HeatTransportController = (props: IProps) => {
</Grid.Row>
</Grid>
</Form>
{props.htm.results && <HeatTransportResults results={props.htm.results}/>}
{props.htm.results && <HeatTransportResults dateTimeFormat={user.settings.dateFormat} results={props.htm.results}/>}
</React.Fragment>
);
};
Expand Down
2 changes: 2 additions & 0 deletions src/scenes/t19/components/HeatTransportInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import React, {SyntheticEvent, useEffect, useState} from 'react';
import uuid from 'uuid';

interface IProps {
dateTimeFormat: string;
input: HtmInput;
label: string;
name: string;
Expand Down Expand Up @@ -253,6 +254,7 @@ const HeatTransportInput = (props: IProps) => {
/>
<HeatTransportInputChart
data={data}
dateTimeFormat={props.dateTimeFormat}
tempTime={tempTime}
timesteps={timesteps}
isLoading={isFetching}
Expand Down
33 changes: 11 additions & 22 deletions src/scenes/t19/components/HeatTransportInputChart.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {IDateTimeValue} from '../../../core/model/rtm/Sensor.type';
import {IHeatTransportInput} from '../../../core/model/htm/Htm.type';
import {LTOB} from 'downsample';
import {ReferenceLine, ResponsiveContainer, Scatter, ScatterChart, XAxis, YAxis} from 'recharts';
import {ResponsiveContainer, Scatter, ScatterChart, XAxis, YAxis} from 'recharts';
import {Segment} from 'semantic-ui-react';
import React from 'react';
import moment from 'moment';

interface IProps {
data?: IHeatTransportInput['data'],
dateTimeFormat: string,
tempTime?: [number, number],
timesteps?: number[],
isLoading: boolean
Expand All @@ -22,13 +23,14 @@ const HeatTransportInputChart = (props: IProps) => {
</ResponsiveContainer>
</Segment>
);

}

const {tempTime, timesteps} = props;

const formatDateTimeTicks = (dt: number) => {
return moment.unix(dt).format('YYYY-MM-DD');
return moment.unix(dt).format(props.dateTimeFormat);
};

const formatTemperatureTicks = (t: number) => {
return t.toFixed(2);
};

const downSampleData = (d: IDateTimeValue[]) => d ? LTOB(d.map((ds) => ({
Expand All @@ -47,30 +49,17 @@ const HeatTransportInputChart = (props: IProps) => {
<XAxis
dataKey={'x'}
domain={[props.data[0].timeStamp, props.data[downsampledData.length - 1].timeStamp]}
name={'Date Time'}
name={'x'}
tickFormatter={formatDateTimeTicks}
type={'number'}
/>
<YAxis
label={{value: 'T', angle: -90, position: 'insideLeft'}}
label={{value: 'T [°C]', angle: -90, position: 'insideLeft'}}
dataKey={'y'}
name={''}
name={'y'}
tickFormatter={formatTemperatureTicks}
domain={['auto', 'auto']}
/>
{tempTime && timesteps &&
<ReferenceLine
x={timesteps[tempTime[0]]}
stroke="#000"
strokeDasharray="3 3"
/>
}
{tempTime && timesteps &&
<ReferenceLine
x={timesteps[tempTime[1]]}
stroke="#000"
strokeDasharray="3 3"
/>
}
<Scatter
data={downsampledData}
line={{strokeWidth: 2, stroke: '#3498DB'}}
Expand Down
105 changes: 85 additions & 20 deletions src/scenes/t19/components/HeatTransportResults.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import {Button, Checkbox, Icon, Menu, MenuItemProps, Segment, Table} from 'semantic-ui-react';
import {
CartesianGrid,
Label,
ReferenceDot,
ResponsiveContainer,
Scatter,
ScatterChart,
Tooltip,
XAxis,
YAxis
} from 'recharts';
import {IHeatTransportResults} from '../../../core/model/htm/Htm.type';
import {Label, ReferenceDot, ResponsiveContainer, Scatter, ScatterChart, Tooltip, XAxis, YAxis} from 'recharts';
import {SemanticCOLORS} from 'semantic-ui-react/dist/commonjs/generic';
import {downloadFile} from '../../shared/simpleTools/helpers';
import React, {MouseEvent, useEffect, useState} from 'react';
import _ from 'lodash';
import moment from 'moment';

interface IProps {
dateTimeFormat: string;
results: IHeatTransportResults;
}

Expand All @@ -31,11 +42,29 @@ const HeatTransportResults = (props: IProps) => {
const handleClickMenuItem = (e: MouseEvent, {index}: MenuItemProps) =>
setActiveIndex(typeof index === 'number' ? index : 0);

const exportData = (arrayOfObjects: Array<{ [key: string]: any }>, filename: string) => () => {
const exportData = (
arrayOfObjects: Array<{ [key: string]: any }>,
filename: string,
properties?: string[]
) => () => {
if (arrayOfObjects.length < 1) {
return null;
}
const keys = Object.keys(arrayOfObjects[0]);
let keys = Object.keys(arrayOfObjects[0]);

if (properties) {
keys = keys.filter((name) => properties.includes(name));
arrayOfObjects = arrayOfObjects.map((row) => {
const newRow: {[key: string]: number} = {};
keys.forEach((k) => {
newRow[k] = row[k];
});
return newRow;
});
}

console.log(arrayOfObjects);

let csvContent = 'data:text/csv;charset=utf-8,';
csvContent += keys.join(',');
csvContent += '\r\n';
Expand All @@ -50,10 +79,12 @@ const HeatTransportResults = (props: IProps) => {

const renderChart = (type: string, dataObs: Array<{ x: number, y: number }>,
dataSim: Array<{ x: number, y: number }>) => {
const RENDER_NO_SHAPE = () => null;

const formatDateTimeTicks = (dt: number) => {
return moment.unix(dt).format('YYYY-MM-DD');
return moment.unix(dt).format(props.dateTimeFormat);
};

const formatTemperatureTicks = (t: number) => {
return t.toFixed(2);
};

const filteredPoints: Array<{
Expand All @@ -68,37 +99,63 @@ const HeatTransportResults = (props: IProps) => {
type: point.point_type
}));

const getTicks = () => {
if (timesteps && useSameTimes) {
const dateStart = moment.unix(timesteps[0]);
const dateEnd = moment.unix(timesteps[1]);
const interim = dateStart.clone();
const timeValues: number[] = [];

while (dateEnd > interim || interim.format('M') === dateEnd.format('M')) {
timeValues.push(moment(interim.format('YYYY-MM')).unix());
interim.add(1, 'month');
}
return timeValues;
}
return undefined;
};

const getTooltip = (value: any, name: string) => {
if (name === 'x') {
return [moment.unix(value).format(props.dateTimeFormat), 'Date'];
}
return [`${value}°C`, 'T'];
};

return (
<ResponsiveContainer height={300}>
<ScatterChart>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis
dataKey={'x'}
domain={useSameTimes ? timesteps : ['auto', 'auto']}
name={'Date Time'}
name={'x'}
tickFormatter={formatDateTimeTicks}
ticks={getTicks()}
type={'number'}
/>
<YAxis
label={{value: 'T', angle: -90, position: 'insideLeft'}}
label={{value: 'T [°C]', angle: -90, position: 'insideLeft'}}
dataKey={'y'}
name={''}
name={'y'}
domain={['auto', 'auto']}
tickFormatter={formatTemperatureTicks}
/>
<Tooltip cursor={{strokeDasharray: '3 3'}}/>
<Scatter
data={dataObs}
line={{strokeWidth: 2, stroke: '#db3434'}}
lineType={'joint'}
name={'observed'}
shape={<RENDER_NO_SHAPE/>}
fill='#00000000'
/>
<Scatter
data={dataSim}
line={{strokeWidth: 2, stroke: '#3498DB'}}
lineType={'joint'}
name={'simulated'}
shape={<RENDER_NO_SHAPE/>}
fill='#00000000'
/>
<Tooltip formatter={getTooltip} cursor={{strokeDasharray: '3 3'}}/>
{filteredPoints.map((point, key) => (
<ReferenceDot
key={key}
Expand Down Expand Up @@ -153,8 +210,8 @@ const HeatTransportResults = (props: IProps) => {
checked={useSameTimes}
toggle={true}
/>
<h3>groundwater</h3>
{renderChart('groundwater', dataGwObs, dataGwSim)}
<h3>surface-water</h3>
{renderChart('surface-water', dataSwObs, dataSwSim)}
<div className="downloadButtons">
<Button
compact={true}
Expand All @@ -166,21 +223,25 @@ const HeatTransportResults = (props: IProps) => {
<Icon name="download"/> CSV
</Button>
</div>
<h3>surface-water</h3>
{renderChart('surface-water', dataSwObs, dataSwSim)}
<h3>groundwater</h3>
{renderChart('groundwater', dataGwObs, dataGwSim)}
</React.Fragment>
);
};

const renderData = (data: Array<{ type: string } & { [key: string]: number }>, digits = 4) => {
const renderData = (
data: Array<{ type: string } & { [key: string]: number }>,
digits = 4,
properties?: string[]
) => {
const dataSw = data.filter((row) => row.type === 'surface-water');
const dataGw = data.filter((row) => row.type === 'groundwater');

if (dataSw.length < 1 || dataGw.length < 1) {
return;
}

const keys: string[] = [];
let keys: string[] = [];
data.forEach((row) => {
const rowKeys = Object.keys(row);
rowKeys.forEach((k) => {
Expand All @@ -190,6 +251,10 @@ const HeatTransportResults = (props: IProps) => {
});
});

if (properties) {
keys = keys.filter((name) => properties.includes(name));
}

return (
<Table celled={true} selectable={true}>
<Table.Header>
Expand Down Expand Up @@ -291,12 +356,12 @@ const HeatTransportResults = (props: IProps) => {
basic={true}
icon={true}
size={'small'}
onClick={exportData(props.results.gof, 'goodnessOfFit')}
onClick={exportData(props.results.gof, 'goodnessOfFit', ['type', 'RMSE', 'R2'])}
>
<Icon name="download"/> CSV
</Button>
</div>
{renderData(props.results.gof)}
{renderData(props.results.gof, undefined, ['RMSE', 'R2'])}
</React.Fragment>
);
case 3:
Expand Down

0 comments on commit 603a8d7

Please sign in to comment.