0

I have created a D3 hierarchical edge bundling visualization.When i download this visualization as PNG,the links in downloaded image are filled with dark color which is not displayed in actual visualization. Downloaded image is Downloaded image

Actual visualization being displayed is Actual visualization

How to get the same visualization in downloaded image. My current code is:

<html>
    <head>

        <style>
            .node {
              font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
            }
            .link {
              stroke: steelblue;
              stroke-opacity: .4;
              fill: none;
            }
        </style>

    </head>
    <body>
            <script src="http://d3js.org/d3.v3.min.js"></script>
            <script>
            var classes=[
{"name":"a","size":3938,"imports":["b","f"]},
{"name":"b","size":3938,"imports":[]},
{"name":"c","size":3938,"imports":["a"]},
{"name":"d","size":3938,"imports":[]},
{"name":"e","size":3938,"imports":[]},
{"name":"f","size":3938,"imports":[]}


];
                    var diameter = 700,
                        radius = diameter / 2,
                        innerRadius = radius - 120;
                    var cluster = d3.layout.cluster()
                        .size([360, innerRadius]);

                    var bundle = d3.layout.bundle();

                    var line = d3.svg.line.radial()
                        .interpolate("bundle")
                        .tension(.85)
                        .radius(function(d) { return d.y; })
                        .angle(function(d) { return d.x / 180 * Math.PI; });

                    var svg = d3.select("body").append("svg")
                    .attr("id","networkSvg")
                        .attr("width", diameter)
                        .attr("height", diameter)
                      .append("g")
                        .attr("transform", "translate(" + radius + "," + radius + ")");



                      var nodes = cluster.nodes(packageHierarchy(classes)),
                          links = packageImports(nodes);

                      svg.selectAll(".link")
                          .data(bundle(links))
                        .enter().append("path")
                          .attr("class", "link")
                          .attr("d", line);

                      svg.selectAll(".node")
                          .data(nodes.filter(function(n) { return !n.children; }))
                        .enter().append("g")
                          .attr("class", "node")
                          .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
                        .on("mouseover", function(d){
                          d3.selectAll(".node").style("fill", "black");
                          d3.selectAll(".link").style("stroke", "steelblue");
                          colorSource(d.name);

                          d.imports.forEach(function(name){
                            colorTarget(name);
                            colorOutgoingLink(d.name, name);
                          });

                          d.parent.children.forEach(function(data){
                              if(data.imports.indexOf(d.name)>-1)
                              {
                                colorTarget(data.name);
                                colorIncomingLink(data.name, d.name);
                              }
                          });
                        })
                        .on("mouseout",function(d)
                        {
                          d3.selectAll(".node").style("fill", "black");
                          d3.selectAll(".link").style("stroke", "steelblue");

                        }) 
                        .append("text")
                          .attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
                          .attr("dy", ".31em")
                          .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
                          .attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
                          .text(function(d) { return d.key; });


                    // Lazily construct the package hierarchy from class names.

                    function packageHierarchy(classes) {
                      var map = {};
                      function find(name, data) {
                        var node = map[name], i;
                        if (!node) {
                          node = map[name] = data || {name: name, children: []};
                          if (name.length) {
                            node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
                            node.parent.children.push(node);
                            node.key = name.substring(i + 1);
                          }
                        }
                        return node;
                      }
                      classes.forEach(function(d) {
                        find(d.name, d);
                      });
                      return map[""];
                    }


                    // Return a list of imports for the given array of nodes.
                    function packageImports(nodes) {
                      var map = {},
                          imports = [];
                      // Compute a map from name to node.
                      nodes.forEach(function(d) {
                        map[d.name] = d;
                      });
                      // For each import, construct a link from the source to target node.
                      nodes.forEach(function(d) {
                        if (d.imports) d.imports.forEach(function(i) {
                          imports.push({source: map[d.name], target: map[i]});
                        });
                      });
                      return imports;
                    }

                    function colorIncomingLink(src,tgt){
                      var link = d3.selectAll(".link")[0].filter(function(d){
                        return (d3.select(d).data()[0][0].name == src && d3.select(d).data()[0][2].name == tgt);
                      });
                      d3.selectAll(link).style("stroke", "green");
                    }

                    function colorOutgoingLink(src,tgt){
                      var link = d3.selectAll(".link")[0].filter(function(d){
                        return (d3.select(d).data()[0][0].name == src && d3.select(d).data()[0][2].name == tgt);
                      });
                      d3.selectAll(link).style("stroke", "red");
                    }

                    function colorTarget(name){
                      //iterate through all teh dom and get the DOM which has the data
                      var node = d3.selectAll(".node")[0].filter(function(d){
                        return d3.select(d).data()[0].name == name;
                      });
                      d3.selectAll(node).style("fill", "red");
                    }

                    function colorSource(name){
                      //iterate through all teh dom and get teh DOM which has the data
                      var node = d3.selectAll(".node")[0].filter(function(d){
                        return d3.select(d).data()[0].name == name;
                      });
                      d3.selectAll(node).style("fill", "green");
                    }

                     function download()
                    {

                        img = new Image(),
                        serializer = new XMLSerializer(),
                        svgStr = serializer.serializeToString(document.getElementById('networkSvg'));

                    img.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);

                    // You could also use the actual string without base64 encoding it:
                    //img.src = "data:image/svg+xml;utf8," + svgStr;

                    var canvas = document.createElement("canvas");

                    var w=2000;
                    var h=2000;

                    canvas.width = w;
                    canvas.height = h;
                    canvas.getContext("2d").drawImage(img,0,0,w,h);

                    var imgURL = canvas.toDataURL("image/png");


                var dlLink = document.createElement('a');
                    dlLink.download = "down";
                    dlLink.href = imgURL;
                    dlLink.dataset.downloadurl = ["image/png", dlLink.download, dlLink.href].join(':');

                    document.body.appendChild(dlLink);
                    dlLink.click();
                    document.body.removeChild(dlLink);


                    }
                    </script>
                    <button onclick="download()">Download</button>



    </body>
