1

I have created a dashboard(a set of 3 graphs) using d3, now i want to export the set of three graphs to any of the downloadable formats in the browser. I referenced the following post and tried to download atleast one graph: SVG to Canvas with d3.js

var  formatAsPercentage = d3.format("%"),
  formatAsPercentage1Dec = d3.format(".1%"),
  formatAsInteger = d3.format(","),
  fsec = d3.time.format("%S s"),
  fmin = d3.time.format("%M m"),
  fhou = d3.time.format("%H h"),
  fwee = d3.time.format("%a"),
  fdat = d3.time.format("%d d"),
  fmon = d3.time.format("%b")
  ;
// Let's create a mock visualization

function dsPieChart(){

 var dataset = [
   {category: "apple", measure: 0.30},
       {category: "mango", measure: 0.25},
       {category: "pineapple", measure: 0.18},
       {category: "orange", measure: 0.0},
       {category: "peach", measure: 0.18}
       ]
       ;

 var  width = 400,
     height = 400,
     outerRadius = Math.min(width, height) / 2,
     innerRadius = outerRadius * .999,   
     // for animation
     innerRadiusFinal = outerRadius * .5,
     innerRadiusFinal3 = outerRadius* .45,
     color = d3.scale.category20()    //builtin range of colors
     
     ;
var svg = d3.select("#pie")
      .append("svg:svg")              //create the SVG element inside the <body>
      .data([dataset])                   //associate our data with the document
          .attr("width", width)           //set the width and height of our visualization (these will be attributes of the <svg> tag
          .attr("height", height)
        .append("svg:g")                //make a group to hold our pie chart
          .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")    //move the center of the pie chart from 0, 0 to radius, radius
    ;

var arc = d3.svg.arc()              //this will create <path> elements for us using arc data
         .outerRadius(outerRadius).innerRadius(innerRadius);
   
   // for animation
   var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
 var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);

   var pie = d3.layout.pie()           //this will create arc data for us given a list of values
        .value(function(d) { return d.measure; });    //we must tell it out to access the value of each element in our data array

   var arcs = svg.selectAll("g.slice")     //this selects all <g> elements with class slice (there aren't any yet)
        .data(pie)                          //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
        .enter()                            //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
            .append("svg:g")                //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
               .attr("class", "slice")    //allow us to style things in the slices (like text)
               .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", up)
        ;
        
        arcs.append("svg:path")
               .attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
               .attr("d", arc)     //this creates the actual SVG path using the associated data (pie) with the arc drawing function
     .append("svg:title") //mouseover title showing the figures
       .text(function(d) { return d.data.category + ": " + formatAsPercentage(d.data.measure); });   

        d3.selectAll("g.slice").selectAll("path").transition()
       .duration(750)
       .delay(10)
       .attr("d", arcFinal )
       ;
 
   // Add a label to the larger arcs, translated to the arc centroid and rotated.
   // source: http://bl.ocks.org/1305337#index.html
   arcs.filter(function(d) { return d.endAngle - d.startAngle > .2; })
     .append("svg:text")
       .attr("dy", ".35em")
       .attr("text-anchor", "middle")
       .attr("transform", function(d) { return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")"; })
       //.text(function(d) { return formatAsPercentage(d.value); })
       .text(function(d) { return d.data.category; })
       ;
    
    // Computes the label angle of an arc, converting from radians to degrees.
  function angle(d) {
      var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
      return a > 90 ? a - 180 : a;
  }
      
  
  // Pie chart title   
  svg.append("svg:text")
       .attr("dy", ".35em")
       .attr("text-anchor", "middle")
       .text("Usage Domainwise")
       .attr("class","title")
       ;      


  
 function mouseover() {
   d3.select(this).select("path").transition()
       .duration(750)
           //.attr("stroke","red")
           //.attr("stroke-width", 1.5)
           .attr("d", arcFinal3)
           ;
 }
 
 function mouseout() {
   d3.select(this).select("path").transition()
       .duration(750)
           //.attr("stroke","blue")
           //.attr("stroke-width", 1.5)
           .attr("d", arcFinal)
           ;
 }
 
 function up(d, i) {
 
    /* update bar chart when user selects piece of the pie chart */
    //updateBarChart(dataset[i].category);
    
    updateBarChart(d.data.category, color(i));
    updateBarStatusChart(d.data.category, color(i));
    
 }
// Create an export button
d3.select("body")
    .append("button")
    .html("Export")
    .on("click",svgToCanvas);

var w = 100, // or whatever your svg width is
    h = 100;

// Create the export function - this will just export 
// the first svg element it finds
function svgToCanvas(){
    // Select the first svg element
    var svg = d3.select("svg")[0][0],
        img = new Image(),
        serializer = new XMLSerializer(),
        svgStr = serializer.serializeToString(svg);

    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");
    document.body.appendChild(canvas);

    canvas.width = w;
    canvas.height = h;
    canvas.getContext("2d").drawImage(img,0,0,w,h);
    // Now save as png or whatever
 //var myCanvas = document.getElementsByTagName("canvas")[0];
 document.write('<img src="'+canvas[0].toDataURL("image/png")+'"/>');
};

 
}

dsPieChart();
 
  #pie {    
 position:absolute;
 top:50px;
 left:10px;
 width:400px;
 height: 400px; 
}
<head>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<div>
<div id="pie"></div>
</div>
</body>

When i click on the export option its not able to recognise my canvas elemt i tried various options available online but to no use. Soebody please let me know how to do it What i want is when i cklick on export i want it to download my graph as a image

Aarika
  • 49
  • 9

1 Answers1

0

you missed image.onLoad function

var  formatAsPercentage = d3.format("%"),
  formatAsPercentage1Dec = d3.format(".1%"),
  formatAsInteger = d3.format(","),
  fsec = d3.time.format("%S s"),
  fmin = d3.time.format("%M m"),
  fhou = d3.time.format("%H h"),
  fwee = d3.time.format("%a"),
  fdat = d3.time.format("%d d"),
  fmon = d3.time.format("%b")
  ;
// Let's create a mock visualization

