Krang Krang - 6 months ago 8
Javascript Question

Custom ticks, values, and format

I'm trying to create two custom ticks on the far left and right side of my horizontal axis in D3.

The labels I want to use for my ticks do not come from any of my data — I just want to add in "Q1 2015" and "Q2 2015" in place of whatever it would normally pull in from my

xScale
.

Here's a fiddle and the code I'm using to construct my
xAxis
.

Right now there is nothing showing up on my axis for ticks — the data seems to be returning from the
tickFormat
function but it doesn't show up, so I'm confused at this point. I can't figure out where I am going wrong.

var xAxis = d3.svg.axis().orient("bottom")
// .ticks(2)
.tickValues([2015, 2016])
.tickFormat(function(d) {
console.log("Q1 " + d);
return "Q1" + d;
})
.outerTickSize(0);

Answer

The issue in here is that you are setting numeric tick values

.tickValues([2015, 2016])

which will basically try to create a tick with the 2015 and 2016 values.

Since your xAxis scale is set to use the following:

xScale.domain(d3.extent(data, function (d){ return d[xColumn]; }));

which was only returning a month from your data object, so the ticks were rendering but outside of your viewport due to the difference of values [monthValue] vs [yearValue].

If you are going to use a time scale you need to give a proper input to the domain of that scale.

// X Axis Scale
var xScale = d3.time.scale().range([0, innerWidth]);

// Axis Function
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(xScale)
.tickFormat(function(d) { // Getting the value of the tick :)
    return "Q1:" + mS[d.getMonth()];
});

/*
 * Setting the domain of the x scale with a correct Date value
 *
 * We use nested[0].values which iterates over the values of just
 * one hotel and the corresponding dates since the dates are the 
 * same for the remaining hotels.
 */
xScale.domain(d3.extent(nested[0].values, function (d){
    var f = new Date('2012', (d[xColumn] - 1), '01');
    return f;
}));

and dont forget to use the same approach in your area function.

var area = d3.svg.area()
.x(function(d) {
    return xScale( new Date('2012', (d[xColumn] - 1), '01'))
})
.y0(function(d) { return yScale(d.y0); })
.y1(function(d) { return yScale(d.y0 + d.y); });

var mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
var outerWidth = 1000;
var outerHeight = 250;
var margin = { left: 55, top: 5, right: 100, bottom: 60 };

var xColumn = "month";
var yColumn = "tours";
var colorColumn = "name";
var areaColumn = colorColumn;

var xAxisLabelText = "Month";
var xAxisLabelOffset = 48;

var innerWidth  = outerWidth  - margin.left - margin.right;
var innerHeight = outerHeight - margin.top  - margin.bottom;

var svg = d3.select("body").append("svg")
  .attr("width", outerWidth)
  .attr("height", outerHeight);
var g = svg.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var xAxisG = g.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + innerHeight + ")")
var xAxisLabel = xAxisG.append("text")
  .style("text-anchor", "middle")
  .attr("transform", "translate(" + (innerWidth / 2) + "," + xAxisLabelOffset + ")")
  .attr("class", "label")
  .text(xAxisLabelText);

var colorLegendG = svg.append("g")
  .attr("class", "color-legend")
  .attr("transform", "translate(403, 5)");

var xScale = d3.time.scale().range([0, innerWidth]);
var yScale = d3.scale.linear().range([innerHeight, 0]);
var colorScale = d3.scale.category10();

var xAxis = d3.svg.axis().orient("bottom").scale(xScale)
  .tickFormat(function(d, i, e) {
    return "Q1:" + mS[d.getMonth()];
  });


var stack = d3.layout.stack()
  .y(function (d){ return d[yColumn]; })
  .values(function (d){ return d.values; });

var area = d3.svg.area()
  .x(function(d) {
    return xScale( new Date('2012', (d[xColumn] - 1), '01'))
  })
  .y0(function(d) { return yScale(d.y0); })
  .y1(function(d) { return yScale(d.y0 + d.y); });



function render(data){
  var nested = d3.nest()
    .key(function (d){ return d[areaColumn]; })
    .entries(data);

  colorScale.domain(nested.map(function (d){ return d.key; }));

  // Reversed the order here so the order matches between legend & areas.
  var layers = stack(nested.reverse());
  xScale.domain(d3.extent(nested[0].values, function (d){
    var f = new Date('2012', (d[xColumn] - 1), '01');
    return f;
  }));
  yScale.domain([0,
    d3.max(layers, function (layer){
      return d3.max(layer.values, function (d){
        return d.y0 + d.y;
      });
    })
  ]);

  var total = d3.sum(layers, function(layer) {
    return d3.sum(layer.values, function(d) {
      return d.tours;
    });
  });

  var paths = g.selectAll(".chart-area").data(layers);
  paths.enter().append("path").attr("class", "chart-line");
  paths.exit().remove();
  paths.attr("d", function (d){ return area(d.values); })
  .attr("fill", function (d){ return colorScale(d.key); });

  xAxisG.call(xAxis);

  svg.append("g")
    .attr("class", "legend")
    .selectAll("text")
    .data(layers)
    .enter().append("text")
    .text(function(d) { return (d.key) + ' (' + d.key + ')'; })
    .attr('fill', function(d) { return colorScale(d.key); })
    .attr('y', function(d, i) { return 20 * (i + 1); })
    .attr('x', "0");
}



var toursByHotel = [
       {
         "name": "Marriott",
         "month": 1,
         "tours": 10
       },
       {
         "name": "Marriott",
         "month": 2,
         "tours": 15
       },
       {
         "name": "Marriott",
         "month": 3,
         "tours": 8
       },
       {
         "name": "Marriott",
         "month": 4,
         "tours": 12
       },
       {
         "name": "Marriott",
         "month": 5,
         "tours": 18
       },
       {
         "name": "Marriott",
         "month": 6,
         "tours": 25
       },
       {
         "name": "Marriott",
         "month": 7,
         "tours": 40
       },
       {
         "name": "Marriott",
         "month": 8,
         "tours": 33
       },
       {
         "name": "Marriott",
         "month": 9,
         "tours": 25
       },
       {
        "name": "Marriott",
         "month": 10,
         "tours": 21
       },
       {
        "name": "Marriott",
         "month": 11,
         "tours": 18
       },
       {
        "name": "Marriott",
         "month": 12,
         "tours": 14
       },
       {
         "name": "Springhill",
         "month": 1,
         "tours": 10
       },
       {
         "name": "Springhill",
         "month": 2,
         "tours": 15
       },
       {
         "name": "Springhill",
         "month": 3,
         "tours": 8
       },
       {
         "name": "Springhill",
         "month": 4,
         "tours": 12
       },
       {
         "name": "Springhill",
         "month": 5,
         "tours": 18
       },
       {
         "name": "Springhill",
         "month": 6,
         "tours": 25
       },
       {
         "name": "Springhill",
         "month": 7,
         "tours": 40
       },
       {
         "name": "Springhill",
         "month": 8,
         "tours": 33
       },
       {
         "name": "Springhill",
         "month": 9,
         "tours": 25
       },
       {
        "name": "Springhill",
         "month": 10,
         "tours": 21
       },
       {
        "name": "Springhill",
         "month": 11,
         "tours": 18
       },
       {
        "name": "Springhill",
         "month": 12,
         "tours": 14
       },
       {
         "name": "Residence",
         "month": 1,
         "tours": 10
       },
       {
         "name": "Residence",
         "month": 2,
         "tours": 15
       },
       {
         "name": "Residence",
         "month": 3,
         "tours": 8
       },
       {
         "name": "Residence",
         "month": 4,
         "tours": 12
       },
       {
         "name": "Residence",
         "month": 5,
         "tours": 18
       },
       {
         "name": "Residence",
         "month": 6,
         "tours": 25
       },
       {
         "name": "Residence",
         "month": 7,
         "tours": 40
       },
       {
         "name": "Residence",
         "month": 8,
         "tours": 33
       },
       {
         "name": "Residence",
         "month": 9,
         "tours": 25
       },
       {
        "name": "Residence",
         "month": 10,
         "tours": 21
       },
       {
        "name": "Residence",
         "month": 11,
         "tours": 18
       },
       {
        "name": "Residence",
         "month": 12,
         "tours": 14
       },
       {
         "name": "Courtyard",
         "month": 1,
         "tours": 10
       },
       {
         "name": "Courtyard",
         "month": 2,
         "tours": 15
       },
       {
         "name": "Courtyard",
         "month": 3,
         "tours": 8
       },
       {
         "name": "Courtyard",
         "month": 4,
         "tours": 12
       },
       {
         "name": "Courtyard",
         "month": 5,
         "tours": 18
       },
       {
         "name": "Courtyard",
         "month": 6,
         "tours": 25
       },
       {
         "name": "Courtyard",
         "month": 7,
         "tours": 40
       },
       {
         "name": "Courtyard",
         "month": 8,
         "tours": 33
       },
       {
         "name": "Courtyard",
         "month": 9,
         "tours": 25
       },
       {
        "name": "Courtyard",
         "month": 10,
         "tours": 21
       },
       {
        "name": "Courtyard",
         "month": 11,
         "tours": 18
       },
       {
        "name": "Courtyard",
         "month": 12,
         "tours": 14
       },
       {
         "name": "Renaissance",
         "month": 1,
         "tours": 10
       },
       {
         "name": "Renaissance",
         "month": 2,
         "tours": 15
       },
       {
         "name": "Renaissance",
         "month": 3,
         "tours": 8
       },
       {
         "name": "Renaissance",
         "month": 4,
         "tours": 12
       },
       {
         "name": "Renaissance",
         "month": 5,
         "tours": 18
       },
       {
         "name": "Renaissance",
         "month": 6,
         "tours": 25
       },
       {
         "name": "Renaissance",
         "month": 7,
         "tours": 40
       },
       {
         "name": "Renaissance",
         "month": 8,
         "tours": 33
       },
       {
         "name": "Renaissance",
         "month": 9,
         "tours": 25
       },
       {
        "name": "Renaissance",
         "month": 10,
         "tours": 21
       },
       {
        "name": "Renaissance",
         "month": 11,
         "tours": 18
       },
       {
        "name": "Renaissance",
         "month": 12,
         "tours": 14
       }
     ];

     render(toursByHotel);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>