Public
Edited
Jul 3, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = () => {
const yearsToFilter = getYearList(data.filter(d => d.entity === entity && d.poverty_line === povLine ));
const plotData = data.filter(d => d.entity === entity
&& d.poverty_line === povLine
&& yearsToFilter.includes(d.year)
)
const minYear = d3.min(plotData, d => d.year);
const maxYear = d3.max(plotData, d => d.year);
const maxPov = d3.max(plotData, d => d.pov_rate);
const zoomGap = 0.02*maxPov; //Gap is 2% of the max poverty value

const randomData = genRandArray((zoom ? maxPov: 1), zoomGap, minYear, maxYear, 0.01*maxPov, 0.005*maxPov)

// get x axis tick lables and positions
const values = [];
for (let value = 0; value <= 100; value++) {
if (value % 20 === 0) {
values.push({"x": value/100, "y": minYear -5, "text": `${value}%`});
}
}

const xAxis = getXAxis(zoom ? maxPov: 1, minYear);

var entityLabel = ""

if (entity === "World") {
entityLabel = "THE WORLD"
}else {
entityLabel = entity.toUpperCase()
}

return Plot.plot({

height: 700,
width: 600,
marginLeft: 100,
marginRight: 50,
marginBottom: 50,

title: htl.html`<h2 id="plotTitle">POVERTY IN ${entityLabel}</h2>`,
caption: htl.html`<div id="plotCaption">Data: World Bank PIP<br>Inspired by W.E.B Du Bois<br>Chart by <strong>Luca Picci @lpicci96</strong></div>`,

style: {
fontSize: "13px",
fontFamily: "Public Sans, sans-serif",
color: "#958475",
},

marks: [
//plot the data
Plot.rectY([{}], {x1: 0,
x2: (maxPov === 0 ? 1: (zoom ? maxPov + zoomGap: 1)),
y1: minYear, y2: maxYear,
fill: "#111111"}),
Plot.areaX(plotData,
{y: "year",
x: "pov_rate", fill: "#c3213e",
tip: true,
title: d =>`In ${d.year}\napproximately ${(d.pov_rate*100).toFixed(1)}% of the population in
${d.entity == "World" ? "the ": ""}${d.entity} lived on less that $${d.poverty_line} per day.`
}),

Plot.line(plotData, {x: "pov_rate", y: "year", stroke: "#e2d0be", strokeWidth: 1}),

(maxPov === 0 ? [] : (zoom ? [Plot.areaX(randomData, {y: "y", x1: "x1", x2: "x2", fill: "#111111", stroke: "#111111"}),
Plot.line(randomData, {y: "y", x: "x1", stroke: "#111111", strokeWidth: 0.5})]
: [])),

// gridlines
Plot.ruleY(yearsToFilter,
{x1: 0,
x2: (maxPov === 0 ? 1: zoom ? maxPov + zoomGap: 1),
stroke: "#e2d0be"}),

// value labels on right
Plot.textY(plotData,
{x: (maxPov === 0 ? -0.15 : (zoom ? 0 - (maxPov + zoomGap)*0.15 : -0.15)),
y: "year",
text: d => d.year === minYear
|| d.year === maxYear ? `${(d.pov_rate*100).toFixed(1)}%`: `${(d.pov_rate*100).toFixed(1)}`
}),

// value label heading
Plot.text([{x: (maxPov === 0 ? -0.15 : (zoom ? 0 - (maxPov + zoomGap)*0.15 : -0.15)),
y: minYear - 4, text: "PEOPLE"}],
{x: "x",y: "y",text: "text"}),
Plot.text([{x: (maxPov === 0 ? -0.15 : (zoom ? 0 - (maxPov + zoomGap)*0.15 : -0.15)),
y: minYear - 3, text: "LIVING BELOW"}],
{x: "x",y: "y",text: "text"}),
Plot.text([{x: (maxPov === 0 ? -0.15 : (zoom ? 0 - (maxPov + zoomGap)*0.15 : -0.15)),
y: minYear - 2, text: `$${povLine} A DAY`}],
{x: "x",y: "y",text: "text"}),

// x axis
Plot.text(xAxis, {x: "x", y: minYear-1, text: "text"}),
Plot.ruleX(xAxis, {x: "x", y1: minYear, y2: minYear - 0.5, stroke: "#958475"})
],

y: {
reverse: true,
ticks: yearsToFilter,
tickFormat: ".0f",
tickPadding: 30,
tickSize: 0,
label: null
},

x: {
domain: [(maxPov === 0 ? -0.15 : (zoom ? 0 - (maxPov + zoomGap)*0.15 : -0.15)),
(maxPov === 0? 1 : zoom ? maxPov + zoomGap: 1)],
reverse: true,
axis: "top",
tickFormat: ".0%",
tickSize: 5,
label: null,
ticks: values
}
})
}
Insert cell
Insert cell
// function to generate random number array for page break

function genRandArray(baseValue, gap, startY, endY, range, fluctuationRange) {

const tornPaperData = [];
const minValue = baseValue + gap;
const yIncrement = 0.1;
const maxValue = minValue + range
let previousX = Math.random() * range + minValue; // Initial random number

for (let y = startY; y <= endY; y = y+ yIncrement) {
let newX = previousX + (Math.random() - 0.5) * 2 * fluctuationRange;
newX = Math.max(minValue, Math.min(newX, maxValue)); // Ensure newX stays within bounds
tornPaperData.push({ x1: newX, y: y, x2: minValue });
previousX = newX; // Update previousX for the next iteration
}

return tornPaperData
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more