Noobster
Noobster

Reputation: 1044

Calculate height of bar in pixels from a Y value in D3 graph

I have a quick question. I am trying to determine the height in pixels of a chart bar. This is for a D3 implementation, and my chart has a logarithmic y-axis.

  1. I know the Y value for the bar I am trying to plot.
  2. I also know the height of the axis in pixels (600px).
  3. I know the min and the max of the Y-axis

I have tried various computations but cannot seem to calculate the height of the bar so that it connects the Y value with the x-Axis.

The picture below should provide a visual illustration of what I am trying to do. Right now I can't seem to get it right ... I think this is essentially a problem in maths, not so much D3. Thank you!

*** EDIT ****

This is the y axis scale that I am using:

var y = d3.scale.log()
.range([height, 0])
.domain([d3.min(sampleData, function(d) {
      return d.y;
    }),
    d3.max(sampleData, function(d) {
      return d.y;
    })
  ]);

enter image description here

Upvotes: 3

Views: 1628

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102218

I'm still not sure about your problem, because the actual height of the bar is being calculated by the very scale you use to append the rectangles. And, if you're in fact appending the rectangles, you're already setting the height attribute!

Let's see an example. This is a bar chart using your log scale (I'm using D3 v4 here, but the principle is the same) and this fake data:

var data = [2000, 5000, 3000, 8000, 1500];

As you can see, there is a minimum and a maximum. I put a padding of 20px in the scale:

var yScale = d3.scaleLog()
    .range([height - padding, padding])
    .domain([d3.min(data), d3.max(data)]);

So, our first value in the range is h - padding and our last value is just padding. Here is the chart:

var width = 300,
  height = 400,
  padding = 20;
var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var data = [2000, 5000, 3000, 8000, 1500];

var xScale = d3.scaleBand()
  .range([50, width])
  .domain(d3.range(data.length))
  .padding(0.2);

var yScale = d3.scaleLog()
  .range([height - padding, padding])
  .domain([d3.min(data), d3.max(data)]);

var bars = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", (d, i) => xScale(i))
  .attr("width", xScale.bandwidth())
  .attr("height", d => height - padding - yScale(d))
  .attr("y", d => yScale(d))
  .attr("fill", "teal");

var gX = svg.append("g")
  .attr("transform", "translate(0," + (height - padding) + ")")
  .call(d3.axisBottom(xScale));

var gY = svg.append("g")
  .attr("transform", "translate(50,0)")
  .call(d3.axisLeft(yScale));
<script src="//d3js.org/d3.v4.min.js"></script>

Suppose you want to calculate the height of the first bar. We can see, by inspecting the DOM, that its height is 61.867984771728516 pixels:

var width = 300,
  height = 400,
  padding = 20;
var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var data = [2000, 5000, 3000, 8000, 1500];

var xScale = d3.scaleBand()
  .range([50, width])
  .domain(d3.range(data.length))
  .padding(0.2);

var yScale = d3.scaleLog()
  .range([height - padding, padding])
  .domain([d3.min(data), d3.max(data)]);

var bars = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", (d, i) => xScale(i))
  .attr("width", xScale.bandwidth())
  .attr("height", d => height - padding - yScale(d))
  .attr("y", d => yScale(d))
  .attr("fill", "teal");

var gX = svg.append("g")
  .attr("transform", "translate(0," + (height - padding) + ")")
  .call(d3.axisBottom(xScale));

var gY = svg.append("g")
  .attr("transform", "translate(50,0)")
  .call(d3.axisLeft(yScale));
  
console.log(d3.select("rect").node().height.animVal.value)
<script src="//d3js.org/d3.v4.min.js"></script>

But this is simply the first value in the range (height - padding) minus yScale(2000):

var width = 300,
  height = 400,
  padding = 20;
var svg = d3.select("body")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

var data = [2000, 5000, 3000, 8000, 1500];

var xScale = d3.scaleBand()
  .range([50, width])
  .domain(d3.range(data.length))
  .padding(0.2);

var yScale = d3.scaleLog()
  .range([height - padding, padding])
  .domain([d3.min(data), d3.max(data)]);

var bars = svg.selectAll("foo")
  .data(data)
  .enter()
  .append("rect")
  .attr("x", (d, i) => xScale(i))
  .attr("width", xScale.bandwidth())
  .attr("height", d => height - padding - yScale(d))
  .attr("y", d => yScale(d))
  .attr("fill", "teal");

var gX = svg.append("g")
  .attr("transform", "translate(0," + (height - padding) + ")")
  .call(d3.axisBottom(xScale));

var gY = svg.append("g")
  .attr("transform", "translate(50,0)")
  .call(d3.axisLeft(yScale));

console.log(height - padding - yScale(2000))
<script src="//d3js.org/d3.v4.min.js"></script>

Which, by the way, is the value used to set the height of the bars:

.attr("height", d => height - padding - yScale(d))

Upvotes: 2

Related Questions