Public
Edited
Feb 5, 2023
Insert cell
Insert cell
treeMapFn = d3.treemapSliceDice
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chartCircles = {
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height)

const innerData = aidData;
const all_types = ["Europe", "Asia", "North America", "Oceania"]

innerData.forEach(d => d.r = Math.sqrt(d['Total commitments']*400) )
const margin={left: 50, right: 50, top: 100, bottom: 20};
const yScale = d3.scaleLinear().domain([0, 4]).range([margin.top, height-margin.bottom])
// const xAxis = g => g
// .attr("transform", `translate(0,${height - margin.bottom})`)
// .call(d3.axisBottom(xScale))

const xScale = d3.scaleLinear().domain([0,1.2]).range([ 2*margin.left+20, width - 2*margin.right ])
const simulation = d3.forceSimulation( innerData )
.force('x', d3.forceX().strength(0.4).x(d=> xScale(+d.total_pct) ) )
// .force('x', d3.forceX().strength(0.1).x(d => width/2))
.force('y', d3.forceY().strength(0.4).y(d => all_types.indexOf(d.continent)>=0? yScale(all_types.indexOf(d.continent)): -width) )
.force('collide', d3.forceCollide().radius(d => d.r + 0.5 ).strength(1) )

const arcFn = d => d3.arc()
.innerRadius(0)
.outerRadius(Math.sqrt(d['Total commitments']*100))


var pieFn = d3.pie()
.value(function(d) {return d.value; })
// .sort(function(a, b) { return d3.ascending(a.key, b.key);} )
// var data_ready = pieFn(d3.entries(data))

const g = svg.selectAll('g.node')
.data( simulation.nodes() )
.join('g')
.attr('class', 'node')
// .call( g => g
// .append('circle')
// .attr('r', d => d.r)
// .style('fill', d => '#e6e7e8' )
// .style('opacity', 0.2)
// )
.call( g => g.selectAll('.arc')
.data(d=>{
const arr = [{'key':'Military commitments', 'value':+d["Military commitments"], 'total':d['Total commitments']},
{'key':'Humanitarian commitments', 'value':+d["Humanitarian commitments"],'total':d['Total commitments']},
{'key':'Financial commitments', 'value':+d["Financial commitments"], 'total':d['Total commitments']}]
const pieData = pieFn(arr)
// console.log('pieData:', pieData)
return pieData
})
.join('path')
.attr('class', 'arc')
.attr('d', d=> d3.arc()
.innerRadius(0)
.outerRadius(Math.sqrt(d.data.total*400))
.startAngle(d.startAngle)
.endAngle(d.endAngle)()
)
.attr('fill', (d, i) => i==0? '#F2bC8D': (i==1? '#A7CE95': '#e2e2e2'))
.attr('stroke', 'white')
.attr('opacity', 0.8)
)
.call( g => g
.append('title')
.text(d => `${d.country}\n${'$'+d.total_pct}`)
)
.call( g => g
.append('text')
// .attr('text-anchor', 'middle')
.attr('font-size', '10px')
.attr('font-family', 'arial')
.attr('dy', 10)
.text(d => (d.total_pct>=200? `${'$'+ d[" Total commitments"]}B`: ``) )
)
.call( g => g
.append('text')
// .attr('text-anchor', 'left')
.attr('font-size', '10px')
.attr('font-family', 'arial')
.text(d => (d.total_pct>=200? `${d.continent}`: ``) )
)
const lineG = svg.selectAll('.note')
.data(all_types)
.join('text')
.attr('class', 'note')
.attr('x', margin.left)
.attr('y', (d,i)=> yScale(i))
.text(d=> d)
.attr('text-anchor', 'middle')
.attr('font-size', '16px')
.attr('font-family', 'arial')
simulation.on("tick", () => g.attr('transform', d => `translate(${ d.x },${ d.y })`) )

return svg.node()
}
Insert cell
// map = {
// const container = document.createElement("div")
// container.setAttribute("id", "map-container")
// container.style.position = "relative"
// container.appendChild(worldMap.node())
// let coordinates = projection([-3.703790, 40.416775]) // [lng, lat]
// // add cities
// const cities = d3.select(container).append("div")
// .style("position", "absolute")
// .style("line-height", "0")
// .style("top", (d) => `${coordinates[1]}px`)
// .style("left", (d) => `${coordinates[0] - 1}px`)
// .style("font-family", "Helvetica, Arial, sans-serif")
// .style("text-shadow", "0 1px 2px white")
// .html('· Madrid')

