Public
Edited
Nov 13
Paused
1 fork
Importers
1 star
Insert cell
Insert cell
ChoroplethMap(BayAreageoJSON, {
// if no value is passsed, it will look for the first quant property, area in this case
tooltip: (d) => d.properties["zip"]
// if no id is passed, it will assume that the features contain the properties
})
Insert cell
BayAreageoJSON
Insert cell
Insert cell
Insert cell
Insert cell
ChoroplethMap(loadedGeoOrTopoJSON, {
value: (d) => d.properties[property],
interpolator: d3.interpolatePurples,
scale: d3.scaleSequentialLog,
colorLegendOptions: { title: property, tickFormat: "s" },
height: 600,
width: 600
})
Insert cell
Insert cell
us2020Election = ChoroplethMap(
nytPresCounties, // data to plot
{
features: fipsGeoJSON.features,
projection: d3.geoAlbersUsa,
id: (d) => d.fips,
idFeatures: (d) => d.id,
value: (d) => d.margin2020,
interpolator: (t) => d3.interpolateRdBu(1 - t),
height: 500,
width: width,
colorLegendOptions: { title: "Margin 2020", format: "%" }
}
)
Insert cell
Insert cell
async function ChoroplethMap(
geoJSON, // also supports topoJSON
{
width = 800,
height = 600,
scale = d3.scaleSequential, // one of the d3.scaleSequential*
interpolator = d3.interpolateBlues,
id = null, // ids of the data array
idFeatures = null, // ids for the features, if null will use id
value = null,
projection = d3.geoEquirectangular,
stroke = "black",
strokeWidth = 0.5,
colorLegendOptions = { title: null },
fill = null, // leave on null to use the value of the node (d) => fillScale(value(d))
features = geoJSON.features,
tooltip = () => "",
topoJSONObject = geoJSON.objects ? Object.keys(geoJSON.objects)[0] : null // if topoJSON what object to use
} = {}
) {
if (geoJSON.type === "Topology") {
// seems like the data is a topoJSON let's try to convert it
geoJSON = topojson.feature(geoJSON, geoJSON.objects[topoJSONObject]);
features = geoJSON.features;
}

let data = Array.isArray(geoJSON) ? geoJSON : geoJSON.features;

let f = scale(interpolator); // Holder for our fillScale with domain

if (!value) {
const quantProperty = findAttribute(
Array.isArray(geoJSON) ? data[0] : data[0].properties,
{
numeric: true
}
);
if (quantProperty) {
value = (d) => d.properties[quantProperty];
colorLegendOptions.title = quantProperty;
} else {
// We couldn't find a suitable attribute
fill = "none";
}
}
if (value) {
if (id) {
// match objects by ID
const valuesArray = data.map((d) => [id(d), +value(d)]);
f = scale(interpolator).domain(d3.extent(valuesArray.map((d) => d[1])));
let mapValues = new Map(valuesArray);

if (!idFeatures) idFeatures = id;
if (!fill) {
fill = (d) => f(mapValues.get(idFeatures(d)));
}
} else {
// assume the properties come with the object
f = scale(interpolator).domain(d3.extent(features, value));

if (!fill && value) {
fill = (d) => f(value(d));
}
}
}

const path = d3.geoPath().projection(
projection().fitSize([width, height], {
type: "FeatureCollection",
features
})
);

// SVG Version
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", width);
svg
.selectAll("path")
.data(features)
.join("path")
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("fill", fill)
.attr("d", path)
.attr("tooltip", tooltip)
.call((path) => path.join("title").append("title").text(tooltip));

// Make the map zoomable
const zoom = d3.zoom().on("zoom", (event) => {
svg.selectAll("path").attr("transform", event.transform);
});
svg.call(zoom);

const legend = await Legend(f, colorLegendOptions);

return html`<div>
${svg.node()}
${legend}
</div>`;
}
Insert cell
// Returns the first numeric/non-numeric attribute in the obj
function findAttribute(obj, { numeric = true } = {}) {
const attribs = Object.keys(obj);

for (let attr of attribs) {
if (numeric && !isNaN(+obj[attr])) return attr;
if (!numeric && isNaN(+obj[attr])) return attr;
}
return null;
}
Insert cell
fipsGeoJSON = FileAttachment("geojson-counties-fips.json").json()
Insert cell
BayAreageoJSON = FileAttachment("BayAreaZIPCodes_simplified.json").json()
Insert cell
ColombiaTopoJSON = FileAttachment("Colombia_departamentos_municipios_poblacion-topov2_simplifiedmapshaer_simpler.json").json()
Insert cell
import {nytPresCounties} from "@codingwithfire/live-election-data"
Insert cell
import {Legend} from "@d3/color-legend";
Insert cell
import {dataInput} from "@john-guerra/data-input";
Insert cell
topojson = require("topojson-server@3", "topojson-simplify@3", "topojson-client@3")
Insert cell
d3 = require("d3@7", "d3-geo-projection@4", "d3-geo-polygon@1", "d3-geo-voronoi@2")
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