Public
Edited
Sep 23
Paused
217 forks
Importers
160 stars
Insert cell
Insert cell
Insert cell
chart = Choropleth(unemployment, {
id: d => d.id,
value: d => d.rate,
scale: d3.scaleQuantize,
domain: [1, 10],
range: d3.schemeBlues[9],
title: (f, d) => `${f.properties.name}, ${statemap.get(f.id.slice(0, 2)).properties.name}\n${d?.rate}%`,
features: counties,
borders: statemesh,
width: 975,
height: 610
})
Insert cell
Insert cell
unemployment = (await FileAttachment("unemployment-x.csv").csv()).map(d => ({...d, rate: +d.rate}))
Insert cell
Insert cell
us = FileAttachment("counties-albers-10m.json").json()
Insert cell
counties = topojson.feature(us, us.objects.counties)
Insert cell
states = topojson.feature(us, us.objects.states)
Insert cell
statemap = new Map(states.features.map(d => [d.id, d]))
Insert cell
Insert cell
statemesh = topojson.mesh(us, us.objects.states, (a, b) => a !== b)
Insert cell
Insert cell
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
} = {}) {
// 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 (color.unknown && 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", "width: 100%; height: auto; height: intrinsic;");

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

svg.append("g")
.selectAll("path")
.data(features.features)
.join("path")
.attr("fill", (d, i) => color(V[Im.get(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 {howto, linkplot} from "@d3/example-components"
Insert cell
import {Legend} from "@d3/color-legend"
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