add data and redraw D3 chord layout

Viewed 43

I struggle with the following D3 Chord layout. So far I splitted the data to dependencies and flows. With the help of this data a matrix will be generated to actually draw the dependencies as a chord layout visualization.

By nature data is growing and I tried to push() a dummy data into dependencies and also flows. Further I call my init() function where all the D3 magic is placed. But instead to to redraw the circle the additional data the new data is drawn on top of existing data.

What do I miss?

  ////////////////////////////////////////////////////////////
        //////////////////////// Set-Up ////////////////////////////
        ////////////////////////////////////////////////////////////

        var dependencies = [
            { id: 0, name: "0", color: "#301E1E" },
            { id: 1, name: "1", color: "#083E77" },
            { id: 2, name: "2", color: "#342350" },
            { id: 3, name: "3", color: "#567235" },
        ]

        var flows = [
            { from: 0, to: 0, quantity: 0 },
            { from: 0, to: 1, quantity: 4 },
            { from: 0, to: 2, quantity: 3 },
            { from: 0, to: 3, quantity: 2 },
            { from: 1, to: 0, quantity: 4 },
            { from: 1, to: 1, quantity: 0 },
            { from: 1, to: 2, quantity: 3 },
            { from: 1, to: 3, quantity: 2 },
            { from: 2, to: 0, quantity: 3 },
            { from: 2, to: 1, quantity: 3 },
            { from: 2, to: 2, quantity: 0 },
            { from: 2, to: 3, quantity: 0 },
            { from: 3, to: 0, quantity: 2 },
            { from: 3, to: 1, quantity: 2 },
            { from: 3, to: 2, quantity: 2 },
            { from: 3, to: 3, quantity: 0 },
        ]

        var matrix = []

        ////////////////////////////////////////////////////////////
        ////////////////////// Create SVG //////////////////////////
        ////////////////////////////////////////////////////////////

        var margin = { left: 20, top: 20, right: 20, bottom: 20 }
        var width = Math.min(window.innerWidth, 900) - margin.left - margin.right
        var height = Math.min(window.innerHeight, 900) - margin.top - margin.bottom
        var innerRadius = Math.min(width, height) * 0.39
        var outerRadius = innerRadius * 1.1;
        var opacityDefault = 0.8;

        var svg = d3.select("#chart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");

        ////////////////////////////////////////////////////////////
        /////////// Create scale and layout functions //////////////
        ////////////////////////////////////////////////////////////

        init()

        function init() {
            // Map flows data to valid matrix
            flows.forEach(function (flow) {
                //initialize sub-array if not yet exists
                if (!matrix[flow.to]) {
                    matrix[flow.to] = [];
                }

                matrix[flow.to][flow.from] = flow.quantity;

            })

            //A "custom" d3 chord function that automatically sorts the order of the chords in such a manner to reduce overlap  
            var chord = d3.layout.chord()
                .padding(.15)
                .sortChords(d3.descending) //which chord should be shown on top when chords cross. Now the biggest chord is at the bottom
                .matrix(matrix);

            var arc = d3.svg.arc()
                .innerRadius(innerRadius * 1.01)
                .outerRadius(outerRadius);

            var path = d3.svg.chord()
                .radius(innerRadius);

            ////////////////////////////////////////////////////////////
            ////////////////// Draw outer Arcs /////////////////////////
            ////////////////////////////////////////////////////////////

            var outerArcs = svg.selectAll("g.group")
                .data(chord.groups)
                .enter().append("g")
                .attr("class", "group")

            outerArcs.append("path")
                .style("fill", function (d) { return dependencies[d.index].color; })
                //.style("fill", function(d) { return colors(d.index); })
                .attr("d", arc)
                .each(function (d, i) {
                    //Search pattern for everything between the start and the first capital L
                    var firstArcSection = /(^.+?)L/;

                    //Grab everything up to the first Line statement
                    var newArc = firstArcSection.exec(d3.select(this).attr("d"))[1];
                    //Replace all the comma's so that IE can handle it
                    newArc = newArc.replace(/,/g, " ");

                    //If the end angle lies beyond a quarter of a circle (90 degrees or pi/2) 
                    //flip the end and start position
                    if (d.endAngle > 90 * Math.PI / 180 & d.startAngle < 270 * Math.PI / 180) {
                        var startLoc = /M(.*?)A/,       //Everything between the first capital M and first capital A
                            middleLoc = /A(.*?)0 0 1/,  //Everything between the first capital A and 0 0 1
                            endLoc = /0 0 1 (.*?)$/;    //Everything between the first 0 0 1 and the end of the string (denoted by $)
                        //Flip the direction of the arc by switching the start en end point (and sweep flag)
                        //of those elements that are below the horizontal line
                        var newStart = endLoc.exec(newArc)[1];
                        var newEnd = startLoc.exec(newArc)[1];
                        var middleSec = middleLoc.exec(newArc)[1];

                        //Build up the new arc notation, set the sweep-flag to 0
                        newArc = "M" + newStart + "A" + middleSec + "0 0 0 " + newEnd;
                    }//if

                    //Create a new invisible arc that the text can flow along
                    svg.append("path")
                        .attr("class", "hiddenArcs")
                        .attr("id", "arc" + i)
                        .attr("d", newArc)
                        .style("fill", "none");
                });

            ////////////////////////////////////////////////////////////
            ////////////////// Append Names ////////////////////////////
            ////////////////////////////////////////////////////////////

            //Append the label names on the outside
            outerArcs.append("text")
                .each(function (d) { d.angle = (d.startAngle + d.endAngle) / 2; })
                //.attr("dy", ".35em")
                .attr("class", "titles")
                .attr("text-anchor", function (d) { return d.angle > Math.PI ? "end" : null; })
                .attr("transform", function (d) {
                    return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                        + "translate(" + (outerRadius + 10) + ")"
                        + (d.angle > Math.PI ? "rotate(180)" : "");
                })
                .style("font-size", 16 + "px")
                .text(function (d, i) { return dependencies[i].name; });

            ////////////////////////////////////////////////////////////
            ////////////////// Draw inner chords ///////////////////////
            ////////////////////////////////////////////////////////////

            svg.selectAll("path.chord")
                .data(chord.chords)
                .enter().append("path")
                .attr("class", "chord")
                .style("fill", "grey")
                .style("opacity", opacityDefault)
                .attr("d", path)
        }

        ////////////////////////////////////////////////////////////
        /////////////////////// Add more Data //////////////////////
        ////////////////////////////////////////////////////////////

        document.getElementById("chart").onclick = function() {
            dependencies.push({
                id: dependencies.length,
                name: dependencies.length,
                color: "black"
            })

            // Creates new Flows and set quantity to 1
            for (var i = 0; i < dependencies.length; i++) {
                flows.push({
                    from: dependencies.length - 1,
                    to: i,
                    quantity: 1,
                })
            }

            // Adds new Flow quantity to existing flows
            for (var j = 0; j < dependencies.length; j++) {
                flows.push({
                    from: j,
                    to: dependencies.length - 1,
                    quantity: 1,
                })
            }

            init()
        }
   body {
            font-size: 12px;
            font-family: 'Lato', sans-serif;
            text-align: center;
            fill: #2B2B2B;
            cursor: default;
        }

        @media (min-width: 600px) {
            #chart {
                font-size: 16px;
            }
        }
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <title>Step 1 - Collaborations between MCU Avengers</title>

    <!-- D3.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>

    <!-- Google Fonts -->
    <link href='https://fonts.googleapis.com/css?family=Lato:400,900' rel='stylesheet' type='text/css'>

