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

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