The most vulnerable and underprepared economies like Chad are among the least developed. Meanwhile, developed economies like Switzerland and Norway are less at risk.
Click to copy this into your story
viewof selectedYear = Inputs.range(
d3.extent(ndgain, d => d.year), {
value: d3.max(ndgain, d => d.year),
label: null,
step: 1
}
)
// add event handlers to year slider so we can suppress flags while sliding
// (they cause performance issues when sliding repeatedly)
mutable isScrubbing = false
selectedYearEvents = d3.select(viewof selectedYear)
.on("input", () => { mutable isScrubbing = true })
.on("change", () => { mutable isScrubbing = false })
.on("mouseup", () => { mutable isScrubbing = false })
// country search is case-insensitive and uses OR, not AND
viewof countrySearch = Inputs.search(ndgain, {
placeholder: "Search countries",
columns: ["name", "iso3"],
datalist: Array.from(new Set(ndgain.map(d => d.name))),
required: false,
filter: function(query) {
const splitQuery = query.toLowerCase().split(/\s+/)
return d =>
splitQuery.map(w => d.name.toLowerCase().includes(w)).some(w => w) ||
splitQuery.map(w => d.iso3.toLowerCase().includes(w)).some(w => w)
}
})
viewof selectedHDI = Inputs.radio(
[
"All economies",
"Low",
"Medium",
"High",
"Very high"
], {
value: "All economies",
label: "Human development index group"
}
)
// hdi filtering functions for each classification
hdiBand = ({
"All economies": d => true,
"Low": d => d < 0.55,
"Medium": d => d >= 0.55 && d < 0.7,
"High": d => d >= 0.7 && d < 0.8,
"Very high": d => d >= 0.8
})
thisYearsData = ndgain.filter(d => d.year == selectedYear)
// calculate plot domain based on selected hdi group
hdiData = ndgain.filter(d => hdiBand[selectedHDI](d.hdi))
hdiVuln = d3.extent(hdiData, d => d.vulnerability)
hdiReadi = d3.extent(hdiData, d => d.readiness)
// get unique country names from search
selectedCountries = new Set(countrySearch.map(d => d.name))
// get all years from selected countries (if there're any)
// so we can show timelines
timelineData = ndgain.filter(d => selectedCountries.has(d.name))
quadrants = [
{
x1: hdiReadi[0],
y1: hdiVuln[0],
x2: 0.45,
y2: 0.45,
t: "Neither prepared nor vulnerable",
fill: "goldenrod"
},
{
x1: hdiReadi[0],
x2: 0.45,
y1: 0.45,
y2: hdiVuln[1],
t: "Vulnerable and less ready",
fill: "red"
},
{
x1: 0.45,
x2: hdiReadi[1],
y1: hdiVuln[0],
y2: 0.45,
t: "Prepared and less vulnerable",
fill: "blue"
},
{
x1: 0.45,
x2: hdiReadi[1],
y1: 0.45,
y2: hdiVuln[1],
t: "Vulnerable but ready",
fill: "green"
}
]
// ratingFunc: classify a country into a quadrant
ratingFunc = function(d) {
if (d.readiness > 0.45) {
if (d.vulnerability > 0.45) {
return "Vulnerable but ready"
} else {
return "Prepared and less vulnerable"
}
} else {
if (d.vulnerability > 0.45) {
return "Vulnerable and less ready"
} else {
return "Neither prepared nor vulnerable"
}
}
}
// showIfSelected: opacity function that fades things out if there are countries
// selected AND they are not in the selection
showIfSelectedFunc = function(d, full = 1, faded = 0.05) {
if (selectedCountries.size > 0) {
return selectedCountries.has(d.name) ? full : faded
} else {
return full
}
}
// if country is in hdi band and, if there is a selection, it's in it
selectedCountryFilterFunc = function(d) {
return (
hdiBand[selectedHDI](d.hdi) &&
(selectedCountries.size > 0 ? selectedCountries.has(d.name) : true))
}
Plot = import("https://esm.run/@observablehq/[email protected]")
viewof scatterPlot = Plot.plot({
marks: [
// box and quarants axes
Plot.frame(),
Plot.ruleX([0.45]),
Plot.ruleY([0.45]),
Plot.rect(quadrants, {
x1: "x1",
x2: "x2",
y1: "y1",
y2: "y2",
fill: "fill",
fillOpacity: 0.15,
}),
// timelines if hovering or searching
selectedCountries.size > 0 && selectedCountries.size <= 10 ?
Plot.line(timelineData, {
x: "readiness",
y: "vulnerability",
z: "name",
filter: d => hdiBand[selectedHDI](d.hdi),
sort: {
channel: "year"
}
}) : null,
selectedCountries.size > 0 && selectedCountries.size <= 10 ?
Plot.dot(timelineData, {
x: "readiness",
y: "vulnerability",
z: "name",
r: 2.5,
fill: "black",
stroke: "white",
filter: d => hdiBand[selectedHDI](d.hdi),
sort: {
channel: "year"
}
}) : null,
// selected hdi band
Plot.text(thisYearsData, {
text: "iso3",
x: "readiness",
y: "vulnerability",
dy: 20,
fill: "black",
stroke: "white",
fillOpacity: d => showIfSelectedFunc(d, 0.5),
strokeOpacity: d => showIfSelectedFunc(d, 0.5),
filter: d => hdiBand[selectedHDI](d.hdi)
}),
Plot.dot(thisYearsData, {
x: "readiness",
y: "vulnerability",
r: selectedHDI == "All economies" ? 9.25 : 11.25,
fill: "black",
opacity: d => showIfSelectedFunc(d),
filter: d => hdiBand[selectedHDI](d.hdi)
}),
!isScrubbing ? Plot.image(thisYearsData, {
src: d => "/data/flags/" + d.iso2 + ".svg",
x: "readiness",
y: "vulnerability",
r: selectedHDI == "All economies" ? 8 : 10,
filter: d => selectedCountryFilterFunc(d)
}) : null,
// hovered point
Plot.dot(thisYearsData, Plot.pointer({
x: "readiness",
y: "vulnerability",
r: 16.25,
fill: "black",
stroke: "white",
filter: d => hdiBand[selectedHDI](d.hdi) && selectedCountries.has(d.name)
})),
!isScrubbing ? Plot.image(thisYearsData, Plot.pointer({
src: d => "/data/flags/" + d.iso2 + ".svg",
x: "readiness",
y: "vulnerability",
r: 15,
filter: d => selectedCountryFilterFunc(d)
})) : null,
// tooltip
Plot.tip(thisYearsData, Plot.pointer({
x: "readiness",
y: "vulnerability",
channels: {
"Country": {
label: "",
value: "name"
},
"Rating": {
label: "",
value: d => ratingFunc(d)
},
"Readiness": "readiness",
"Vulnerability": "vulnerability"
},
format: {
"x": null,
"y": null
},
lineHeight: 1.25,
filter: d => hdiBand[selectedHDI](d.hdi)
}))
],
x: {
domain: hdiReadi,
label: "Readiness score"
},
y: {
domain: hdiVuln,
label: "Vulnerability score"
},
color: {
type: "identity"
},
style: {
fontSize: 14,
fontFamily: "Roboto Condensed"
},
height: 550,
marginBottom: 40
})
micro = require("[email protected]")
micro.init({
awaitOpenAnimation: true,
awaitCloseAnimation: true
});
This chart, as well as the analysis that underpins it, is available under a Creative Commons Attribution 4.0 licence.
Please acknowledge 360info and our data sources when you use them.
Copy and paste the following code:
<div style="aspect-ratio: 7 / 10; width: 100%; min-height: 600px; max-height: 865px;">
<iframe
allow="fullscreen; clipboard-write self https://climatereadinessvulnerability.360visuals.org"
allowfullscreen="true"
src="https://climatereadinessvulnerability.360visuals.org/scatter/"
title="Interactive: Climate vulnerability and readiness"
style="width:100%; height:100%; position: relative; top: 0; left: 0; border:none; background-color: white;" scrolling="no"></iframe>
</div>
This content is subject to 360info’s Terms of Use.
Visit the GitHub repository to:
The data behind this chart comes from the Notre Dame Global Adaptation Initiative.
The ND-GAIN dataset scores countries on their Readiness and Vulnerability to climate change impacts (as well as presenting a combined score based on the difference that is used to rank countries).
These scores are made up of indicators, grouped to evaluate how aspects like food, water or health are expected to be impacted, or how ready aspects of a society are, like its governance or its education system.
(These indicators are not included in this chart, but the original dataset analysed by 360info includes them.)
Country flag designs are provided by HatScripts/circle-flags
.