blob: 614aaceb7efc2fc4136cf8ecf87f7ffe0bfd6581 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {addChart, updateChart} from '@parallax/data/manager';
import {Chart} from '@parallax/chart/chart';
import {Matrix} from '@parallax/common/math';
import {ParallaxError} from '@parallax/common/error';
import {registerParser} from '@parallax/data/parse';
import {PlotRow} from '@parallax/interface/plot_row';
/**
* Contains a LineChart featuring a set of meta data and 2D-arrays of samples.
*
* Meta data access:
* meta = data['meta']
* type = meta['type']
*
* Measurements are stored in a matrix where a row will typically represent
* an individual sensor or timestamp. Columns represent samples measured
* at the same time.
*
* Row meta data access:
* rolMeta = data['rowMeta']
* rolName = rolMeta[rowNum]['name']
* rolCount = len(data['rowMeta'])
* Shape access:
* matrix = data['matrix']
* colCount = len(matrix)
* rowCount = len(matrix[0])
* Data access:
* cellValue = matrix[rowNum][colNum]
* colValues = matrix[rowNum]
* rowValues = [matrix[x][colNum] for x in range(colCount)]
*/
export class LineChart extends Chart {
private meta: any;
private matrix: Matrix<any>;
private dataTable: google.visualization.DataTable|undefined;
private dataView: google.visualization.DataView|undefined;
/**
* Creates a new LineChart.
*
* @param meta Chart meta data.
* @param matrix Chart sample data as a 2D-array.
*/
constructor(meta: any, matrix: any) {
super(meta?.rowMeta);
this.matrix = new Matrix<any>(matrix);
this.meta = meta;
if (this.matrix.rows === 0 || this.matrix.rows !== this.axisMeta.length) {
throw new Error('Invalid number of columns');
}
}
/**
* Renders the plot.
* TODO (bnemec): Replace mock.
*
* @param plotRow PlotRow we are rending inside.
*/
draw(plotRow: PlotRow) {
console.log('LineChart.draw()');
let dataView = this.configureView();
let options = {
fontSize: 14,
interpolateNulls: false,
legend: {
maxLines: 3,
},
chartArea: {
left: 50,
width: plotRow.plotArea.clientWidth - 100,
top: 100,
height: plotRow.plotArea.clientHeight - 200,
},
explorer: {
maxZoomIn: 0,
keepInBounds: true,
actions: ['dragToZoom', 'rightClickToReset'],
},
hAxis: {
format: 'hh:mm:ss a',
},
};
// @ts-ignore: The ChartLegendPosition guard is rejecting all values.
options.legend.position = 'top';
let chart = new google.visualization.LineChart(plotRow.plotArea);
// Define the columns as assign names
let columns = [];
for (const [index, axis] of this.axisMeta.entries()) {
let name = axis.getMeta().get('name');
if (typeof name !== 'string') {
name = index.toString();
}
let value: any = {
label: name,
sourceColumn: index,
};
// The value in index 0 is the unix time, we transform
// the unix time format to user readable time format
if (index === 0) {
value['type'] = 'datetime';
// The calculate function could transform the time format
value['calc'] =
((dataTable: google.visualization.DataTable, row: number) => {
let unixtime = dataTable.getValue(row, index) as number;
// Get the data in millisecond
let time = new Date(unixtime * 1000);
return time;
});
}
columns.push(value);
}
dataView.setColumns(columns);
chart.draw(dataView, options);
}
/**
* Configures the DataView.
* TODO (bnemec): Replace mock.
*
* @return A configured DataView
*/
protected configureView() {
const transposed = this.matrix.transpose();
if (this.dataView === undefined) {
this.dataTable = google.visualization.arrayToDataTable(
transposed.asArray() as any[], true);
this.dataView = new google.visualization.DataView(this.dataTable);
}
return this.dataView;
}
/**
* @returns The JSON serializable representation.
*/
toJSON() {
return {
'type': 'linechart',
'meta': this.meta,
'matrix': this.matrix,
};
}
}
registerParser((data: any) => {
if (data.type === 'linechart') {
const chart = new LineChart(data.meta, data.matrix);
addChart(chart);
}
});
registerParser((data: any) => {
if (data.type === 'streaming_chart') {
const chart = new LineChart(data.meta, data.matrix);
updateChart(chart);
const stream = new CustomEvent('stream', {'detail': {'chart': chart}});
document.dispatchEvent(stream);
}
});