Published
Edited
Dec 28, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
excessDeathsLatestDay.view(220)
Insert cell
// + (d.name.length % 3 === 0 ? offsetPendulum * 10 : 0)
Insert cell
generatePatternArgs = (id, size, angle) => ({
id: id,
patternUnits: "userSpaceOnUse",
width: size,
height: size,
patternTransform: `rotate(${angle})`
})
Insert cell
generateLine1Args = (size, color, strokeWidth) => ({
x1: `${strokeWidth/2}`,
x2: `${strokeWidth/2}`,
y1: "0",
y2: `${size}`,
stroke: color,
"stroke-width": `${strokeWidth}`
})
Insert cell
generateLine2Args = (size, color, strokeWidth) => ({
x1: `${strokeWidth/2+size/2}`,
x2: `${strokeWidth/2+size/2}`,
y1: "0",
y2: `${size}`,
stroke: color,
"stroke-width": `${size - strokeWidth}`
})
Insert cell
test = generatePattern("TEST", "#ffaaaa", "#aaffaa", patternSize, patternStrokeWidth, patternAngle)
Insert cell
patternMap = excessDeathsLatestDay
.objects()
.map((row) => [
row.numericCode,
{
patternArgs: generatePatternArgs(
`pattern-${row.numericCode}`,
patternSize,
patternAngle
),
line1Args: generateLine1Args(
patternSize,
colorScale(row.excessDeathsLower),
patternStrokeWidth
),
line2Args: generateLine2Args(
patternSize,
colorScale(row.excessDeathsUpper),
patternSize - patternStrokeWidth
)
}
])
Insert cell
Insert cell
Insert cell
colorScale = d3.scaleSequential([minValue, maxValue], d3.interpolateYlOrRd)
Insert cell
excessDeaths = (
await FileAttachment("excess-deaths-cumulative-per-100k-economist.csv").csv()
).map((d) => ({
name: d.Entity,
code: d.Code,
numericCode: isoCodeTranslationMap.get(d.Code, 0),
day: d.Day,
excessDeathsCentral: Number.parseFloat(
d.cumulative_estimated_daily_excess_deaths_per_100k
),
confirmedDeaths: Number.parseInt(d["Total confirmed deaths due to COVID-19"]),
excessDeathsUpper: Number.parseFloat(
d.cumulative_estimated_daily_excess_deaths_ci_95_top_per_100k
),
excessDeathsLower: Number.parseFloat(
d.cumulative_estimated_daily_excess_deaths_ci_95_bot_per_100k
)
}))
Insert cell
excessDeathsTable = aq.from(excessDeaths)
Insert cell
latestDayInData = "2021-12-26"
Insert cell
excessDeathsLatestDay = excessDeathsTable.params({latestDay: latestDayInData}).filter((row, p) => row.day === p.latestDay && row.numericCode !== undefined).derive({diffOfUpperAndLower: d => d.excessDeathsUpper - d.excessDeathsLower}).orderby(aq.desc("diffOfUpperAndLower"))
Insert cell
excessDeathsLatestDay.array("excessDeathsUpper")
Insert cell
maxValue = aq.agg(excessDeathsLatestDay, op.max("excessDeathsUpper"))
Insert cell
minValue = aq.agg(excessDeathsLatestDay, op.min("excessDeathsLower"))
Insert cell
generatePattern = (id, color1, color2, size, strokeWidth, angle) => {
return `<pattern id="${id}" patternUnits="userSpaceOnUse" width="${size}" height="${size}" patternTransform="rotate(${angle})">
<line x1="${strokeWidth/2}" y1="0" x2="${strokeWidth/2}" y2="${size}" stroke="${color1}" stroke-width="${strokeWidth}" />
<line x1="${strokeWidth/2+size/2}" y1="0" x2="${strokeWidth/2+size/2}" y2="${size}" stroke="${color2}" stroke-width="${
size - strokeWidth - 1
}" />
</pattern>`;
}
Insert cell
html`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<defs>
${test}
</defs>
<rect width="100%" height="100%" fill="url(#TEST)" :opacity="1" />
</svg>`
Insert cell
Insert cell
worldRaw = FileAttachment("countries-50m.json").json()
Insert cell
world = ({...worldRaw, objects: {...worldRaw.objects, countries: {...worldRaw.objects.countries, geometries: worldRaw.objects.countries.geometries.map(row => ({...row, id: Number.parseInt(row.id)}))}}})
Insert cell
countries = topojson.feature(world, world.objects.countries)
Insert cell
Insert cell
countrymesh = topojson.mesh(world, world.objects.countries, (a, b) => a !== b)
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/choropleth
function Choropleth(
data,
{
id = (d) => d.id, // given d in data, returns the feature id
value = () => undefined, // given d in data, returns the quantitative value
title, // given a feature f and possibly a datum d, returns the hover text
format, // optional format specifier for the title
scale = d3.scaleSequential, // type of color scale
domain, // [min, max] values; input of color scale
range = d3.interpolateBlues, // output of color scale
width = 640, // outer width, in pixels
height, // outer height, in pixels
projection, // a D3 projection; null for pre-projected geometry
features, // a GeoJSON feature collection
featureId = (d) => d.id, // given a feature, returns its id
borders, // a GeoJSON object for stroking borders
outline = projection && projection.rotate ? { type: "Sphere" } : null, // a GeoJSON object for the background
unknown = "#ccc", // fill color for missing data
fill = "white", // fill color for outline
stroke = "white", // stroke color for borders
strokeLinecap = "round", // stroke line cap for borders
strokeLinejoin = "round", // stroke line join for borders
strokeWidth, // stroke width for borders
strokeOpacity, // stroke opacity for borders
patternMap // Map of country id => svg pattern
} = {}
) {
// Compute values.
const N = d3.map(data, id);
const V = d3.map(data, value).map((d) => (d == null ? NaN : +d));
const Im = new d3.InternMap(N.map((id, i) => [id, i]));
const If = d3.map(features.features, featureId);

// Compute default domains.
if (domain === undefined) domain = d3.extent(V);

// Construct scales.
const color = scale(domain, range);
if (unknown !== undefined) color.unknown(unknown);

// Compute titles.
if (title === undefined) {
format = color.tickFormat(100, format);
title = (f, i) => `${f.properties.name}\n${format(V[i])}`;
} else if (title !== null) {
const T = title;
const O = d3.map(data, (d) => d);
title = (f, i) => T(f, O[i]);
}

// Compute the default height. If an outline object is specified, scale the projection to fit
// the width, and then compute the corresponding height.
if (height === undefined) {
if (outline === undefined) {
height = 400;
} else {
const [[x0, y0], [x1, y1]] = d3
.geoPath(projection.fitWidth(width, outline))
.bounds(outline);
const dy = Math.ceil(y1 - y0),
l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale((projection.scale() * (l - 1)) / l).precision(0.2);
height = dy;
}
}

// Construct a path generator.
const path = d3.geoPath(projection);

const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");

if (outline != null)
svg
.append("path")
.attr("fill", fill)
.attr("stroke", "currentColor")
.attr("d", path(outline));

if (patternMap !== undefined) {
const pattern = svg
.append("defs")
.selectAll("pattern")
.data(patternMap.values())
.join("pattern")
.attrs((d, i) => d[1].patternArgs);
const line1 = pattern.append("line").attrs((d, i) => d[1].line1Args);
const line2 = pattern.append("line").attrs((d, i) => d[1].line2Args);
}

svg
.append("g")
.selectAll("path")
.data(features.features)
.join("path")
.attr("fill", (d, i) => `url(#pattern-${If[i]})`)
.attr("d", path)
.append("title")
.text((d, i) => title(d, Im.get(If[i])));

if (borders != null)
svg
.append("path")
.attr("pointer-events", "none")
.attr("fill", "none")
.attr("stroke", stroke)
.attr("stroke-linecap", strokeLinecap)
.attr("stroke-linejoin", strokeLinejoin)
.attr("stroke-width", strokeWidth)
.attr("stroke-opacity", strokeOpacity)
.attr("d", path(borders));

return Object.assign(svg.node(), { scales: { color } });
}
Insert cell
import {Legend} from "@d3/color-legend"
Insert cell
import { aq, op } from '@uwdata/arquero'
Insert cell
d3 = require.alias({
'd3-selection': 'd3',
'd3-transition': 'd3'
})('d3', 'd3-selection-multi')
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