Public
Edited
Dec 3, 2023
3 forks
43 stars
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

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