-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
211 lines (203 loc) · 8.29 KB
/
script.js
File metadata and controls
211 lines (203 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/**
* @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]);
};