You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
4.9 KiB
143 lines
4.9 KiB
import { |
|
Datapoint, Color, ValueX, Opacity, |
|
BarPodType, BarSerie |
|
} from '../types'; |
|
|
|
import { BarConfig } from './bar_options'; |
|
|
|
import * as _ from 'lodash'; |
|
|
|
export type DataRow = { |
|
key: number; // x |
|
items: DataItem[]; |
|
maxSumm: number; |
|
minSubtraction: number; |
|
}; |
|
export type DataItem = { |
|
target: string; // serie target |
|
values: number[]; // y list |
|
colors: string[]; |
|
opacity: number[]; |
|
}; |
|
const DEFAULT_BAR_COLOR = 'green'; |
|
const DEFAULT_OPACITY = 1; |
|
|
|
export class DataProcessor { |
|
_formattedDataRows: DataRow[] = []; |
|
|
|
constructor(protected visibleSeries: BarSerie[], protected options: BarConfig) { |
|
this.validateData(); |
|
switch(this.options.barType) { |
|
case BarPodType.DISCRETE: |
|
this.setDataRowsForDiscreteType(); |
|
break; |
|
case BarPodType.NON_DISCRETE: |
|
this.setDataRowsForNonDiscreteType(); |
|
break; |
|
default: |
|
throw new Error(`Bar DataProcessor: Unknown BarPodType ${this.options.barType}`); |
|
} |
|
} |
|
|
|
public get dataRows(): DataRow[] { |
|
return this._formattedDataRows; |
|
} |
|
|
|
protected setDataRowsForDiscreteType(): void { |
|
const zippedData = _.zip(..._.map(this.visibleSeries, serie => serie.datapoints)); |
|
this._formattedDataRows = _.map(zippedData, (dataRow: Datapoint[], rowIdx: number) => { |
|
return { |
|
key: _.first(_.first(dataRow)), // x |
|
items: _.map(dataRow, (datapoint: Datapoint, pointIdx: number) => { |
|
const serie = this.visibleSeries[pointIdx]; |
|
const values = _.tail(datapoint); |
|
return { |
|
target: serie.target, |
|
values, // y list |
|
colors: this.getColorsForEachValue(values, serie.color, datapoint[0], serie.target), |
|
opacity: this.getOpacityForEachValue(values, serie.opacity, datapoint[0], serie.target), |
|
} |
|
}), |
|
maxSumm: _.max(_.map(dataRow, (datapoint: Datapoint) => this.getMaxSumm(_.tail(datapoint)))), // max y axis scale |
|
minSubtraction: _.min(_.map(dataRow, (datapoint: Datapoint) => this.getMinSubtraction(_.tail(datapoint)))), // min y axis scale |
|
} |
|
}); |
|
} |
|
|
|
protected setDataRowsForNonDiscreteType(): void { |
|
for(let serie of this.visibleSeries) { |
|
const rows = _.map(serie.datapoints, (datapoint: Datapoint) => { |
|
const values = _.tail(datapoint); |
|
return { |
|
key: datapoint[0], // x |
|
items: [ |
|
{ |
|
target: serie.target, |
|
values, // y list |
|
colors: this.getColorsForEachValue(values, serie.color, datapoint[0], serie.target), |
|
opacity: this.getOpacityForEachValue(values, serie.opacity, datapoint[0], serie.target), |
|
} |
|
], |
|
maxSumm: this.getMaxSumm(values), // max y axis scale |
|
minSubtraction: this.getMinSubtraction(values), // min y axis scale |
|
} |
|
}); |
|
this._formattedDataRows = _.concat(this._formattedDataRows, rows); |
|
} |
|
this._formattedDataRows = _.sortBy(this._formattedDataRows, 'key'); |
|
} |
|
|
|
getColorsForEachValue(values: number[], color: Color, key: ValueX, target: string): string[] { |
|
if(_.isString(color)) { |
|
return _.map(values, value => color); |
|
} |
|
if(_.isArray(color)) { |
|
return _.map(values, (value, i) => color[i] || DEFAULT_BAR_COLOR); |
|
} |
|
if(_.isFunction(color)) { |
|
return _.map(values, (value, i) => (color as Function)({ value, barIndex: i, key, target })); |
|
} |
|
throw new Error(`Unknown type of serie color: ${target} ${color}`); |
|
} |
|
|
|
getOpacityForEachValue(values: number[], opacity: Opacity | undefined, key: ValueX, target: string): number[] { |
|
if(opacity === undefined) { |
|
return _.map(values, value => DEFAULT_OPACITY); |
|
} |
|
if(_.isNumber(opacity)) { |
|
return _.map(values, value => opacity); |
|
} |
|
if(_.isArray(opacity)) { |
|
return _.map(values, (value, i) => !_.isNil(opacity[i]) ? opacity[i] : DEFAULT_OPACITY); |
|
} |
|
if(_.isFunction(opacity)) { |
|
return _.map(values, (value, i) => (opacity as Function)({ value, barIndex: i, key, target })); |
|
} |
|
throw new Error(`Unknown type of serie opacity: ${target} ${opacity}`); |
|
} |
|
|
|
getMaxSumm(values: number[]): number { |
|
return values.reduce((prev, curr) => curr > 0 ? prev + curr : prev, 0); |
|
} |
|
|
|
getMinSubtraction(values: number[]): number { |
|
return values.reduce((prev, curr) => curr < 0 ? prev + curr : prev, 0); |
|
} |
|
|
|
validateData(): void { |
|
if(this.options.barType === BarPodType.NON_DISCRETE) { |
|
return; |
|
} |
|
// TODO: move non-empty datapoints validation to core |
|
if(_.isEmpty(this.visibleSeries)) { |
|
return; |
|
} |
|
const xValuesList = _.map(_.first(this.visibleSeries).datapoints, dp => dp[0]); |
|
_.forEach(this.visibleSeries, serie => { |
|
const serieXList = _.map(serie.datapoints, dp => dp[0]); |
|
if(!_.isEqual(xValuesList, serieXList)) { |
|
throw new Error(`Bar DataProcessor: All series should have equal X values lists`); |
|
} |
|
}); |
|
} |
|
}
|
|
|