Commit dd5a4269 by Torkel Ödegaard

heatmap: refactoring heatmap

parent 43c6f749
......@@ -11,26 +11,20 @@ weight = 20
# Grafana TestData
> NOTE: This plugin is disable by default.
The purpose of this data sources is to make it easier to create fake data for any panel.
Using `Grafana TestData` you can build your own time series and have any panel render it.
This make is much easier to verify functionally since the data can be shared very
## Enable
`Grafana TestData` is not enabled by default. To enable it you have to go to `/plugins/testdata/edit` and click the enable button to enable it for each server.
`Grafana TestData` is not enabled by default. To enable it you have to go to `/plugins/testdata/edit` and click the enable button to enable.
## Create mock data.
Once `Grafana TestData` is enabled you use it as a datasource in the metric panel.
Once `Grafana TestData` is enabled you can use it as a data source in any metric panel.
![](/img/docs/v41/test_data_add.png)
## Scenarios
You can now choose different scenario that you want rendered in the drop down menu. If you have scenarios that you think should be added, please add them to `` and submit a pull request.
## CSV
The comma separated values scenario is the most powerful one since it lets you create any kind of graph you like.
......@@ -38,7 +32,6 @@ Once you provided the numbers `Grafana TestData` will distribute them evenly bas
![](/img/docs/v41/test_data_csv_example.png)
## Dashboards
`Grafana TestData` also contains some dashboards with example. `/plugins/testdata/edit`
......
+++
title = "Heatmap Panel"
description = "Heatmap panel documentation"
keywords = ["grafana", "heatmap", "panel", "documentation"]
type = "docs"
[menu.docs]
parent = "panels"
weight = 3
+++
# Heatmap Panel
> New panel only available in Grafana v4.3+
![](/img/docs/v43/heatmap_panel.png)
......@@ -16,54 +16,36 @@ interface YBucket {
values: number[];
}
function elasticHistogramToHeatmap(series) {
let seriesBuckets = _.map(series, (s: TimeSeries) => {
return convertEsSeriesToHeatmap(s);
});
let buckets = mergeBuckets(seriesBuckets);
return buckets;
}
function convertEsSeriesToHeatmap(series: TimeSeries, saveZeroCounts = false) {
let xBuckets: XBucket[] = [];
function elasticHistogramToHeatmap(seriesList) {
let heatmap = {};
_.forEach(series.datapoints, point => {
let bound = series.alias;
let count = point[VALUE_INDEX];
if (!count) {
for (let series of seriesList) {
let bound = Number(series.alias);
if (isNaN(bound)) {
return;
}
let values = new Array(Math.round(count));
values.fill(Number(bound));
let valueBuckets = {};
valueBuckets[bound] = {
y: Number(bound),
values: values
};
for (let point of series.datapoints) {
let count = point[VALUE_INDEX];
let time = point[TIME_INDEX];
let xBucket: XBucket = {
x: point[TIME_INDEX],
buckets: valueBuckets
};
if (!_.isNumber(count)) {
continue;
}
// Don't push buckets with 0 count until saveZeroCounts flag is set
if (count !== 0 || (count === 0 && saveZeroCounts)) {
xBuckets.push(xBucket);
let bucket = heatmap[time];
if (!bucket) {
bucket = heatmap[time] = {x: time, buckets: {}};
}
});
let heatmap: any = {};
_.forEach(xBuckets, (bucket: XBucket) => {
heatmap[bucket.x] = bucket;
});
bucket.buckets[bound] = {y: bound, count: count};
}
}
return heatmap;
}
/**
/**
* Convert set of time series into heatmap buckets
* @return {Object} Heatmap object:
* {
......@@ -109,18 +91,16 @@ function convertToHeatMap(series, yBucketSize, xBucketSize, logBase) {
function convertToCards(buckets) {
let cards = [];
_.forEach(buckets, xBucket => {
_.forEach(xBucket.buckets, (yBucket, key) => {
if (yBucket.values.length) {
_.forEach(xBucket.buckets, yBucket=> {
let card = {
x: Number(xBucket.x),
y: Number(key),
x: xBucket.x,
y: yBucket.y,
yBounds: yBucket.bounds,
values: yBucket.values,
count: yBucket.count,
seriesStat: getSeriesStat(yBucket.points)
};
cards.push(card);
}
});
});
......
......@@ -2,7 +2,7 @@
<div class="section gf-form-group">
<h5 class="section-heading">Y Axis</h5>
<div class="gf-form">
<label class="gf-form-label width-6">Unit</label>
<label class="gf-form-label width-8">Unit</label>
<div class="gf-form-dropdown-typeahead max-width-10"
ng-model="ctrl.panel.yAxis.format"
dropdown-typeahead2="editor.unitFormats"
......@@ -10,21 +10,21 @@
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Scale</label>
<label class="gf-form-label width-8">Scale</label>
<div class="gf-form-select-wrapper max-width-10">
<select class="gf-form-input" ng-model="ctrl.panel.yAxis.logBase" ng-options="v as k for (k, v) in editor.logScales" ng-change="ctrl.refresh()"></select>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Y-Min</label>
<label class="gf-form-label width-8">Y-Min</label>
<input type="text" class="gf-form-input width-10" placeholder="auto" empty-to-null ng-model="ctrl.panel.yAxis.min" ng-change="ctrl.render()" ng-model-onblur>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Y-Max</label>
<label class="gf-form-label width-8">Y-Max</label>
<input type="text" class="gf-form-input width-10" placeholder="auto" empty-to-null ng-model="ctrl.panel.yAxis.max" ng-change="ctrl.render()" ng-model-onblur>
</div>
<div class="gf-form">
<label class="gf-form-label width-6">Decimals</label>
<label class="gf-form-label width-8">Decimals</label>
<input type="number" class="gf-form-input width-10" placeholder="auto" data-placement="right"
bs-tooltip="'Override automatic decimal precision for axis.'"
ng-model="ctrl.panel.yAxis.decimals" ng-change="ctrl.render()" ng-model-onblur>
......@@ -55,7 +55,7 @@
<label class="gf-form-label">Buckets</label>
<input type="number" class="gf-form-input width-5" placeholder="auto" data-placement="right"
bs-tooltip="'Number of buckets for Y axis.'"
ng-model="ctrl.panel.yBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
ng-model="ctrl.panel.xBucketNumber" ng-change="ctrl.refresh()" ng-model-onblur>
</div>
<div class="gf-form">
<label class="gf-form-label">Size</label>
......
......@@ -366,14 +366,12 @@ export default function link(scope, elem, attrs, ctrl) {
data.buckets = mergeZeroBuckets(data.buckets, _.min(tick_values));
}
}
let cardsData = convertToCards(data.buckets);
let max_value = d3.max(cardsData, card => {
return card.values.length;
});
let cardsData = convertToCards(data.buckets);
let maxValue = d3.max(cardsData, card => card.count);
colorScale = getColorScale(max_value);
setOpacityScale(max_value);
colorScale = getColorScale(maxValue);
setOpacityScale(maxValue);
setCardSize();
let cards = heatmap.selectAll(".heatmap-card").data(cardsData);
......@@ -431,14 +429,14 @@ export default function link(scope, elem, attrs, ctrl) {
return d3.scaleSequential(colorInterpolator).domain([start, end]);
}
function setOpacityScale(max_value) {
function setOpacityScale(maxValue) {
if (panel.color.colorScale === 'linear') {
opacityScale = d3.scaleLinear()
.domain([0, max_value])
.domain([0, maxValue])
.range([0, 1]);
} else if (panel.color.colorScale === 'sqrt') {
opacityScale = d3.scalePow().exponent(panel.color.exponent)
.domain([0, max_value])
.domain([0, maxValue])
.range([0, 1]);
}
}
......@@ -529,13 +527,13 @@ export default function link(scope, elem, attrs, ctrl) {
if (panel.color.mode === 'opacity') {
return panel.color.cardColor;
} else {
return colorScale(d.values.length);
return colorScale(d.count);
}
}
function getCardOpacity(d) {
if (panel.color.mode === 'opacity') {
return opacityScale(d.values.length);
return opacityScale(d.count);
} else {
return 1;
}
......@@ -831,8 +829,3 @@ function getPrecision(num) {
return str.length - dot_index - 1;
}
}
function getTicksPrecision(values) {
let precisions = _.map(values, getPrecision);
return _.max(precisions);
}
......@@ -200,7 +200,7 @@ describe('ES Histogram converter', () => {
alias: '1', label: '1'
}));
ctx.series.push(new TimeSeries({
datapoints: [[1, 1422774000000], [3, 1422774060000]],
datapoints: [[5, 1422774000000], [3, 1422774060000]],
alias: '2', label: '2'
}));
ctx.series.push(new TimeSeries({
......@@ -219,21 +219,23 @@ describe('ES Histogram converter', () => {
'1422774000000': {
x: 1422774000000,
buckets: {
'1': { y: 1, values: [1] },
'2': { y: 2, values: [2] }
'1': { y: 1, count: 1 },
'2': { y: 2, count: 5 },
'3': { y: 3, count: 0 }
}
},
'1422774060000': {
x: 1422774060000,
buckets: {
'2': { y: 2, values: [2, 2, 2] },
'3': { y: 3, values: [3] }
'1': { y: 1, count: 0 },
'2': { y: 2, count: 3 },
'3': { y: 3, count: 1 }
}
},
};
let heatmap = elasticHistogramToHeatmap(ctx.series);
expect(isHeatmapDataEqual(heatmap, expectedHeatmap)).to.be(true);
expect(heatmap).to.eql(expectedHeatmap);
});
});
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment