Home United States Educational Attainment
Post
Cancel

United States Educational Attainment

Visualize Data with a Chloropleth Map

This is a walkthrough to make a Chloropleth Map project from start to finish. We import some JSON data with geographical and educational data about counties in the United States and render it using the javascript library d3.js, by converting topojson into geojson and then using the geoPath() method to draw paths.

Notes

1. Project Setup

Looking at what we need to create and talking through the structure of the task.

  • Create skeleton page, set title
  • Import D3 and TopoJSON
  • Create and link script page
  • Create and link stylesheet
  • Create svg canvas with id
  • Set body display and svg bg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Visualize data with a Chloropleth map</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <div id="main-wrapper">
        <svg id="canvas">
        </svg>
    </div>
</body>
<script defer src="./script.js"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
}

html, body {
    min-height: 100%;
}

body {
    background-image: radial-gradient( circle 610px at 5.2% 51.6%,  rgba(5,8,114,1) 0%, rgba(7,3,53,1) 97.5% );
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-family: 'Roboto', 'Trebuchet MS', sans-serif;
}

svg {
    background-color: rgb(56,58,74);
}

2. Creating variables and functions

  • countyUrl points to the freeCodeCamp county map data JSON
  • educationDataUrl points to the freeCodeCamp education data JSON
  • countiesData will be used to store an array of data to give to d3 to draw the map
  • educationData will be used to store an array of data to give to d3 to associate education information with the counties
  • canvas is a d3 selection of the svg canvas to reference easily
  • drawCounties() is a function that will draw the map, once we have obtained the data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
countyUrl = 'https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json';
educationDataUrl = 'https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json';
method = 'GET';
countyXMLRequest = new XMLHttpRequest();
dataXMLRequest = new XMLHttpRequest();

//Store Data
let countiesData;
let educationData;

//Canvas Dimensions
canvasDimension = {
    width : 1000,
    height : 630
}

//Legend Dimensions
legendDimension = {
    width: 170,
    height: 325
}

//Create Canvas
let canvas = d3.select('#canvas')

3. Fetching the data

  • Create variables to store both data sets
  • For each, call the d3.json method, giving as a string the url of the json store in countyUrl
  • This works asynchronously and returns a promise, so call the .then() method to give a function to run when the promise is completed
  • This function takes in the data and error (if unsuccessful)
  • If there is an error, log it, otherwise set the data to our variables
  • d3 will automatically convert them into JavaScript Objects
  • I’ve embedded one inside the other so that we can add some flow to these **async **functions
  • Finally, we will run the drawCounties() function to draw the map once everything is ready
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
d3.json(countyUrl).then(
    (data, error) => {
        if(error){
            console.log(error);
        } else {
            countiesData = topojson.feature(data, data.objects.counties).features;
            console.log('County Data')
            console.log(countiesData);

            d3.json(educationDataUrl).then(
                (data, error) => {
                    if(error) {
                        console.log(error);
                    } else {
                        educationData = data;
                        console.log('Education Data')
                        console.log(educationData);

                        //Calling function
                        drawCanvas();
                        drawCounties();
                    }
                }
            )
        }
    }
)

d3/d3-request

4. Understanding and converting the fetched data

countiesData contains information on the actual geographic lines to draw to create the map:

county data img 01

  • The actual data we need is in the objects > counties
  • The geometry field is an array with a set of objects for each county
  • They have an id in the form of a number
  • They also have an array of ‘arcs’ which will be used to create paths. These reference the arcs object which has an array of objects with coordinates to draw each arc

This is currently in a format called topoJSON, but d3 only supports geoJSON to draw paths with, so we need to convert it.

GeoJSON

We can do this using the feature() method from topoJSON, giving it the data set as the first argument, and what we want to extract as the second argument:

1
2
3
4
countyData = data
countyData = topojson.feature(countyData, countyData.objects.counties)
	console.log('County Data')
	console.log(countyData)

Now we have it in geojson format:

county data img 02

  • features contains an array of objects for each county
  • The geometry field of each object has an array of coordinates from which we can draw lines to draw a map outline of that county (lines connect each set of coordinates sequentially)
  • The id has also be transferred across We only need the features array part of this, so we can do:
1
countiesData = topojson.feature(data, data.objects.counties).features;

So now we have an array like this, which we can give as d3 data:

county data img 03

educationData contains education information for each country

education data img 04

  • This is an array of objects representing each county
  • They contain information on the county name, it’s state and the percentage of adults who have a bachelors degree or higher
  • There is also a FIPS to act as an ID for the county
  • These are unique codes than identify counties in the USA

FIPS county code

Note that there are exactly 3142 objects in each set of data, and the ids from the map array match the fips codes from the second array, so we can associate these together. Now we have the county map data and education data set up, ready for use!

topojson/topojson GeoJSON

5. Creating a title and a description with the corresponding ids

  • Add an element with the id of ‘title’ to your document:
1
2
3
<h1 id="title">
        <strong><i>United States Adult Education</i></strong>
    </h1>
  • Add an element with the id of ‘description’ to your document:
1
2
3
4
<div id="description"> <i>
                Percentage of adults age 25 and older with a bachelor's degree or higher
                (2010-2014). Source: <a href="https://www.ers.usda.gov/data-products/county-level-data-sets/download-data.aspx">USDA Economic Research Service</a></i>
            </div>

6. Creating counties elements with class ‘county’ representing the data

In drawCounties():

  • select all svg path elements in the canvas
  • bind it to our array of county data sets by calling data()
  • call enter() to specify what to do for county objects with no paths (all of them in this case)
  • append the canvas with a new path element
  • set the ‘d’ attribute of the path (d is the instructions to draw the path) to call d3.geoPath() which generates path instructions from the data we bound to it
  • set the class to ‘country’ as required
1
2
3
4
5
6
7
8
canvas.append('g')
          .attr('class', 'counties')
          .selectAll('path')
          .data(countiesData)
          .enter()
          .append('path')
          .attr('d', d3.geoPath())
          .attr('class', 'county')

d3/d3-geo

7. Fill each county with a color specifying a range of date

  • set a ‘fill’ attribute on the new path selection, giving a function that takes in an item from the array
  • create a variable called fips and set this to the id (we can use this on the other array)
  • call the .find() method on the educationData array to return the county object with the matching fips code
  • create a variable called percentage and reference the ‘bachelorsOrHigher’ field which contains the percentage of adults in the county with a bachelors degree or higher
  • return different colors based on this percentage
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
.attr('fill', (d) => {

            let countyId = d.id;
            let county = educationData.find((d) => {
                return countyId === d.fips;
            });
            let ratio = county.bachelorsOrHigher;
            

            if(ratio <= 3) {
                return '#330000';
            } else if(ratio <= 12) {
                return '#CC0000'
            } else if(ratio <= 21) {
                return '#6666FF'
            } else if(ratio <= 30) {
                return 'red';
            } else if(ratio <= 39) {
                return '#00CCCC';
            } else if(ratio <= 45){
                return '#CC0066'
            } else if(ratio <= 57){
                return '#CC6600'
            } else {
                return '#CCCC00'
            }
          })

Change styles based on data

8. Adding some properties to county containing their corresponding fips and education values

data-fips:

  • add an attribute of ‘data-fips’ to the new path selection, giving it a function that takes in an item from the countyData array
  • return the ‘id’ field which is the same as the fips code

data-education:

  • add an attribute of ‘data-education’ to the new path selection, giving it a function that takes in an item from the countyData array
  • create a variable called fips and set this to the id (we can use this on the other array)
  • call the .find() method on the educationData array to return the county object with the matching fips code
  • create a variable called percentage and reference the ‘bachelorsOrHigher’ field which contains the percentage of adults in the county with a bachelors degree or higher
  • return the percentage variable
1
2
3
4
5
6
7
8
9
10
.attr('data-fips',(d) => {
            return d.id;
          })
.attr('data-education',(d) => {
            let countyId = d.id;
            let county = educationData.find((d) => {
                return countyId === d.fips
            });
            return county.bachelorsOrHigher;
          })

9. Create a legend and fill each block with the corresponding color

  • Create an svg element with the id of ‘legend’ anywhere in your document:
1
2
3
let legend = d3.select('#legend')
                   .attr('width', legendDimension.width)
                   .attr('height', legendDimension.height);
  • Create svg rectangles in your ‘legend’ canvas for each fill color
  • Add fill colors and coordinates to the rectangles
  • Give some text to specify the meaning of each color like in the example down below
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 0)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#330000');
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 20)
          .attr('fill', 'white')
          .text('Less than 3%')
          .attr('class', 'legendText');

10. Create a tooltip and some properties to it

  • Create a div element in the document with the id of tooltip
  • Set the variable tooltip to a d3 selection of tooltip
1
2
3
4
5
6
let tooltip = d3.select('body')
                .append('div')
                .attr('id', 'tooltip')
                .attr('class', 'tooltip')


  • Set the css of the tooltip div to visibility hidden, width and height auto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.tooltip {
    border: solid 1px green;
    position:absolute;
    padding: .5rem;
    text-align: center;
    border-radius: .3rem;
    font: 'Roboto', sans-serif;
    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.4);
    font-size: 14px;
    opacity: 0;
    background: #333;
    align-items: centerx;
    justify-items: center;
    justify-content: center;
    display: flex;
}
  • Add a mouseover() event to cells to make the tooltips visible
  • create a variable called fips and set this to the id (we can use this on the other array)
  • call the .find() method on the educationData array to return the county object with the matching fips code
  • Set the text of the tooltip to display the fields from this county object
  • It doesn’t really matter what the tooltip text contains
  • Add a mouseout() event to the circles to make the tooltip hidden
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.on('mouseover', (d, i) => {
            tooltip.transition()
                   .duration(200)
                   .style('opacity', 0.9);

            let countyId = d.id;
            let county = educationData.find((item) => {
                return countyId === item.fips;
            });

            tooltip.html("" + "Fips: " + county.fips + "<br/>" 
                        + county.area_name + ", " + county.state + ": " + "<br/>"
                        + "Percentage: " + county.bachelorsOrHigher + "%");

            tooltip.style('left', (d3.event.pageX + 10) + 'px')
                   .style('top', (d3.event.pageY - 30) + 'px');

          })
          .on('mouseout', (d, i) => {
            tooltip.transition()
                   .duration(50)
                   .style('opacity', 0);
          })
  • In the mouseover() event in the county paths call the attribute method on tooltip
  • Set the attribute ‘data-education’ to the ‘bachelorsOrHigher’ field from the county object
1
tooltip.attr('data-education', county.bachelorsOrHigher);

d3/d3-selection d3/d3-selection

11. Final touches and CSS Styling

Source Code

index.html

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
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Visualize data with a Chloropleth map</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <h1 id="title">
        <strong><i>United States Educational Attainment</i></strong>
    </h1>

    <div id="main-wrapper">
        <svg id="canvas">
        </svg>

        <div id="rightSide">

            <svg id="legend">
            </svg>

            <div id="description"> <i>
                Percentage of adults age 25 and older with a bachelor's degree or higher
                (2010-2014). Source: <a href="https://www.ers.usda.gov/data-products/county-level-data-sets/download-data.aspx">USDA Economic Research Service</a></i>
            </div>
            <div id="signature">Made by <i>Venom Cocytus</i></div>

        </div>
    </div>

</body>
<script defer src="./script.js"></script>
</html>

style.css

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
@import url('https://fonts.googleapis.com/css?family=Roboto');

*{
    font-family: 'Roboto', 'Trebuchet MS', sans-serif;
}

html, body {
    min-height: 100%;
}

body {
    background-image: radial-gradient( circle 610px at 5.2% 51.6%,  rgba(5,8,114,1) 0%, rgba(7,3,53,1) 97.5% );
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-family: 'Roboto', 'Trebuchet MS', sans-serif;
}

#main-wrapper{
    display: flex;
    align-items: center;
    width: 100%;
}

#canvas {
    margin-left: 30px;
    border-radius: 10px;
    min-width: 1000px;
    min-height: 600px;
}

#title {
    margin: 25px;
    font-size: 36px;
    font-weight: 600;
    color:rgb(56,58,74);
}

#description {
    margin: 25px;
    padding: 10px;
    text-align: center;
    font-size: 18px;
}

#description a:hover{
    color:rgb(56,58,74);
}

.counties {
    justify-content: center;
    align-items: center;
}

.county {
    stroke: white;
    cursor: pointer;
}

.county:hover {
    fill: darkblue;
}

svg {
    background-color: rgb(56,58,74);
    /* border: solid 1px cyan; */
}

#legend {
    border-radius: 3px;
    margin: 10px;
    padding: 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    font-size: 15px;
}

.legendText {
    font-family: 'Roboto', 'Trebuchet MS', sans-serif;
}

.tooltip {
    border: solid 1px green;
    position:absolute;
    padding: .5rem;
    text-align: center;
    border-radius: .3rem;
    font: 'Roboto', sans-serif;
    box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.4);
    font-size: 14px;
    opacity: 0;
    background: #333;
    align-items: centerx;
    justify-items: center;
    justify-content: center;
    display: flex;
}

#rightSide {
    display: flex;
    flex-direction: column;
    align-items: center;
}

#signature {
    font-size: 12px;
}

script.js

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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//Declaration of variable

//fetch Data from API
countyUrl = 'https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json';
educationDataUrl = 'https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json';
method = 'GET';
countyXMLRequest = new XMLHttpRequest();
dataXMLRequest = new XMLHttpRequest();

//Store Data
var countiesData;
var educationData;

//Canvas Dimensions
canvasDimension = {
    width : 1000,
    height : 630
}

//Legend Dimensions
legendDimension = {
    width: 170,
    height: 325
}

//Create Canvas
var canvas = d3.select('#canvas')

//Create tooltip
var tooltip = d3.select('body')
                .append('div')
                .attr('id', 'tooltip')
                .attr('class', 'tooltip')

