mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Heatmap: Use dashboard timeRange for auto-sizing x buckets (#89382)
This commit is contained in:
parent
7c69f3657b
commit
74bcfb284c
@ -15,6 +15,7 @@ import {
|
||||
durationToMilliseconds,
|
||||
parseDuration,
|
||||
TransformationApplicabilityLevels,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
import { isLikelyAscendingVector } from '@grafana/data/src/transformations/transformers/joinDataFrames';
|
||||
import { config } from '@grafana/runtime';
|
||||
@ -292,7 +293,14 @@ export function prepBucketFrames(frames: DataFrame[]): DataFrame[] {
|
||||
}));
|
||||
}
|
||||
|
||||
export function calculateHeatmapFromData(frames: DataFrame[], options: HeatmapCalculationOptions): DataFrame {
|
||||
interface HeatmapCalculationOptionsWithTimeRange extends HeatmapCalculationOptions {
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
export function calculateHeatmapFromData(
|
||||
frames: DataFrame[],
|
||||
options: HeatmapCalculationOptionsWithTimeRange
|
||||
): DataFrame {
|
||||
// Find fields in the heatmap
|
||||
const { xField, yField, xs, ys } = findHeatmapFields(frames);
|
||||
|
||||
@ -329,6 +337,9 @@ export function calculateHeatmapFromData(frames: DataFrame[], options: HeatmapCa
|
||||
ySize: yBucketsCfg.value ? +yBucketsCfg.value : undefined,
|
||||
yLog:
|
||||
scaleDistribution?.type === ScaleDistribution.Log ? (scaleDistribution?.log as 2 | 10 | undefined) : undefined,
|
||||
|
||||
xMin: options.timeRange?.from.valueOf(),
|
||||
xMax: options.timeRange?.to.valueOf(),
|
||||
});
|
||||
|
||||
const frame = {
|
||||
@ -460,20 +471,23 @@ function heatmap(xs: number[], ys: number[], opts?: HeatmapOpts) {
|
||||
let ySorted = opts?.ySorted ?? false;
|
||||
|
||||
// find x and y limits to pre-compute buckets struct
|
||||
let minX = xSorted ? xs[0] : Infinity;
|
||||
let minX = opts?.xMin ?? (xSorted ? xs[0] : Infinity);
|
||||
let minY = ySorted ? ys[0] : Infinity;
|
||||
let maxX = xSorted ? xs[len - 1] : -Infinity;
|
||||
let maxX = opts?.xMax ?? (xSorted ? xs[len - 1] : -Infinity);
|
||||
let maxY = ySorted ? ys[len - 1] : -Infinity;
|
||||
|
||||
let yExp = opts?.yLog;
|
||||
|
||||
let withPredefX = opts?.xMin != null && opts?.xMax != null;
|
||||
let withPredefY = opts?.yMin != null && opts?.yMax != null;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (!xSorted) {
|
||||
if (!xSorted && !withPredefX) {
|
||||
minX = Math.min(minX, xs[i]);
|
||||
maxX = Math.max(maxX, xs[i]);
|
||||
}
|
||||
|
||||
if (!ySorted) {
|
||||
if (!ySorted && !withPredefY) {
|
||||
if (!yExp || ys[i] > 0) {
|
||||
minY = Math.min(minY, ys[i]);
|
||||
maxY = Math.max(maxY, ys[i]);
|
||||
@ -500,7 +514,6 @@ function heatmap(xs: number[], ys: number[], opts?: HeatmapOpts) {
|
||||
}
|
||||
|
||||
if (xMode === HeatmapCalculationMode.Count) {
|
||||
// TODO: optionally use view range min/max instead of data range for bucket sizing
|
||||
let approx = (maxX - minX) / Math.max(xBinIncr - 1, 1);
|
||||
// nice-ify
|
||||
let xIncrs = opts?.xTime ? niceTimeIncrs : niceLinearIncrs;
|
||||
@ -509,7 +522,6 @@ function heatmap(xs: number[], ys: number[], opts?: HeatmapOpts) {
|
||||
}
|
||||
|
||||
if (yMode === HeatmapCalculationMode.Count) {
|
||||
// TODO: optionally use view range min/max instead of data range for bucket sizing
|
||||
let approx = (maxY - minY) / Math.max(yBinIncr - 1, 1);
|
||||
// nice-ify
|
||||
let yIncrs = opts?.yTime ? niceTimeIncrs : niceLinearIncrs;
|
||||
|
@ -59,11 +59,19 @@ export const HeatmapPanel = ({
|
||||
|
||||
const info = useMemo(() => {
|
||||
try {
|
||||
return prepareHeatmapData(data.series, data.annotations, options, palette, theme, replaceVariables);
|
||||
return prepareHeatmapData({
|
||||
frames: data.series,
|
||||
annotations: data.annotations,
|
||||
options,
|
||||
palette,
|
||||
theme,
|
||||
replaceVariables,
|
||||
timeRange,
|
||||
});
|
||||
} catch (ex) {
|
||||
return { warning: `${ex}` };
|
||||
}
|
||||
}, [data.series, data.annotations, options, palette, theme, replaceVariables]);
|
||||
}, [data.series, data.annotations, options, palette, theme, replaceVariables, timeRange]);
|
||||
|
||||
const facets = useMemo(() => {
|
||||
let exemplarsXFacet: number[] | undefined = []; // "Time" field
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
GrafanaTheme2,
|
||||
InterpolateFunction,
|
||||
outerJoinDataFrames,
|
||||
TimeRange,
|
||||
ValueFormatter,
|
||||
} from '@grafana/data';
|
||||
import { parseSampleValue, sortSeriesByLabel } from '@grafana/prometheus';
|
||||
@ -65,14 +66,25 @@ export interface HeatmapData {
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
export function prepareHeatmapData(
|
||||
frames: DataFrame[],
|
||||
annotations: DataFrame[] | undefined,
|
||||
options: Options,
|
||||
palette: string[],
|
||||
theme: GrafanaTheme2,
|
||||
replaceVariables: InterpolateFunction = (v) => v
|
||||
): HeatmapData {
|
||||
interface PrepareHeatmapDataOptions {
|
||||
frames: DataFrame[];
|
||||
annotations?: DataFrame[];
|
||||
options: Options;
|
||||
palette: string[];
|
||||
theme: GrafanaTheme2;
|
||||
replaceVariables?: InterpolateFunction;
|
||||
timeRange?: TimeRange;
|
||||
}
|
||||
|
||||
export function prepareHeatmapData({
|
||||
frames,
|
||||
annotations,
|
||||
options,
|
||||
palette,
|
||||
theme,
|
||||
replaceVariables = (v) => v,
|
||||
timeRange,
|
||||
}: PrepareHeatmapDataOptions): HeatmapData {
|
||||
if (!frames?.length) {
|
||||
return {};
|
||||
}
|
||||
@ -104,7 +116,7 @@ export function prepareHeatmapData(
|
||||
}
|
||||
|
||||
return getDenseHeatmapData(
|
||||
calculateHeatmapFromData(frames, optionsCopy.calculation ?? {}),
|
||||
calculateHeatmapFromData(frames, { ...options.calculation, timeRange }),
|
||||
exemplars,
|
||||
optionsCopy,
|
||||
palette,
|
||||
@ -113,7 +125,7 @@ export function prepareHeatmapData(
|
||||
}
|
||||
|
||||
return getDenseHeatmapData(
|
||||
calculateHeatmapFromData(frames, options.calculation ?? {}),
|
||||
calculateHeatmapFromData(frames, { ...options.calculation, timeRange }),
|
||||
exemplars,
|
||||
options,
|
||||
palette,
|
||||
|
@ -53,7 +53,12 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(HeatmapPanel)
|
||||
// NOTE: this feels like overkill/expensive just to assert if we have an ordinal y
|
||||
// can probably simplify without doing full dataprep
|
||||
const palette = quantizeScheme(opts.color, config.theme2);
|
||||
const v = prepareHeatmapData(context.data, undefined, opts, palette, config.theme2);
|
||||
const v = prepareHeatmapData({
|
||||
frames: context.data,
|
||||
options: opts,
|
||||
palette,
|
||||
theme: config.theme2,
|
||||
});
|
||||
isOrdinalY = readHeatmapRowsCustomMeta(v.heatmap).yOrdinalDisplay != null;
|
||||
} catch {}
|
||||
}
|
||||
|
@ -20,7 +20,12 @@ export class HeatmapSuggestionsSupplier {
|
||||
}
|
||||
|
||||
const palette = quantizeScheme(defaultOptions.color, config.theme2);
|
||||
const info = prepareHeatmapData(builder.data.series, undefined, defaultOptions, palette, config.theme2);
|
||||
const info = prepareHeatmapData({
|
||||
frames: builder.data.series,
|
||||
options: defaultOptions,
|
||||
palette,
|
||||
theme: config.theme2,
|
||||
});
|
||||
if (!info || info.warning) {
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user