Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 2 additions & 3 deletions src/app/editor/components/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ export default {

chartOptions() {
const chartBridge = (this as any)?.$chartBridge;
return chartBridge
?.reactiveGet('buildChartOptions', this.reactToParameterUpdates, this.tableCSV) ||
{};
const options = chartBridge?.reactiveGet('buildChartOptions', this.reactToParameterUpdates, this.tableCSV) || {};
return options;
}
},

Expand Down
11 changes: 6 additions & 5 deletions src/app/editor/core/ChartBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getSeriesId } from './utils/chartUtils';
import { defaultChartOptions } from './defaultChartOptions';
import { getChartOptionsFromParameters } from './optionsMapper/chartOptionsMapper';
import { getSeriesOptionsFromParameters } from './optionsMapper/seriesOptionsMapper';
import { GoogleSheetStatus } from '../store/modules/data';
import { GoogleSheetStatus, dataStore } from '../store/modules/data';
import { Store } from 'vuex';


Expand Down Expand Up @@ -145,7 +145,6 @@ export class ChartBridge {

const dataSource = this.getStoreParam('dataStore', 'selectedDataSource');
const spreadsheetSetupComplete = this.getStoreParam('dataStore', 'spreadsheetSetupComplete');

const dataSourceOptions = dataSource === 'googlesheets' && spreadsheetSetupComplete ? {
csv: void 0,
firstRowAsNames: true,
Expand Down Expand Up @@ -185,7 +184,6 @@ export class ChartBridge {
delete s.index;
return id;
});

// Extend parsed results with series options
const seriesOptions = this.buildSeriesOptions(seriesIds);
if (seriesOptions) {
Expand All @@ -197,13 +195,16 @@ export class ChartBridge {
}
})
};

// Need to update the chart data first to know which (new) series to build options for.
// Only once the CSV has been parsed can we build the series options. This is why
// series options are built separately.
const newOptions = Object.assign(dataOptions, chartSettings);
const plotParametersMap = deepMerge(defaultOptions, newOptions);


console.log(plotParametersMap);

return deepMerge(defaultOptions, newOptions);
return plotParametersMap;
}


Expand Down
83 changes: 83 additions & 0 deletions src/app/editor/core/optionsMapper/chartMappings.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,94 @@
import { GenericObject } from '../utils/objects';

function parseCsvToMap(csvString: string): Map<string, Array<number | string | [number, number]>> {
const map = new Map<string, Array<number | string | [number, number]>>();
const rows = csvString.split('\n').map(row => row.trim()).filter(row => row !== '');
if (rows.length < 2) return map; // return empty map if there's no data or only headers
console.log(rows);
// Process headers
const headers = rows[0].split(';').map(header =>
header.toLowerCase().replace(/\s+/g, '')
);

// Initialize the map with empty arrays for each header
headers.forEach(header => {
map.set(header, []);
});

// Populate the map
for (let i = 1; i < rows.length; i++) {
const values = rows[i].split(';');
values.forEach((value, index) => {
const key = headers[index];
map.get(key)?.push(parseFloat(value));
});
}

// Special handling for error bars: identify pairs and group them
const errorBarDataMap = new Map<string, Array<[number, number]>>();

// Identify low and high pairs dynamically
headers.filter(header => header.includes('lowvalue')).forEach(lowKey => {
const suffix = lowKey.match(/\d+$/); // matches the trailing number in 'lowvalue1', 'lowvalue2', etc.
if (suffix) {
const highKey = `highvalue${suffix[0]}`;
if (map.has(highKey)) {
const errorBarData = [];
for (let i = 0; i < map.get(lowKey)!.length; i++) {
errorBarData.push([map.get(lowKey)![i], map.get(highKey)![i]]);
}
const errorBarKey = `errorbardata${suffix[0]}`;
errorBarDataMap.set(errorBarKey, errorBarData);
}
}});

// Add error bar data to main map
errorBarDataMap.forEach((data, key) => {
map.set(key, data);
});

return map;
}



export class ChartMappings {

public static type(value: string): GenericObject {
return { chart: { type: value } };
}

public static series(value: any, chart: GenericObject): Array<GenericObject> | GenericObject {
const dataMap = parseCsvToMap(chart.options.data.csv);
const columnNames = Array.from(dataMap.keys()).filter((key, index) => index !== 0 && !key.startsWith('lowvalue') && !key.startsWith('highvalue'));
if (value === 'Errorbar') {
const seriesArray: Array<GenericObject> = [];
// Loop through the map to handle error bar data and normal data\
columnNames.forEach((columnName) => {
if (columnName.startsWith('errorbardata')) {
// Handle error bar series
const errorBarData = dataMap.get(columnName) || [];
const errorBarSeries: GenericObject = {
name: columnName,
type: 'errorbar',
data: errorBarData
};
seriesArray.push(errorBarSeries);
} else {
// Handle normal data series
const normalData = dataMap.get(columnName) || [];
const normalSeries: GenericObject = {
name: columnName,
type: 'column',
data: normalData
};
seriesArray.push(normalSeries);
}});
return seriesArray;
}
return { chart: { type: value } };
}

public static legendEnabled(value: boolean): GenericObject {
return { legend: { enabled: value } };
}
Expand Down
13 changes: 11 additions & 2 deletions src/app/editor/core/optionsMapper/chartOptionsMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@ class ChartOptionsMapper {
}

public addChartParameter(param: string, value: unknown) {
const newOptions = (ChartMappings as any)[param](value, this.chart);
this.options = deepMerge(this.options, newOptions);
if (value === 'Errorbar') {
const newOptions = ChartMappings.series(value, this.chart);
if (!this.options.series) this.options.series = [];
newOptions.forEach((seriesConfig: any) => {
this.options.series.push(seriesConfig);});
} else {
// Existing logic
const newOptions = (ChartMappings as any)[param](value, this.chart);
this.options = deepMerge(this.options, newOptions);
}
}
}