function dsPieChart(){

 var dataset = [
   {category: "apple", measure: 0.30},
       {category: "mango", measure: 0.25},
       {category: "pineapple", measure: 0.18},
       {category: "orange", measure: 0.0},
       {category: "peach", measure: 0.18}
       ]
       ;

 var  width = 400,
     height = 400,
     outerRadius = Math.min(width, height) / 2,
     innerRadius = outerRadius * .999,   
     // for animation
     innerRadiusFinal = outerRadius * .5,
     innerRadiusFinal3 = outerRadius* .45,
     color = d3.scale.category20()    //builtin range of colors
     
     ;
var svg = d3.select("#pie")
      .append("svg:svg")              //create the SVG element inside the <body>
      .data([dataset])                   //associate our data with the document
          .attr("width", width)           //set the width and height of our visualization (these will be attributes of the <svg> tag
          .attr("height", height)
        .append("svg:g")                //make a group to hold our pie chart
          .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")    //move the center of the pie chart from 0, 0 to radius, radius
    ;

var arc = d3.svg.arc()              //this will create <path> elements for us using arc data
         .outerRadius(outerRadius).innerRadius(innerRadius);
   
   // for animation
   var arcFinal = d3.svg.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
 var arcFinal3 = d3.svg.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);

   var pie = d3.layout.pie()           //this will create arc data for us given a list of values
        .value(function(d) { return d.measure; });    //we must tell it out to access the value of each element in our data array

   var arcs = svg.selectAll("g.slice")     //this selects all <g> elements with class slice (there aren't any yet)
        .data(pie)                          //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
        .enter()                            //this will create <g> elements for every "extra" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
            .append("svg:g")                //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
               .attr("class", "slice")    //allow us to style things in the slices (like text)
               .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", up)
        ;
        
        arcs.append("svg:path")
               .attr("fill", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
               .attr("d", arc)     //this creates the actual SVG path using the associated data (pie) with the arc drawing function
     .append("svg:title") //mouseover title showing the figures
       .text(function(d) { return d.data.category + ": " + formatAsPercentage(d.data.measure); });   

        d3.selectAll("g.slice").selectAll("path").transition()
       .duration(750)
       .delay(10)
       .attr("d", arcFinal )
       ;
 
   // Add a label to the larger arcs, translated to the arc centroid and rotated.
   // source: http://bl.ocks.org/1305337#index.html
   arcs.filter(function(d) { return d.endAngle - d.startAngle > .2; })
     .append("svg:text")
       .attr("dy", ".35em")
       .attr("text-anchor", "middle")
       .attr("transform", function(d) { return "translate(" + arcFinal.centroid(d) + ")rotate(" + angle(d) + ")"; })
       //.text(function(d) { return formatAsPercentage(d.value); })
       .text(function(d) { return d.data.category; })
       ;
    
    // Computes the label angle of an arc, converting from radians to degrees.
  function angle(d) {
      var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
      return a > 90 ? a - 180 : a;
  }
      
  
  // Pie chart title   
  svg.append("svg:text")
       .attr("dy", ".35em")
       .attr("text-anchor", "middle")
       .text("Usage Domainwise")
       .attr("class","title")
       ;      


  
 function mouseover() {
   d3.select(this).select("path").transition()
       .duration(750)
           //.attr("stroke","red")
           //.attr("stroke-width", 1.5)
           .attr("d", arcFinal3)
           ;
 }
 
 function mouseout() {
   d3.select(this).select("path").transition()
       .duration(750)
           //.attr("stroke","blue")
           //.attr("stroke-width", 1.5)
           .attr("d", arcFinal)
           ;
 }
 
 function up(d, i) {
 
    /* update bar chart when user selects piece of the pie chart */
    //updateBarChart(dataset[i].category);
    
    updateBarChart(d.data.category, color(i));
    updateBarStatusChart(d.data.category, color(i));
    
 }
// Create an export button
d3.select("body")
    .append("button")
    .html("Export")
    .on("click",svgToCanvas);

var w = 100, // or whatever your svg width is
    h = 100;

// Create the export function - this will just export 
// the first svg element it finds
function svgToCanvas(){
    // Select the first svg element
    debugger;
    var svg = d3.select("svg")[0][0],
        img = new Image(),
        serializer = new XMLSerializer(),
        svgStr = serializer.serializeToString(svg);

    data = 'data:image/svg+xml;base64,'+window.btoa(svgStr);
    
    var canvas = document.createElement("canvas");
    canvas.width = 400;
    canvas.height = 400;
    context = canvas.getContext("2d");
    img.src = data;
    img.onload = function() {
      context.drawImage(img, 0, 0);
      var canvasdata = canvas.toDataURL("image/png");
      var pngimg = '<img src="'+canvasdata+'">'; 
      var a = document.createElement("a");
      a.download = "sample.png";
      a.href = canvasdata;
      a.click();
    };
};

 
}

dsPieChart();
#pie {    
 position:absolute;
 top:50px;
 left:10px;
 width:400px;
 height: 400px; 
}
<script src="https://d3js.org/d3.v3.min.js"></script>


<div>
<div id="pie"></div>
</div>
subramanian
  • 1,125
  • 1
  • 12
  • 12
  • :The image exported is only the top part of the image, initially i thought it was because of the width and height specifications , but it still gives the same after specifying the correct width and height . how do i correct it? – Aarika Aug 28 '17 at 04:40
  • its width and height issue only , set the width and height of the canvas once the canvas element is created like this ..canvas.width = 400;canvas.height = 400; – subramanian Aug 28 '17 at 05:26
  • Thank you so much the issue is fixed now and it works completely well:) – Aarika Aug 31 '17 at 04:21
  • Further more when i was trying to extend the same functionality to more than one graph, like say a bar chart along with this pie, it akways takes first element (always downloads pie chart) any idea of what i could do to achieve it – Aarika Aug 31 '17 at 04:22
  • instead of ... var svg = d3.select("svg")[0][0]....use.. var svg = d3.selectAll("svg")[0][0]....[0][0] will select your first svg and [0][1] will select you second svg ......if you use select method it will select only the first element wereas selectAll will select all the relevent elements from your html – subramanian Aug 31 '17 at 06:13