/** * A concrete implementation of a general purpose bubble chart that allows data visualization using the * following dimensions: * - x axis position * - y axis position * - bubble radius * - color * * Examples: * - {@link http://dc-js.github.com/dc.js/ Nasdaq 100 Index} * - {@link http://dc-js.github.com/dc.js/vc/index.html US Venture Capital Landscape 2011} * @class bubbleChart * @memberof dc * @mixes dc.bubbleMixin * @mixes dc.coordinateGridMixin * @example * // create a bubble chart under #chart-container1 element using the default global chart group * var bubbleChart1 = dc.bubbleChart('#chart-container1'); * // create a bubble chart under #chart-container2 element using chart group A * var bubbleChart2 = dc.bubbleChart('#chart-container2', 'chartGroupA'); * @param {String|node|d3.selection} parent - Any valid * {@link https://github.com/mbostock/d3/wiki/Selections#selecting-elements d3 single selector} specifying * a dom block element such as a div; or a dom element or d3 selection. * @param {String} [chartGroup] - The name of the chart group this chart instance should be placed in. * Interaction with a chart will only trigger events and redraws within the chart's group. * @return {dc.bubbleChart} */ dc.bubbleChart = function (parent, chartGroup) { var _chart = dc.bubbleMixin(dc.coordinateGridMixin({})); var _elasticRadius = false; var _sortBubbleSize = false; _chart.transitionDuration(750); var bubbleLocator = function (d) { return 'translate(' + (bubbleX(d)) + ',' + (bubbleY(d)) + ')'; }; /** * Turn on or off the elastic bubble radius feature, or return the value of the flag. If this * feature is turned on, then bubble radii will be automatically rescaled to fit the chart better. * @method elasticRadius * @memberof dc.bubbleChart * @instance * @param {Boolean} [elasticRadius=false] * @return {Boolean} * @return {dc.bubbleChart} */ _chart.elasticRadius = function (elasticRadius) { if (!arguments.length) { return _elasticRadius; } _elasticRadius = elasticRadius; return _chart; }; /** * Turn on or off the bubble sorting feature, or return the value of the flag. If enabled, * bubbles will be sorted by their radius, with smaller bubbles in front. * @method sortBubbleSize * @memberof dc.bubbleChart * @instance * @param {Boolean} [sortBubbleSize=false] * @return {Boolean} * @return {dc.bubbleChart} */ _chart.sortBubbleSize = function (sortBubbleSize) { if (!arguments.length) { return _sortBubbleSize; } _sortBubbleSize = sortBubbleSize; return _chart; }; _chart.plotData = function () { if (_elasticRadius) { _chart.r().domain([_chart.rMin(), _chart.rMax()]); } _chart.r().range([_chart.MIN_RADIUS, _chart.xAxisLength() * _chart.maxBubbleRelativeSize()]); var data = _chart.data(); if (_sortBubbleSize) { // sort descending so smaller bubbles are on top var radiusAccessor = _chart.radiusValueAccessor(); data.sort(function (a, b) { return d3.descending(radiusAccessor(a), radiusAccessor(b)); }); } var bubbleG = _chart.chartBodyG().selectAll('g.' + _chart.BUBBLE_NODE_CLASS) .data(data, function (d) { return d.key; }); if (_sortBubbleSize) { // Call order here to update dom order based on sort bubbleG.order(); } renderNodes(bubbleG); updateNodes(bubbleG); removeNodes(bubbleG); _chart.fadeDeselectedArea(); }; function renderNodes (bubbleG) { var bubbleGEnter = bubbleG.enter().append('g'); bubbleGEnter .attr('class', _chart.BUBBLE_NODE_CLASS) .attr('transform', bubbleLocator) .append('circle').attr('class', function (d, i) { return _chart.BUBBLE_CLASS + ' _' + i; }) .on('click', _chart.onClick) .attr('fill', _chart.getColor) .attr('r', 0); dc.transition(bubbleG, _chart.transitionDuration()) .selectAll('circle.' + _chart.BUBBLE_CLASS) .attr('r', function (d) { return _chart.bubbleR(d); }) .attr('opacity', function (d) { return (_chart.bubbleR(d) > 0) ? 1 : 0; }); _chart._doRenderLabel(bubbleGEnter); _chart._doRenderTitles(bubbleGEnter); } function updateNodes (bubbleG) { dc.transition(bubbleG, _chart.transitionDuration()) .attr('transform', bubbleLocator) .selectAll('circle.' + _chart.BUBBLE_CLASS) .attr('fill', _chart.getColor) .attr('r', function (d) { return _chart.bubbleR(d); }) .attr('opacity', function (d) { return (_chart.bubbleR(d) > 0) ? 1 : 0; }); _chart.doUpdateLabels(bubbleG); _chart.doUpdateTitles(bubbleG); } function removeNodes (bubbleG) { bubbleG.exit().remove(); } function bubbleX (d) { var x = _chart.x()(_chart.keyAccessor()(d)); if (isNaN(x)) { x = 0; } return x; } function bubbleY (d) { var y = _chart.y()(_chart.valueAccessor()(d)); if (isNaN(y)) { y = 0; } return y; } _chart.renderBrush = function () { // override default x axis brush from parent chart }; _chart.redrawBrush = function () { // override default x axis brush from parent chart _chart.fadeDeselectedArea(); }; return _chart.anchor(parent, chartGroup); };