mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Heatmap: Build cell colors during data prep instead of render (#71509)
This commit is contained in:
parent
c3a0207615
commit
46c5e5417b
@ -5269,9 +5269,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "13"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "14"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "15"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "16"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "17"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "18"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "16"]
|
||||
],
|
||||
"public/app/plugins/panel/histogram/Histogram.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
|
@ -54,13 +54,15 @@ export const HeatmapPanel = ({
|
||||
[replaceVariables]
|
||||
);
|
||||
|
||||
const palette = useMemo(() => quantizeScheme(options.color, theme), [options.color, theme]);
|
||||
|
||||
const info = useMemo(() => {
|
||||
try {
|
||||
return prepareHeatmapData(data.series, data.annotations, options, theme, getFieldLinksSupplier);
|
||||
return prepareHeatmapData(data.series, data.annotations, options, palette, theme, getFieldLinksSupplier);
|
||||
} catch (ex) {
|
||||
return { warning: `${ex}` };
|
||||
}
|
||||
}, [data.series, data.annotations, options, theme, getFieldLinksSupplier]);
|
||||
}, [data.series, data.annotations, options, palette, theme, getFieldLinksSupplier]);
|
||||
|
||||
const facets = useMemo(() => {
|
||||
let exemplarsXFacet: number[] = []; // "Time" field
|
||||
@ -84,8 +86,6 @@ export const HeatmapPanel = ({
|
||||
return [null, info.heatmap?.fields.map((f) => f.values), [exemplarsXFacet, exemplarsyFacet]];
|
||||
}, [info.heatmap, info.exemplars]);
|
||||
|
||||
const palette = useMemo(() => quantizeScheme(options.color, theme), [options.color, theme]);
|
||||
|
||||
const [hover, setHover] = useState<HeatmapHoverEvent | undefined>(undefined);
|
||||
const [shouldDisplayCloseButton, setShouldDisplayCloseButton] = useState<boolean>(false);
|
||||
const isToolTipOpen = useRef<boolean>(false);
|
||||
@ -118,6 +118,7 @@ export const HeatmapPanel = ({
|
||||
const builder = useMemo(() => {
|
||||
const scaleConfig = dataRef.current?.heatmap?.fields[1].config?.custom
|
||||
?.scaleDistribution as ScaleDistributionConfig;
|
||||
|
||||
return prepConfig({
|
||||
dataRef,
|
||||
theme,
|
||||
@ -134,7 +135,6 @@ export const HeatmapPanel = ({
|
||||
timeZone,
|
||||
getTimeRange: () => timeRangeRef.current,
|
||||
sync,
|
||||
palette,
|
||||
cellGap: options.cellGap,
|
||||
hideLE: options.filterValues?.le,
|
||||
hideGE: options.filterValues?.ge,
|
||||
@ -167,8 +167,8 @@ export const HeatmapPanel = ({
|
||||
<ColorScale
|
||||
hoverValue={hoverValue}
|
||||
colorPalette={palette}
|
||||
min={dataRef.current.minValue!}
|
||||
max={dataRef.current.maxValue!}
|
||||
min={dataRef.current.heatmapColors?.minValue!}
|
||||
max={dataRef.current.heatmapColors?.maxValue!}
|
||||
display={info.display}
|
||||
/>
|
||||
</div>
|
||||
|
@ -21,10 +21,21 @@ import {
|
||||
import { parseSampleValue, sortSeriesByLabel } from 'app/plugins/datasource/prometheus/result_transformer';
|
||||
|
||||
import { CellValues, Options } from './types';
|
||||
import { boundedMinMax } from './utils';
|
||||
import { boundedMinMax, valuesToFills } from './utils';
|
||||
|
||||
export interface HeatmapData {
|
||||
heatmap?: DataFrame; // data we will render
|
||||
heatmapColors?: {
|
||||
// quantized palette
|
||||
palette: string[];
|
||||
// indices into palette
|
||||
values: number[];
|
||||
|
||||
// color scale range
|
||||
minValue: number;
|
||||
maxValue: number;
|
||||
};
|
||||
|
||||
exemplars?: DataFrame; // optionally linked exemplars
|
||||
exemplarColor?: string;
|
||||
|
||||
@ -43,10 +54,6 @@ export interface HeatmapData {
|
||||
xLogSplit?: number;
|
||||
yLogSplit?: number;
|
||||
|
||||
// color scale range
|
||||
minValue?: number;
|
||||
maxValue?: number;
|
||||
|
||||
// Print a heatmap cell value
|
||||
display?: (v: number) => string;
|
||||
|
||||
@ -58,6 +65,7 @@ export function prepareHeatmapData(
|
||||
frames: DataFrame[],
|
||||
annotations: DataFrame[] | undefined,
|
||||
options: Options,
|
||||
palette: string[],
|
||||
theme: GrafanaTheme2,
|
||||
getFieldLinks?: (exemplars: DataFrame, field: Field) => (config: ValueLinkConfig) => Array<LinkModel<Field>>
|
||||
): HeatmapData {
|
||||
@ -74,7 +82,13 @@ export function prepareHeatmapData(
|
||||
}
|
||||
|
||||
if (options.calculate) {
|
||||
return getDenseHeatmapData(calculateHeatmapFromData(frames, options.calculation ?? {}), exemplars, options, theme);
|
||||
return getDenseHeatmapData(
|
||||
calculateHeatmapFromData(frames, options.calculation ?? {}),
|
||||
exemplars,
|
||||
options,
|
||||
palette,
|
||||
theme
|
||||
);
|
||||
}
|
||||
|
||||
// Check for known heatmap types
|
||||
@ -83,8 +97,8 @@ export function prepareHeatmapData(
|
||||
switch (frame.meta?.type) {
|
||||
case DataFrameType.HeatmapCells:
|
||||
return isHeatmapCellsDense(frame)
|
||||
? getDenseHeatmapData(frame, exemplars, options, theme)
|
||||
: getSparseHeatmapData(frame, exemplars, options, theme);
|
||||
? getDenseHeatmapData(frame, exemplars, options, palette, theme)
|
||||
: getSparseHeatmapData(frame, exemplars, options, palette, theme);
|
||||
|
||||
case DataFrameType.HeatmapRows:
|
||||
rowsHeatmap = frame; // the default format
|
||||
@ -134,6 +148,7 @@ export function prepareHeatmapData(
|
||||
}),
|
||||
exemplars,
|
||||
options,
|
||||
palette,
|
||||
theme
|
||||
);
|
||||
}
|
||||
@ -142,6 +157,7 @@ const getSparseHeatmapData = (
|
||||
frame: DataFrame,
|
||||
exemplars: DataFrame | undefined,
|
||||
options: Options,
|
||||
palette: string[],
|
||||
theme: GrafanaTheme2
|
||||
): HeatmapData => {
|
||||
if (frame.meta?.type !== DataFrameType.HeatmapCells || isHeatmapCellsDense(frame)) {
|
||||
@ -154,11 +170,13 @@ const getSparseHeatmapData = (
|
||||
// y axis tick label display
|
||||
updateFieldDisplay(frame.fields[1], options.yAxis, theme);
|
||||
|
||||
const valueField = frame.fields[3];
|
||||
|
||||
// cell value display
|
||||
const disp = updateFieldDisplay(frame.fields[3], options.cellValues, theme);
|
||||
const disp = updateFieldDisplay(valueField, options.cellValues, theme);
|
||||
|
||||
let [minValue, maxValue] = boundedMinMax(
|
||||
frame.fields[3].values,
|
||||
valueField.values,
|
||||
options.color.min,
|
||||
options.color.max,
|
||||
options.filterValues?.le,
|
||||
@ -167,8 +185,12 @@ const getSparseHeatmapData = (
|
||||
|
||||
return {
|
||||
heatmap: frame,
|
||||
minValue,
|
||||
maxValue,
|
||||
heatmapColors: {
|
||||
palette,
|
||||
values: valuesToFills(valueField.values, palette, minValue, maxValue),
|
||||
minValue,
|
||||
maxValue,
|
||||
},
|
||||
exemplars,
|
||||
display: (v) => formattedValueToString(disp(v)),
|
||||
};
|
||||
@ -178,6 +200,7 @@ const getDenseHeatmapData = (
|
||||
frame: DataFrame,
|
||||
exemplars: DataFrame | undefined,
|
||||
options: Options,
|
||||
palette: string[],
|
||||
theme: GrafanaTheme2
|
||||
): HeatmapData => {
|
||||
if (frame.meta?.type !== DataFrameType.HeatmapCells) {
|
||||
@ -269,6 +292,13 @@ const getDenseHeatmapData = (
|
||||
|
||||
const data: HeatmapData = {
|
||||
heatmap: frame,
|
||||
heatmapColors: {
|
||||
palette,
|
||||
values: valuesToFills(valueField.values, palette, minValue, maxValue),
|
||||
minValue,
|
||||
maxValue,
|
||||
},
|
||||
|
||||
exemplars: exemplars?.length ? exemplars : undefined,
|
||||
xBucketSize: xBinIncr,
|
||||
yBucketSize: yBinIncr,
|
||||
@ -281,9 +311,6 @@ const getDenseHeatmapData = (
|
||||
xLogSplit: calcX?.scale?.log ? +(calcX?.value ?? '1') : 1,
|
||||
yLogSplit: calcY?.scale?.log ? +(calcY?.value ?? '1') : 1,
|
||||
|
||||
minValue,
|
||||
maxValue,
|
||||
|
||||
// TODO: improve heuristic
|
||||
xLayout:
|
||||
xName === 'xMax' ? HeatmapCellLayout.le : xName === 'xMin' ? HeatmapCellLayout.ge : HeatmapCellLayout.unknown,
|
||||
|
@ -47,10 +47,15 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(HeatmapPanel)
|
||||
|
||||
let isOrdinalY = false;
|
||||
|
||||
try {
|
||||
const v = prepareHeatmapData(context.data, undefined, opts, config.theme2);
|
||||
isOrdinalY = readHeatmapRowsCustomMeta(v.heatmap).yOrdinalDisplay != null;
|
||||
} catch {}
|
||||
if (context.data.length > 0) {
|
||||
try {
|
||||
// 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);
|
||||
isOrdinalY = readHeatmapRowsCustomMeta(v.heatmap).yOrdinalDisplay != null;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let category = ['Heatmap'];
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { prepareHeatmapData } from './fields';
|
||||
import { quantizeScheme } from './palettes';
|
||||
import { Options, defaultOptions } from './types';
|
||||
|
||||
export class HeatmapSuggestionsSupplier {
|
||||
@ -18,7 +19,8 @@ export class HeatmapSuggestionsSupplier {
|
||||
return;
|
||||
}
|
||||
|
||||
const info = prepareHeatmapData(builder.data.series, undefined, defaultOptions, config.theme2);
|
||||
const palette = quantizeScheme(defaultOptions.color, config.theme2);
|
||||
const info = prepareHeatmapData(builder.data.series, undefined, defaultOptions, palette, config.theme2);
|
||||
if (!info || info.warning) {
|
||||
return;
|
||||
}
|
||||
|
@ -67,7 +67,6 @@ interface PrepConfigOpts {
|
||||
isToolTipOpen: MutableRefObject<boolean>;
|
||||
timeZone: string;
|
||||
getTimeRange: () => TimeRange;
|
||||
palette: string[];
|
||||
exemplarColor: string;
|
||||
cellGap?: number | null; // in css pixels
|
||||
hideLE?: number;
|
||||
@ -88,7 +87,6 @@ export function prepConfig(opts: PrepConfigOpts) {
|
||||
isToolTipOpen,
|
||||
timeZone,
|
||||
getTimeRange,
|
||||
palette,
|
||||
cellGap,
|
||||
hideLE,
|
||||
hideGE,
|
||||
@ -97,6 +95,8 @@ export function prepConfig(opts: PrepConfigOpts) {
|
||||
sync,
|
||||
} = opts;
|
||||
|
||||
// console.log(dataRef.current);
|
||||
|
||||
const xScaleKey = 'x';
|
||||
let xScaleUnit = 'time';
|
||||
let isTime = true;
|
||||
@ -525,16 +525,8 @@ export function prepConfig(opts: PrepConfigOpts) {
|
||||
ySizeDivisor,
|
||||
disp: {
|
||||
fill: {
|
||||
values: (u, seriesIdx) => {
|
||||
let countFacetIdx = !isSparseHeatmap ? 2 : 3;
|
||||
return valuesToFills(
|
||||
u.data[seriesIdx][countFacetIdx] as unknown as number[],
|
||||
palette,
|
||||
dataRef.current?.minValue!,
|
||||
dataRef.current?.maxValue!
|
||||
);
|
||||
},
|
||||
index: palette,
|
||||
values: (u, seriesIdx) => dataRef.current?.heatmapColors?.values!,
|
||||
index: dataRef.current?.heatmapColors?.palette!,
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -965,7 +957,7 @@ export const boundedMinMax = (
|
||||
return [minValue, maxValue];
|
||||
};
|
||||
|
||||
export const valuesToFills = (values: number[], palette: string[], minValue: number, maxValue: number) => {
|
||||
export const valuesToFills = (values: number[], palette: string[], minValue: number, maxValue: number): number[] => {
|
||||
let range = maxValue - minValue || 1;
|
||||
|
||||
let paletteSize = palette.length;
|
||||
|
Loading…
Reference in New Issue
Block a user