Chartwerk Bar Pod
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

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`);
}
});
}
}