</head>
<body>
 <div id="chart"></div>
</body>

</html>

1 Answers

Ok I found the problem. I need to clear the content first, before redrawing the updated chart. To do so I placed the svg creation in the init() function and added a remove() line before.

 d3.select("#chart").selectAll("*").remove();

        var svg = d3.select("#chart").append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");

        ////////////////////////////////////////////////////////////
        //////////////////////// Set-Up ////////////////////////////
        ////////////////////////////////////////////////////////////

        var dependencies = [
            { id: 0, name: "0", color: "#301E1E" },
            { id: 1, name: "1", color: "#083E77" },
            { id: 2, name: "2", color: "#342350" },
            { id: 3, name: "3", color: "#567235" },
        ]

        var flows = [
            { from: 0, to: 0, quantity: 0 },
            { from: 0, to: 1, quantity: 4 },
            { from: 0, to: 2, quantity: 3 },
            { from: 0, to: 3, quantity: 2 },
            { from: 1, to: 0, quantity: 4 },
            { from: 1, to: 1, quantity: 0 },
            { from: 1, to: 2, quantity: 3 },
            { from: 1, to: 3, quantity: 2 },
            { from: 2, to: 0, quantity: 3 },
            { from: 2, to: 1, quantity: 3 },
            { from: 2, to: 2, quantity: 0 },
            { from: 2, to: 3, quantity: 0 },
            { from: 3, to: 0, quantity: 2 },
            { from: 3, to: 1, quantity: 2 },
            { from: 3, to: 2, quantity: 2 },
            { from: 3, to: 3, quantity: 0 },
        ]

        var matrix = []

        ////////////////////////////////////////////////////////////
        ////////////////////// Create SVG //////////////////////////
        ////////////////////////////////////////////////////////////

        var margin = { left: 20, top: 20, right: 20, bottom: 20 }
        var width = Math.min(window.innerWidth, 900) - margin.left - margin.right
        var height = Math.min(window.innerHeight, 900) - margin.top - margin.bottom
        var innerRadius = Math.min(width, height) * 0.39
        var outerRadius = innerRadius * 1.1;
        var opacityDefault = 0.8;

        

        ////////////////////////////////////////////////////////////
        /////////// Create scale and layout functions //////////////
        ////////////////////////////////////////////////////////////

        init()

        function init() {

            d3.select("#chart").selectAll("*").remove();

            var svg = d3.select("#chart").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + (width / 2 + margin.left) + "," + (height / 2 + margin.top) + ")");


            // Map flows data to valid matrix
            flows.forEach(function (flow) {
                //initialize sub-array if not yet exists
                if (!matrix[flow.to]) {
                    matrix[flow.to] = [];
                }

                matrix[flow.to][flow.from] = flow.quantity;

            })

            //A "custom" d3 chord function that automatically sorts the order of the chords in such a manner to reduce overlap  
            var chord = d3.layout.chord()
                .padding(.15)
                .sortChords(d3.descending) //which chord should be shown on top when chords cross. Now the biggest chord is at the bottom
                .matrix(matrix);

            var arc = d3.svg.arc()
                .innerRadius(innerRadius * 1.01)
                .outerRadius(outerRadius);

            var path = d3.svg.chord()
                .radius(innerRadius);

            ////////////////////////////////////////////////////////////
            ////////////////// Draw outer Arcs /////////////////////////
            ////////////////////////////////////////////////////////////

            var outerArcs = svg.selectAll("g.group")
                .data(chord.groups)
                .enter().append("g")
                .attr("class", "group")

            outerArcs.append("path")
                .style("fill", function (d) { return dependencies[d.index].color; })
                //.style("fill", function(d) { return colors(d.index); })
                .attr("d", arc)
                .each(function (d, i) {
                    //Search pattern for everything between the start and the first capital L
                    var firstArcSection = /(^.+?)L/;

                    //Grab everything up to the first Line statement
                    var newArc = firstArcSection.exec(d3.select(this).attr("d"))[1];
                    //Replace all the comma's so that IE can handle it
                    newArc = newArc.replace(/,/g, " ");

                    //If the end angle lies beyond a quarter of a circle (90 degrees or pi/2) 
                    //flip the end and start position
                    if (d.endAngle > 90 * Math.PI / 180 & d.startAngle < 270 * Math.PI / 180) {
                        var startLoc = /M(.*?)A/,       //Everything between the first capital M and first capital A
                            middleLoc = /A(.*?)0 0 1/,  //Everything between the first capital A and 0 0 1
                            endLoc = /0 0 1 (.*?)$/;    //Everything between the first 0 0 1 and the end of the string (denoted by $)
                        //Flip the direction of the arc by switching the start en end point (and sweep flag)
                        //of those elements that are below the horizontal line
                        var newStart = endLoc.exec(newArc)[1];
                        var newEnd = startLoc.exec(newArc)[1];
                        var middleSec = middleLoc.exec(newArc)[1];

                        //Build up the new arc notation, set the sweep-flag to 0
                        newArc = "M" + newStart + "A" + middleSec + "0 0 0 " + newEnd;
                    }//if

                    //Create a new invisible arc that the text can flow along
                    svg.append("path")
                        .attr("class", "hiddenArcs")
                        .attr("id", "arc" + i)
                        .attr("d", newArc)
                        .style("fill", "none");
                });

            ////////////////////////////////////////////////////////////
            ////////////////// Append Names ////////////////////////////
            ////////////////////////////////////////////////////////////

            //Append the label names on the outside
            outerArcs.append("text")
                .each(function (d) { d.angle = (d.startAngle + d.endAngle) / 2; })
                //.attr("dy", ".35em")
                .attr("class", "titles")
                .attr("text-anchor", function (d) { return d.angle > Math.PI ? "end" : null; })
                .attr("transform", function (d) {
                    return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                        + "translate(" + (outerRadius + 10) + ")"
                        + (d.angle > Math.PI ? "rotate(180)" : "");
                })
                .style("font-size", 16 + "px")
                .text(function (d, i) { return dependencies[i].name; });

            ////////////////////////////////////////////////////////////
            ////////////////// Draw inner chords ///////////////////////
            ////////////////////////////////////////////////////////////

            svg.selectAll("path.chord")
                .data(chord.chords)
                .enter().append("path")
                .attr("class", "chord")
                .style("fill", "grey")
                .style("opacity", opacityDefault)
                .attr("d", path)
        }

        ////////////////////////////////////////////////////////////
        /////////////////////// Add more Data //////////////////////
        ////////////////////////////////////////////////////////////

        document.getElementById("chart").onclick = function() {
            dependencies.push({
                id: dependencies.length,
                name: dependencies.length,
                color: "black"
            })

            // Creates new Flows and set quantity to 1
            for (var i = 0; i < dependencies.length; i++) {
                flows.push({
                    from: dependencies.length - 1,
                    to: i,
                    quantity: 1,
                })
            }

            // Adds new Flow quantity to existing flows
            for (var j = 0; j < dependencies.length; j++) {
                flows.push({
                    from: j,
                    to: dependencies.length - 1,
                    quantity: 1,
                })
            }

            matrix = []
            init()
        }
 body {
            font-size: 12px;
            font-family: 'Lato', sans-serif;
            text-align: center;
            fill: #2B2B2B;
            cursor: default;
        }

        @media (min-width: 600px) {
            #chart {
                font-size: 16px;
            }
        }
<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <title>Step 1 - Collaborations between MCU Avengers</title>

    <!-- D3.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>

    <!-- Google Fonts -->
    <link href='https://fonts.googleapis.com/css?family=Lato:400,900' rel='stylesheet' type='text/css'>

</head>

<body>
    <div id="chart"></div>
</body>

</html>

Related