UI: Improve time series charts ticks generation.
This commit is contained in:
parent
15a88a90a3
commit
c4bc1ba6bb
@ -207,3 +207,144 @@ index b8a9b95..8e4cb2f 100644
|
||||
return;
|
||||
}
|
||||
var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt);
|
||||
diff --git a/node_modules/echarts/lib/coord/axisHelper.js b/node_modules/echarts/lib/coord/axisHelper.js
|
||||
index a76c66b..be22cb0 100644
|
||||
--- a/node_modules/echarts/lib/coord/axisHelper.js
|
||||
+++ b/node_modules/echarts/lib/coord/axisHelper.js
|
||||
@@ -187,7 +187,9 @@ export function createScaleByModel(model, axisType) {
|
||||
});
|
||||
default:
|
||||
// case 'value'/'interval', 'log', or others.
|
||||
- return new (Scale.getClass(axisType) || IntervalScale)();
|
||||
+ return new (Scale.getClass(axisType) || IntervalScale)({
|
||||
+ ticksGenerator: model.get('ticksGenerator')
|
||||
+ });
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/node_modules/echarts/lib/coord/cartesian/Grid.js b/node_modules/echarts/lib/coord/cartesian/Grid.js
|
||||
index 5b18f02..4960e67 100644
|
||||
--- a/node_modules/echarts/lib/coord/cartesian/Grid.js
|
||||
+++ b/node_modules/echarts/lib/coord/cartesian/Grid.js
|
||||
@@ -91,11 +91,11 @@ var Grid = /** @class */function () {
|
||||
var scale = axis.scale;
|
||||
if (
|
||||
// Only value and log axis without interval support alignTicks.
|
||||
- isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) {
|
||||
+ isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null && model.get('ticksGenerator') == null) {
|
||||
axisNeedsAlign.push(axis);
|
||||
} else {
|
||||
niceScaleExtent(scale, model);
|
||||
- if (isIntervalOrLogScale(scale)) {
|
||||
+ if (isIntervalOrLogScale(scale) && !scale.isBlank()) {
|
||||
// Can only align to interval or log axis.
|
||||
alignTo = axis;
|
||||
}
|
||||
@@ -105,10 +105,15 @@ var Grid = /** @class */function () {
|
||||
// All axes has set alignTicks. Pick the first one.
|
||||
// PENDING. Should we find the axis that both set interval, min, max and align to this one?
|
||||
if (axisNeedsAlign.length) {
|
||||
- if (!alignTo) {
|
||||
- alignTo = axisNeedsAlign.pop();
|
||||
- niceScaleExtent(alignTo.scale, alignTo.model);
|
||||
+ while (!alignTo && axisNeedsAlign.length) {
|
||||
+ var axis = axisNeedsAlign.pop();
|
||||
+ niceScaleExtent(axis.scale, axis.model);
|
||||
+ if (!axis.scale.isBlank()) {
|
||||
+ alignTo = axis;
|
||||
+ }
|
||||
}
|
||||
+ }
|
||||
+ if (axisNeedsAlign.length && alignTo) {
|
||||
each(axisNeedsAlign, function (axis) {
|
||||
alignScaleTicks(axis.scale, axis.model, alignTo.scale);
|
||||
});
|
||||
diff --git a/node_modules/echarts/lib/scale/Interval.js b/node_modules/echarts/lib/scale/Interval.js
|
||||
index 1094662..8f4e07a 100644
|
||||
--- a/node_modules/echarts/lib/scale/Interval.js
|
||||
+++ b/node_modules/echarts/lib/scale/Interval.js
|
||||
@@ -46,12 +46,17 @@ import * as numberUtil from '../util/number.js';
|
||||
import * as formatUtil from '../util/format.js';
|
||||
import Scale from './Scale.js';
|
||||
import * as helper from './helper.js';
|
||||
+import { isFunction } from 'zrender/lib/core/util.js';
|
||||
var roundNumber = numberUtil.round;
|
||||
var IntervalScale = /** @class */function (_super) {
|
||||
__extends(IntervalScale, _super);
|
||||
- function IntervalScale() {
|
||||
- var _this = _super !== null && _super.apply(this, arguments) || this;
|
||||
+ function IntervalScale(setting) {
|
||||
+ var _this = _super.call(this, setting) || this;
|
||||
_this.type = 'interval';
|
||||
+ var ticksGenerator = _this.getSetting('ticksGenerator');
|
||||
+ if (isFunction(ticksGenerator)) {
|
||||
+ _this._ticksGenerator = ticksGenerator;
|
||||
+ }
|
||||
// Step is calculated in adjustExtent.
|
||||
_this._interval = 0;
|
||||
_this._intervalPrecision = 2;
|
||||
@@ -104,7 +109,17 @@ var IntervalScale = /** @class */function (_super) {
|
||||
var extent = this._extent;
|
||||
var niceTickExtent = this._niceExtent;
|
||||
var intervalPrecision = this._intervalPrecision;
|
||||
- var ticks = [];
|
||||
+ var ticksGenerator = this._ticksGenerator;
|
||||
+ var ticks;
|
||||
+ if (ticksGenerator) {
|
||||
+ try {
|
||||
+ ticks = ticksGenerator(extent, interval, niceTickExtent, intervalPrecision);
|
||||
+ if (ticks) {
|
||||
+ return ticks;
|
||||
+ }
|
||||
+ } catch (_e) {}
|
||||
+ }
|
||||
+ ticks = [];
|
||||
// If interval is 0, return [];
|
||||
if (!interval) {
|
||||
return ticks;
|
||||
diff --git a/node_modules/echarts/types/dist/shared.d.ts b/node_modules/echarts/types/dist/shared.d.ts
|
||||
index ca74097..98f8b18 100644
|
||||
--- a/node_modules/echarts/types/dist/shared.d.ts
|
||||
+++ b/node_modules/echarts/types/dist/shared.d.ts
|
||||
@@ -2422,6 +2422,9 @@ interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMixin {
|
||||
max: number;
|
||||
}) => ScaleDataValue);
|
||||
}
|
||||
+
|
||||
+declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[];
|
||||
+
|
||||
interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon {
|
||||
boundaryGap?: [number | string, number | string];
|
||||
/**
|
||||
@@ -2447,6 +2450,8 @@ interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon {
|
||||
* Will be ignored if interval is set.
|
||||
*/
|
||||
alignTicks?: boolean;
|
||||
+
|
||||
+ ticksGenerator?: NumericAxisTicksGenerator;
|
||||
}
|
||||
interface CategoryAxisBaseOption extends AxisBaseOptionCommon {
|
||||
type?: 'category';
|
||||
diff --git a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts
|
||||
index c5c2792..d524b70 100644
|
||||
--- a/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts
|
||||
+++ b/node_modules/echarts/types/src/coord/axisCommonTypes.d.ts
|
||||
@@ -56,6 +56,9 @@ export interface AxisBaseOptionCommon extends ComponentOption, AnimationOptionMi
|
||||
max: number;
|
||||
}) => ScaleDataValue);
|
||||
}
|
||||
+
|
||||
+export declare type NumericAxisTicksGenerator = (extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[];
|
||||
+
|
||||
export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon {
|
||||
boundaryGap?: [number | string, number | string];
|
||||
/**
|
||||
@@ -81,6 +84,8 @@ export interface NumericAxisBaseOptionCommon extends AxisBaseOptionCommon {
|
||||
* Will be ignored if interval is set.
|
||||
*/
|
||||
alignTicks?: boolean;
|
||||
+
|
||||
+ ticksGenerator?: NumericAxisTicksGenerator;
|
||||
}
|
||||
export interface CategoryAxisBaseOption extends AxisBaseOptionCommon {
|
||||
type?: 'category';
|
||||
|
||||
@ -44,7 +44,6 @@ import {
|
||||
getDataKey,
|
||||
getLatestSingleTsValue,
|
||||
overlayStyle,
|
||||
simpleDateFormat,
|
||||
textStyle
|
||||
} from '@shared/models/widget-settings.models';
|
||||
import { DataKey } from '@shared/models/widget.models';
|
||||
@ -55,10 +54,9 @@ import { ImagePipe } from '@shared/pipe/image.pipe';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart';
|
||||
import {
|
||||
TimeSeriesChartAxisSettings,
|
||||
TimeSeriesChartKeySettings,
|
||||
TimeSeriesChartSeriesType,
|
||||
TimeSeriesChartSettings, TimeSeriesChartYAxisSettings
|
||||
TimeSeriesChartSettings
|
||||
} from '@home/components/widget/lib/chart/time-series-chart.models';
|
||||
import { DeepPartial } from '@shared/models/common';
|
||||
|
||||
@ -189,8 +187,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit
|
||||
showSplitLines: true,
|
||||
min: 'dataMin',
|
||||
max: 'dataMax',
|
||||
intervalCalculator:
|
||||
'var scale = axis.scale; return !scale.isBlank() ? ((scale.getExtent()[1] - scale.getExtent()[0]) / 2) : undefined;'
|
||||
ticksGenerator: (extent?: number[]) => (extent ? [{ value: (extent[0] + extent[1]) / 2}] : [])
|
||||
}
|
||||
},
|
||||
tooltipDateInterval: false,
|
||||
|
||||
@ -19,21 +19,23 @@ import {
|
||||
EChartsOption,
|
||||
EChartsSeriesItem,
|
||||
EChartsTooltipTrigger,
|
||||
EChartsTooltipWidgetSettings, getYAxis,
|
||||
EChartsTooltipWidgetSettings,
|
||||
measureThresholdLabelOffset
|
||||
} from '@home/components/widget/lib/chart/echarts-widget.models';
|
||||
import {
|
||||
autoDateFormat, AutoDateFormatSettings,
|
||||
autoDateFormat,
|
||||
AutoDateFormatSettings,
|
||||
ComponentStyle,
|
||||
Font,
|
||||
simpleDateFormat,
|
||||
textStyle, tsToFormatTimeUnit
|
||||
textStyle,
|
||||
tsToFormatTimeUnit
|
||||
} from '@shared/models/widget-settings.models';
|
||||
import { XAXisOption, YAXisOption } from 'echarts/types/dist/shared';
|
||||
import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts';
|
||||
import {
|
||||
formatValue,
|
||||
isDefinedAndNotNull,
|
||||
isFunction,
|
||||
isNumeric,
|
||||
isUndefined,
|
||||
isUndefinedOrNull,
|
||||
@ -42,7 +44,6 @@ import {
|
||||
} from '@core/utils';
|
||||
import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D';
|
||||
import { ValueAxisBaseOption } from 'echarts/types/src/coord/axisCommonTypes';
|
||||
import { SeriesLabelOption } from 'echarts/types/src/util/types';
|
||||
import {
|
||||
@ -320,6 +321,12 @@ export const defaultXAxisTicksFormat: AutoDateFormatSettings = {
|
||||
|
||||
export type TimeSeriesChartYAxisId = 'default' | string;
|
||||
|
||||
export type TimeSeriesChartTicksGenerator =
|
||||
(extent?: number[], interval?: number, niceTickExtent?: number[], intervalPrecision?: number) => {value: number}[];
|
||||
|
||||
export type TimeSeriesChartTicksFormatter =
|
||||
(value: any) => string;
|
||||
|
||||
export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSettings {
|
||||
id?: TimeSeriesChartYAxisId;
|
||||
order?: number;
|
||||
@ -329,8 +336,8 @@ export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSetting
|
||||
splitNumber?: number;
|
||||
min?: number | string;
|
||||
max?: number | string;
|
||||
intervalCalculator?: string;
|
||||
ticksFormatter?: string;
|
||||
ticksGenerator?: TimeSeriesChartTicksGenerator | string;
|
||||
ticksFormatter?: TimeSeriesChartTicksFormatter | string;
|
||||
}
|
||||
|
||||
export const timeSeriesChartYAxisValid = (axis: TimeSeriesChartYAxisSettings): boolean =>
|
||||
@ -788,8 +795,6 @@ export interface TimeSeriesChartYAxis {
|
||||
decimals: number;
|
||||
settings: TimeSeriesChartYAxisSettings;
|
||||
option: YAXisOption & ValueAxisBaseOption;
|
||||
intervalCalculator?: (axis: Axis2D) => number;
|
||||
ticksFormatter?: (value: any) => string;
|
||||
}
|
||||
|
||||
export const createTimeSeriesYAxis = (units: string,
|
||||
@ -800,11 +805,37 @@ export const createTimeSeriesYAxis = (units: string,
|
||||
settings.tickLabelColor, darkMode, 'axis.tickLabel');
|
||||
const yAxisNameStyle = createChartTextStyle(settings.labelFont,
|
||||
settings.labelColor, darkMode, 'axis.label');
|
||||
let ticksFormatter: (value: any) => string;
|
||||
if (settings.ticksFormatter && settings.ticksFormatter.length) {
|
||||
ticksFormatter = parseFunction(settings.ticksFormatter, ['value']);
|
||||
|
||||
let ticksFormatter: TimeSeriesChartTicksFormatter;
|
||||
if (settings.ticksFormatter) {
|
||||
if (isFunction(settings.ticksFormatter)) {
|
||||
ticksFormatter = settings.ticksFormatter as TimeSeriesChartTicksFormatter;
|
||||
} else if (settings.ticksFormatter.length) {
|
||||
ticksFormatter = parseFunction(settings.ticksFormatter, ['value']);
|
||||
}
|
||||
}
|
||||
const yAxis: TimeSeriesChartYAxis = {
|
||||
let ticksGenerator: TimeSeriesChartTicksGenerator;
|
||||
let minInterval: number;
|
||||
let interval: number;
|
||||
let splitNumber: number;
|
||||
if (settings.ticksGenerator) {
|
||||
if (isFunction(settings.ticksGenerator)) {
|
||||
ticksGenerator = settings.ticksGenerator as TimeSeriesChartTicksGenerator;
|
||||
} else if (settings.ticksGenerator.length) {
|
||||
ticksGenerator = parseFunction(settings.ticksGenerator, ['extent', 'interval', 'niceTickExtent', 'intervalPrecision']);
|
||||
}
|
||||
}
|
||||
if (!ticksGenerator) {
|
||||
interval = settings.interval;
|
||||
if (isUndefinedOrNull(interval)) {
|
||||
if (isDefinedAndNotNull(settings.splitNumber)) {
|
||||
splitNumber = settings.splitNumber;
|
||||
} else {
|
||||
minInterval = (1 / Math.pow(10, decimals));
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: settings.id,
|
||||
decimals,
|
||||
settings,
|
||||
@ -818,6 +849,10 @@ export const createTimeSeriesYAxis = (units: string,
|
||||
scale: true,
|
||||
min: settings.min,
|
||||
max: settings.max,
|
||||
minInterval,
|
||||
splitNumber,
|
||||
interval,
|
||||
ticksGenerator,
|
||||
name: settings.label,
|
||||
nameLocation: 'middle',
|
||||
nameRotate: settings.position === AxisPosition.left ? 90 : -90,
|
||||
@ -853,7 +888,8 @@ export const createTimeSeriesYAxis = (units: string,
|
||||
if (ticksFormatter) {
|
||||
try {
|
||||
result = ticksFormatter(value);
|
||||
} catch (_e) {}
|
||||
} catch (_e) {
|
||||
}
|
||||
}
|
||||
if (isUndefined(result)) {
|
||||
result = formatValue(value, decimals, units, false);
|
||||
@ -869,54 +905,6 @@ export const createTimeSeriesYAxis = (units: string,
|
||||
}
|
||||
}
|
||||
};
|
||||
if (settings.intervalCalculator && settings.intervalCalculator.length) {
|
||||
yAxis.intervalCalculator = parseFunction(settings.intervalCalculator, ['axis']);
|
||||
}
|
||||
return yAxis;
|
||||
};
|
||||
|
||||
export const updateYAxisIntervals = (chart: ECharts,
|
||||
yAxis: TimeSeriesChartYAxis, empty: boolean): boolean => {
|
||||
let changed = false;
|
||||
let interval: number;
|
||||
let splitNumber: number;
|
||||
let minInterval: number;
|
||||
if (!empty) {
|
||||
interval = calculateYAxisInterval(chart, yAxis);
|
||||
if (isUndefinedOrNull(interval)) {
|
||||
if (isDefinedAndNotNull(yAxis.settings.splitNumber)) {
|
||||
splitNumber = yAxis.settings.splitNumber;
|
||||
} else {
|
||||
minInterval = (1 / Math.pow(10, yAxis.decimals));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (yAxis.option.interval !== interval) {
|
||||
yAxis.option.interval = interval;
|
||||
changed = true;
|
||||
}
|
||||
if (yAxis.option.splitNumber !== splitNumber) {
|
||||
yAxis.option.splitNumber = splitNumber;
|
||||
changed = true;
|
||||
}
|
||||
if (yAxis.option.minInterval !== minInterval) {
|
||||
yAxis.option.minInterval = minInterval;
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
};
|
||||
|
||||
const calculateYAxisInterval = (chart: ECharts, yAxis: TimeSeriesChartYAxis): number | undefined => {
|
||||
let interval = yAxis.settings.interval;
|
||||
if (yAxis.intervalCalculator) {
|
||||
const axis = getYAxis(chart, yAxis.id);
|
||||
if (axis) {
|
||||
try {
|
||||
interval = yAxis.intervalCalculator(axis);
|
||||
} catch (_e) {}
|
||||
}
|
||||
}
|
||||
return interval;
|
||||
};
|
||||
|
||||
export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartXAxisSettings,
|
||||
|
||||
@ -27,7 +27,8 @@ import {
|
||||
TimeSeriesChartDataItem,
|
||||
timeSeriesChartDefaultSettings,
|
||||
timeSeriesChartKeyDefaultSettings,
|
||||
TimeSeriesChartKeySettings, TimeSeriesChartNoAggregationBarWidthStrategy,
|
||||
TimeSeriesChartKeySettings,
|
||||
TimeSeriesChartNoAggregationBarWidthStrategy,
|
||||
TimeSeriesChartSeriesType,
|
||||
TimeSeriesChartSettings,
|
||||
TimeSeriesChartShape,
|
||||
@ -39,7 +40,7 @@ import {
|
||||
TimeSeriesChartYAxis,
|
||||
TimeSeriesChartYAxisId,
|
||||
TimeSeriesChartYAxisSettings,
|
||||
updateDarkMode, updateYAxisIntervals
|
||||
updateDarkMode
|
||||
} from '@home/components/widget/lib/chart/time-series-chart.models';
|
||||
import { ResizeObserver } from '@juggle/resize-observer';
|
||||
import {
|
||||
@ -611,11 +612,6 @@ export class TbTimeSeriesChart {
|
||||
this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option);
|
||||
this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: true});
|
||||
}
|
||||
changed = this.updateYAxesIntervals(this.yAxisList);
|
||||
if (changed) {
|
||||
this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option);
|
||||
this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis'], lazyUpdate: true});
|
||||
}
|
||||
if (this.yAxisList.length) {
|
||||
const extent = getAxisExtent(this.timeSeriesChart, this.yAxisList[0].id);
|
||||
const min = extent[0];
|
||||
@ -680,9 +676,6 @@ export class TbTimeSeriesChart {
|
||||
yAxis.option.scale = scaleYAxis;
|
||||
changed = true;
|
||||
}
|
||||
if (updateYAxisIntervals(this.timeSeriesChart, yAxis, this.yAxisEmpty(yAxis))) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
@ -693,22 +686,6 @@ export class TbTimeSeriesChart {
|
||||
return !axisBarDataItems.length;
|
||||
}
|
||||
|
||||
private yAxisEmpty(yAxis: TimeSeriesChartYAxis): boolean {
|
||||
const axisDataItems = this.dataItems.filter(d => d.yAxisId === yAxis.id && d.enabled &&
|
||||
d.data.length);
|
||||
return !axisDataItems.length;
|
||||
}
|
||||
|
||||
private updateYAxesIntervals(axisList: TimeSeriesChartYAxis[]): boolean {
|
||||
let changed = false;
|
||||
for (const yAxis of axisList) {
|
||||
if (updateYAxisIntervals(this.timeSeriesChart, yAxis, this.yAxisEmpty(yAxis))) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private minTopOffset(): number {
|
||||
const showTickLabels =
|
||||
!!this.yAxisList.find(yAxis => yAxis.settings.show && yAxis.settings.showTickLabels);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user