From c104f31149cb3b0bea25aade7ad0860ddb8984b8 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 18:57:18 +0300 Subject: [PATCH 1/9] heatmap: fix legend for small values, #14019 #15683 --- public/app/plugins/panel/heatmap/color_legend.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index c36fad45cba..a62589a6bf9 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -95,10 +95,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - let rangeStep = 1; - if (rangeTo - rangeFrom > legendWidth) { - rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth); - } + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -115,7 +112,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal .attr('stroke-width', 0) .attr('fill', d => colorScale(d)); - drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth); + drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange); } function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue) { @@ -126,10 +123,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - let rangeStep = 1; - if (rangeTo - rangeFrom > legendWidth) { - rangeStep = Math.floor((rangeTo - rangeFrom) / legendWidth); - } + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -147,10 +141,10 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue .attr('fill', options.cardColor) .style('opacity', d => opacityScale(d)); - drawLegendValues(elem, opacityScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth); + drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange); } -function drawLegendValues(elem, colorScale, rangeFrom, rangeTo, maxValue, minValue, legendWidth) { +function drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWidth, valuesRange) { const legendElem = $(elem).find('svg'); const legend = d3.select(legendElem.get(0)); From 55d5219a053cbe294b3168f5f749d3de83aade4b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 19:49:14 +0300 Subject: [PATCH 2/9] heatmap: fix legend padding --- public/app/plugins/panel/heatmap/color_legend.ts | 9 ++++++++- public/sass/components/_panel_heatmap.scss | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index a62589a6bf9..b397d0b3eee 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -11,6 +11,7 @@ const LEGEND_HEIGHT_PX = 6; const LEGEND_WIDTH_PX = 100; const LEGEND_TICK_SIZE = 0; const LEGEND_VALUE_MARGIN = 0; +const LEGEND_PADDING_LEFT = 10; /** * Color legend for heatmap editor. @@ -101,6 +102,9 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const colorScale = getColorScale(colorScheme, contextSrv.user.lightTheme, maxValue, minValue); legend + .append('g') + .attr('class', 'legend-color-bar') + .attr('transform', 'translate(' + LEGEND_PADDING_LEFT + ',0)') .selectAll('.heatmap-color-legend-rect') .data(valuesRange) .enter() @@ -129,6 +133,9 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const opacityScale = getOpacityScale(options, maxValue, minValue); legend + .append('g') + .attr('class', 'legend-color-bar') + .attr('transform', 'translate(' + LEGEND_PADDING_LEFT + ',0)') .selectAll('.heatmap-opacity-legend-rect') .data(valuesRange) .enter() @@ -165,7 +172,7 @@ function drawLegendValues(elem, rangeFrom, rangeTo, maxValue, minValue, legendWi const colorRect = legendElem.find(':first-child'); const posY = getSvgElemHeight(legendElem) + LEGEND_VALUE_MARGIN; - const posX = getSvgElemX(colorRect); + const posX = getSvgElemX(colorRect) + LEGEND_PADDING_LEFT; d3.select(legendElem.get(0)) .append('g') diff --git a/public/sass/components/_panel_heatmap.scss b/public/sass/components/_panel_heatmap.scss index 279b9392caa..dad1dc3235b 100644 --- a/public/sass/components/_panel_heatmap.scss +++ b/public/sass/components/_panel_heatmap.scss @@ -66,7 +66,6 @@ $font-size-heatmap-tick: 11px; height: 18px; float: left; white-space: nowrap; - padding-left: 10px; } .heatmap-legend-values { From 7167fa9d07e9f23307b37bf949cb32ec1ef7d432 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Thu, 7 Mar 2019 20:05:32 +0300 Subject: [PATCH 3/9] heatmap: reduce number of legend segments to reasonable value and round x values to prevent gaps --- public/app/plugins/panel/heatmap/color_legend.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/heatmap/color_legend.ts b/public/app/plugins/panel/heatmap/color_legend.ts index b397d0b3eee..4b4926c01bd 100644 --- a/public/app/plugins/panel/heatmap/color_legend.ts +++ b/public/app/plugins/panel/heatmap/color_legend.ts @@ -12,6 +12,7 @@ const LEGEND_WIDTH_PX = 100; const LEGEND_TICK_SIZE = 0; const LEGEND_VALUE_MARGIN = 0; const LEGEND_PADDING_LEFT = 10; +const LEGEND_SEGMENT_WIDTH = 10; /** * Color legend for heatmap editor. @@ -96,7 +97,7 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -109,9 +110,9 @@ function drawColorLegend(elem, colorScheme, rangeFrom, rangeTo, maxValue, minVal .data(valuesRange) .enter() .append('rect') - .attr('x', d => d * widthFactor) + .attr('x', d => Math.round(d * widthFactor)) .attr('y', 0) - .attr('width', rangeStep * widthFactor + 1) // Overlap rectangles to prevent gaps + .attr('width', Math.round(rangeStep * widthFactor + 1)) // Overlap rectangles to prevent gaps .attr('height', legendHeight) .attr('stroke-width', 0) .attr('fill', d => colorScale(d)); @@ -127,7 +128,7 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue const legendWidth = Math.floor(legendElem.outerWidth()) - 30; const legendHeight = legendElem.attr('height'); - const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * 2; + const rangeStep = ((rangeTo - rangeFrom) / legendWidth) * LEGEND_SEGMENT_WIDTH; const widthFactor = legendWidth / (rangeTo - rangeFrom); const valuesRange = d3.range(rangeFrom, rangeTo, rangeStep); @@ -140,9 +141,9 @@ function drawOpacityLegend(elem, options, rangeFrom, rangeTo, maxValue, minValue .data(valuesRange) .enter() .append('rect') - .attr('x', d => d * widthFactor) + .attr('x', d => Math.round(d * widthFactor)) .attr('y', 0) - .attr('width', rangeStep * widthFactor) + .attr('width', Math.round(rangeStep * widthFactor)) .attr('height', legendHeight) .attr('stroke-width', 0) .attr('fill', options.cardColor) From 9dbcc0fb6ec04c879b641290d1a53639462baa0f Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 11 Mar 2019 11:46:30 +0300 Subject: [PATCH 4/9] heatmap: middle bucket bound option, #15683 --- public/app/plugins/panel/heatmap/axes_editor.ts | 1 + public/app/plugins/panel/heatmap/heatmap_tooltip.ts | 7 +++++-- public/app/plugins/panel/heatmap/rendering.ts | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/heatmap/axes_editor.ts b/public/app/plugins/panel/heatmap/axes_editor.ts index 81df957e2ea..cec3df9f8c5 100644 --- a/public/app/plugins/panel/heatmap/axes_editor.ts +++ b/public/app/plugins/panel/heatmap/axes_editor.ts @@ -32,6 +32,7 @@ export class AxesEditorCtrl { Auto: 'auto', Upper: 'upper', Lower: 'lower', + Middle: 'middle', }; } diff --git a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts index c447825eef8..90cbecd6657 100644 --- a/public/app/plugins/panel/heatmap/heatmap_tooltip.ts +++ b/public/app/plugins/panel/heatmap/heatmap_tooltip.ts @@ -114,7 +114,9 @@ export class HeatmapTooltip { }; boundBottom = tickFormatter(yBucketIndex); - boundTop = yBucketIndex < data.tsBuckets.length - 1 ? tickFormatter(yBucketIndex + 1) : ''; + if (this.panel.yBucketBound !== 'middle') { + boundTop = yBucketIndex < data.tsBuckets.length - 1 ? tickFormatter(yBucketIndex + 1) : ''; + } } else { // Display 0 if bucket is a special 'zero' bucket const bottom = yData.y ? yData.bounds.bottom : 0; @@ -122,8 +124,9 @@ export class HeatmapTooltip { boundTop = bucketBoundFormatter(yData.bounds.top); } valuesNumber = countValueFormatter(yData.count); + const boundStr = boundTop && boundBottom ? `${boundBottom} - ${boundTop}` : boundBottom || boundTop; tooltipHtml += `
- bucket: ${boundBottom} - ${boundTop}
+ bucket: ${boundStr}
count: ${valuesNumber}
`; } else { diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index 42b418b06cb..a4649428722 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -379,6 +379,12 @@ export class HeatmapRenderer { const posX = this.getYAxisWidth(this.heatmap) + Y_AXIS_TICK_PADDING; this.heatmap.select('.axis-y').attr('transform', 'translate(' + posX + ',' + posY + ')'); + if (this.panel.yBucketBound === 'middle' && tickValues && tickValues.length) { + // Shift Y axis labels to the middle of bucket + const tickShift = 0 - this.chartHeight / (tickValues.length - 1) / 2; + this.heatmap.selectAll('.axis-y text').attr('transform', 'translate(' + 0 + ',' + tickShift + ')'); + } + // Remove vertical line in the right of axis labels (called domain in d3) this.heatmap .select('.axis-y') From bef024e4eb3026c1cf3eb33f56b4cf6bb06e250a Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 11 Mar 2019 11:58:15 +0300 Subject: [PATCH 5/9] heatmap: fix error when series empty --- public/app/plugins/panel/heatmap/heatmap_ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 71e059a5750..0f44dbd477b 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -146,7 +146,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl { } onRender() { - if (!this.range) { + if (!this.range || !this.series) { return; } From 57f48f17a0d8d81db7462a4d3d7da843623fd85d Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 11 Mar 2019 12:42:16 +0300 Subject: [PATCH 6/9] heatmap: don't display cut cards --- public/app/plugins/panel/heatmap/rendering.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/heatmap/rendering.ts b/public/app/plugins/panel/heatmap/rendering.ts index a4649428722..59d704f0d55 100644 --- a/public/app/plugins/panel/heatmap/rendering.ts +++ b/public/app/plugins/panel/heatmap/rendering.ts @@ -621,8 +621,8 @@ export class HeatmapRenderer { w = this.cardWidth; } - // Card width should be MIN_CARD_SIZE at least - w = Math.max(w, MIN_CARD_SIZE); + // Card width should be MIN_CARD_SIZE at least, but cut cards shouldn't be displayed + w = w > 0 ? Math.max(w, MIN_CARD_SIZE) : 0; return w; } From 2085397f31a672be01aa234416bf1366202b4a9b Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 11 Mar 2019 15:54:40 +0300 Subject: [PATCH 7/9] heatmap: fix middle bucket bound for prometheus --- public/app/plugins/panel/heatmap/heatmap_ctrl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 0f44dbd477b..33d4511f18e 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -231,7 +231,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl { tsBuckets = _.map(this.series, 'label'); const yBucketBound = this.panel.yBucketBound; - if ((panelDatasource === 'prometheus' && yBucketBound !== 'lower') || yBucketBound === 'upper') { + if ( + (panelDatasource === 'prometheus' && yBucketBound !== 'lower' && yBucketBound !== 'middle') || + yBucketBound === 'upper' + ) { // Prometheus labels are upper inclusive bounds, so add empty bottom bucket label. tsBuckets = [''].concat(tsBuckets); } else { From abb8e71fdc99f283018fab8e23d5ba89445d4dca Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 11 Mar 2019 17:59:59 +0300 Subject: [PATCH 8/9] heatmap: able to reverse Y buckets order, #15683 --- public/app/plugins/panel/heatmap/heatmap_ctrl.ts | 9 +++++++-- .../app/plugins/panel/heatmap/partials/axes_editor.html | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts index 33d4511f18e..329acbd7ee4 100644 --- a/public/app/plugins/panel/heatmap/heatmap_ctrl.ts +++ b/public/app/plugins/panel/heatmap/heatmap_ctrl.ts @@ -34,6 +34,7 @@ const panelDefaults = { }, dataFormat: 'timeseries', yBucketBound: 'auto', + reverseYBuckets: false, xAxis: { show: true, }, @@ -108,7 +109,7 @@ export class HeatmapCtrl extends MetricsPanelCtrl { selectionActivated: boolean; unitFormats: any; data: any; - series: any; + series: any[]; timeSrv: any; dataWarning: any; decimals: number; @@ -225,8 +226,12 @@ export class HeatmapCtrl extends MetricsPanelCtrl { this.series.sort(sortSeriesByLabel); } + if (this.panel.reverseYBuckets) { + this.series.reverse(); + } + // Convert histogram to heatmap. Each histogram bucket represented by the series which name is - // a top (or bottom, depends of datasource) bucket bound. Further, these values will be used as X axis labels. + // a top (or bottom, depends of datasource) bucket bound. Further, these values will be used as Y axis labels. bucketsData = histogramToHeatmap(this.series); tsBuckets = _.map(this.series, 'label'); diff --git a/public/app/plugins/panel/heatmap/partials/axes_editor.html b/public/app/plugins/panel/heatmap/partials/axes_editor.html index 0b12ac52e24..0327ee87251 100644 --- a/public/app/plugins/panel/heatmap/partials/axes_editor.html +++ b/public/app/plugins/panel/heatmap/partials/axes_editor.html @@ -40,6 +40,11 @@ + +
From c248c1f4f19702be9f92581790bb2c97920f2f68 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Tue, 12 Mar 2019 16:38:57 +0300 Subject: [PATCH 9/9] changelog: add notes about heatmap issues #15683 #14019 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 343e3ccfbb1..5483529f75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,15 @@ ### Minor * **Cloudwatch**: Add AWS RDS MaximumUsedTransactionIDs metric [#15077](https://github.com/grafana/grafana/pull/15077), thx [@activeshadow](https://github.com/activeshadow) +* **Heatmap**: `Middle` bucket bound option [#15683](https://github.com/grafana/grafana/issues/15683) +* **Heatmap**: `Reverse order` option for changing order of buckets [#15683](https://github.com/grafana/grafana/issues/15683) ### Bug Fixes * **Api**: Invalid org invite code [#10506](https://github.com/grafana/grafana/issues/10506) * **Datasource**: Handles nil jsondata field gracefully [#14239](https://github.com/grafana/grafana/issues/14239) * **Gauge**: Interpolate scoped variables in repeated gauges [#15739](https://github.com/grafana/grafana/issues/15739) * **Datasource**: Empty user/password was not updated when updating datasources [#15608](https://github.com/grafana/grafana/pull/15608), thx [@Maddin-619](https://github.com/Maddin-619) +* **Heatmap**: legend shows wrong colors for small values [#14019](https://github.com/grafana/grafana/issues/14019) # 6.0.1 (2019-03-06)