PlotlyChartBuilder

Source

Histogram.js
export default function Histogram(chartState, csvReader, arraysInfo) {
if (!chartState.x) {
chartState.x = Object.keys(arraysInfo)[0];
}
return [
{
x: csvReader.getColumn(chartState.x),
type: 'histogram',
},
];
}
Histogram2D.js
export default function Histogram2D(chartState, csvReader, arraysInfo) {
const arrayNames = Object.keys(arraysInfo);
if (!chartState.x) {
chartState.x = arrayNames[0];
}

if (!chartState.y) {
chartState.y = arrayNames[arrayNames.length >= 2 ? 1 : 0];
}

return [
{
x: csvReader.getColumn(chartState.x),
y: csvReader.getColumn(chartState.y),
type: 'histogram2d',
forceNewPlot: true, // Due to a bug in plotly redraw w/ 2D histogram
},
];
}
PieChart.js
import {
averageValues,
uniqueValues,
} from '../../../IO/Core/CSVReader/Processing';

const operations = {
Count: uniqueValues,
Average: averageValues,
};

export default function PieChart(chartState, csvReader, arraysInfo) {
const arrayNames = Object.keys(arraysInfo);
if (!chartState.labels) {
chartState.labels = arrayNames[0];
}

if (!chartState.values) {
chartState.values = arrayNames[arrayNames.length >= 2 ? 1 : 0];
}

if (!chartState.operation) {
chartState.operation = 'Average';
}

const opMethod = operations[chartState.operation];

const [labels, values] = opMethod(
csvReader.getColumn(chartState.labels),
csvReader.getColumn(chartState.values)
);
return [
{
labels,
values,
type: 'pie',
},
];
}
Scatter3D.js
export default function Scatter3D(chartState, csvReader, arraysInfo) {
const arrayNames = Object.keys(arraysInfo);
if (!chartState.x) {
chartState.x = arrayNames[0];
}

if (!chartState.y) {
chartState.y = arrayNames[arrayNames.length >= 2 ? 1 : 0];
}

if (!chartState.z) {
chartState.z =
arrayNames[arrayNames.length >= 3 ? 2 : arrayNames.length >= 2 ? 1 : 0];
}

return [
{
x: csvReader.getColumn(chartState.x),
y: csvReader.getColumn(chartState.y),
z: csvReader.getColumn(chartState.z),
type: 'scatter3d',
mode: 'markers+text',
},
];
}
chartTypes.json
{
"Histogram": {
"inputArrays": [
{
"key": "x",
"type": "numeric"
}
],
"type": "histogram",
"multiTrace": false
},
"3D Scatter Plot": {
"inputArrays": [
{
"key": "x",
"type": "numeric"
},
{
"key": "y",
"type": "numeric"
},
{
"key": "z",
"type": "numeric"
}
],
"type": "scatter3d",
"multiTrace": false,
"plotAttributes": {
"mode": "markers+text"
}
},
"2D Histogram": {
"inputArrays": [
{
"key": "x",
"type": "numeric"
},
{
"key": "y",
"type": "numeric"
}
],
"type": "histogram2d",
"multiTrace": false
},
"Histogram 2D Contour": {
"inputArrays": [
{
"key": "x",
"type": "numeric"
},
{
"key": "y",
"type": "numeric"
}
],
"type": "histogram2dcontour",
"multiTrace": false
},
"Box": {
"inputArrays": [
{
"key": "y",
"type": "numeric"
}
],
"type": "box",
"multiTrace": true
},
"Bar": {
"inputArrays": [
{
"key": "x",
"type": "categorical"
},
{
"key": "y",
"type": "numeric"
}
],
"type": "bar",
"multiTrace": true
},
"Pie": {
"inputArrays": [
{
"key": "values",
"type": "numeric"
},
{
"key": "labels",
"type": "categorical"
}
],
"type": "pie",
"multiTrace": false
}
}
index.js
import Monologue from 'monologue.js';
import CSVReader from '../../../IO/Core/CSVReader';
import chartTypes from './chartTypes.json';
import Histogram from './Histogram';
import Histogram2D from './Histogram2D';
import Scatter3D from './Scatter3D';
import PieChart from './PieChart';

import '../../../React/CollapsibleControls/CollapsibleControlFactory/PlotlyChartControl';
import '../../../React/CollapsibleControls/CollapsibleControlFactory/QueryDataModelWidget';

const chartFactory = {
Histogram,
Histogram2D,
Scatter3D,
PieChart,
};

const DATA_READY_TOPIC = 'data-ready';

export default class PlotlyChartBuilder {
constructor(queryDataModel) {
this.queryDataModel = queryDataModel;
this.csvReader = null;
this.currentChartInfo = null;
this.availableChartTypes = Object.keys(chartTypes);
this.chartState = {
chartType: this.availableChartTypes[0],
};

// Handle data fetching
if (this.queryDataModel) {
this.queryDataModel.onDataChange((data, envelope) => {
if (data.chart) {
if (this.csvReader === null) {
this.csvReader = new CSVReader(data.chart.data);
} else {
this.csvReader.setData(data.chart.data);
}

this.updateState();
}
});
}

this.controlWidgets = [];
if (this.queryDataModel) {
this.controlWidgets.push({
name: 'PlotlyChartControl',
model: this,
});
this.controlWidgets.push({
name: 'QueryDataModelWidget',
queryDataModel,
});
}
}

// ------------------------------------------------------------------------

getArrays() {
return this.queryDataModel.originalData.Chart.arrays;
}

// ------------------------------------------------------------------------

buildChart() {
if (this.chartState.chartType) {
const builder = chartFactory[this.chartState.chartType];
const dataArrays = this.queryDataModel.originalData.Chart.arrays;
const traces = builder(this.chartState, this.csvReader, dataArrays);
if (traces) {
this.dataReady({
forceNewPlot: this.chartState.forceNewPlot,
traces,
});
}
}
}

// ------------------------------------------------------------------------

updateState(state) {
if (state) {
this.chartState = Object.assign(this.chartState, state);
}

this.buildChart();
}

// ------------------------------------------------------------------------

getState() {
return this.chartState;
}

// ------------------------------------------------------------------------

setChartData(chartData, layout) {
this.dataReady({
forceNewPlot: false,
traces: chartData,
layout,
});
}

// ------------------------------------------------------------------------

onDataReady(callback) {
return this.on(DATA_READY_TOPIC, callback);
}

// ------------------------------------------------------------------------

dataReady(readyData) {
this.emit(DATA_READY_TOPIC, readyData);
}

// ------------------------------------------------------------------------

// Method meant to be used with the WidgetFactory
getControlWidgets() {
return this.controlWidgets;
}

// ------------------------------------------------------------------------

getControlModels() {
return {
queryDataModel: this.queryDataModel,
};
}
}

// Add Observer pattern using Monologue.js
Monologue.mixInto(PlotlyChartBuilder);