Built with blockbuilder.org
The data comes from the World Happiness Report. Direct link to the data download can be found here.
The work for this block was developed locally using Curran Kelleher's dataviz-project-template as a starting point and also used the block World Happiness Ranking.
The focus of this block is to scale both the chart and axes to take up the full screen. We do not maintian view ratio. We scale for width and height individually. Note that as the chart gets bigger the circles in the scatter plot get bigger, same with axes fonts and chart padding for the axes.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quattrocento+Sans">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Marcellus+SC">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
html, body {
margin: 0px;
background-color: rgb(48, 48, 54);
}
div#appHeader {
box-sizing: border-box;
position: absolute;
top: 0px;
left: 20px;
right: 20px;
height: 40px;
font-family: "Marcellus SC";
font-size: 32px;
color: #eeeee5;
}
div#appContent {
box-sizing: border-box;
position: absolute;
top: 40px;
left: 20px;
right: 20px;
bottom: 20px;
background-color: #ffffff;
}
.labelW {
font-family: "Quattrocento Sans";
font-size: 2vmax;
font-weight: bold;
color: rgb(48, 48, 54);
}
.labelH {
font-family: "Quattrocento Sans";
font-size: 2vmax;
font-weight: bold;
color: rgb(48, 48, 54);
}
.tick {
font-family: "Quattrocento Sans";
font-size: 1vw;
</style>
</head>
<body>
<div id="appHeader">Responding to Resize</div>
<div id="appContent">
<svg id="chart1" width="100%" height="100%"></svg>
</div>
<script>
let _d;
let _n;
let draw;
let axis = [];
let xLinear;
let yLinear;
let chartYear = 2016;
let margin = [];
document.addEventListener("DOMContentLoaded", (event) => {
_d = new Data();
draw = new Draw();
_n = new Navigation();
_d.loadHappy("https://CJKraenzle.github.io/data/data.csv", _n.setChart);
margin.width = window.innerWidth - 40; // svg#chart1 widith
margin.height = window.innerHeight - 60; // svg#chart1 height
margin.paddingLeft = margin.height / 10;
margin.paddingRight = 10;
margin.paddingTop = 10;
margin.paddingBottom = margin.width / 15;
});
class Navigation {
constructor() {
this.setEventListeners();
}
setEventListeners() {
window.addEventListener("resize", () => { this.executeResize(); });
}
executeResize() {
margin.width = window.innerWidth - 40; // svg#chart1 widith
margin.height = window.innerHeight - 60; // svg#chart1 height
margin.paddingBottom = margin.width / 15;
margin.paddingLeft = margin.height / 10;
draw.renderWorldHappiness(_d.getDataByYear(chartYear));
}
setChart() {
draw.renderWorldHappiness(_d.getDataByYear(chartYear));
}
}
class Draw {
createScale(data) {
xLinear = d3.scaleLinear()
.domain([0,d3.max(data,(d,i)=>{ return d.x; })])
//.domain([d3.min(data,(d,i)=>{ return d.x; }),d3.max(data,(d,i)=>{ return d.x; })])
.range([margin.paddingLeft, (margin.width - margin.paddingRight)])
.nice();
yLinear = d3.scaleLinear()
.domain([0, d3.max(data,(d,i)=>{ return d.y; })])
.range([(margin.height - margin.paddingBottom), margin.paddingTop])
.nice();
}
renderWorldHappiness(data) {
this.createScale(data);
let radius = Math.ceil((margin.width < margin.height ? margin.height : margin.width) / 100);
let t = d3.transition().duration(750);
let t2 = d3.transition().duration(1500);
let scatter = d3.select("#chart1")
.selectAll("circle")
.data(data);
scatter
.exit()
.remove();
scatter
.enter()
.append("circle")
.merge(scatter)
// Start attributes on resize
.attr("r",6)
// Transition after resize
.transition(t)
.attr("cx", (d)=>{ return xLinear(d.x); })
.attr("cy", (d)=>{ return yLinear(d.y); })
.attr("fill", (d)=>{
if (d.y < 3) return "rgba(255,36,0,.8)";
if (d.y < 4) return "rgba(102,165,255,.8)";
if (d.y < 5) return "rgba(50,135,255,.8)";
if (d.y < 6) return "rgba(0,105,255,.8)";
if (d.y < 7) return "rgba(0,84,204,.8)";
return "rgba(12,148,0,.8)";
})
.attr("r", 20)
// Transition to final state
.transition(t2)
.attr("r",radius);
this.xAxis(data);
this.yAxis(data);
}
xAxis(data) {
if (axis.xAxisG!=undefined) axis.xAxisG.remove();
if (axis.xAxisTitle!=undefined) axis.xAxisTitle.remove();
axis.xAxis = d3.axisBottom(xLinear)
.tickFormat((d,i)=>{
if (data[d]==undefined) return "";
return data[d].country;
});
axis.xAxisG = d3.select("#chart1")
.append("g")
.attr("transform", "translate(0," + (margin.height - margin.paddingBottom) + ")")
.call(axis.xAxis);
axis.xAxisTitle = d3.select("#chart1").append("text")
.attr("class","labelW")
.attr("y", margin.height - (margin.paddingBottom / 5))
.attr("x", margin.width / 2)
.style("text-anchor", "middle")
.text("World Happiness - Surveyed Countries");
}
yAxis(data) {
if (axis.yAxisG!=undefined) axis.yAxisG.remove();
if (axis.yAxisTitle!=undefined) axis.yAxisTitle.remove();
axis.yAxis = d3.axisLeft(yLinear);
axis.yAxisG = d3.select("#chart1")
.append("g")
.attr("transform", "translate(" + margin.paddingLeft + ", 0 )")
.call(axis.yAxis);
axis.yAxisTitle = d3.select("#chart1").append("text")
.attr("class", "labelH")
.attr("transform", "rotate(-90)")
.attr("y", margin.paddingLeft / 2.5)
.attr("x", -(margin.height - margin.paddingBottom) / 2)
.style("text-anchor", "middle")
.text("World Happiness Ranking");
}
}
class Data {
constructor() {
this.data = [];
this.country = [];
this.year = [];
}
processData(d) {
return {
wp5country: d["wp5country"],
country: d["country"],
year: +d["year"],
lifeladder: +d["lifeLadder"],
gdpPerCapita: +d["gdpPerCapita"],
social: +d["social"],
lifeExpect: +d["lifeExpect"],
freedom: +d["freedom"],
generosity: +d["generosity"],
corruption: +d["corruption"],
pos: +d["pos"],
neg: +d["neg"],
govConf: +d["govConf"],
demQual: +d["demQual"],
delQual: +d["delQual"],
stdDevLadder: +d["stdDevLadder"],
stdDevLadMean: +d["stdDevLadMean"],
giniIndex: +d["giniIndex"],
giniIndex00_13: +d["giniIndex00_13"],
householdIncome: +d["householdIncome"],
peopleTrust: +d["peopleTrust"],
peopleTrust81_84: +d["peopleTrust81_84"],
peopleTrust89_93: +d["peopleTrust89_93"],
peopleTrust94_98: +d["peopleTrust94_98"],
peopleTrust99_04: +d["peopleTrust99_04"],
peopleTrust05_09: +d["peopleTrust05_09"],
peopleTrust10_14: +d["peopleTrust10_14"]
}
}
loadHappy(file, callback) {
d3.csv(file, this.processData, data=>{
this.data = data;
this.uniqueCountryList();
this.uniqueYearList();
callback();
});
}
uniqueCountryList() {
const getCountryList = (array, n) => array.map(x => x[n]);
const countries = getCountryList(this.data, "country");
this.country = [...new Set(countries)];
}
uniqueYearList() {
const getYearList = (array,n) => array.map(x=> x[n]);
const years = getYearList(this.data, "year");
this.year = [...new Set(years)];
}
getDataByYear(year) {
let d = [];
let count = 0;
this.data.forEach((row)=>{
if (row.year==year) {
count++;
d.push({ "x": count, "y": row.lifeladder, "country": row.country });
}
});
return d;
}
}
</script>
</body>
https://d3js.org/d3.v4.min.js