import d3 from 'd3'; import box from '@/lib/visualizations/d3box'; import editorTemplate from './box-plot-editor.html'; function boxPlotRenderer() { return { restrict: 'E', template: '
', link($scope, elm) { function calcIqr(k) { return (d) => { const q1 = d.quartiles[0]; const q3 = d.quartiles[2]; const iqr = (q3 - q1) * k; let i = -1; let j = d.length; // eslint-disable-next-line no-plusplus while (d[++i] < q1 - iqr); // eslint-disable-next-line no-plusplus while (d[--j] > q3 + iqr); return [i, j]; }; } $scope.$watch('[queryResult && queryResult.getData(), visualization.options]', () => { if ($scope.queryResult.getData() === null) { return; } const data = $scope.queryResult.getData(); const parentWidth = d3.select(elm[0].parentNode).node().getBoundingClientRect().width; const margin = { top: 10, right: 50, bottom: 40, left: 50, inner: 25, }; const width = parentWidth - margin.right - margin.left; const height = 500 - margin.top - margin.bottom; let min = Infinity; let max = -Infinity; const mydata = []; let value = 0; let d = []; const xAxisLabel = $scope.visualization.options.xAxisLabel; const yAxisLabel = $scope.visualization.options.yAxisLabel; const columns = $scope.queryResult.getColumnNames(); const xscale = d3.scale.ordinal() .domain(columns) .rangeBands([0, parentWidth - margin.left - margin.right]); let boxWidth; if (columns.length > 1) { boxWidth = Math.min(xscale(columns[1]), 120.0); } else { boxWidth = 120.0; } margin.inner = boxWidth / 3.0; columns.forEach((column, i) => { d = mydata[i] = []; data.forEach((row) => { value = row[column]; d.push(value); if (value > max) max = Math.ceil(value); if (value < min) min = Math.floor(value); }); }); const yscale = d3.scale.linear() .domain([min * 0.99, max * 1.01]) .range([height, 0]); const chart = box() .whiskers(calcIqr(1.5)) .width(boxWidth - 2 * margin.inner) .height(height) .domain([min * 0.99, max * 1.01]); const xAxis = d3.svg.axis() .scale(xscale) .orient('bottom'); const yAxis = d3.svg.axis() .scale(yscale) .orient('left'); const xLines = d3.svg.axis() .scale(xscale) .tickSize(height) .orient('bottom'); const yLines = d3.svg.axis() .scale(yscale) .tickSize(width) .orient('right'); function barOffset(i) { return xscale(columns[i]) + (xscale(columns[1]) - margin.inner) / 2.0; } d3.select(elm[0]).selectAll('svg').remove(); const plot = d3.select(elm[0]) .append('svg') .attr('width', parentWidth) .attr('height', height + margin.bottom + margin.top) .append('g') .attr('width', parentWidth - margin.left - margin.right) .attr('transform', `translate(${margin.left},${margin.top})`); d3.select('svg').append('text') .attr('class', 'box') .attr('x', parentWidth / 2.0) .attr('text-anchor', 'middle') .attr('y', height + margin.bottom) .text(xAxisLabel); d3.select('svg').append('text') .attr('class', 'box') .attr('transform', `translate(10,${(height + margin.top + margin.bottom) / 2.0})rotate(-90)`) .attr('text-anchor', 'middle') .text(yAxisLabel); plot.append('rect') .attr('class', 'grid-background') .attr('width', width) .attr('height', height); plot.append('g') .attr('class', 'grid') .call(yLines); plot.append('g') .attr('class', 'grid') .call(xLines); plot.append('g') .attr('class', 'x axis') .attr('transform', `translate(0,${height})`) .call(xAxis); plot.append('g') .attr('class', 'y axis') .call(yAxis); plot.selectAll('.box').data(mydata) .enter().append('g') .attr('class', 'box') .attr('width', boxWidth) .attr('height', height) .attr('transform', (_, i) => `translate(${barOffset(i)},${0})`) .call(chart); }, true); }, }; } function boxPlotEditor() { return { restrict: 'E', template: editorTemplate, }; } export default function init(ngModule) { ngModule.directive('boxplotRenderer', boxPlotRenderer); ngModule.directive('boxplotEditor', boxPlotEditor); ngModule.config((VisualizationProvider) => { const renderTemplate = '