// return container
// }
Insert cell
Insert cell
worldMap = {
const svg = d3.select(DOM.svg(width, height))
// world map
svg.append("g").selectAll("path")
.data(topojson.feature(world, world.objects.countries).features)
.enter()
.append("path")
.attr("d", path)
.style('fill', colorFn)
.style('fill-opacity', 0.8)
.style('stroke', 'white')
.style('stroke-width', '0.5px')

return svg
}
Insert cell
function colorFn(d){
console.log( d.id )
return 'steelblue'
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
groupByContinent = d3.group(aidData, t=> t.continent)
Insert cell
groupByContinent.get('Europe')
Insert cell
groupByContinent.get("Europe")
Insert cell
aidData = sheet_data_fn("https://docs.google.com/spreadsheets/d/e/2PACX-1vQkB_wvvMByfZIoJR1Szfyf0d0y444kL3Njq3-yVT_Qt1onkj4o4SpIr_4zIIC6mE5g5kP_3le_BzST/pub?gid=1496032341&single=true&output=tsv")
Insert cell
Insert cell
function Treemap(data, {
path,
id = d => d.STORE,
parentId = Array.isArray(data) ? d => d.parentId : null,
children,
value,
sort = (a, b) => d3.descending(a.value, b.value),
label,
group,
title,
link,
linkTarget = "_blank",
tile = d3.treemapBinary,
width = 640,
height = 400,
margin = 0,
marginTop = margin,
marginRight = margin,
marginBottom = margin,
marginLeft = margin,
padding = 1,
paddingInner = padding,
paddingOuter = padding,
paddingTop = paddingOuter,
paddingRight = paddingOuter,
paddingBottom = paddingOuter,
paddingLeft = paddingOuter,
round = true,
colors = d3.schemeTableau10,
zDomain,
fill = "#ccc",
fillOpacity = group == null ? null : 0.6,
stroke,
strokeWidth,
strokeOpacity,
strokeLinejoin,
} = {}) {

const root = path != null ? d3.stratify().path(path)(data)
: id != null || parentId != null ? d3.stratify().id(id).parentId(parentId)(data)
: d3.hierarchy(data, children);

// Compute the values of internal nodes by aggregating from the leaves.
value == null ? root.count() : root.sum(d => Math.max(0, value(d)));

// Prior to sorting, if a group channel is specified, construct an ordinal color scale.
const leaves = root.leaves();
const G = group == null ? null : leaves.map(d => group(d.data, d));
if (zDomain === undefined) zDomain = G;
zDomain = new d3.InternSet(zDomain);
const color = group == null ? null : d3.scaleOrdinal(zDomain, colors);

// Compute labels and titles.
const L = label == null ? null : leaves.map(d => label(d.data, d));
const T = title === undefined ? L : title == null ? null : leaves.map(d => title(d.data, d));

// Sort the leaves (typically by descending value for a pleasing layout).
if (sort != null) root.sort(sort);

// Compute the treemap layout.
d3.treemap()
.tile(tile)
.size([width - marginLeft - marginRight, height - marginTop - marginBottom])
.paddingInner(paddingInner)
.paddingTop(paddingTop)
.paddingRight(paddingRight)
.paddingBottom(paddingBottom)
.paddingLeft(paddingLeft)
.round(round)
(root);

const svg = d3.create("svg")
.attr("viewBox", [-marginLeft, -marginTop, width, height])
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: auto; height: intrinsic;")
.attr("font-family", "sans-serif")
.attr("font-size", 10);

const node = svg.selectAll("g")
.data(leaves)
.join("g")
// .attr("xlink:href", link == null ? null : (d, i) => link(d.data, d))
// .attr("target", link == null ? null : linkTarget)
.attr("transform", d => `translate(${d.x0},${d.y0})`);

node
// .selectAll('rect')
// .data(d=> [d], id)
// .join('rect')
.append("rect")
.attr("fill", color ? (d, i) => color(G[i]) : fill)
.attr("fill-opacity", fillOpacity)
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("stroke-opacity", strokeOpacity)
.attr("stroke-linejoin", strokeLinejoin)
.transition(500)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);

if (T) {
node.append("title").text((d, i) => T[i]);
}

if (L) {
// A unique identifier for clip paths (to avoid conflicts).
const uid = `O-${Math.random().toString(16).slice(2)}`;

node.append("clipPath")
.attr("id", (d, i) => `${uid}-clip-${i}`)
.append("rect")
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);

node.append("text")
.attr("clip-path", (d, i) => `url(${new URL(`#${uid}-clip-${i}`, location)})`)
.selectAll("tspan")
.data((d, i) => `${L[i]}`.split(/\n/g))
.join("tspan")
.attr("x", 3)
.attr("y", (d, i, D) => `${(i === D.length - 1) * 0.3 + 1.1 + i * 0.9}em`)
.attr("fill-opacity", (d, i, D) => i === D.length - 1 ? 0.7 : null)
.text(d => d);
}

return Object.assign(svg.node(), {scales: {color}});
}
Insert cell
function swatches({
color,
columns = null,
format = x => x,
swatchSize = 15,
swatchWidth = swatchSize,
swatchHeight = swatchSize,
marginLeft = 0
}) {
const id = DOM.uid().id;

if (columns !== null) return html`<div style="display: flex; align-items: center; margin-left: ${+marginLeft}px; min-height: 33px; font: 10px sans-serif;">
<style>

.${id}-item {
break-inside: avoid;
display: flex;
align-items: center;
padding-bottom: 1px;
}

.${id}-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: calc(100% - ${+swatchWidth}px - 0.5em);
}

.${id}-swatch {
width: ${+swatchWidth}px;
height: ${+swatchHeight}px;
margin: 0 0.5em 0 0;
}

</style>
<div style="width: 100%; columns: ${columns};">${color.domain().map(value => {
const label = format(value);
return html`<div class="${id}-item">
<div class="${id}-swatch" style="background:${color(value)};"></div>
<div class="${id}-label" title="${label.replace(/["&]/g, entity)}">${document.createTextNode(label)}</div>
</div>`;
})}
</div>
</div>`;

return html`<div style="display: flex; align-items: center; min-height: 33px; margin-left: ${+marginLeft}px; font: 10px sans-serif;">
<style>

.${id} {
display: inline-flex;
align-items: center;
margin-right: 1em;
}

.${id}::before {
content: "";
width: ${+swatchWidth}px;
height: ${+swatchHeight}px;
margin-right: 0.5em;
background: var(--color);
}

</style>
<div>${color.domain().map(value => html`<span class="${id}" style="--color: ${color(value)}">${document.createTextNode(format(value))}</span>`)}</div>`;
}
Insert cell
function entity(character) {
return `&#${character.charCodeAt(0).toString()};`;
}
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