Arun AK
Arun AK

Reputation: 4370

why the last tick doesn't have values - D3.js

The last ticks in x-axis and y-axis don't have values drawn next to them. I cannot figure out why these values are missing.

This is the JS code :

var xAxisScale = d3.scale.linear()
         .domain([0, d3.max(data, function(d) { return d.x; })])
       .range([padding, containerWidth - padding * 2]);

    var xAxis = d3.svg.axis().scale(xAxisScale).orient("bottom");

    var xGuide = chartContainer.append('g')
      .attr("class", "xAxis")
      .call(xAxis)
      .attr('transform', 'translate(0 ,' + (containerHeight -padding) + ')');


    /* Code for adding y axis in chart
     * 
     * */
    var yAxisScale = d3.scale.linear()
      .domain([0, d3.max(data, function(d){return d.y})])
      .range([containerHeight-padding, padding ]);

    var yAxis = d3.svg.axis().scale(yAxisScale).orient("left");

    var yGuide = chartContainer.append('g')
      .attr("class", "yAxis")
      .call(yAxis)
      .attr('transform', 'translate('+padding + ',0)');

This is my live demo.

Upvotes: 1

Views: 2957

Answers (3)

Jun Yin
Jun Yin

Reputation: 2419

Add .nice() to line 69 of your live demo. will solve the issue. See graph below for the end result.

For more details and explanation, please refer to this closed issue on the d3-scale library. mbostock commented on it on Dec 6, 2016 and provided the solution.

enter image description here

Upvotes: 1

altocumulus
altocumulus

Reputation: 21578

This can be done by overriding the default behavior for determining tick values using axis.tickValues():

var yAxis = d3.svg.axis().scale(yAxisScale).orient("left")
  .tickValues(yAxisScale.ticks().concat(yAxisScale.domain()));

This still resorts to the automatically generated tick values by calling yAxisScale.ticks() which is the default behavior for D3's axes. These values are then supplemented with the outer bounds of your data values, i.e. the array returned by yAxisScale.ticks(). To set just the upper bound, if would be sufficient to specify yAxisScale.domain()[1], although it won't hurt having duplicate values in the array provided to .tickValues().

Doing it this way frees you from any hardcoding of tick values.

Have a look at this working example:

var padding = 70;

    	//Width and height
    	var containerWidth = 1000;
    	var containerHeight = 500;
    	var data = [{
    	  "x": 82,
    	  "y": 1730
    	}, {
    	  "x": 533,
    	  "y": 16385
    	}, {
    	  "x": 41,
    	  "y": 783
    	}, {
    	  "x": 20.5,
    	  "y": 5873
    	}, {
    	  "x": 553.5,
    	  "y": 25200
    	}, {
    	  "x": 61.5,
    	  "y": 30000
    	}, {
    	  "x": 184.5,
    	  "y": 2853
    	}, {
    	  "x": 1476,
    	  "y": 83775
    	}];
    	//Create scale functions
			var xScale = d3.scale.linear()
				
								 
    	var chartContainer = d3.select("body")
    	  .append("svg")
    	  .attr("class", "chartContainer")
    	  .attr("width", containerWidth)
    	  .attr("height", containerHeight);

    	$(".chartContainer").css({
    	  "background-color": "",
    	  "position": "absolute",
    	  "top": 50,
    	  "left": 15
    	});
      
								 
    	var xAxisScale = d3.scale.linear()
  			 .domain([0, d3.max(data, function(d) { return d.x; })])
    	   .range([padding, containerWidth - padding * 2]);

    	var xAxis = d3.svg.axis().scale(xAxisScale).orient("bottom")
    	  .tickValues(xAxisScale.ticks().concat(xAxisScale.domain()));
 
    	var xGuide = chartContainer.append('g')
    	  .attr("class", "xAxis")
    	  .call(xAxis)
    	  .attr('transform', 'translate(0 ,' + (containerHeight -padding) + ')');

xGuide.selectAll('path')
	  		.style({ fill: 'none', stroke: "#000"});
	  	xGuide.selectAll('line')
	  		.style({ stroke: "#000"});
    	/* Code for adding y axis in chart
    	 * 
    	 * */
    	var yAxisScale = d3.scale.linear()
    	  .domain([0, d3.max(data, function(d){return d.y})])
    	  .range([containerHeight-padding, padding ]);

    	var yAxis = d3.svg.axis().scale(yAxisScale).orient("left")
    	  .tickValues(yAxisScale.ticks().concat(yAxisScale.domain()[1]));

    	var yGuide = chartContainer.append('g')
    	  .attr("class", "yAxis")
    	  .call(yAxis)
    	  .attr('transform', 'translate('+padding + ',0)');

    	chartContainer.selectAll("circle")
    	  .data(data)
    	  .enter()
    	  .append("circle")
    	  .attr("cx", function(d) {
    	    return xAxisScale(d.x);
    	  })
    	  .attr("cy", function(d) {
    	    return yAxisScale(d.y);
    	  })
    	  .attr("r", 4)
    	  .attr("fill", "red")
    	  .attr({
    	    "z-index": "9999"
    	  });
    	  
    	  yGuide.selectAll('path')
	  		.style({ fill: 'none', stroke: "#000"});
	  	yGuide.selectAll('line')
	  		.style({ stroke: "#000"});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Upvotes: 2

WittyID
WittyID

Reputation: 619

You are using d3's automatic tick generator which makes a best guess on how best to draw your axes given the data in the chart.

You have two options for overriding this:

  1. Setting a number of tick values to show. For example:

    var xAxis = d3.svg.axis() .scale(xAxisScale) .orient("bottom") .ticks(10);

  2. Passing a tick values array to explicitly tell d3 what values you want on your x or y axis; like this:

    var xAxis = d3.svg.axis() .scale(xAxisScale) .orient("bottom") .tickValues([0, 100, 200, 300, 400]);

If you want to get rid of the ticks that are missing values (in your case, the outer ticks) just set .outerTickSize(0) on the x or y-axis.

You should also probably read up on the d3's axes API here.

Upvotes: 0

Related Questions