Swaroop
Swaroop

Reputation: 541

Get In between points of an Ordinal Scale Axis in d3

I am having a hard time in finding the points in between the given X scale domain. For eg. I have a bar chart having its X axis values as

[Bob,Robin,Anne,Mark,Joe,Eve,Karen,Kirsty,Chris,Lisa,Tom,Stacy,Charles,Mary]

The above values are used to plot the x-axis.I have used scaleBand method since the values are discrete.

  var x = d3.scaleBand()
          .range([0, width])
          .padding(0.1);
  x.domain(data.map(function(d) { return d.salesperson; })); 

My Aim is to find points in between x scale from the given points,i.e Is there any way that we can locate a point between Bob,Robinor Robin,Anne, So that it would be possible for me to plot a point provided a Y axis value is given.

I have added a plunker which illustrates bar chart having the mentioned x-axis.I need to plot a point on the chart having X value point between Bob,Robin and Y value as 30(any value on Y axis)

Upvotes: 2

Views: 899

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102198

Provided that Robin is the discrete value just after Bob, you can use a combination of bandwidth() and step():

x("Bob") + x.bandwidth() + (x.step()-x.bandwidth()) / 2

Ugly, but it works. Talking about ugliness, if you want an uglier math, you can also use a combination of step() and paddingInner():

x("Bob") + x.step() * (1 - x.paddingInner() / 2))

Here is a demo, I put the point at y(10) so you can better see that it will be right between the "Bob" and "Robin" bars:

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// set the ranges
var x = d3.scaleBand()
          .range([0, width])
          .padding(0.1);
var y = d3.scaleLinear()
          .range([height, 0]);
          
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", 
          "translate(" + margin.left + "," + margin.top + ")");


  var data = [
  {
    "salesperson": "Bob",
    "sales": 33
  },
  {
    "salesperson": "Robin",
    "sales": 12
  },
  {
    "salesperson": "Anne",
    "sales": 41
  },
  {
    "salesperson": "Mark",
    "sales": 16
  },
  {
    "salesperson": "Joe",
    "sales": 59
  },
  {
    "salesperson": "Eve",
    "sales": 38
  },
  {
    "salesperson": "Karen",
    "sales": 21
  },
  {
    "salesperson": "Kirsty",
    "sales": 25
  },
  {
    "salesperson": "Chris",
    "sales": 30
  },
  {
    "salesperson": "Lisa",
    "sales": 47
  },
  {
    "salesperson": "Tom",
    "sales": 5
  },
  {
    "salesperson": "Stacy",
    "sales": 20
  },
  {
    "salesperson": "Charles",
    "sales": 13
  },
  {
    "salesperson": "Mary",
    "sales": 29
  }
];
  // format the data
  data.forEach(function(d) {
    d.sales = +d.sales;
  });

  // Scale the range of the data in the domains
  x.domain(data.map(function(d) { return d.salesperson; }));
  y.domain([0, d3.max(data, function(d) { return d.sales; })]);

  // append the rectangles for the bar chart
  svg.selectAll(".bar")
      .data(data)
    .enter().append("rect")
      .attr("class", "bar")
      .attr("x", function(d) { return x(d.salesperson); })
      .attr("width", x.bandwidth())
      .attr("y", function(d) { return y(d.sales); })
      .attr("height", function(d) { return height - y(d.sales); });

  // add the x Axis
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // add the y Axis
  svg.append("g")
      .call(d3.axisLeft(y));
      
  var pointBetween = svg.append("circle")
    .attr("r", 2)
    .attr("cy", y(10))
    .attr("cx", x("Bob") + x.bandwidth() + (x.step()-x.bandwidth())/2)
.bar { fill: steelblue; }
<script src="//d3js.org/d3.v4.min.js"></script>

Upvotes: 3

Related Questions