</html>
Jason
  • 676
  • 1
  • 12
  • 34
  • 3
    This is because you are not considering the CSS on export you can give this a try 1st answer. http://stackoverflow.com/questions/20394041/convert-svg-to-png-and-maintain-css-integrity – Cyril Cherian May 30 '16 at 07:28
  • @Cyril i tried setting the fill property,stroke and opacity using d3.selectAll(".links").style("fill","none")...but on doing this ....on clicking the download button....download failed shows up – Jason May 30 '16 at 07:42

1 Answers1

2

As mentioned by Cyril, you can set all styles through d3 rather than using external style sheet.

Here is the working code snippet.

var classes = [{
    "name": "a",
    "size": 3938,
    "imports": ["b", "f"]
  }, {
    "name": "b",
    "size": 3938,
    "imports": []
  }, {
    "name": "c",
    "size": 3938,
    "imports": ["a"]
  }, {
    "name": "d",
    "size": 3938,
    "imports": []
  }, {
    "name": "e",
    "size": 3938,
    "imports": []
  }, {
    "name": "f",
    "size": 3938,
    "imports": []
  }


];
var diameter = 700,
  radius = diameter / 2,
  innerRadius = radius - 120;
var cluster = d3.layout.cluster()
  .size([360, innerRadius]);

var bundle = d3.layout.bundle();

var line = d3.svg.line.radial()
  .interpolate("bundle")
  .tension(.85)
  .radius(function(d) {
    return d.y;
  })
  .angle(function(d) {
    return d.x / 180 * Math.PI;
  });

var svg = d3.select("body").append("svg")
  .attr("id", "networkSvg")
  .attr("width", diameter)
  .attr("height", diameter)
  .style("background-color","white")
  .append("g")
  .attr("transform", "translate(" + radius + "," + radius + ")");


var nodes = cluster.nodes(packageHierarchy(classes)),
  links = packageImports(nodes);

svg.selectAll(".link")
  .data(bundle(links))
  .enter().append("path")
  .attr("class", "link")
  .style("stroke", "steelblue")
  .style("stroke-opacity", .4)
  .style("fill", "none")
  .attr("d", line);