//Draw Canvas
function drawCanvas() {

    canvas.attr('width', canvasDimension.width)
          .attr('height', canvasDimension.height)
}

//Draw Countries
function drawCounties() { 

    canvas.append('g')
          .attr('class', 'counties')
          .selectAll('path')
          .data(countiesData)
          .enter()
          .append('path')
          .attr('d', d3.geoPath())
          .attr('class', 'county')
          .attr('data-fips',(d) => {
            return d.id;
          })
          .attr('data-education',(d) => {
            var countyId = d.id;
            var county = educationData.find((d) => {
                return countyId === d.fips
            });
            return county.bachelorsOrHigher;
          })
          .attr('fill', (d) => {

            var countyId = d.id;
            var county = educationData.find((d) => {
                return countyId === d.fips;
            });
            var ratio = county.bachelorsOrHigher;
            

            if(ratio <= 3) {
                return '#330000';
            } else if(ratio <= 12) {
                return '#CC0000'
            } else if(ratio <= 21) {
                return '#6666FF'
            } else if(ratio <= 30) {
                return 'red';
            } else if(ratio <= 39) {
                return '#00CCCC';
            } else if(ratio <= 45){
                return '#CC0066'
            } else if(ratio <= 57){
                return '#CC6600'
            } else {
                return '#CCCC00'
            }
          })
          .on('mouseover', (d, i) => {
            tooltip.transition()
                   .duration(200)
                   .style('opacity', 0.9);

            var countyId = d.id;
            var county = educationData.find((item) => {
                return countyId === item.fips;
            });

            tooltip.html("" + "Fips: " + county.fips + "<br/>" 
                        + county.area_name + ", " + county.state + ": " + "<br/>"
                        + "Percentage: " + county.bachelorsOrHigher + "%");

            tooltip.style('left', (d3.event.pageX + 10) + 'px')
                   .style('top', (d3.event.pageY - 30) + 'px');

            tooltip.attr('data-education', county.bachelorsOrHigher);

          })
          .on('mouseout', (d, i) => {
            tooltip.transition()
                   .duration(50)
                   .style('opacity', 0);
          });
}

//Draw Legend
function drawLegend() {

    var legend = d3.select('#legend')
                   .attr('width', legendDimension.width)
                   .attr('height', legendDimension.height);

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 0)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#330000');
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 20)
          .attr('fill', 'white')
          .text('Less than 3%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 40)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#CC0000');
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 60)
          .attr('fill', 'white')
          .text('Less than 12%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 80)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#6666FF');
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 100)
          .attr('fill', 'white')
          .text('Less than 21%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 120)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', 'red')
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 140)
          .attr('fill', 'white')
          .text('Less than 30%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 160)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#00CCCC')
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 180)
          .attr('fill', 'white')
          .text('Less than 39%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 200)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#CC0066')
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 220)
          .attr('fill', 'white')
          .text('Less than 45%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 240)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#CC6600')
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 260)
          .attr('fill', 'white')
          .text('Less than 57%')
          .attr('class', 'legendText');

    legend.append('g')
          .append('rect')
          .attr('class', 'legendCell')
          .attr('x', 10)
          .attr('y', 280)
          .attr('width', 40)
          .attr('height', 40)
          .attr('fill', '#CCCC00')
          
    legend.append('text')
          .attr('x', 60)
          .attr('y', 300)
          .attr('fill', 'white')
          .text('Above 57%')
          .attr('class', 'legendText');
}

//Fetching JSON Data Method 02
d3.json(countyUrl).then(
    (data, error) => {
        if(error){
            console.log(error);
        } else {
            countiesData = topojson.feature(data, data.objects.counties).features;
            console.log('County Data')
            console.log(countiesData);

            d3.json(educationDataUrl).then(
                (data, error) => {
                    if(error) {
                        console.log(error);
                    } else {
                        educationData = data;
                        console.log('Education Data')
                        console.log(educationData);

                        // console.log(d3.max(educationData, (d) => {
                        //     return d.bachelorsOrHigher;
                        // }))

                        //Calling function
                        drawCanvas();
                        drawCounties();
                        drawLegend();
                    }
                }
            )
        }
    }
)

Interactive Frame

Getting The Code On CodePen

The entire codePen containing all the code mentioned in this post can be found here.

Getting The Code On Github

The entire folder containing all the code mentioned in this post can be found via this link.

Just bear in mind that you will need to install all the dependencies. If you find any issues with the code, feel free to either comment down below or raise an issue on Github.

Add me on LinkedIn

Don’t hesitate to follow me on linkedIn or o other social network to encourage me to do more posts on IT.

This post is licensed under CC BY 4.0 by the author.

Monthly Global Land-Surface Temperature between 1753 - 2015

Top 100 Video Games Sales sorted by Console

Comments powered by Venom Cocytus.