kibana-123
Version:
Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic
225 lines (192 loc) • 5.97 kB
JavaScript
import d3 from 'd3';
import _ from 'lodash';
import $ from 'jquery';
import errors from 'ui/errors';
import VislibVisualizationsChartProvider from './_chart';
export default function PieChartFactory(Private) {
const Chart = Private(VislibVisualizationsChartProvider);
const defaults = {
isDonut: false,
showTooltip: true,
color: undefined,
fillColor: undefined,
xValue: d => d.x,
yValue: d => d.y
};
/**
* Pie Chart Visualization
*
* @class PieChart
* @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 PieChart extends Chart {
constructor(handler, chartEl, chartData) {
super(handler, chartEl, chartData);
const charts = this.handler.data.getVisData();
this._validatePieData(charts);
this._attr = _.defaults(handler.visConfig.get('chart', {}), defaults);
}
/**
* Checks whether pie slices have all zero values.
* If so, an error is thrown.
*/
_validatePieData(charts) {
const isAllZeros = charts.every(function (chart) {
return chart.slices.children.length === 0;
});
if (isAllZeros) {
throw new errors.PieContainsAllZeros();
}
};
/**
* Adds Events to SVG paths
*
* @method addPathEvents
* @param element {D3.Selection} Reference to SVG path
* @returns {D3.Selection} SVG path with event listeners attached
*/
addPathEvents(element) {
const events = this.events;
return element
.call(events.addHoverEvent())
.call(events.addMouseoutEvent())
.call(events.addClickEvent());
};
convertToPercentage(slices) {
(function assignPercentages(slices) {
if (slices.sumOfChildren != null) return;
const parent = slices;
const children = parent.children;
const parentPercent = parent.percentOfParent;
const sum = parent.sumOfChildren = Math.abs(children.reduce(function (sum, child) {
return sum + Math.abs(child.size);
}, 0));
children.forEach(function (child) {
child.percentOfGroup = Math.abs(child.size) / sum;
child.percentOfParent = child.percentOfGroup;
if (parentPercent != null) {
child.percentOfParent *= parentPercent;
}
if (child.children) {
assignPercentages(child);
}
});
}(slices));
};
/**
* Adds pie paths to SVG
*
* @method addPath
* @param width {Number} Width of SVG
* @param height {Number} Height of SVG
* @param svg {HTMLElement} Chart SVG
* @param slices {Object} Chart data
* @returns {D3.Selection} SVG with paths attached
*/
addPath(width, height, svg, slices) {
const self = this;
const marginFactor = 0.95;
const isDonut = self._attr.isDonut;
const radius = (Math.min(width, height) / 2) * marginFactor;
const color = self.handler.data.getPieColorFunc();
const tooltip = self.tooltip;
const isTooltip = self._attr.addTooltip;
const partition = d3.layout.partition()
.sort(null)
.value(function (d) {
return d.percentOfParent * 100;
});
const x = d3.scale.linear()
.range([0, 2 * Math.PI]);
const y = d3.scale.sqrt()
.range([0, radius]);
const arc = d3.svg.arc()
.startAngle(function (d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function (d) {
if (d.dx < 1e-8) return x(d.x);
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function (d) {
// option for a single layer, i.e pie chart
if (d.depth === 1 && !isDonut) {
// return no inner radius
return 0;
}
return Math.max(0, y(d.y));
})
.outerRadius(function (d) {
return Math.max(0, y(d.y + d.dy));
});
const path = svg
.datum(slices)
.selectAll('path')
.data(partition.nodes)
.enter()
.append('path')
.attr('d', arc)
.attr('class', function (d) {
if (d.depth === 0) {
return;
}
return 'slice';
})
.call(self._addIdentifier, 'name')
.style('stroke', '#fff')
.style('fill', function (d) {
if (d.depth === 0) {
return 'none';
}
return color(d.name);
});
if (isTooltip) {
path.call(tooltip.render());
}
return path;
};
_validateContainerSize(width, height) {
const minWidth = 20;
const minHeight = 20;
if (width <= minWidth || height <= minHeight) {
throw new errors.ContainerTooSmall();
}
};
/**
* Renders d3 visualization
*
* @method draw
* @returns {Function} Creates the pie chart
*/
draw() {
const self = this;
return function (selection) {
selection.each(function (data) {
const slices = data.slices;
const div = d3.select(this);
const width = $(this).width();
const height = $(this).height();
if (!slices.children.length) return;
self.convertToPercentage(slices);
self._validateContainerSize(width, height);
const svg = div.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
const path = self.addPath(width, height, svg, slices);
self.addPathEvents(path);
self.events.emit('rendered', {
chart: data
});
return svg;
});
};
};
}
return PieChart;
};