svg.selectAll(".node")
  .data(nodes.filter(function(n) {
    return !n.children;
  }))
  .enter().append("g")
  .attr("class", "node")
  .style("font", "11px Helvetica Neue Helvetica, Arial, sans-serif")
  .attr("transform", function(d) {
    return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
  })
  .on("mouseover", function(d) {
    d3.selectAll(".node").style("fill", "black");
    d3.selectAll(".link").style("stroke", "steelblue");
    colorSource(d.name);

    d.imports.forEach(function(name) {
      colorTarget(name);
      colorOutgoingLink(d.name, name);
    });

    d.parent.children.forEach(function(data) {
      if (data.imports.indexOf(d.name) > -1) {
        colorTarget(data.name);
        colorIncomingLink(data.name, d.name);
      }
    });
  })
  .on("mouseout", function(d) {
    d3.selectAll(".node").style("fill", "black");
    d3.selectAll(".link").style("stroke", "steelblue");

  })
  .append("text")
  .style("stroke","black")
  .attr("dx", function(d) {
    return d.x < 180 ? 8 : -8;
  })
  .attr("dy", ".31em")
  .attr("text-anchor", function(d) {
    return d.x < 180 ? "start" : "end";
  })
  .attr("transform", function(d) {
    return d.x < 180 ? null : "rotate(180)";
  })
  .text(function(d) {
    return d.key;
  });


// Lazily construct the package hierarchy from class names.

function packageHierarchy(classes) {
  var map = {};

  function find(name, data) {
    var node = map[name],
      i;
    if (!node) {
      node = map[name] = data || {
        name: name,
        children: []
      };
      if (name.length) {
        node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
        node.parent.children.push(node);
        node.key = name.substring(i + 1);
      }
    }
    return node;
  }
  classes.forEach(function(d) {
    find(d.name, d);
  });
  return map[""];
}


// Return a list of imports for the given array of nodes.
function packageImports(nodes) {
  var map = {},
    imports = [];
  // Compute a map from name to node.
  nodes.forEach(function(d) {
    map[d.name] = d;
  });
  // For each import, construct a link from the source to target node.
  nodes.forEach(function(d) {
    if (d.imports) d.imports.forEach(function(i) {
      imports.push({
        source: map[d.name],
        target: map[i]
      });
    });
  });
  return imports;
}

function colorIncomingLink(src, tgt) {
  var link = d3.selectAll(".link")[0].filter(function(d) {
    return (d3.select(d).data()[0][0].name == src && d3.select(d).data()[0][2].name == tgt);
  });
  d3.selectAll(link).style("stroke", "green");
}

function colorOutgoingLink(src, tgt) {
  var link = d3.selectAll(".link")[0].filter(function(d) {
    return (d3.select(d).data()[0][0].name == src && d3.select(d).data()[0][2].name == tgt);
  });
  d3.selectAll(link).style("stroke", "red");
}

function colorTarget(name) {
  //iterate through all teh dom and get the DOM which has the data
  var node = d3.selectAll(".node")[0].filter(function(d) {
    return d3.select(d).data()[0].name == name;
  });
  d3.selectAll(node).style("fill", "red");
}

function colorSource(name) {
  //iterate through all teh dom and get teh DOM which has the data
  var node = d3.selectAll(".node")[0].filter(function(d) {
    return d3.select(d).data()[0].name == name;
  });
  d3.selectAll(node).style("fill", "green");
}

function download() {

  img = new Image(),
    serializer = new XMLSerializer(),
    svgStr = serializer.serializeToString(document.getElementById('networkSvg'));

  img.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);

  // You could also use the actual string without base64 encoding it:
  //img.src = "data:image/svg+xml;utf8," + svgStr;

  var canvas = document.createElement("canvas");

  var w = 2000;
  var h = 2000;

  canvas.width = w;
  canvas.height = h;
  canvas.getContext("2d").drawImage(img, 0, 0, w, h);

  var imgURL = canvas.toDataURL("image/png");


  var dlLink = document.createElement('a');
  dlLink.download = "down";
  dlLink.href = imgURL;
  dlLink.dataset.downloadurl = ["image/png", dlLink.download, dlLink.href].join(':');

  document.body.appendChild(dlLink);
  dlLink.click();
  document.body.removeChild(dlLink);


}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button onclick="download()">Download</button>
Gilsha
  • 14,431
  • 3
  • 32
  • 47