1

Below is how I creat a bar chart using rects in D3. However, how would I modify that to get the bar chart with path property in d3js?

I have a json file which has the data to be read and am trying to create a bar chart with paths rather than rectangles in d3js.

Json :

[
  {
    "name": "sam",
    "age": 24
  },
  {
    "name": "baby",
    "age": 23
  },
  {
    "name": "adu",
    "age": 21
  },
  {
    "name": "ja",
    "age": 23
  },
  {
    "name": "mack",
    "age": 34
  }
]

Code:

   <script>
        d3.json("mydata.json", function (data) {

           var canvas = d3.select('body').append('svg')
               .attr('width', 500)
               .attr('height', 500);

           canvas.selectAll('rect')
               .data(data)
               .enter()
                   .append('rect')
                   .attr('width',function (d) { return d.age * 10; })
                   .attr('height', 48)
                   .attr('y', function (d, i) { return i * 50; })
                   .attr('fill', 'blue');

           canvas.selectAll('text')
               .data(data)
               .enter()
                    .append('text')
                    .attr('fill','white')
                    .attr('y', function (d, i) {
                        return i* 50 + 24;
                    })
               .text(function (d) {
                   return d.name;
               })
        });
    </script>

I have searched through many sites. I am unable to get though

Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
Sameer Pradhan
  • 135
  • 2
  • 15

1 Answers1

2

You can't assign a d attribute to a rectangle, but you can make a bar chart out of paths rather than rectangles. All you need to do is know the coordinates of the corners of the rectangle. You really only need two corners on opposite sides. If you had top left and bottom right coordinates ([x0,y0] and [x1,y1] respectively) you could do something like:

function drawRect(x0,y0,x1,y1) {
  var p1 = x0 + " " + y0;
  var p2 = x0 + " " + y1;
  var p3 = x1 + " " + y1;
  var p4 = x1 + " " + y0;

  var l = "L";  // cause I'm lazy.
  return "M"+p1+l+p2+l+p3+l+p4+"Z";

}

This moves the cursor to p1, then draws connecting lines from p1 to p2 to p3 to p4 and then returns to the start with Z.

With scaled values this could get a bit verbose either passing scaled parameters or scaling the values in the path making function (as I do below).

This might look like (paths fade in so you can see that they match the rects):

var data = [1,2,3,5,8,3];
var width = 500;
var height = 300;

var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height);
  
var x = d3.scaleBand()
  .domain(d3.range(data.length))
  .range([0,width]);
  
var y = d3.scaleLinear()
  .domain([0,d3.max(data)])
  .range([height,0]);
  
svg.selectAll("rect")
  .data(data)
  .enter()
  .append("rect")
  .attr("width", x.bandwidth())
  .attr("height", function(d) { return height-y(d); })
  .attr("x", function(d,i) { return x(i); })
  .attr("y", function(d) { return y(d); })
  
svg.selectAll("path")
  .data(data)
  .enter()
  .append("path")
  .attr("d", makeRect)
  .attr("fill","orange")
  .style("opacity",0)
  .transition()
  .style("opacity",1)
  .duration(1500);
  
  
function makeRect(d,i) {
  var x0 = x(i);
  var y0 = y(d);
  var x1 = x(i) + x.bandwidth();
  var y1 = height;

  var p1 = x0 + " " + y0;
  var p2 = x0 + " " + y1;
  var p3 = x1 + " " + y1;
  var p4 = x1 + " " + y0;
  var l = "L";
  
  return "M"+p1+l+p2+l+p3+l+p4+"Z";

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

But, this can't really be a preferred option in most cases. It is more complex than the rectangles for sure. The rectangle should be preferable, unless of course you are trying to do some sort of manipulation on the path that you can't do with a rectangle, or maybe you want rounded edges. Perhaps you are projecting the path on a globe or maybe you want to do some path transition (I've added extra vertices to the rectangle path to smooth the transition, in most cases, ideally the start and end paths of a transition have the same number of vertices):

var data = [1,2,3,5,8,3];
var width = 500;
var height = 200;

var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height);
  
var x = d3.scaleBand()
  .domain(d3.range(data.length))
  .range([0,width]);
  
var y = d3.scaleLinear()
  .domain([0,d3.max(data)])
  .range([height,0]);
  
  
  
svg.selectAll("path")
  .data(data)
  .enter()
  .append("path")
  .attr('d', d3.symbol().type( d3.symbols[1]).size(function(d){ return  d*100; }) )
  .attr("transform","translate(0,"+height/2+")")
  .transition()
  .attr("transform",function(d,i) { return "translate("+(x(i)+x.bandwidth()/2) + "," + height/2 + ")" })
  .attr("fill","steelblue")
  .duration(1500)
  .transition()
  .attr("d", makeRect)
  .attr("fill","orange")
  .attr("transform","translate(0,0)")
  .duration(1500);
  //*/
  
function makeRect(d,i) {
  var x0 = x(i);
  var y0 = y(d);
  var x1 = x(i) + x.bandwidth();
  var y1 = height;
  
  var p1 = x0 + " " + y0;
  var p2 = x0 + " " + y1;
  var p3 = x1 + " " + y1;
  var p4 = x1 + " " + y0;
  var l = "L";
  
  return "M"+p1+l+p1+l+p1+l+p4+l+p4+l+p4+l+p3+l+p3+l+p3+l+p2+l+p2+l+p2+"Z";

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
Andrew Reid
  • 37,021
  • 7
  • 64
  • 83