d3 Pie Chart Overlapping Labels

Viewed 17

I know there are other solutions to this problem, but honestly they are very hard to parse out and implement into my own code. If someone can point me in the right direction in the code snippets below, I would greatly appreciate it.

ISSUE: Overlapping lines in d3 drilldown pie chart: enter image description here

GOAL: To show d3 drilldown pie chart without overlapping labels.

VisualizationScopeModel.prototype.renderDrilldownPieGraph = function (data) {
    let width = 950,
        height = 500,
        margin = 75,
        radius = Math.min(width - margin, height - margin) / 2,
        pieChart = d3.layout.pie().sort(null).value(function (d) {
            return d.hasOwnProperty('genres') ? d.genres.length : d.count;
        }),
        arc = d3.svg.arc().outerRadius(radius),
        outerArc = d3.svg.arc()
            .innerRadius(radius * 0.9)
            .outerRadius(radius * 0.9),
        polylineArc = d3.svg.arc()
            .innerRadius(radius * 0.7)
            .outerRadius(radius * 0.9);

    // clear existing svg element
    d3.select("svg").text("");
    let svg = d3.select("#my_dataviz")
        .append("svg"),
        defs = svg.append("svg:defs"),

        // Declare a main gradient with the dimensions for all gradient entries to refer
        mainGrad = defs.append("svg:radialGradient")
            .attr("gradientUnits", "userSpaceOnUse")
            .attr("cx", 0).attr("cy", 0).attr("r", radius).attr("fx", 0).attr("fy", 0)
            .attr("id", "master"),

        // The pie sectors container
        arcGroup = svg.append("svg:g")
            .attr("class", "arcGroup")
            .attr("filter", "url(#shadow)")
            .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"),

        // The sector text labels
        labels = svg.append("g")
            .attr("class", "labels")
            .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")"),

        // The text lines point a sector
        lines = svg.append("g")
            .attr("class", "lines")
            .attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");

    // Declare shadow filter
    let shadow = defs.append("filter").attr("id", "shadow")
        .attr("filterUnits", "userSpaceOnUse")
        .attr("x", -1 * (width / 2)).attr("y", -1 * (height / 2))
        .attr("width", width).attr("height", height);
    shadow.append("feGaussianBlur")
        .attr("in", "SourceAlpha")
        .attr("stdDeviation", "4")
        .attr("result", "blur");
    shadow.append("feOffset")
        .attr("in", "blur")
        .attr("dx", "4").attr("dy", "4")
        .attr("result", "offsetBlur");
    shadow.append("feBlend")
        .attr("in", "SourceGraphic")
        .attr("in2", "offsetBlur")
        .attr("mode", "normal");

    // "Fold" pie sectors by tweening its current start/end angles
    // into 2*PI
    function tweenOut(data) {
        data.startAngle = data.endAngle = (2 * Math.PI);
        var interpolation = d3.interpolate(this._current, data);
        this._current = interpolation(0);
        return function (t) {
            return arc(interpolation(t));
        };
    }

    // "Unfold" pie sectors by tweening its start/end angles
    // from 0 into their final calculated values
    function tweenIn(data) {
        var interpolation = d3.interpolate({ startAngle: 0, endAngle: 0 }, data);
        this._current = interpolation(0);
        return function (t) {
            return arc(interpolation(t));
        };
    }

    // Helper function to extract color from data object
    function getColor(data, index) {
        return data.color;
    }

    // Helper function to extract a darker version of the color
    function getDarkerColor(data, index) {
        return d3.rgb(getColor(data, index)).darker();
    }

    function findChildenByName(name) {
        for (i = 0; i < data.length; i++) {
            if (data[i].name == name) {
                return data[i].genres;
            }
        }

        return data;
    }

    // Redraw the graph given a certain level of data
    function updateGraph(name) {
        var currData = data;

        if (name != undefined) currData = findChildenByName(name);

        // Create a gradient for each entry (each entry identified by its unique category)
        var gradients = defs.selectAll(".gradient").data(currData, function (d) { return d.name; });
        gradients.enter().append("svg:radialGradient")
            .attr("id", function (d, i) { return "gradient" + d.name; })
            .attr("class", "gradient")
            .attr("xlink:href", "#master");

        gradients.text("").append("svg:stop").attr("offset", "0%").attr("stop-color", getColor);
        gradients.append("svg:stop").attr("offset", "90%").attr("stop-color", getColor);
        gradients.append("svg:stop").attr("offset", "100%").attr("stop-color", getDarkerColor);

        // Create a sector for each entry in the enter selection
        var paths = arcGroup.selectAll("path")
            .data(pieChart(currData), function (d) { return d.data.name; });
        paths.enter().append("svg:path").attr("class", "sector");

        // Each sector will refer to its gradient fill
        paths.attr("fill", function (d, i) { return "url(#gradient" + d.data.name + ")"; })
            .transition().duration(1000)
            .attrTween("d", tweenIn).each("end", function () {
                this._listenToEvents = true;
            });

        // Mouse interaction handling
        paths.on("click", function (d) {
            if (this._listenToEvents) {
                // Reset inmediatelly
                d3.select(this).attr("transform", "translate(0,0)")
                // Change level on click if no transition has started                
                paths.each(function () {
                    this._listenToEvents = false;
                });
                if (d.data.genres) updateGraph(d.data.name);
                else updateGraph()
            }
        }).on("mouseover", function (d) {
            // Mouseover effect if no transition has started                
            if (this._listenToEvents) {
                // Calculate angle bisector
                var ang = d.startAngle + (d.endAngle - d.startAngle) / 2;
                // Transformate to SVG space
                ang = (ang - (Math.PI / 2)) * -1;

                // Calculate a 10% radius displacement
                var x = Math.cos(ang) * radius * 0.1;
                var y = Math.sin(ang) * radius * -0.1;

                d3.select(this).transition()
                    .duration(250).attr("transform", "translate(" + x + "," + y + ")");
            }
        }).on("mouseout", function (d) {
            // Mouseout effect if no transition has started                
            if (this._listenToEvents) {
                d3.select(this).transition()
                    .duration(150).attr("transform", "translate(0,0)");
            }
        });

        // Collapse sectors for the exit selection
        paths.exit().transition().duration(1000)
            .attrTween("d", tweenOut).remove();

        // Create sector texts
        var texts = labels.text("").selectAll('text')
            .data(pieChart(currData), function (d) { return d.data.org_name; });
        texts.enter().append("text").attr("dy", ".75em")
            .text(function (d) {
                return `${d.data.org_name} (${d.data.hasOwnProperty('genres') ? d.data.genres.length : d.data.count})`;
            });

        function midAngle(d) {
            return d.startAngle + (d.endAngle - d.startAngle) / 2;
        }

        texts.transition().duration(1000)
            .attrTween("transform", function (d) {
                this._current = this._current || d;
                var interpolate = d3.interpolate(this._current, d);
                this._current = interpolate(0);

                return function (t) {
                    var d2 = interpolate(t);
                    var pos = outerArc.centroid(d2);
                    pos[0] = (radius + margin / 2) * (midAngle(d2) < Math.PI ? 1 : -1);
                    return "translate(" + pos + ")";
                };
            })
            .styleTween("text-anchor", function (d) {
                this._current = this._current || d;
                var interpolate = d3.interpolate(this._current, d);
                this._current = interpolate(0);
                return function (t) {
                    var d2 = interpolate(t);
                    return midAngle(d2) < Math.PI ? "start" : "end";
                };
            });

        texts.exit().transition().duration(1000)
            .attrTween("d", tweenOut).remove();

        // Create text lines point sectors
        var polylines = lines.text("").selectAll("polyline")
            .data(pieChart(currData), function (d) { return d.data.name; });
        polylines.enter().append("polyline");

        polylines.transition().duration(1000)
            .attrTween("points", function (d) {
                this._current = this._current || d;
                var interpolate = d3.interpolate(this._current, d);
                this._current = interpolate(0);
                return function (t) {
                    var d2 = interpolate(t);
                    var pos = outerArc.centroid(d2);
                    pos[0] = (radius + margin / 2) * (midAngle(d2) < Math.PI ? 1 : -1);
                    return [polylineArc.centroid(d2), outerArc.centroid(d2), pos];
                };
            });

        polylines.exit().transition().duration(1000)
            .attrTween("d", tweenOut).remove();
    }

    updateGraph();
}
0 Answers
Related