mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
adding heatmap visualization
This commit is contained in:
parent
d3b3065603
commit
9bc3380648
10 changed files with 666 additions and 0 deletions
|
@ -0,0 +1,94 @@
|
|||
<div>
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="colorSchema">
|
||||
Color Schema
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<select
|
||||
id="colorSchema"
|
||||
class="kuiSelect kuiSideBarSelect"
|
||||
ng-model="vis.params.colorSchema"
|
||||
ng-options="mode for mode in vis.type.params.colorSchemas"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="colorsNumber">
|
||||
Number of colors
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input
|
||||
id="colorsNumber"
|
||||
type="range"
|
||||
step="1"
|
||||
min="3"
|
||||
max="10"
|
||||
class="kuiInput kuiSideBarInput"
|
||||
ng-model="vis.params.colorsNumber"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="axisScale">
|
||||
Z Axis Scale
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<select
|
||||
id="axisScale"
|
||||
class="kuiSelect kuiSideBarSelect"
|
||||
ng-model="vis.params.scale"
|
||||
ng-options="mode for mode in vis.type.params.scales"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="setColorRange">
|
||||
Custom Range
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input class="kuiCheckBox" id="setColorRange" type="checkbox" ng-model="vis.params.setColorRange">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vis.params.setColorRange">
|
||||
<div class="kuiSideBarCollapsibleTitle">
|
||||
<div
|
||||
class="kuiSideBarCollapsibleTitle__label"
|
||||
ng-click="isColorRangeOpen = !isColorRangeOpen"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
ng-class="{ 'fa-caret-down': isColorRangeOpen, 'fa-caret-right': !isColorRangeOpen }"
|
||||
class="fa fa-caret-right kuiSideBarCollapsibleTitle__caret"
|
||||
></span>
|
||||
<span class="kuiSideBarCollapsibleTitle__text">
|
||||
Custom Ranges
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="isColorRangeOpen" class="kuiSideBarCollapsibleSection">
|
||||
<div class="kuiSideBarSection">
|
||||
<div class="kuiSideBarFormRow" ng-repeat="color in vis.params.colorsRange track by $index">
|
||||
<label class="kuiSideBarFormRow__label" for="{{ 'colorRange' + $index }}">
|
||||
<div class="kuiSideBarFormRow__label__colorbox" ng-style="{ 'background-color': getColor($index) }"></div>
|
||||
value over
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input
|
||||
id="{{ 'colorRange' + $index }}"
|
||||
class="kuiInput kuiSideBarInput"
|
||||
ng-model="color.value"
|
||||
type="number"
|
||||
ng-required="vis.params.setColorRange"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text text-center text-info">note: colors can be changed in the legend</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,39 @@
|
|||
import uiModules from 'ui/modules';
|
||||
import heatmapOptionsTemplate from 'plugins/kbn_vislib_vis_types/controls/heatmap_range_option.html';
|
||||
import colorFunc from 'ui/vislib/components/color/heatmap_color';
|
||||
const module = uiModules.get('kibana');
|
||||
|
||||
module.directive('heatmapOptions', function ($parse, $compile) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: heatmapOptionsTemplate,
|
||||
replace: true,
|
||||
link: function ($scope) {
|
||||
$scope.isColorRangeOpen = false;
|
||||
|
||||
$scope.getColor = function (index) {
|
||||
const colors = $scope.uiState.get('vis.colors');
|
||||
return colors ? Object.values(colors)[index] : 'transparent';
|
||||
};
|
||||
|
||||
function fillColorsRange() {
|
||||
for (let i = $scope.vis.params.colorsRange.length; i < $scope.vis.params.colorsNumber; i++) {
|
||||
$scope.vis.params.colorsRange.push({ value: 0 });
|
||||
}
|
||||
$scope.vis.params.colorsRange.length = $scope.vis.params.colorsNumber;
|
||||
}
|
||||
|
||||
fillColorsRange();
|
||||
$scope.$watch('vis.params.colorsNumber', newVal => {
|
||||
if (newVal) {
|
||||
fillColorsRange();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.uiState.on('colorChanged', () => {
|
||||
$scope.realVis.params.colorSchema = 'custom';
|
||||
$scope.vis.params.colorSchema = 'custom';
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
<div class="kuiSideBarSection">
|
||||
<div class="kuiSideBarSectionTitle">
|
||||
<div class="kuiSideBarSectionTitle__text">
|
||||
Global Settings
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="addTooltip">
|
||||
Show Tooltips
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input class="kuiCheckBox" id="addTooltip" type="checkbox" ng-model="vis.params.addTooltip">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="enableHover">
|
||||
Highlight
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input class="kuiCheckBox" id="enableHover" type="checkbox" ng-model="vis.params.enableHover">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow" ng-show="vis.params.addLegend">
|
||||
<label class="kuiSideBarFormRow__label" for="legendPosition">
|
||||
Legend Position
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<select
|
||||
id="legendPosition"
|
||||
class="kuiSelect kuiSideBarSelect"
|
||||
ng-model="vis.params.legendPosition"
|
||||
ng-options="position.value as position.text for position in vis.type.params.legendPositions"
|
||||
></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarFormRow">
|
||||
<label class="kuiSideBarFormRow__label" for="defaultYExtents">
|
||||
Scale to Data Bounds
|
||||
</label>
|
||||
<div class="kuiSideBarFormRow__control">
|
||||
<input class="kuiCheckBox" id="defaultYExtents" type="checkbox" ng-model="vis.params.defaultYExtents">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="kuiSideBarSectionTitle">
|
||||
<div class="kuiSideBarSectionTitle__text">
|
||||
Heatmap Settings
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<heatmap-options></heatmap-options>
|
||||
</div>
|
86
src/core_plugins/kbn_vislib_vis_types/public/heatmap.js
Normal file
86
src/core_plugins/kbn_vislib_vis_types/public/heatmap.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
import VislibVisTypeVislibVisTypeProvider from 'ui/vislib_vis_type/vislib_vis_type';
|
||||
import VisSchemasProvider from 'ui/vis/schemas';
|
||||
import heatmapTemplate from 'plugins/kbn_vislib_vis_types/editors/heatmap.html';
|
||||
|
||||
export default function HeatmapVisType(Private) {
|
||||
const VislibVisType = Private(VislibVisTypeVislibVisTypeProvider);
|
||||
const Schemas = Private(VisSchemasProvider);
|
||||
|
||||
return new VislibVisType({
|
||||
name: 'heatmap',
|
||||
title: 'Heatmap chart',
|
||||
icon: 'fa-barcode',
|
||||
description: 'A heat map is a graphical representation of data' +
|
||||
' where the individual values contained in a matrix are represented as colors. ',
|
||||
params: {
|
||||
defaults: {
|
||||
addTooltip: true,
|
||||
addLegend: true,
|
||||
enableHover: false,
|
||||
legendPosition: 'right',
|
||||
scale: 'linear',
|
||||
times: [],
|
||||
addTimeMarker: false,
|
||||
defaultYExtents: false,
|
||||
setYExtents: false,
|
||||
colorsNumber: 4,
|
||||
colorSchema: 'yellow to red',
|
||||
setColorRange: false,
|
||||
colorsRange: [],
|
||||
},
|
||||
legendPositions: [{
|
||||
value: 'left',
|
||||
text: 'left',
|
||||
}, {
|
||||
value: 'right',
|
||||
text: 'right',
|
||||
}, {
|
||||
value: 'top',
|
||||
text: 'top',
|
||||
}, {
|
||||
value: 'bottom',
|
||||
text: 'bottom',
|
||||
}],
|
||||
scales: ['linear', 'log', 'square root'],
|
||||
colorSchemas: ['yellow to red', 'reds', 'greens', 'blues', 'custom'],
|
||||
editor: heatmapTemplate
|
||||
},
|
||||
schemas: new Schemas([
|
||||
{
|
||||
group: 'metrics',
|
||||
name: 'metric',
|
||||
title: 'Value',
|
||||
min: 1,
|
||||
max: 1,
|
||||
aggFilter: ['count', 'avg', 'median', 'sum', 'min', 'max', 'cardinality', 'std_dev'],
|
||||
defaults: [
|
||||
{ schema: 'metric', type: 'count' }
|
||||
]
|
||||
},
|
||||
{
|
||||
group: 'buckets',
|
||||
name: 'segment',
|
||||
title: 'X-Axis',
|
||||
min: 0,
|
||||
max: 1,
|
||||
aggFilter: '!geohash_grid'
|
||||
},
|
||||
{
|
||||
group: 'buckets',
|
||||
name: 'group',
|
||||
title: 'Y-Axis',
|
||||
min: 0,
|
||||
max: 1,
|
||||
aggFilter: '!geohash_grid'
|
||||
},
|
||||
{
|
||||
group: 'buckets',
|
||||
name: 'split',
|
||||
title: 'Split Chart',
|
||||
min: 0,
|
||||
max: 1,
|
||||
aggFilter: '!geohash_grid'
|
||||
}
|
||||
])
|
||||
});
|
||||
};
|
|
@ -4,3 +4,4 @@ visTypes.register(require('plugins/kbn_vislib_vis_types/line'));
|
|||
visTypes.register(require('plugins/kbn_vislib_vis_types/pie'));
|
||||
visTypes.register(require('plugins/kbn_vislib_vis_types/area'));
|
||||
visTypes.register(require('plugins/kbn_vislib_vis_types/tile_map'));
|
||||
visTypes.register(require('plugins/kbn_vislib_vis_types/heatmap'));
|
||||
|
|
62
src/ui/public/vislib/__tests__/components/heatmap_color.js
Normal file
62
src/ui/public/vislib/__tests__/components/heatmap_color.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import getColors from 'ui/vislib/components/color/heatmap_color';
|
||||
|
||||
describe('Vislib Heatmap Color Module Test Suite', function () {
|
||||
const emptyObject = {};
|
||||
const nullValue = null;
|
||||
let notAValue;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
it('should throw an error if input is not a number', function () {
|
||||
expect(function () {
|
||||
getColors([200]);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
getColors('help');
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
getColors(true);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
getColors(notAValue);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
getColors(nullValue);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
getColors(emptyObject);
|
||||
}).to.throwError();
|
||||
});
|
||||
|
||||
it('should throw an error if input is less than 0', function () {
|
||||
expect(function () {
|
||||
getColors(-2);
|
||||
}).to.throwError();
|
||||
});
|
||||
|
||||
it('should throw an error if input is greater than 9', function () {
|
||||
expect(function () {
|
||||
getColors(10);
|
||||
}).to.throwError();
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof getColors).to.be('function');
|
||||
});
|
||||
|
||||
it('should return a color for numbers from 0 to 9', function () {
|
||||
const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
||||
const schema = 'reds';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
expect(getColors(i, schema)).to.match(colorRegex);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
127
src/ui/public/vislib/__tests__/visualizations/heatmap_chart.js
Normal file
127
src/ui/public/vislib/__tests__/visualizations/heatmap_chart.js
Normal file
|
@ -0,0 +1,127 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import _ from 'lodash';
|
||||
import d3 from 'd3';
|
||||
|
||||
// Data
|
||||
import series from 'fixtures/vislib/mock_data/date_histogram/_series';
|
||||
import seriesPosNeg from 'fixtures/vislib/mock_data/date_histogram/_series_pos_neg';
|
||||
import seriesNeg from 'fixtures/vislib/mock_data/date_histogram/_series_neg';
|
||||
import termsColumns from 'fixtures/vislib/mock_data/terms/_columns';
|
||||
import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series';
|
||||
import $ from 'jquery';
|
||||
import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture';
|
||||
import PersistedStatePersistedStateProvider from 'ui/persisted_state/persisted_state';
|
||||
|
||||
// tuple, with the format [description, mode, data]
|
||||
const dataTypesArray = [
|
||||
['series', series],
|
||||
['series with positive and negative values', seriesPosNeg],
|
||||
['series with negative values', seriesNeg],
|
||||
['terms columns', termsColumns],
|
||||
['stackedSeries', stackedSeries],
|
||||
];
|
||||
|
||||
describe('Vislib Heatmap Cyart Test Suite', function () {
|
||||
dataTypesArray.forEach(function (dataType, i) {
|
||||
const name = dataType[0];
|
||||
const data = dataType[1];
|
||||
|
||||
describe('for ' + name + ' Data', function () {
|
||||
let vis;
|
||||
let persistedState;
|
||||
const visLibParams = {
|
||||
type: 'heatmap',
|
||||
addLegend: true,
|
||||
addTooltip: true,
|
||||
colorsNumber: 4,
|
||||
colorSchema: 'yellow to red',
|
||||
};
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
vis = Private(FixturesVislibVisFixtureProvider)(visLibParams);
|
||||
persistedState = new (Private(PersistedStatePersistedStateProvider))();
|
||||
vis.on('brush', _.noop);
|
||||
vis.render(data, persistedState);
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
vis.destroy();
|
||||
});
|
||||
|
||||
describe('addSquares method', function () {
|
||||
it('should append rects', function () {
|
||||
let numOfSeries;
|
||||
let numOfValues;
|
||||
let product;
|
||||
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
const numOfRects = chart.chartData.series.reduce((result, series) => {
|
||||
return result + series.values.length;
|
||||
}, 0);
|
||||
expect($(chart.chartEl).find('.series rect')).to.have.length(numOfRects);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addBarEvents method', function () {
|
||||
function checkChart(chart) {
|
||||
const rect = $(chart.chartEl).find('.series rect').get(0);
|
||||
|
||||
return {
|
||||
click: !!rect.__onclick,
|
||||
mouseOver: !!rect.__onmouseover,
|
||||
// D3 brushing requires that a g element is appended that
|
||||
// listens for mousedown events. This g element includes
|
||||
// listeners, however, I was not able to test for the listener
|
||||
// function being present. I will need to update this test
|
||||
// in the future.
|
||||
brush: !!d3.select('.brush')[0][0]
|
||||
};
|
||||
}
|
||||
|
||||
it('should attach the brush if data is a set of ordered dates', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
const has = checkChart(chart);
|
||||
const ordered = vis.handler.data.get('ordered');
|
||||
const date = Boolean(ordered && ordered.date);
|
||||
expect(has.brush).to.be(date);
|
||||
});
|
||||
});
|
||||
|
||||
it('should attach a click event', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
const has = checkChart(chart);
|
||||
expect(has.click).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should attach a hover event', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
const has = checkChart(chart);
|
||||
expect(has.mouseOver).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw method', function () {
|
||||
it('should return a function', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
expect(_.isFunction(chart.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a yMin and yMax', function () {
|
||||
vis.handler.charts.forEach(function (chart) {
|
||||
const yAxis = chart.handler.valueAxes[0];
|
||||
const domain = yAxis.getScale().domain();
|
||||
|
||||
expect(domain[0]).to.not.be(undefined);
|
||||
expect(domain[1]).to.not.be(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
36
src/ui/public/vislib/components/color/heatmap_color.js
Normal file
36
src/ui/public/vislib/components/color/heatmap_color.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
const reds = [
|
||||
'#99000E', '#A41926', '#AF333E', '#BB4C56', '#C6666E', '#D17F86', '#DD999E', '#E8B2B6', '#F3CCCE', '#FFE6E6'
|
||||
];
|
||||
const greens = [
|
||||
'#E5F5F9', '#CBE1E0', '#B2CDC7', '#98BAAF', '#7FA696', '#65927D', '#4C7F65', '#326B4C', '#195733', '#00441B'
|
||||
];
|
||||
const blues = [
|
||||
'#DEEBF7', '#C6D6E7', '#AEC1D7', '#96ACC8', '#7E97B8', '#6783A9', '#4F6E99', '#37598A', '#1F447A', '#08306B'
|
||||
];
|
||||
const yellowtored = [
|
||||
'#F8F840', '#EEDA35', '#E4BC2A', '#DB9F1F', '#D18114', '#C8640A', '#D05415', '#D84520', '#E0362B', '#E82736'
|
||||
];
|
||||
|
||||
export default function (value, colorSchema) {
|
||||
if (!_.isNumber(value) || value < 0 || value > 9) {
|
||||
throw new Error('heatmap_color expects a number from 0 to 9 as first parameter');
|
||||
}
|
||||
switch (colorSchema) {
|
||||
case 'reds':
|
||||
return reds[9 - value];
|
||||
case 'greens':
|
||||
return greens[value];
|
||||
case 'blues':
|
||||
return blues[value];
|
||||
case 'yellow to red':
|
||||
return yellowtored[value];
|
||||
default:
|
||||
const start = 120;
|
||||
const end = 360;
|
||||
const c = start + (end - start) * (value * 10);
|
||||
|
||||
return `hsl(${c},60%,50%)`;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,164 @@
|
|||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import VislibVisualizationsPointSeriesProvider from './_point_series';
|
||||
import colorFunc from 'ui/vislib/components/color/heatmap_color';
|
||||
|
||||
export default function HeatmapChartFactory(Private) {
|
||||
|
||||
const PointSeries = Private(VislibVisualizationsPointSeriesProvider);
|
||||
|
||||
const defaults = {
|
||||
color: undefined, // todo
|
||||
fillColor: undefined // todo
|
||||
};
|
||||
/**
|
||||
* Line Chart Visualization
|
||||
*
|
||||
* @class HeatmapChart
|
||||
* @constructor
|
||||
* @extends Chart
|
||||
* @param handler {Object} Reference to the Handler Class Constructor
|
||||
* @param el {HTMLElement} HTML element to which the chart will be appended
|
||||
* @param chartData {Object} Elasticsearch query results for this specific chart
|
||||
*/
|
||||
class HeatmapChart extends PointSeries {
|
||||
constructor(handler, chartEl, chartData, seriesConfigArgs) {
|
||||
super(handler, chartEl, chartData, seriesConfigArgs);
|
||||
this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults);
|
||||
}
|
||||
|
||||
addSquares(svg, data) {
|
||||
const xScale = this.getCategoryAxis().getScale();
|
||||
const yScale = this.handler.categoryAxes[1].getScale();
|
||||
const zScale = this.getValueAxis().getScale();
|
||||
const ordered = this.handler.data.get('ordered');
|
||||
const tooltip = this.baseChart.tooltip;
|
||||
const isTooltip = this.handler.visConfig.get('tooltip.show');
|
||||
const isHorizontal = this.getCategoryAxis().axisConfig.isHorizontal();
|
||||
const colorsNumber = this.handler.visConfig.get('colorsNumber');
|
||||
const setColorRange = this.handler.visConfig.get('setColorRange');
|
||||
const colorsRange = this.handler.visConfig.get('colorsRange');
|
||||
const color = this.handler.data.getColorFunc();
|
||||
|
||||
const layer = svg.append('g')
|
||||
.attr('class', 'series');
|
||||
|
||||
const squares = layer
|
||||
.selectAll('rect')
|
||||
.data(data.values);
|
||||
|
||||
squares
|
||||
.exit()
|
||||
.remove();
|
||||
|
||||
let barWidth;
|
||||
if (this.getCategoryAxis().axisConfig.isTimeDomain()) {
|
||||
const { min, interval } = this.handler.data.get('ordered');
|
||||
const start = min;
|
||||
const end = moment(min).add(interval).valueOf();
|
||||
|
||||
barWidth = xScale(end) - xScale(start);
|
||||
if (!isHorizontal) barWidth *= -1;
|
||||
barWidth = barWidth - Math.min(barWidth * 0.25, 15);
|
||||
}
|
||||
|
||||
function x(d) {
|
||||
return xScale(d.x);
|
||||
}
|
||||
|
||||
function y(d) {
|
||||
return yScale(d.series);
|
||||
}
|
||||
|
||||
const [min, max] = zScale.domain();
|
||||
function getColorBucket(d) {
|
||||
let val = 0;
|
||||
if (setColorRange && colorsRange.length) {
|
||||
if (d.y < colorsRange[0].value) return -1;
|
||||
while (val + 1 < colorsRange.length && d.y > colorsRange[val + 1].value) val++;
|
||||
} else {
|
||||
if (isNaN(min) || isNaN(max)) {
|
||||
val = colorsNumber - 1;
|
||||
} else {
|
||||
val = (d.y - min) / (max - min); /* get val from 0 - 1 */
|
||||
val = Math.floor(val * (colorsNumber - 1));
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
function label(d) {
|
||||
const colorBucket = getColorBucket(d);
|
||||
let label;
|
||||
if (colorBucket < 0) return '';
|
||||
const val = Math.ceil(colorBucket * (100 / colorsNumber));
|
||||
if (setColorRange) {
|
||||
const greaterThan = colorsRange[colorBucket].value;
|
||||
label = `> ${greaterThan}`;
|
||||
} else {
|
||||
const nextVal = Math.ceil((colorBucket + 1) * (100 / colorsNumber));
|
||||
label = `${val}% - ${nextVal}%`;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
function z(d) {
|
||||
if (label(d) === '') return 'transparent';
|
||||
return color(label(d));
|
||||
}
|
||||
|
||||
function widthFunc() {
|
||||
return barWidth || xScale.rangeBand();
|
||||
}
|
||||
|
||||
function heightFunc() {
|
||||
return yScale.rangeBand();
|
||||
}
|
||||
|
||||
squares
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('x', isHorizontal ? x : y)
|
||||
.attr('width', isHorizontal ? widthFunc : heightFunc)
|
||||
.attr('y', isHorizontal ? y : x)
|
||||
.attr('height', isHorizontal ? heightFunc : widthFunc)
|
||||
.attr('data-label', label)
|
||||
.attr('fill', z)
|
||||
.attr('style', 'cursor: pointer');
|
||||
|
||||
if (isTooltip) {
|
||||
squares.call(tooltip.render());
|
||||
}
|
||||
|
||||
return squares;
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders d3 visualization
|
||||
*
|
||||
* @method draw
|
||||
* @returns {Function} Creates the line chart
|
||||
*/
|
||||
draw() {
|
||||
const self = this;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
const svg = self.chartEl.append('g');
|
||||
svg.data([self.chartData]);
|
||||
|
||||
const squares = self.addSquares(svg, self.chartData);
|
||||
self.addCircleEvents(squares);
|
||||
|
||||
self.events.emit('rendered', {
|
||||
chart: self.chartData
|
||||
});
|
||||
|
||||
return svg;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
return HeatmapChart;
|
||||
};
|
|
@ -3,6 +3,7 @@ import 'ui/vislib';
|
|||
import 'plugins/kbn_vislib_vis_types/controls/vislib_basic_options';
|
||||
import 'plugins/kbn_vislib_vis_types/controls/point_series_options';
|
||||
import 'plugins/kbn_vislib_vis_types/controls/line_interpolation_option';
|
||||
import 'plugins/kbn_vislib_vis_types/controls/heatmap_range_option';
|
||||
import VisSchemasProvider from 'ui/vis/schemas';
|
||||
import VisVisTypeProvider from 'ui/vis/vis_type';
|
||||
import AggResponsePointSeriesPointSeriesProvider from 'ui/agg_response/point_series/point_series';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue