Skip to content

Instantly share code, notes, and snippets.

@PappuKP
Created August 24, 2020 21:26
Show Gist options
  • Save PappuKP/fb375a1d03ee83fd9e02474afd6510a2 to your computer and use it in GitHub Desktop.
Save PappuKP/fb375a1d03ee83fd9e02474afd6510a2 to your computer and use it in GitHub Desktop.
Project-2: Visualize Data with a Scatterplot Graph
<!Doctype html>
<html lang="en-US">
<head>
<title>Scatter Plot</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 scatterplot 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">Doping in Professional Bicycle Racing</h1>
</body>
</html>
/**
* @fileOverview Scatter Plot implementation for comapring international professional cyclists across
* several years and with information about the doping allegations on them, the scatter plot is created
* using D3.js library.
* @author Anuj Yadav
* @see Access Source Data {@link https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json|Datasource}
* @see Other Charts {@link https://github.com/yadavanuj1996/D3.js-Charts|Other D3.js-Charts}
* @see Cod Pen {@link https://codepen.io/yadavanuj1996/pen/PMQEeW|Codpen}
*/
/**
* Function listens to DOM loading and will will call getCyclistData function on loading of DOM.
* @function
* @name DOMContentLoadedFunction
* @listens DOMContentLoaded
*/
document.addEventListener("DOMContentLoaded",()=>{
getCyclistData();
});
/**
* @external XMLHttpRequest
* @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
*/
/**
* The function will send ajax request to get cyclist dataset, access cyclist data set at
* {@link https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json|Datasource}
*/
let getCyclistData= ()=>{
let ajaxRequest=new XMLHttpRequest();
ajaxRequest.open("GET","https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/cyclist-data.json","true");
ajaxRequest.send();
ajaxRequest.onload=()=>{
const responseData=ajaxRequest.responseText;
const responseDatasetInJSON=JSON.parse(responseData);
loadDopingChart(responseDatasetInJSON);
}
}
/**
* This function will load the chart create svg add circles and display them on screen.
* @param {Array<Object>} dopingDataset The dataset for construcing the chart
*/
let loadDopingChart=(dopingDataset)=>{
const svgHeight=getChartHeight();
const svgWidth=getChartWidth();
const padding=getChartPadding();
const specifier="%M:%S";
const parsedData=getParsedData(dopingDataset,specifier);
let svg=loadSvg(svgHeight,svgWidth);
let legend=getCustomLegend();
let tooltip=getTooltip();
let circlesInSvg=addCirclesInSvg(svg,dopingDataset);
circlesInSvg.attr("cx",(d,i)=>xScale(dopingDataset,svgWidth,padding)(new Date(d.Year)))
.attr("cy",(d,i)=>yScale(parsedData,svgHeight,padding)(d3.timeParse(specifier)(d.Time)))
.attr("r",6)
.attr("data-xvalue",(d)=>d.Year)
.attr("data-yvalue",(d,i)=>d3.timeParse(specifier)(d.Time).toISOString())
.attr("fill",(d,i)=>{
if(d.Doping){
return "#5790C2";
}
return "#FC813F";
})
.on("mouseover", (d)=> {
tooltip.style("opacity", 1)
.attr("data-year", d.Year)
.html(`${d.Name}: ${d.Nationality}<br>
Year: ${d.Year} Time: ${d.Time}<br></br>${d.Doping}`)
.style("left",`${20+xScale(dopingDataset,svgWidth,padding)(new Date(d.Year))}px`)
.style("top", `${yScale(parsedData,svgHeight,padding)(d3.timeParse(specifier)(d.Time))}px`);
})
.on("mouseout",(d,i)=>{
tooltip.style("opacity",0);
});
let xAxis=d3.axisBottom(xScale(dopingDataset,svgWidth,padding)).tickFormat(d3.format("d"));
loadXAxis(svg,xAxis,svgHeight,padding);
let yAxis = d3.axisLeft(yScale(parsedData,svgHeight,padding)).tickFormat((d)=> d3.timeFormat(specifier)(d));
loadYAxis(svg,yAxis,padding);
}
/** Return chart height.*/
const getChartHeight=()=>600;
/** Return chart width.*/
const getChartWidth=()=>1200;
/** Return chart padding.*/
const getChartPadding=()=>100;
/** This will return array of Time(MM:SS) format after extracting it from dataset.*/
const getParsedData=(dataset,specifier)=>{
return dataset.map((d)=> {
return d3.timeParse(specifier)(d.Time);
});
};
/** Add svg in body and return it.*/
const loadSvg=(height,width)=>{
return d3.select("body")
.append("svg")
.attr("height",height)
.attr("width",width);
};
/** Returns custom legend sectin to represent the detail and color use of scatter plot. */
const getCustomLegend=(legendSvg)=>{
return d3.select("body")
.append("div")
.attr("id","legend")
.attr("height",100)
.attr("width",100)
.attr("fill","red")
.style("left",`50px`)
.style("top", `100px`)
.html(`<div style="display:flex;font-size:13px;">
<div style="background-color:#5790C2;width:20px;height:20px;">
</div>
<div style="padding-left:10px;">Riders with doping allegations</div>
</div>
<div style="display:flex;padding-top:5px;font-size:13px;">
<div style="background-color:#FC813F;width:20px;height:20px;">
</div>
<div style="padding-left:10px;">No doping allegations</div>
</div>`);
};
/** Return tooltip to show inforamtion about a point when it is hovered. */
const getTooltip=()=>{
return d3.select("body")
.append("div")
.attr("id","tooltip")
.style("opacity",0);
};
/**
* This function will add circles equal to number of elements in dataset inside svg.
* @param {object} svg The svg element.
* @param {Array<object>} dataset The doping Dataset.
* @return {object} The svg element with number of cicles equal to number of elements in dataset.
*/
const addCirclesInSvg=(svg,dataset)=>{
return svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("class","dot");
};
/** Add labels in svg.*/
const addLabelsOnCircles=(svg,dataset,fontSize)=>{
return svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.style("font-size",fontSize)
.text((d,i)=>`${d.Time}`);
};
/* This function will append a x-axis (g element)in svg (param).*/
const loadXAxis=(svg,xAxis,height,padding)=>{
svg.append("g")
.attr("transform",`translate(0,${height-padding})`)
.attr("id","x-axis")
.call(xAxis);
};
/* This function will append a y-axis (g element)in svg (param).*/
const loadYAxis=(svg,yAxis,padding)=>{
svg.append("g")
.attr("transform",`translate(${padding},0)`)
.attr("id","y-axis")
.call(yAxis);
};
/**
* The Curried Function that will return interpolated value on x axis and will return
* function on first call then number value in second
* @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 on that the point will be represented on chart.
*/
let xScale=(dataset,width,padding)=>{
return d3.scaleTime()
.domain([d3.min(dataset,(d,i)=>new Date(d.Year-1)),d3.max(dataset,(d,i)=>new Date(d.Year+1))])
.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 on that the point will be represented on chart
*/
let yScale=(parsedData,height,padding)=>{
return d3.scaleTime()
.domain(d3.extent(parsedData))
.range([padding,height-padding])
.nice();
};
/**
* 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 on that the point will be represented on chart
*/
let ySecondsScale=(dataset,height,padding)=>{
return d3.scaleTime()
.domain([d3.min(dataset,(d,i)=>d.Seconds),d3.max(dataset,(d,i)=>d.Seconds)])
.range([padding,height-padding]);
};
h1{
text-align: center;
}
#tooltip{
position: absolute;
background-color: #e36f1c !important;
color: white;
font-size: 15px;
max-width: 200px;
border-radius: 2%;
padding: 5px;
border: 1px solid gray;
border-radius: 2px;
background-color: tan;
}
svg {
border: 1px solid gray;
position: relative;
}
/*#tooltip {
top: 0px;
left: 0px;
border: 1px solid gray;
border-radius: 2px;
padding: 4px;
background-color: tan;
}*/
#legend{
position:absolute;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment