Krang Krang - 1 year ago 77
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 Source

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>