Heat map scales

Tell us what’s happening:

I am having trouble to understand which scale should be used when. I mean, that should depend on data right? On previous project scatterplot there was dates, then i used scaleTime. In this there are also dates(Year) so i used also scaleTime(x axis). But instead to look like example project x axis, my axis ticks go 20 years apart instead 10y. When i start drawing rects instead 12 rects rendered per year, there was one and the rest are behind that first one.
For month part( y axis) i used either scaleBand or scaleLinear.
Together, for whole chart, i used combinations, for both axises, of all methods previously mentioned.

Methods i tried(x axis):

  • scaleTime()
  • scaleBand()

Methods i tried(y axis):

  • scaleTime()
  • scaleBand()
  • scaleLinear()

Your code so far

$(document).ready(() => {
  let url =
    url: url,
    success: (response) => {
      let dataset = JSON.parse(response);

      let baseTemp = dataset.baseTemperature;
      // set the dimensions and margins of the graph
      let m = { top: 30, right: 30, bottom: 30, left: 30 };
      let w = 800;
      let h = 600;

      // append the svg object to the body of the page
      let svg = d3
        .attr("width", w + m.left * 2)
        .attr("height", h + m.top + m.bottom * 3);

      //Translating axises to accomodate for margin.If we don't, ticks and numbers would look cut of.
      const g = svg
        .attr("transform", "translate(" + m.left * 2 + "," + m.top * 3 + ")");

      //Getting actual (only)year parts from dataset.
      let years = dataset.monthlyVariance.map((item, i) => {
        return item.year;

      years = years.filter((item, i) => {
        return years.indexOf(item) === i && item % 10 === 0;

      // Build X scales and axis:
      let x = d3.scaleBand().range([0, w]).domain(years);

      //Drawing y axis.
      const xAxis = d3.axisBottom(x);

      //Appending x axis to chart.
        .attr("transform", "translate(0," + h + ")")
        .attr("class", "tick axis")
        .attr("id", "x-axis")

      //y axis.
      //Months for y axis.
      const monthNames = [

      let y = d3
        .range([0, h]) //Actual y axis length.
        .domain(monthNames); //Setting values from zero to max value on y axis.

      let yAxis = d3.axisLeft(y);

        .attr("transform", "translate(0, 0)")
        .attr("id", "y-axis")

      let rectWidth = w / dataset.monthlyVariance.length;
      let rectHeight = h / 12;

      let tempVariance = dataset.monthlyVariance.map((item, i) => {
        return item.variance;

      //Calculating year.
      let minVariance = d3.min(tempVariance); //Calculating lowest temp in dataset.
      let maxVariance = d3.max(tempVariance); //Calculating highest temp in dataset.

      //Color scale
      let colorScale = d3
        .range(["white", "red"])
        .domain([minVariance, maxVariance]);

      //Creating tooltip element.
      const tooltip = d3
        .attr("id", "tooltip")
        .style("opacity", 0);

      //Creating bar/s.
        .attr("x", (d, i) => {
          if (x(d.year)) {
            return x(d.year);
        .attr("y", (d, i) => {
          if (y(monthNames[d.month])) {
            return y(monthNames[d.month]);
        .attr("width", (d, i) => {
          return x.bandwidth();
          //return rectWidth;
        .attr("height", (d, i) => {
          return y.bandwidth();
          //return rectHeight;
        .attr("data-year", (d, i) => {
          return d.year;
        .attr("data-month", (d, i) => {
          return d.month - 1;
        .attr("data-temp", (d, i) => {
          return baseTemp + d.variance;
        .attr("class", "cell")
        .style("fill", (d) => {
          return colorScale(baseTemp + d.variance);
        .on("mouseover", (d, i) => {
          tooltip.style("opacity", 1);
            "<div style='margin-bottom: 5px;'>Year: " +
              d.year +
              " Month: " +
              d.month +
              " variance: " +
              d.variance +
        .on("mouseout", (d) => {
          tooltip.style("opacity", 0);

      //Draw the Heat Label:
        .attr("id", "title")
        .attr("class", "headline")
        .attr("x", w / 2)
        .attr("y", m.top)
        .attr("font-family", "sans-serif")
        .attr("fill", "green")
        .attr("text-anchor", "middle")
        .text("Monthly Global Land-Surface Temperature");

      //Draw the Heat Sub-Label:
        .attr("id", "description")
        .attr("class", "headline1")
        .attr("x", w / 2)
        .attr("y", m.top * 2)
        .attr("font-family", "sans-serif")
        .attr("fill", "green")
        .attr("text-anchor", "middle")
        .text("1753 - 2015: base temperature 8.66℃");
    error: (xhr, ajaxOptions, thrownError) => {
      console.log(xhr, ajaxOptions, thrownError);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36.

Challenge: Visualize Data with a Heat Map

Link to the challenge:

I didn’t try only scaleOrdinal or scale.ordinal as stated sometimes. Couldn’t get it to work.


  1. When to use each method and why?
  2. Does method use depends on data ingested or by type of chart and why, on both accounts?

My latest pen.


Ok, i think i finally figured it out(for this case at least), you’ll need besides creating svg itself with or w/o margins/padding is to follow these steps.

  1. Do you have year amongst data you receive? If yes, then your best bet is to use scaleTime alongside with new Date method where you construct dateTime object of it for use in scaleTime.
  • range part for it should be array with two members. First one should be 0(zero), second member should be your width variable which contains chart width.
  • domain should contain also an array where first member is lowest year and second highest year. To calculate these see “minYear” and “maxYear” in my new pen.
  1. Since in this case for y axis are used month names, but not in dateTime format, just regular strings in y axis as in example project, your best bet is to use scaleBand. There is some conditions to be met:
  • Since month in data you’re receiving, from json provided in challenge, is in form of month number as in 1 => january, 2 => february and so forth, you need to make array with month names in order to match said number. Caution is this: since arrays start at 0(zero), you’ll need to account this in later steps.
  1. You’ll need to calculate rect width and height. Since x axis is divided by years and every year has twelve months, there should be twelve rects for one year, in total, to calculate width for one individual rect is to:
  • divide width of chart with dataset length. In this case, actual data is in monthlyVariance property so length of that.
  • height is easier. Since there is twelve months on y axis height should be divided by twelve.

Last thing, actually two things that bothered me are x and y coordinates for said rects. How to calculate that? I tried to use examples from previous challenges, but none of them applied for this case considering 12 rect per year and rects between years om x axis.
But do not fear! I found solution for that! :smiley:

  • For x coordinate just multiply rect width with index of dataset.
  • For y coordinate, when you pass your month names array with month number as index from dataset into y scale, subtract 1( words: one) from month number. And you have to do that subtraction since arrays start from 0(zero), even thought your month number data from dataset start from 1. So to account for that you do subtraction. That also apllies to your data-month attribute of rects. Why they did that for attribute, i don’t know, but to pass test case, you’ll need to do that.

Rest of stuff needed to pass tests, are more or less the same like in previous challenges, i mean you got your tooltip which is never mentioned is should be done by creating(not title element from svg) but regular html element and appending it to the chart, then there is various attributes and class specifics depending from project to project which should be ok for anyone following d3 exercises from the beginning.

My New & revised pen. It still needs labels/headText though and years(ticks on x axis) should be 10y apart instead 20, but at least it passes all important tests and i didn’t used any hacks :smiley:

For those wanting to know what i tried before coming to this solution here is my github repo with all my tries and fails.

PS: English ain’t my native language, so if anything is vague or unintelligible, feel free to ask for clarification.
For anyone knowing how to make x axis to have 10y apart between ticks, please let me know, that’s only part left to figure out.