Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mattrelph/f3e810e14abfa212c8bebf1971b1ea5a to your computer and use it in GitHub Desktop.
Save mattrelph/f3e810e14abfa212c8bebf1971b1ea5a to your computer and use it in GitHub Desktop.
FCC - Data Visualization Projects - Visualize Data with a Heat Map

FCC - Data Visualization Projects - Visualize Data with a Heat Map

User Story #1: My heat map should have a title with a corresponding id="title". User Story #2: My heat map should have a description with a corresponding id="description". User Story #3: My heat map should have an x-axis with a corresponding id="x-axis". User Story #4: My heat map should have a y-axis with a corresponding id="y-axis". User Story #5: My heat map should have rect elements with a class="cell" that represent the data. User Story #6: There should be at least 4 different fill colors used for the cells. User Story #7: Each cell will have the properties data-month, data-year, data-temp containing their corresponding month, year, and temperature values. User Story #8: The data-month, data-year of each cell should be within the range of the data. User Story #9: My heat map should have cells that align with the corresponding month on the y-axis. User Story #10: My heat map should have cells that align with the corresponding year on the x-axis. User Story #11: My heat map should have multiple tick labels on the y-axis with the full month name. User Story #12: My heat map should have multiple tick labels on the x-axis with the years between 1754 and 2015. User Story #13: My heat map should have a legend with a corresponding id="legend". User Story #14: My legend should contain rect elements. User Story #15: The rect elements in the legend should use at least 4 different fill colors. User Story #16: I can mouse over an area and see a tooltip with a corresponding id="tooltip" which displays more information about the area. User Story #17: My tooltip should have a data-year property that corresponds to the data-year of the active area.

A Pen by Matthew Relph on CodePen.

License.

