function Choropleth(data, {
id = d => d.id,
value = () => undefined,
title,
format,
scale = d3.scaleSequential,
domain,
range = d3.interpolateBlues,
width = 640,
height,
projection,
features,
featureId = d => d.id,
borders,
outline = projection && projection.rotate ? {type: "Sphere"} : null,
unknown = "#ccc",
fill = "white",
stroke = "white",
strokeLinecap = "round",
strokeLinejoin = "round",
strokeWidth,
strokeOpacity,
} = {}) {
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);
if (domain === undefined) domain = d3.extent(V);
const color = scale(domain, range);
if (color.unknown && unknown !== undefined) color.unknown(unknown);
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]);
}
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;
}
}
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}});
}