Expand All @@ -41,5 +49,6 @@ export function getChartOptionsFromParameters(
optionsMapper.addChartParameter(param, chartParameters[param])
);


return optionsMapper.build();
}
3 changes: 3 additions & 0 deletions src/app/editor/core/optionsMapper/seriesMappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export class SeriesMappings {
}

public static seriesType(value: string): GenericObject {
if(!value){
return { type: '' };
}
return { type: value };
}

Expand Down
4 changes: 2 additions & 2 deletions src/app/editor/core/optionsMapper/seriesOptionsMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { GenericObject, deepMerge } from '../utils/objects';
* Map parameters to options object using series mappings and series sonification mappings.
*/
function toSeriesOptionsObject(seriesParameters: GenericObject): GenericObject {
let res = {};
let res: GenericObject = {};
res['type'] = '';

for (const [param, val] of Object.entries(seriesParameters)) {
const mapper = (SeriesMappings as any)[param] || (SeriesSonificationMappings as any)[param];
const options = mapper ? mapper(val) : {};
res = deepMerge(res, options);
}

return res;
}

Expand Down
3 changes: 3 additions & 0 deletions src/app/editor/core/utils/chartUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export const seriesTypes = [{
}, {
name: 'Scatter',
value: 'scatter'
},{
name: 'Errorbar',
value: 'Errorbar'
}];

export function getSeriesId(series: GenericObject): string {
Expand Down
40 changes: 27 additions & 13 deletions src/app/editor/store/modules/data.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/*
Data store for the chart/table data.
Data store for the chart/table data.

The source data for the table is kept here, and updating this will
update the data grid. Updating the data grid will trigger a recompute
of the CSV. We are using the table CSV generator in order to preserve
table sorting/filtering, but could consider rolling our own for better
performance.
The source data for the table is kept here, and updating this will
update the data grid. Updating the data grid will trigger a recompute
of the CSV. We are using the table CSV generator in order to preserve
table sorting/filtering, but could consider rolling our own for better
performance.

Updating values in the data grid should be reflected in the source data,
so that the source data always mirrors the data the grid is working with.
*/
Updating values in the data grid should be reflected in the source data,
so that the source data always mirrors the data the grid is working with.
*/

import Vue from 'vue';
import { parseCSV } from '../../core/utils/csvParser';
Expand Down Expand Up @@ -41,12 +41,22 @@ function getFillValue(row: GenericObject, rowIx: number, fillData: FillColumnPro
}

function getPlaceholderData() {
const res = [{ A: 'Index (X value)', B: 'Sensor Data (Y value)' }];

for (let i = 0; i < 175; ++i) {
const res = [{ A: 'Index (X value)', B: 'Sensor Data (Y value)', C: 'LowVaLue1', D: 'High Value 1', E: 'Lo wVaLue2', F: 'High Val ue 2' }];

for (let i = 0; i < 5; ++i) {
const B = (Math.sin(i / 3) * i / 2);
const C = (B * (0.85 + Math.random() * 0.14)).toFixed(3); // Randomly between 85% to 99% of B
const D = (B * (1.03 + Math.random() * 0.12)).toFixed(3); // Randomly between 103% to 115% of B
const E = (B * (0.85 + Math.random() * 0.14)).toFixed(3); // Randomly between 85% to 99% of B
const F = (B * (1.03 + Math.random() * 0.12)).toFixed(3); // Randomly between 103% to 115% of B
const bValue = B.toFixed(3);
res.push({
A: '' + i,
B: (Math.sin(i / 3) * i / 2).toFixed(3)
B: bValue,
C: C,
D: D,
E:E,
F:F
});
}

Expand Down Expand Up @@ -80,6 +90,10 @@ export const dataStore = {
return state.tableRowData.length;
},

tableRowData: (state: GenericObject): number => {
return state.tableRowData.length;
},

tableColumnNamesWithData: (state: GenericObject): Array<string> => {
const rows = state.tableRowData;
const res: GenericObject = {};
Expand Down