Public
Edited
Dec 3, 2023
3 forks
44 stars
Also listed in…
Research
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
caSP3TractsTopo = FileAttachment("acs-2020-ca-sp3-tracts.json").json()
Insert cell
caSP3Tracts = topojson.feature(
caSP3TractsTopo,
caSP3TractsTopo.objects["acs-2020-ca-sp3-tracts"]
)
Insert cell
Insert cell
Insert cell
Insert cell
d3Choropleth = {
// Define a height for the map. We'll use Observable's derived width.
const height = 610;

// Define projection. We use CA State Plane Zone 3 (EPSG:26943) as our projection.
// We source these particular values from Noah Veltman's d3-stateplane:
// https://github.com/veltman/d3-stateplane
const projection = d3
.geoConicConformal()
.parallels([37 + 4 / 60, 38 + 26 / 60])
.rotate([120 + 30 / 60, 0])
.fitSize([width, height], caSP3Tracts);

// Create the SVG container.
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "width: 100%; height: auto; height: intrinsic;");

// Create the color scale.
// We'll set the breaks manually rather than rely on quantile or quantized scales.
const domain = [0, 1, 5, 10, 20];
const range = d3.schemePurples[5];
const color = d3.scaleThreshold(domain, range);

// Render Census tracts.
svg
.append("g")
.selectAll("path")
.data(caSP3Tracts.features)
.join("path")
.attr("d", d3.geoPath(projection))
.attr("fill", (d) => {
// Compute the percentage of the population that is AIAN, feed this value
// into our color scales, and use the output as the fill of our polygons.
const { B02001004: nativePop, B02001001: totalPop } = d.properties;

return color((nativePop / totalPop) * 100) || "#fff";
})
.attr("stroke", "#fff")
.attr("stroke-width", "0.5");

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
leaflet = {
// Create the root element into which we'll render the map.
// We yield this early to ensure the div is sized by the time
// Leaflet accesses its offsetWidth and offsetHeight.
//
// This neat trick is courtesy Tom MacWright: https://observablehq.com/@tmcw/leaflet
const root = DOM.element("div", {
style: `width:${width}px; height:${width / 1.6}px`
});

yield root;

// Establish the map's center view and zoom level.
const map = L.map(root).setView([37.88536, -120.56827], 8);

// Add tiles from the CARTO tile server as a base layer.
const tiles = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png",
{
attribution:
'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}
).addTo(map);

// Define color scale. To keep it consistent, we'll use the values from d3.schemePurples
// but encode them manually since they are not a part of Leaflet's APIs.
const color = (value) => {
if (value > 20) {
return "#f2f0f7";
} else if (value > 10) {
return "#54278f";
} else if (value > 5) {
return "#756bb1";
} else if (value > 1) {
return "#9e9ac8";
} else if (value >= 0) {
return "#cbc9e2";
}
};

// Define a style function to color each polygon in the dataset according to the color scale.
const style = (feature) => {
const { B02001004: nativePop, B02001001: totalPop } = feature.properties;

const value = (nativePop / totalPop) * 100;

return {
fillColor: color(value) || "transparent",
weight: 0.5,
fillOpacity: 0.8,
color: totalPop === 0 ? "transparent" : "#fff"
};
};

// Add the GeoJSON layer to the map, applying the style function to each object.
L.geoJson(caSP3Tracts, { style }).addTo(map);
}
Insert cell
Insert cell
Insert cell
Insert cell
mapbox = {
const root = DOM.element("div", {
style: `width:${width}px; height:${width / 1.6}px`
});

yield root;

// Establish the map's center view and zoom level.
const map = new mapboxgl.Map({
container: root,
style: "mapbox://styles/mapbox/light-v10",
center: [-120.56827, 37.88536],
zoom: 7,
// Mapbox does have support for custom projections!
// We'll use California State Plane Zone 3, which is a Lambert Conic Conformal projection.
projection: {
name: "lambertConformalConic",
center: [-120, 37],
parallels: [37 + 4 / 60, 38 + 26 / 60]
}
});

map.on("load", () => {
map.addSource("acs-2020-ca-sp3-tracts", {
type: "geojson",
data: caSP3Tracts
});

// Add the polygon layer first.
map.addLayer({
id: "acs-2020-ca-sp3-tracts",
type: "fill",
source: "acs-2020-ca-sp3-tracts",
layout: {},
paint: {
// Assign the fill using expressions from Mapbox's Style Specification.
"fill-color": [
"let",
"value",
["*", ["/", ["get", "B02001004"], ["get", "B02001001"]], 100],
[
"case",
[">", ["var", "value"], 20],
"#f2f0f7",
[">", ["var", "value"], 10],
"#54278f",
[">", ["var", "value"], 5],
"#756bb1",
[">", ["var", "value"], 1],
"#9e9ac8",
[">=", ["var", "value"], 0],
"#cbc9e2",
"transparent"
]
],
"fill-opacity": 0.8
}
});

// Create the outline on polygons using a separate line layer.
// See: https://github.com/mapbox/mapbox-gl-js/issues/3018
map.addLayer({
id: "acs-2020-ca-sp3-tracts-outline",
type: "line",
source: "acs-2020-ca-sp3-tracts",
layout: {},
paint: {
"line-color": [
"let",
"totalPop",
["get", "B02001001"],
["match", ["var", "totalPop"], 0, "transparent", "#fff"]
],
"line-width": 0.5
}
});
});
}
Insert cell
mapboxgl = {
const gl = await require("mapbox-gl@2.10");
if (!gl.accessToken) {
gl.accessToken =
"pk.eyJ1IjoidG1jdyIsImEiOiJja3FmbGJoNXMxNmx5Mm9uejIxcmpiNjh2In0.2F8HR-8J859J7frYE6DG9g";
const href = await require.resolve("mapbox-gl@2.10/dist/mapbox-gl.css");
document.head.appendChild(html`<link href=${href} rel=stylesheet>`);
}
return gl;
}
Insert cell
Insert cell
Insert cell
Insert cell
deckGlChoropleth = {
const root = DOM.element("div", {
style: `width:${width}px; height:${width / 1.6}px`
});

yield root;
const map = new mapboxgl.Map({
container: root,
style: "mapbox://styles/mapbox/light-v10",
center: [-120.56827, 37.88536],
zoom: 7
});

// Define color scale. To keep it consistent, we'll use the values from d3.schemePurples
// but encode them manually since they are not a part of deck.gl's APIs. We have to
// encode them as rgba arrays to fit the deck.gl API.
const color = (value) => {
if (value > 20) {
return [242, 240, 247, 204];
} else if (value > 10) {
return [84, 39, 143, 204];
} else if (value > 5) {
return [117, 107, 177, 204];
} else if (value > 1) {
return [158, 154, 200, 204];
} else if (value >= 0) {
return [203, 201, 226, 204];
}
};

// Use deck's MapboxOverlay API to synchrnoize the GeoJson layer with the Mapbox basemap.
const choropleth = new deck.MapboxOverlay({
layers: [
new deck.GeoJsonLayer({
id: "acs-2020-ca-sp3-tracts",
data: caSP3Tracts,
getFillColor: (d) => {
const { B02001004: nativePop, B02001001: totalPop } = d.properties;

const value = (nativePop / totalPop) * 100;
return color(value) || [255, 255, 255, 0];
},
lineWidthUnits: "pixels",
getLineWidth: 0.5,
getLineColor: (d) => {
const { B02001001: totalPop } = d.properties;
return totalPop === 0 ? [255, 255, 255, 0] : [255, 255, 255, 255];
}
})
]
});

map.addControl(choropleth);
}
Insert cell
deck = require.alias({
h3: {}
})("deck.gl@latest/dist.min.js")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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