<div class = "container">
<div class = "row">
<div class="col">
</div>
<div class="col-10 text-center">
<div class="infoGraphic card">
<div class="card-body">
<h1 id="title" class="card-title h1">Monthly Global Land-Surface Temperature</h1>
<h2 id="description" class="card-title h2"></h2>
<p id="graph" class="card-text block-center"></p>
<!--<a href="" class="btn btn-primary" target="_blank">More Info</a>-->
</div>
</div>
</div>
<div class="col">
</div>
</div>
</div>
/*Heat Map visualization project*/
//Source Dataset -https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json
//Font Awesome CSS - https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css
//Bootstrap CSS - https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css
//Bootstrap JS - https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js
//D3 Datavisualization Library - https://d3js.org/d3.v5.min.js
//Testing JS - https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js
document.addEventListener('DOMContentLoaded',function() {
var url = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json";
req=new XMLHttpRequest();
req.open("GET",url,true);
req.send();
req.onload=function(){
var json=JSON.parse(req.responseText); //Read file data
/* Expected format: JSON Array with these object pairs
{
"baseTemperature": 8.66,
"monthlyVariance": [
{
"year": 1753,
"month": 1,
"variance": -1.366
}]}*/
var baseTemperature = json.baseTemperature;
var dataset = json.monthlyVariance;
//We will make the time and year JS date objects, All the other fields can be interpretted as text
var timeData=[];
var date;
for (var i = 0; i<dataset.length; ++i)
{
date = new Date(dataset[i].year,dataset[i].month-1);
timeData.push(date); //Store time here
//console.log(dataset[i].year + "-" +dataset[i].month + " = " + timeData[i].toISOString()); //Test output for correct time conversion
}
const fullwidth = 1000;
const fullheight = 600;
const padding = 65;
const width = fullwidth - 2*padding;
const height = fullheight - 2*padding;
//Get the range we want to display on X axis
var maxX = d3.max(timeData, (d) => d);
var minX = d3.min(timeData, (d) => d);
//console.log("MaxYear: " + maxX + " MinYear: " + minX); //Test X range
document.getElementById("description").innerHTML = minX.getFullYear() + "-" + maxX.getFullYear() + " : Base Temperature = " + baseTemperature + " &degC"; //Set description
//Get the range we want to display on Y axis
var maxY = new Date (1970,0,0);
var minY = new Date (1970,11, 31);
//console.log("MaxTime: " + maxY.toISOString() + " MinTime: " + minY.toISOString()); //Test Y range
//Define the X Scale
var xScale = d3.scaleTime()
.domain([minX, maxX])
.rangeRound([padding, width]) ;
//.nice();
//Define the Y Scale
var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
var monthsBackwards = months.slice();
monthsBackwards.reverse();
var yScale = d3.scaleBand()
.domain(monthsBackwards) //We make the scale in reverse so the graph appears to go from January down to December
.rangeRound([height, padding]);
//.nice(); */
// Define the y and x axis
var yAxis = d3.axisLeft(yScale);
var xAxis = d3.axisBottom(xScale);
//Create toolTips DIV
var toolTips = d3.select("body").append("div")
.attr("class", "tooltip")
.attr("id", "tooltip")
.style("background", "Beige")
.style("color", "Black")
.style("opacity", 0); //Hide until mouseover
//Create SVG
var svg = d3.select("#graph")
.append("svg")
.attr("width", fullwidth)
.attr("height", fullheight);
// Draw y axis
svg.append("g")
.attr("transform", "translate("+padding+",0)")
.attr("id", "y-axis")
.call(yAxis
//.tickFormat(d3.utcFormat('%B')) //Specify showing of time as Full Name of the Month
.tickPadding(10)
);
// Draw x axis
svg.append("g")
.attr("class", "xaxis")
.attr("id", "x-axis")
.attr("transform", "translate(0," + (height) + ")")
.call(xAxis
.tickFormat(d3.utcFormat('%Y')) //Specify showing of time as Full Year
.tickPadding(10)
);
//Use this to add height and width of cells
var oneMonth = height / 12;
var oneYear = (width-200) / (maxX.getFullYear() - minX.getFullYear());
//Setup Color Scale
var minVariance = baseTemperature + d3.min(dataset, (d) => d.variance);
var maxVariance = baseTemperature + d3.max(dataset, (d) => d.variance);
var stepVariance = (Math.abs(maxVariance) - Math.abs(minVariance))/10;
//console.log("minVariance: " + minVariance + " maxVariance: " + maxVariance); //Test Variance range
var colorPallete= [ "midnightblue", "indigo", "darkslateblue","blue", "green", "greenyellow", "yellow", "orange", "red", "maroon"];
var colorSteps=[minVariance, minVariance + 1*stepVariance, minVariance + 2*stepVariance, minVariance + 3*stepVariance, minVariance + 4*stepVariance, minVariance + 5*stepVariance, minVariance + 6*stepVariance, minVariance + 7*stepVariance, minVariance + 8*stepVariance, minVariance + 9*stepVariance, maxVariance];
var colorScale = d3.scaleLinear()
.domain(colorSteps)
.range(colorPallete);
//console.log("One Year: " + xScale(oneYear) + " One Month: "+ yScale(oneMonth));
//Draw data points
svg.append("g")
.selectAll("rect")
.data(dataset)
.enter().append("rect")
.attr("x", (d,i) => xScale(new Date(timeData[i].getFullYear(),0)))
//.attr("y", (d,i) => yScale(new Date(1970, timeData[i].getMonth()))-1)
.attr("y", (d,i) => yScale(months[timeData[i].getMonth()]))
//.attr("y", (d,i) => yScale(timeData[i].getMonth()))
.attr("width", (d,i) => oneYear)
//.attr("height", (d,i) => oneMonth)
.attr("height", (d,i) => yScale.bandwidth())
.attr("class", "cell")
.attr("data-year", (d,i) => timeData[i].getFullYear())
.attr("data-month", (d,i) => timeData[i].getMonth())
.attr("data-temp", (d,i) => baseTemperature + d.variance)
.style("fill",(d,i) => colorScale( baseTemperature + d.variance))
//Tooltip DIV control
.on("mouseover", function(d,i) {
d3.select(this).attr("stroke", "black")
.attr("stroke-width", 0.5);
toolTips.attr("data-year", timeData[i].getFullYear())
.html(timeData[i].getFullYear() + " - " + timeData[i].getMonth() + "<br/>" + (baseTemperature + d.variance) + " &degC<br/>" + d.variance + " &degC")
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 50) + "px")
.style("opacity", .9)
.style("background", colorScale( baseTemperature + d.variance));
})
.on("mouseout", function(d) {
d3.select(this).attr("stroke", "none");
toolTips.style("opacity", 0);
})
.style("opacity", .8);
//Add legend element
var legend = svg.selectAll(".legend")
.data(dataset)
.enter().append("g")
.attr("class", "legend")
.attr("id", "legend");
//Add colored rectangles to legend
var legendSize = 25;
for (var i =0; i< colorPallete.length; ++i)
{
legend.append("rect")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("x", 0.1 * fullwidth+i*(legendSize*2+1))
.attr("y", fullheight - padding)
.attr("width", legendSize*2)
.attr("height", legendSize)
.attr("fill", colorPallete[i]);
}
//Add text to legend
for (var j=0; j< colorSteps.length; ++j)
{
legend.append("text")
.attr("x", 0.1 * fullwidth +j*(legendSize*2+1))
.attr("y", fullheight - padding - legendSize*0.5)
.text(Math.round(colorSteps[j] * 100) / 100);
}
};
});
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
body {
margin-top: 50px;
font: 10px sans-serif;
background : LightGray;
}
.infoGraphic{
background : white;
padding: 10px;
box-shadow: 10px 10px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment