mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Improve plot widgets
Add y-axis scale options Add time option to table-plot
This commit is contained in:
parent
7825d119de
commit
bb943cce9c
7 changed files with 107 additions and 68 deletions
|
@ -60,7 +60,8 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
<EditWidgetTimeControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetSimulatorControl key={2} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={3} controlId={'signals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={4} controlId={'ylabel'} label={'Y-Axis name'} placeholder={'Enter a name for the y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetTextControl key={4} controlId={'ylabel'} label={'Y-Axis name'} placeholder={'Enter a name for the y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetMinMaxControl key={5} widget={widget} controlId="y" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Table':
|
||||
|
@ -94,7 +95,9 @@ export default function createControls(widgetType = null, widget = null, session
|
|||
dialogControls.push(
|
||||
<EditWidgetSimulatorControl key={1} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => plotTableBoundOnChange(e)} />,
|
||||
<EditWidgetSignalsControl key={2} controlId={'preselectedSignals'} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTextControl key={3} controlId={'ylabel'} label={'Y-Axis'} placeholder={'Enter a name for the Y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />
|
||||
<EditWidgetTextControl key={3} controlId={'ylabel'} label={'Y-Axis'} placeholder={'Enter a name for the Y-axis'} widget={widget} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetTimeControl key={4} widget={widget} validate={(id) => validateForm(id)} simulation={simulation} handleChange={(e) => handleChange(e)} />,
|
||||
<EditWidgetMinMaxControl key={5} widget={widget} controlId="y" handleChange={e => handleChange(e)} />
|
||||
);
|
||||
break;
|
||||
case 'Slider':
|
||||
|
|
|
@ -47,6 +47,9 @@ class WidgetFactory {
|
|||
widget.minHeight = 200;
|
||||
widget.width = 400;
|
||||
widget.height = 200;
|
||||
widget.yMin = 0;
|
||||
widget.yMax = 10;
|
||||
widget.yUseMinMax = false;
|
||||
break;
|
||||
case 'Table':
|
||||
widget.simulator = defaultSimulator;
|
||||
|
@ -68,11 +71,14 @@ class WidgetFactory {
|
|||
widget.preselectedSignals = [];
|
||||
widget.signals = []; // initialize selected signals
|
||||
widget.ylabel = '';
|
||||
widget.minWidth = 400;
|
||||
widget.minHeight = 300;
|
||||
widget.width = 500;
|
||||
widget.height = 500;
|
||||
widget.minWidth = 200;
|
||||
widget.minHeight = 100;
|
||||
widget.width = 600;
|
||||
widget.height = 300;
|
||||
widget.time = 60;
|
||||
widget.yMin = 0;
|
||||
widget.yMax = 10;
|
||||
widget.yUseMinMax = false;
|
||||
break;
|
||||
case 'Image':
|
||||
widget.minWidth = 20;
|
||||
|
|
|
@ -127,8 +127,6 @@ class WidgetGauge extends Component {
|
|||
zones = zones.map(zone => {
|
||||
return Object.assign({}, zone, { min: (zone.min * step) + +minValue, max: zone.max * step + +minValue, strokeStyle: '#' + zone.strokeStyle });
|
||||
});
|
||||
|
||||
console.log(zones);
|
||||
}
|
||||
|
||||
this.gauge.setOptions({
|
||||
|
|
|
@ -37,7 +37,6 @@ class WidgetPlotTable extends Component {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
||||
// Update internal selected signals state with props (different array objects)
|
||||
if (this.props.widget.signals !== nextProps.widget.signals) {
|
||||
this.setState( {signals: nextProps.widget.signals});
|
||||
|
@ -158,6 +157,9 @@ class WidgetPlotTable extends Component {
|
|||
time={this.props.widget.time}
|
||||
width={this.props.widget.width - 100}
|
||||
height={this.props.widget.height - 55}
|
||||
yMin={this.props.widget.yMin}
|
||||
yMax={this.props.widget.yMax}
|
||||
yUseMinMax={this.props.widget.yUseMinMax}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,45 +25,59 @@ import Plot from './widget-plot/plot';
|
|||
import PlotLegend from './widget-plot/plot-legend';
|
||||
|
||||
class WidgetPlot extends React.Component {
|
||||
render() {
|
||||
const simulator = this.props.widget.simulator;
|
||||
const simulation = this.props.simulation;
|
||||
let legendSignals = [];
|
||||
let simulatorData = [];
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
data: [],
|
||||
legend: []
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const simulator = nextProps.widget.simulator;
|
||||
const simulation = nextProps.simulation;
|
||||
|
||||
// Proceed if a simulation with models and a simulator are available
|
||||
if (simulator && this.props.data[simulator.node] != null && this.props.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) {
|
||||
if (simulator && nextProps.data[simulator.node] != null && nextProps.data[simulator.node][simulator.simulator] != null && simulation && simulation.models.length > 0) {
|
||||
const model = simulation.models.find(model => model.simulator.node === simulator.node && model.simulator.simulator === simulator.simulator);
|
||||
const chosenSignals = this.props.widget.signals;
|
||||
const chosenSignals = nextProps.widget.signals;
|
||||
|
||||
simulatorData = this.props.data[simulator.node][simulator.simulator].values.filter((values, index) => (
|
||||
this.props.widget.signals.findIndex(value => value === index) !== -1
|
||||
const data = nextProps.data[simulator.node][simulator.simulator].values.filter((values, index) => (
|
||||
nextProps.widget.signals.findIndex(value => value === index) !== -1
|
||||
));
|
||||
|
||||
// Query the signals that will be displayed in the legend
|
||||
legendSignals = model.mapping.reduce( (accum, model_signal, signal_index) => {
|
||||
const legend = model.mapping.reduce( (accum, model_signal, signal_index) => {
|
||||
if (chosenSignals.includes(signal_index)) {
|
||||
accum.push({ index: signal_index, name: model_signal.name });
|
||||
}
|
||||
|
||||
return accum;
|
||||
}, []);
|
||||
|
||||
this.setState({ data, legend });
|
||||
} else {
|
||||
this.setState({ data: [], legend: [] });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="plot-widget" ref="wrapper">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={simulatorData}
|
||||
height={this.props.widget.height - 55}
|
||||
width={this.props.widget.width - 20}
|
||||
time={this.props.widget.time}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PlotLegend signals={legendSignals} />
|
||||
render() {
|
||||
return <div className="plot-widget" ref="wrapper">
|
||||
<div className="widget-plot">
|
||||
<Plot
|
||||
data={this.state.data}
|
||||
height={this.props.widget.height - 55}
|
||||
width={this.props.widget.width - 20}
|
||||
time={this.props.widget.time}
|
||||
yMin={this.props.widget.yMin}
|
||||
yMax={this.props.widget.yMax}
|
||||
yUseMinMax={this.props.widget.yUseMinMax}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
<PlotLegend signals={this.state.legend} />
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,25 +7,21 @@
|
|||
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
||||
**********************************************************************************/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { scaleOrdinal, schemeCategory10 } from 'd3-scale';
|
||||
|
||||
class PlotLegend extends Component {
|
||||
// constructor(props) {
|
||||
// super(props);
|
||||
// }
|
||||
|
||||
class PlotLegend extends React.Component {
|
||||
render() {
|
||||
var colorScale = scaleOrdinal(schemeCategory10);
|
||||
const colorScale = scaleOrdinal(schemeCategory10);
|
||||
|
||||
return (
|
||||
<div className="plot-legend">
|
||||
{ this.props.signals.map( (signal) =>
|
||||
<div key={signal.index} className="signal-legend"><span className="legend-color" style={{ background: colorScale(signal.index) }}> </span> {signal.name} </div>)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
return <div className="plot-legend">
|
||||
{this.props.signals.map(signal =>
|
||||
<div key={signal.index} className="signal-legend">
|
||||
<span className="legend-color" style={{ background: colorScale(signal.index) }}> </span>{signal.name}
|
||||
</div>
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default PlotLegend;
|
||||
export default PlotLegend;
|
||||
|
|
|
@ -22,22 +22,38 @@ class Plot extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// create dummy axes
|
||||
const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, props.width]);
|
||||
const yScale = scaleLinear().domain([0, 10]).range([props.height, bottomMargin]);
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
this.state = {
|
||||
data: null
|
||||
data: null,
|
||||
xAxis,
|
||||
yAxis
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// check if data is valid
|
||||
if (nextProps.data == null || nextProps.data.length === 0 || nextProps.data[0].length === 0) {
|
||||
this.setState({ data: null });
|
||||
// create empty plot axes
|
||||
const xScale = scaleTime().domain([Date.now(), Date.now() + 5 * 1000]).range([leftMargin, nextProps.width]);
|
||||
const yScale = scaleLinear().domain([0, 10]).range([nextProps.height, bottomMargin]);
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
this.setState({ data: null, xAxis, yAxis });
|
||||
return;
|
||||
}
|
||||
|
||||
// only show data in requested time
|
||||
let data = nextProps.data;
|
||||
|
||||
const firstTimestamp = data[0][data[0].length - 1].x - this.props.time * 1000;
|
||||
const firstTimestamp = data[0][data[0].length - 1].x - nextProps.time * 1000;
|
||||
if (data[0][0].x < firstTimestamp) {
|
||||
// only show data in range (+100 ms)
|
||||
const index = data[0].findIndex(value => value.x >= firstTimestamp - 100);
|
||||
|
@ -50,20 +66,26 @@ class Plot extends React.Component {
|
|||
xRange[0] = xRange[1] - nextProps.time * 1000;
|
||||
}
|
||||
|
||||
let yRange = [0, 0];
|
||||
let yRange;
|
||||
|
||||
data.map(values => {
|
||||
const range = extent(values, p => p.y);
|
||||
if (range[0] < yRange[0]) yRange[0] = range[0];
|
||||
if (range[1] > yRange[1]) yRange[1] = range[1];
|
||||
if (nextProps.yUseMinMax) {
|
||||
yRange = [nextProps.yMin, nextProps.yMax];
|
||||
} else {
|
||||
yRange = [0, 0];
|
||||
|
||||
return values;
|
||||
});
|
||||
data.map(values => {
|
||||
const range = extent(values, p => p.y);
|
||||
if (range[0] < yRange[0]) yRange[0] = range[0];
|
||||
if (range[1] > yRange[1]) yRange[1] = range[1];
|
||||
|
||||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
// create scale functions for both axes
|
||||
const xScale = scaleTime().domain(xRange).range([leftMargin, nextProps.width]);
|
||||
const yScale = scaleLinear().domain(yRange).range([nextProps.height, bottomMargin]);
|
||||
|
||||
|
||||
const xAxis = axisBottom().scale(xScale).ticks(5).tickFormat(date => timeFormat("%M:%S")(date));
|
||||
const yAxis = axisLeft().scale(yScale).ticks(5);
|
||||
|
||||
|
@ -77,18 +99,16 @@ class Plot extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (this.state.data == null) return false;
|
||||
console.log(this.state);
|
||||
|
||||
return(
|
||||
<svg width={this.props.width + leftMargin} height={this.props.height + bottomMargin}>
|
||||
<g ref={node => select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} />
|
||||
<g ref={node => select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} />
|
||||
return <svg width={this.props.width + leftMargin} height={this.props.height + bottomMargin}>
|
||||
<g ref={node => select(node).call(this.state.xAxis)} style={{ transform: `translateY(${this.props.height}px)` }} />
|
||||
<g ref={node => select(node).call(this.state.yAxis)} style={{ transform: `translateX(${leftMargin}px)`}} />
|
||||
|
||||
<g>
|
||||
{this.state.data}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
<g>
|
||||
{this.state.data}
|
||||
</g>
|
||||
</svg>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue