Wednesday 21 September 2022

Web Tutorial: D3 Heatmap (Part 2/3)

Let us concentrate on filling up the left and bottom of the heatmap. These are the Y and X axes, respectively. The Y-axis will show seasons, while the X-axis will show players.

Start on legendY, using the rows array of the graphData object as data, and append text tags. This should be a straightforward value.
filler
.style("width", function(d)
{
    return config.legendYWidth + "em";
})
.style("height", function(d)
{
    return config.legendXHeight + "em";
});

legendY.selectAll("text")
.data(graphData.rows)
.enter()
.append("text")
.text(function(d)
{
    return d;
});


Now we will set the x attribute. We'll use half the value of legendYWidth.
legendY.selectAll("text")
.data(graphData.rows)
.enter()
.append("text")
.attr("x", function(d)
{
    return (config.legendYWidth / 2) + "em";
})

.text(function(d)
{
    return d;
});


Now set the y attribute. Logically, the text will appear lower with each iteration of the array. So we will use the index, i. We'll add 1 to i to make sure that the value is at least 1, then multiply that by the scale property. And because we want the text position to be vertically in the middle, we should subtract half the value of scale from that result.
legendY.selectAll("text")
.data(graphData.rows)
.enter()
.append("text")
.attr("x", function(d)
{
    return (config.legendYWidth / 2) + "em";
})
.attr("y", function(d, i)
{
    return (((i + 1) * config.scale) - (config.scale / 2)) + "em";
})

.text(function(d)
{
    return d;
});


Finally, make this change to the CSS. The text tags in the hmLegendYSvg class should use the default font size.
.hmLegendYSvg text
{
    fill: rgba(255, 255, 0, 1);
    text-anchor: end;
    /*font-size: 0.5em;*/
}


The green column is now filled with years in yellow font!




Time to work on the X-axis. As before, we're adding text tags. We are using the cols array of the graphData object. This time, d is an object rather than a string, so we return the title attribute of d.
legendY.selectAll("text")
.data(graphData.rows)
.enter()
.append("text")
.attr("x", function(d)
{
    return (config.legendYWidth / 2) + "em";
})
.attr("y", function(d, i)
{
    return (((i + 1) * config.scale) - (config.scale / 2)) + "em";
})
.text(function(d)
{
    return d;
});

legendX
.selectAll("text")
.data(graphData.cols)
.enter()
.append("text")
.text(function(d)
{
    return d.title;
});


The x attribute is the horizontal positioning of the text. This will increase with each iteration of the data, so we use the index, i. We multiply the value of i by the dataWidth property. This time, we don't add 1 beforehand because a 0 is acceptable. And after that, we add half the value of dataWidth. Because we want the text to begin from the center line. If you check the CSS for the text-anchor property for text tags within hmLegendXSvg CSS class, it is set to middle. The combination of this ensures that the text tags, no matter how long they are, always align to the middle of that allotted space.
legendX
.selectAll("text")
.data(graphData.cols)
.enter()
.append("text")
.attr("x", function(d, i)
{
    return ((i * config.dataWidth) + (config.dataWidth / 2)) + "em";
})

.text(function(d)
{
    return d.title;
});


And for the y attribute, we use half the value of legendXHeight.
legendX
.selectAll("text")
.data(graphData.cols)
.enter()
.append("text")
.attr("x", function(d, i)
{
    return ((i * config.dataWidth) + (config.dataWidth / 2)) + "em";
})
.attr("y", function(d)
{
    return (config.legendXHeight / 2) + "em";
})

.text(function(d)
{
    return d.title;
});


And now you can see the player names appear on the X-axis!




Putting in the data

Let us begin with making some placeholders. We need a For loop to iterate through the elements of the rows array of graphData.
legendX
.selectAll("text")
.data(graphData.cols)
.enter()
.append("text")
.attr("x", function(d, i)
{
    return ((i * config.dataWidth) + (config.dataWidth / 2)) + "em";
})
.attr("y", function(d)
{
    return (config.legendXHeight / 2) + "em";
})
.text(function(d)
{
    return d.title;
});

for (let r = 0; r <= graphData.rows.length; r++)
{

}


And for each element, we append a row of rect tags into chart. In the selectAll() method, we just give it a class which we will never use, just to give each row of elements some uniqueness. For data, we use the cols array of the graphData object.
for (let r = 0; r <= graphData.rows.length; r++)
{
    chart.selectAll("rect.rect_" + graphData.rows[r])
    .data(graphData.cols)
    .enter()
    .append("rect");  
            
}


Each rect uses dataWidth as width and scale as height.
for (let r = 0; r <= graphData.rows.length; r++)
{
    chart.selectAll("rect.rect_" + graphData.rows[r])
    .data(graphData.cols)
    .enter()
    .append("rect")
    .attr("width", function(d)
    {
        return (config.dataWidth) + "em";
    })           
    .attr("height", function(d)
    {
        return (config.scale) + "em";
    })
;                 
}


For the x attribute, the rect will appear further to the right progressively per row. So we use i as the index and multiply it by dataWidth (which is the width of each rect).
for (let r = 0; r <= graphData.rows.length; r++)
{
    chart.selectAll("rect.rect_" + graphData.rows[r])
    .data(graphData.cols)
    .enter()
    .append("rect")
    .attr("x", function(d, i)
    {
        return (i * config.dataWidth) + "em";
    })

    .attr("width", function(d)
    {
        return (config.dataWidth) + "em";
    })           
    .attr("height", function(d)
    {
        return (config.scale) + "em";
    });                 
}


For the y attribute, the row will appear lower for each iteration of the rows array, so have the value as r multiplied by scale (which is the height of each rect).
for (let r = 0; r <= graphData.rows.length; r++)
{
    chart.selectAll("rect.rect_" + graphData.rows[r])
    .data(graphData.cols)
    .enter()
    .append("rect")
    .attr("x", function(d, i)
    {
        return (i * config.dataWidth) + "em";
    })
    .attr("y", function(d)
    {
        return (r * config.scale) + "em";
    })

    .attr("width", function(d)
    {
        return (config.dataWidth) + "em";
    })           
    .attr("height", function(d)
    {
        return (config.scale) + "em";
    });                 
}


In the CSS, this class is no longer needed. Remove the properties.
.hmChartSvg text
{
    /*
    fill: rgba(255, 255, 0, 1);
    text-anchor: middle;
    font-weight: bold;
    */
}


Change it to target rect tags instead, and add specifications for stroke and stroke-width. For this one, I am using a white color.
.hmChartSvg rect
{
    /*
    fill: rgba(255, 255, 0, 1);
    text-anchor: middle;
    font-weight: bold;
    */
    stroke: rgba(255, 255, 255, 1);
    stroke-width: 1;

}


So now you can see the individual rect tags!




Next

The basic placeholders are there, along with X and Y axes. We will follow up with filling in the Heatmap with colors!

No comments:

Post a Comment