Public
Edited
May 16
Insert cell
Insert cell
Insert cell
Inputs.table(aiddata)
Insert cell
margin = ({ top: 20, right: 20, bottom: 20, left: 20 })

Insert cell
aiddata = {
const data = await d3.csv(googleSheetCsvUrl, row => ({
yearDate: d3.timeParse('%Y')(row.year),
yearInt: +row.year,
donor: row.donor,
recipient: row.recipient,
amount: +row.commitment_amount_usd_constant,
purpose: row.coalesced_purpose_name,
}));
data.columns = Object.keys(data[0]);
return data;
}
Insert cell
world = FileAttachment("countries-50m.json").json()
Insert cell
countries = world.features // 241 country polygons

Insert cell
projection = d3.geoMercator()
.fitExtent(
[[margin.left, margin.top],
[width - margin.right,
height - margin.bottom]],
world // <- GeoJSON works directly
)

Insert cell

path = d3.geoPath(projection)
Insert cell
Insert cell
Insert cell
donationByCountry = d3.rollup(
aiddata,
v => d3.sum(v, d => d.amount),
d => d.donor_iso || d.donor // usa el ISO-3 si existe
)

Insert cell

receptionByCountry = d3.rollup(
aiddata,
v => d3.sum(v, d => d.amount),
d => d.recipient_iso || d.recipient
)

Insert cell

// net = lo que dona – lo que recibe
netBalanceByCountry = new Map(
[...new Set([...donationByCountry.keys(), ...receptionByCountry.keys()])]
.map(c => {
const donated = donationByCountry.get(c) ?? 0
const received = receptionByCountry.get(c) ?? 0
return [c, donated - received]
})
)
Insert cell
height = 800
Insert cell
// Function to get unique countries from both donor and recipient fields
function getUniqueCountries(data) {
// Create a Set to store unique country names
const uniqueCountries = new Set();
// Add all donor and recipient countries to the set
data.forEach(row => {
if (row.donor) uniqueCountries.add(row.donor);
if (row.recipient) uniqueCountries.add(row.recipient);
});
// Convert to array and sort alphabetically
return Array.from(uniqueCountries).sort();
}
Insert cell
maxAbs = d3.max(netBalanceByCountry.values(), v => Math.abs(v))

Insert cell

color = d3.scaleDiverging(d3.interpolateRdYlGn) // rojo ▸ amarillo ▸ verde
.domain([-maxAbs, 0, maxAbs])

Insert cell

alpha = d3.scaleLinear() // 0.2 – 1 p/ visibilidad
.domain([0, maxAbs])
.range([0.2, 1])
Insert cell
Insert cell
Insert cell
Insert cell
topojsonServer = require("topojson-server");
Insert cell
topology = topojsonServer.topology;
Insert cell
countries
Insert cell
topo = topology({
countries: { type: "FeatureCollection", features: countries }
});
Insert cell
typeByName = new Map(
countries.map(f => [
f.properties.ADMIN, // <- adjust if you use NAME
Math.sign(netBalanceByCountry.get(f.properties.ADMIN) ?? 0)
])
);
Insert cell
contrastMesh = topojson.mesh(
topo,
topo.objects.countries,
(a, b) => a !== b &&
typeByName.get(a.properties.ADMIN) !==
typeByName.get(b.properties.ADMIN)
);
Insert cell
Insert cell
Insert cell
catFill = ({
"1": "#1565c0",
"0": "#e0e0e0",
"-1": "#c62828"
});
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require('d3@7')
Insert cell
googleSheetCsvUrl = 'https://docs.google.com/spreadsheets/d/1YiuHdfZv_JZ-igOemKJMRaU8dkucfmHxOP6Od3FraW8/gviz/tq?tqx=out:csv'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more