A Pen by Pappu Kumar Pashi on CodePen.
Created
August 24, 2020 21:26
-
-
Save PappuKP/a22b277c78fc71ded9e71cd98281d4c7 to your computer and use it in GitHub Desktop.
Project-3: Visualize Data with a Heat Map
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!Doctype html> | |
<html lang="en-US"> | |
<head> | |
<title>Heat Map</title> | |
<meta charset="utf-8"> | |
<meta name="author" content="Anuj Yadav"> | |
<meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
<meta name="description" content="Solution for heat map for freecodecamp project, this uses d3 library for charting purpose"> | |
<!--<script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>--> | |
<script src="https://d3js.org/d3.v5.min.js"></script> | |
<script src="./script.babel"></script> | |
<link rel="stylesheet" href="./style.css"> | |
</head> | |
<body> | |
<h1 id="title">Monthly Global Land-Surface Temperature | |
</h1> | |
<h3 id="description">1753 - 2015: base temperature 8.66℃ | |
</h3> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileOverview Heat Map implementation for comapring Monthly Global Land-Surface Temperature | |
* across several years starting from 1753 to 2015 and with variation from the base temperature. | |
* The data is represented month wise across the years using D3.js library. | |
* @author Anuj Yadav | |
* @see Access Source Data {@link https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json|Datasource} | |
* @see Other Charts {@link https://github.com/yadavanuj1996/D3.js-Charts|D3.js-Charts} | |
* @see Cod Pen {@link https://codepen.io/yadavanuj1996/pen/mdbVgGb|Codpen} | |
*/ | |
/** | |
* Function listens to DOM loading and will will call getTemperatureData function on loading of DOM. | |
* @function | |
* @name DOMContentLoadedFunction | |
* @listens DOMContentLoaded | |
*/ | |
document.addEventListener("DOMContentLoaded",()=>{ | |
getTemperatureData(); | |
}); | |
/** | |
* @external XMLHttpRequest | |
* @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest | |
*/ | |
/** | |
* The function will send ajax request to get temperature dataset, access the data set at | |
* {@link https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json|Datasource} | |
*/ | |
let getTemperatureData= ()=>{ | |
let ajaxRequest=new XMLHttpRequest(); | |
ajaxRequest.open("GET","https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/global-temperature.json","true"); | |
ajaxRequest.send(); | |
ajaxRequest.onload=()=>{ | |
const responseData=ajaxRequest.responseText; | |
const responseDatasetInJSON=JSON.parse(responseData); | |
loadTemperatureChart(responseDatasetInJSON); | |
} | |
} | |
/** | |
* This function will load the chart after creating svg, adding rectangles and display them on screen. | |
* @param {Array<Object>} tempDataset The dataset for construcing the chart. | |
*/ | |
let loadTemperatureChart=(tempDataset)=>{ | |
const svgHeight=getChartHeight(); | |
const svgWidth=getChartWidth(); | |
const padding=getChartPadding(); | |
const baseTemperature=tempDataset.baseTemperature; | |
let svg=loadSvg(svgHeight,svgWidth); | |
let xAxis=d3.axisBottom(xScale(tempDataset,svgWidth,padding)).tickFormat(d3.format("d")); | |
loadXAxis(svg,xAxis,svgHeight,padding); | |
let yAxis = d3.axisLeft(yScale(tempDataset,svgHeight,padding)).tickFormat((d)=>convertMonthDigitToName(d)); | |
loadYAxis(svg,yAxis,padding); | |
let tooltip=loadTooltip(); | |
let rectanglesInSvg=addRectanglesInSvg(svg,tempDataset.monthlyVariance); | |
rectanglesInSvg.attr("class","cell") | |
.attr("x",(d,i)=>xScale(tempDataset,svgWidth,padding)(d.year)) | |
.attr("y",(d,i)=>yScale(tempDataset,svgHeight,padding)(d.month-1)) | |
.attr("height",`${(svgHeight-2*padding)/12}px`) | |
.attr("width","7px"); | |
rectanglesInSvg=addCustomPropertiesInSvgRectangles(rectanglesInSvg,baseTemperature); | |
rectanglesInSvg=addMouseEventsInSvgRectangles(rectanglesInSvg,tooltip,tempDataset,svgHeight,svgWidth,padding,baseTemperature); | |
let legendSvg=loadSvg(100,300); | |
let legend=legendSvg.attr("id","legend") | |
.style("float","right"); | |
let legendTempRange=getLegendTempRange(); | |
let rectanglesInLegendSvg=addRectanglesInSvg(legendSvg,legendTempRange); | |
rectanglesInLegendSvg.attr("x",(d,i)=>20*i) | |
.attr("y",(d,i)=>20) | |
.attr("height",(d,i)=>"20px") | |
.attr("width",(d,i)=>"20px"); | |
rectanglesInLegendSvg=addCustomPropertiesInLegendRectangles(rectanglesInLegendSvg); | |
} | |
/** Return chart height.*/ | |
const getChartHeight=()=>600; | |
/** Return chart width.*/ | |
const getChartWidth=()=>1200; | |
/** Return chart padding.*/ | |
const getChartPadding=()=>100; | |
/** Add svg in body and return it.*/ | |
const loadSvg=(height,width)=>{ | |
return d3.select("body") | |
.append("svg") | |
.attr("height",height) | |
.attr("width",width); | |
}; | |
/** Load X axis for chart.*/ | |
const loadXAxis=(svg,xAxis,height,padding)=>{ | |
svg.append("g") | |
.attr("transform",`translate(0,${height-padding})`) | |
.attr("id","x-axis") | |
.call(xAxis); | |
}; | |
/** Load Y axis for chart.*/ | |
const loadYAxis=(svg,yAxis,padding)=>{ | |
svg.append("g") | |
.attr("transform",`translate(${padding},0)`) | |
.attr("id","y-axis") | |
.call(yAxis); | |
}; | |
/** Takes month number(1,2,3,...,11) to month name(jan,feb,mar,...,dec).*/ | |
const convertMonthDigitToName=(monthNumber)=>d3.timeFormat("%B")(new Date(0).setUTCMonth(monthNumber)); | |
/** Return tooltip that will show inforamtion about a rectangle when it is hovered.*/ | |
const loadTooltip=()=>{ | |
return d3.select("body") | |
.append("div") | |
.attr("id","tooltip") | |
.style("opacity",0); | |
}; | |
/** | |
* This function will add rectangles in svg equaling the number of elements in dataset. | |
* @param {object} svg The svg element. | |
* @param {Array<object>} dataset The heat Dataset. | |
* @return {object} The svg.selectAll("rect") element or all the rectangles inside svg element. | |
*/ | |
const addRectanglesInSvg=(svg,dataset)=>{ | |
return svg.selectAll("rect") | |
.data(dataset) | |
.enter() | |
.append("rect"); | |
}; | |
/** | |
* This will add custom properties such as fill color and data attributes to the input rect | |
* elements and return reference of input param object with new added attributes. | |
* @function | |
* @param {object} rectanglesInSvg This will contain all the rectagles inside an svg, svg.selectAll("rect") element. | |
* @param {number} baseTemperature This is the base temperature of earth. | |
* @return {object} A object that will have all the attributes and values of input | |
* svg rect elements and also newly added custom porperties. | |
*/ | |
const addCustomPropertiesInSvgRectangles=(rectanglesInSvg,baseTemperature)=>{ | |
return rectanglesInSvg.attr("data-month",(d,i)=>d.month-1) | |
.attr("data-year",(d,i)=>d.year) | |
.attr("data-temp",(d,i)=>d.variance) | |
.attr("fill",(d,i)=> { | |
let temperature=getTemperatureFromVariance(baseTemperature,d.variance); | |
return getTemperatureColor(temperature); | |
}); | |
}; | |
/** | |
* This function will add mouse events like mouseover and mouseout to input recanglesInSvg | |
* param (all rect elements in svg) and will show tooltip on mouseover and hide tooltip on | |
* mouseout, the tooltip will reflect the data on hover of individual rectangle. | |
* @function | |
* @param {object} rectanglesInSvg All the selected rect elements (rect is a svg element). | |
* @param {object} tooltip The tooltip is a object that is a div appended in body element. | |
* @param {object} datset The dataset object contains the json dataset that is used to construct | |
* the chart. | |
* @param {number} height Height of chart. | |
* @param {number} width Width of chart. | |
* @param {number} padding Padding of chart. | |
* @param {number} baseTemp Base temperature of earth. | |
*/ | |
const addMouseEventsInSvgRectangles=(rectanglesInSvg,tooltip,datset,height,width,padding,baseTemp)=>{ | |
return rectanglesInSvg.on("mouseover", (d,i)=> { | |
tooltip.style("opacity", 0.8) | |
.attr("data-year", d.year) | |
.html(`${d.year}-${convertMonthDigitToName(d.month-1)} | |
<br>${Math.floor(baseTemp+d.variance*100)/100} | |
<br>${d.variance}`) | |
.style("left",`${15+xScale(datset,width,padding)(d.year)}px`) | |
.style("top", `${60+yScale(datset,height,padding)(d.month-1)}px`) | |
.style("text-align","center"); | |
}) | |
.on("mouseout",(d,i)=>{ | |
tooltip.style("opacity",0); | |
}); | |
}; | |
/** | |
* The Curried Function that will return interpolated value on x axis and will return | |
* function on first call then number value in second, the xScale function is used | |
* for getting the coordinate on x axis on basis of the year value and also used as | |
* callback in generating both axes i.e., x and y axis. | |
* @function | |
* @param {Array<object>} dataset Dataset of all elements. | |
* @param {number} width Width for chart area. | |
* @param {number} padding Padding for chart inside svg. | |
* @return {number} The interpolated value that will be represented on the chart corresponding to x-axis. | |
*/ | |
const xScale=(dataset,width,padding)=>{ | |
return d3.scaleLinear() | |
.domain([d3.min(dataset.monthlyVariance,(d,i)=>d.year),d3.max(dataset.monthlyVariance,(d,i)=>d.year)]) | |
.range([padding,width-padding]); | |
}; | |
/** | |
* The Curried Function that will return interpolated value on y axis and return | |
* function in first call then number value in second | |
* @function | |
* @param {Array<object>} dataset Dataset of all elements. | |
* @param {number} height Height for chart area. | |
* @param {number} padding Padding for chart inside svg. | |
* @return {number} The interpolated value that will be represented on the chart corresponding to y-axis. | |
*/ | |
const yScale=(dataset,height,padding)=>{ | |
return d3.scaleBand() | |
.domain([0,1,2,3,4,5,6,7,8,9,10,11]) | |
.range([padding,height-padding]); | |
}; | |
/** | |
* This will add the baseTemperature & variance and will return the value, | |
* the variance can be both negative or positive so the net sum or difference | |
* value will be returned. | |
* @param {number} baseTemperature The base temperature of earth. | |
* @param {number} variance The variance in this month's temperature. | |
* @return {number} The sum of base temperature and variance. | |
*/ | |
const getTemperatureFromVariance=(baseTemperature,variance)=> baseTemperature + variance; | |
/** | |
* This function will get the temperature value as input parameter and return the color | |
* corresponding to the temperature range. | |
* @function | |
* @param {number} temperatureValue Value of temperature. | |
*/ | |
const getTemperatureColor=(temperatureValue)=>{ | |
let colorRange=getLegendColorRange(); | |
if(temperatureValue < 2.8) | |
return colorRange[0]; | |
else if(temperatureValue < 3.9) | |
return colorRange[1]; | |
else if(temperatureValue < 5.0) | |
return colorRange[2]; | |
else if(temperatureValue < 6.1) | |
return colorRange[3]; | |
else if(temperatureValue < 7.2) | |
return colorRange[4]; | |
else if(temperatureValue < 8.3) | |
return colorRange[5]; | |
else if(temperatureValue < 9.5) | |
return colorRange[6]; | |
else if(temperatureValue < 10.6) | |
return colorRange[7]; | |
else if(temperatureValue < 11.7) | |
return colorRange[8]; | |
else if(temperatureValue < 12.8) | |
return colorRange[9]; | |
else | |
return colorRange[10]; | |
}; | |
/** Return temperature range.*/ | |
const getLegendTempRange=()=>{ | |
return ["<2.8","<3.9","<5.0","<6.1","<7.2","<8.3","<9.5","<10.6","<11.7","<12.8",">12.8"]; | |
}; | |
/** Return the color range corresponding to the temperature ranges.*/ | |
const getLegendColorRange=()=>{ | |
return ["#344595","#4575B4","#74ADD1","#ABD9E9","#E0F3F8","#FDF4BF","#FEE090","#FDAE61","#F46D43","#D73836","#A52A27"]; | |
}; | |
/** Add custom properties to all the rect elements of legend svg element. */ | |
const addCustomPropertiesInLegendRectangles=(legendSvgRectangles)=>{ | |
let colorData=getLegendColorRange(); | |
return legendSvgRectangles.attr("fill",(d,i)=> colorData[i]) | |
.append("title") | |
.text((d,i)=>d) | |
}; | |
/** | |
* Return legend html, this is other way of generating the legend section.This is | |
* alternate way of generating the section, comment other one and append it so | |
* this gets visible. | |
*/ | |
let getLegendHTML=()=>{ | |
return `<div style="display:flex;justify-content:center;align-items:center;font-size:13px;"> | |
<div style="background-color:#344595;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"> <2.8 </div> | |
<div style="background-color:#4575B4;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"> <3.9 </div> | |
<div style="background-color:#74ADD1;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><5.0</div> | |
<div style="background-color:#ABD9E9;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><6.1</div> | |
<div style="background-color:#E0F3F8;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><7.2</div> | |
<div style="background-color:#FDF4BF;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><8.3</div> | |
<div style="background-color:#FEE090;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><9.5</div> | |
<div style="background-color:#FDAE61;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><10.6</div> | |
<div style="background-color:#F46D43;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"><11.7</div> | |
<div style="background-color:#D73836;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;padding-right:10px;"> < 12.8</div> | |
<div style="background-color:#A52A27;width:20px;height:20px;"> | |
</div> | |
<div style="padding-left:10px;"> >12.8</div> | |
</div>`; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
h1,h3{ | |
text-align: center; | |
} | |
#tooltip{ | |
position: absolute; | |
background-color: #414141 !important; | |
color: white; | |
font-size: 15px; | |
max-width: 100px; | |
border-radius: 2%; | |
padding: 5px; | |
border: 1px solid gray; | |
border-radius: 2px; | |
} | |
svg { | |
position: relative; | |
} | |
svg2{ | |
display: flex; | |
justify-content: center; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment