kendoChart:有没有办法使用单个 valueAxis 显示多个系列的不同值标度?
kendoChart: Is there any way to display multiple series of differing value scales using a single valueAxis?
我正在使用单个 kendoChart 来显示最多 10 行数据。
每行代表可能具有广泛不同上下文和 min/max 范围的过程数据,但所有行在时间上相关,类别轴。显示时,每个 valueAxis 正确显示相应行的比例。
但是,对于 10 行,10 个 valueAxes 占据了太多屏幕,无法满足我的要求。
我尝试隐藏除一个轴以外的所有轴,期望图表会扩展以填满隐藏轴采用的 space,但确实如此
不会发生。我得到一个被空白包围的单独轴 space 并且图表的绘图区域保持相同大小。
我尝试将所有系列设置为使用相同的 valueAxis,然后根据通过单击选择的活动通道改变 valueAxis min/max
传奇物品。这会根据需要扩展绘图区域,但由于比例特定于一条线,因此无法查看所有线。
kendoChart 是否可以独立于单个 valueAxis 显示多个图(例如,值在 0.5 和 0.7 之间的线会显示为缩放到整个图表区域,值在 25 和 100 之间的线也会如此,但 valueAxis 可能会显示任一刻度。)
我用于此问题的解决方案比我预期需要的代码更多。也许 Telerik 的其他产品对此有 API。
本质上,我在 kendoChart 之外维护了一个结构,该结构存储每个系列的真实数据,并将这些真实数据映射到当前可见的 valueAxis 的预期比例。映射函数是从一种尺度到另一种尺度的标准变换。
valueAxis 是 'swapped',具体取决于单击的图例项,并且该事件会触发图表上的重绘,其中所有系列数据都映射到 'active' 轴。
一些代码片段。一个系列也被描述为一个频道。
// The data structure.
this._channelDescriptors.push({
fullName: ch.fullName || "",
axisTitle: (ch.fullName + axisEUString) || "",
axisFont: ch.axisFont || "",
axisColor: ch.color || "#000000",
realData: [],
minData: Number.MAX_VALUE,
maxData: Number.MIN_VALUE
});
// This event causes the switching of valueAxis for all members of the series.
$("#" + chartID).kendoChart({
// Other kendoChart configurations
//
legendItemClick: function (e) {
var idx = e.seriesIndex;
sncTrender.updateAxis(idx);
e.preventDefault();
},
tooltip: {
visible: true,
template: "#=series.name# : #=kendo.format('{0:N4}', dataItem.realValue)#<br />#=kendo.format('{0:MM-dd HH:mm:ss.fff}', dataItem.Time)#",
},
//
// Other kendoChart configurations
});
// All code snippets are members of a wrapper object.
updateAxis: function (ch) {
if (this.series[ch].visible) {
this.setAxis(ch);
}
},
// Every series is set to the same valueAxis via the selected series' valueAxis.name property.
setAxis: function (ch) {
var i,
channel = this._channelDescriptors[ch];
this._currentChannel = ch;
for (i = 0; i < this.series.length; i++) {
this.series[i].axis = this._channelDescriptors[ch].fullName;
}
// Set the active valueAxis properties. This is the only axis visible maintained for the chart.
this.valueAxis.name = channel.fullName;
this.valueAxis.title.text = channel.axisTitle;
this.valueAxis.title.font = channel.axisFont;
this.valueAxis.line.color = channel.axisColor;
},
// The mapping occurs here, and the transform calculation is this line
// Y: (yRange * (chDesc.realData[k].realValue - newMin) / newRange) + this.valueAxis.min,
//
updateChart: function (allTrends) {
// ...
timeStamps = trendDataResponse.curve.Timestamp;
t1 = trendArgs.t1;
t2 = trendArgs.t2;
xValues = trendDataResponse.curve.X;
yValues = trendDataResponse.curve.Y;
pointCount = xValues.length;
min = Number.MAX_VALUE;
max = Number.MIN_VALUE;
categoryTimes = [pointCount];
newData = [];
for (l = 0; l < pointCount; l++) {
min = Math.min(min, yValues[l]);
max = Math.max(max, yValues[l]);
ts = new Date(timeStamps[l]);
categoryTimes[l] = ts;
// The Y data will be plotted on the chart, but the cursor tooltip will
// use the realValue data. In this way, the series can be visible regardless of
// the valueAxis scaling, but the actual data is also available. Refer to the
// tooltip template.
newData.push({ X: xValues[l], Y: yValues[l], realValue: yValues[l], Time: ts });
}
// Real data for each channel is stored in channelDescriptors.
chDesc = this._channelDescriptors[channelID];
chDesc.realData = newData;
chDesc.minData = min;
chDesc.maxData = max;
// The valueAxis min/max is set only for the 'active' series.
if (this._currentChannel === channelID) {
this.categoryAxis.categories = categoryTimes;
yRange = max - min;
scaleAdjustment = yRange * SNC.CONST_yAxisScaleAdjustmentFactor;
this.valueAxis.min = min - scaleAdjustment;
this.valueAxis.max = max + scaleAdjustment;
}
}
// Scale curves to current axis.
// Use real data for the current series.
for (j = 0; j < this.series.length; ++j) {
chDesc = this._channelDescriptors[j];
if (j === this._currentChannel) {
this.series[j].data = chDesc.realData;
continue;
}
// Use mapped data for all other series.
recalcData = [];
newMin = chDesc.minData;
newMax = chDesc.maxData;
newRange = newMax - newMin;
rangeAdjustment = newRange * SNC.CONST_yAxisScaleAdjustmentFactor;
newMin = newMin - rangeAdjustment;
newMax = newMax + rangeAdjustment;
for (k = 0; k < chDesc.realData.length; ++k) {
recalcData.push({
X: chDesc.realData[k].X,
Y: (yRange * (chDesc.realData[k].realValue - newMin) / newRange) + this.valueAxis.min,
realValue: chDesc.realData[k].realValue,
Time: chDesc.realData[k].Time,
});
}
this.series[j].data = recalcData;
}
chart.redraw();
}
我正在使用单个 kendoChart 来显示最多 10 行数据。
每行代表可能具有广泛不同上下文和 min/max 范围的过程数据,但所有行在时间上相关,类别轴。显示时,每个 valueAxis 正确显示相应行的比例。
但是,对于 10 行,10 个 valueAxes 占据了太多屏幕,无法满足我的要求。
我尝试隐藏除一个轴以外的所有轴,期望图表会扩展以填满隐藏轴采用的 space,但确实如此 不会发生。我得到一个被空白包围的单独轴 space 并且图表的绘图区域保持相同大小。
我尝试将所有系列设置为使用相同的 valueAxis,然后根据通过单击选择的活动通道改变 valueAxis min/max 传奇物品。这会根据需要扩展绘图区域,但由于比例特定于一条线,因此无法查看所有线。
kendoChart 是否可以独立于单个 valueAxis 显示多个图(例如,值在 0.5 和 0.7 之间的线会显示为缩放到整个图表区域,值在 25 和 100 之间的线也会如此,但 valueAxis 可能会显示任一刻度。)
我用于此问题的解决方案比我预期需要的代码更多。也许 Telerik 的其他产品对此有 API。
本质上,我在 kendoChart 之外维护了一个结构,该结构存储每个系列的真实数据,并将这些真实数据映射到当前可见的 valueAxis 的预期比例。映射函数是从一种尺度到另一种尺度的标准变换。
valueAxis 是 'swapped',具体取决于单击的图例项,并且该事件会触发图表上的重绘,其中所有系列数据都映射到 'active' 轴。
一些代码片段。一个系列也被描述为一个频道。
// The data structure.
this._channelDescriptors.push({
fullName: ch.fullName || "",
axisTitle: (ch.fullName + axisEUString) || "",
axisFont: ch.axisFont || "",
axisColor: ch.color || "#000000",
realData: [],
minData: Number.MAX_VALUE,
maxData: Number.MIN_VALUE
});
// This event causes the switching of valueAxis for all members of the series.
$("#" + chartID).kendoChart({
// Other kendoChart configurations
//
legendItemClick: function (e) {
var idx = e.seriesIndex;
sncTrender.updateAxis(idx);
e.preventDefault();
},
tooltip: {
visible: true,
template: "#=series.name# : #=kendo.format('{0:N4}', dataItem.realValue)#<br />#=kendo.format('{0:MM-dd HH:mm:ss.fff}', dataItem.Time)#",
},
//
// Other kendoChart configurations
});
// All code snippets are members of a wrapper object.
updateAxis: function (ch) {
if (this.series[ch].visible) {
this.setAxis(ch);
}
},
// Every series is set to the same valueAxis via the selected series' valueAxis.name property.
setAxis: function (ch) {
var i,
channel = this._channelDescriptors[ch];
this._currentChannel = ch;
for (i = 0; i < this.series.length; i++) {
this.series[i].axis = this._channelDescriptors[ch].fullName;
}
// Set the active valueAxis properties. This is the only axis visible maintained for the chart.
this.valueAxis.name = channel.fullName;
this.valueAxis.title.text = channel.axisTitle;
this.valueAxis.title.font = channel.axisFont;
this.valueAxis.line.color = channel.axisColor;
},
// The mapping occurs here, and the transform calculation is this line
// Y: (yRange * (chDesc.realData[k].realValue - newMin) / newRange) + this.valueAxis.min,
//
updateChart: function (allTrends) {
// ...
timeStamps = trendDataResponse.curve.Timestamp;
t1 = trendArgs.t1;
t2 = trendArgs.t2;
xValues = trendDataResponse.curve.X;
yValues = trendDataResponse.curve.Y;
pointCount = xValues.length;
min = Number.MAX_VALUE;
max = Number.MIN_VALUE;
categoryTimes = [pointCount];
newData = [];
for (l = 0; l < pointCount; l++) {
min = Math.min(min, yValues[l]);
max = Math.max(max, yValues[l]);
ts = new Date(timeStamps[l]);
categoryTimes[l] = ts;
// The Y data will be plotted on the chart, but the cursor tooltip will
// use the realValue data. In this way, the series can be visible regardless of
// the valueAxis scaling, but the actual data is also available. Refer to the
// tooltip template.
newData.push({ X: xValues[l], Y: yValues[l], realValue: yValues[l], Time: ts });
}
// Real data for each channel is stored in channelDescriptors.
chDesc = this._channelDescriptors[channelID];
chDesc.realData = newData;
chDesc.minData = min;
chDesc.maxData = max;
// The valueAxis min/max is set only for the 'active' series.
if (this._currentChannel === channelID) {
this.categoryAxis.categories = categoryTimes;
yRange = max - min;
scaleAdjustment = yRange * SNC.CONST_yAxisScaleAdjustmentFactor;
this.valueAxis.min = min - scaleAdjustment;
this.valueAxis.max = max + scaleAdjustment;
}
}
// Scale curves to current axis.
// Use real data for the current series.
for (j = 0; j < this.series.length; ++j) {
chDesc = this._channelDescriptors[j];
if (j === this._currentChannel) {
this.series[j].data = chDesc.realData;
continue;
}
// Use mapped data for all other series.
recalcData = [];
newMin = chDesc.minData;
newMax = chDesc.maxData;
newRange = newMax - newMin;
rangeAdjustment = newRange * SNC.CONST_yAxisScaleAdjustmentFactor;
newMin = newMin - rangeAdjustment;
newMax = newMax + rangeAdjustment;
for (k = 0; k < chDesc.realData.length; ++k) {
recalcData.push({
X: chDesc.realData[k].X,
Y: (yRange * (chDesc.realData[k].realValue - newMin) / newRange) + this.valueAxis.min,
realValue: chDesc.realData[k].realValue,
Time: chDesc.realData[k].Time,
});
}
this.series[j].data = recalcData;
}
chart.redraw();
}