Public
Edited
Dec 28, 2024
Paused
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
For debugging: ${position.coords.longitude}, ${position.coords.latitude}
Insert cell
viewof clear = Inputs.button("Clear History")
Insert cell
{
clear ? localStorage.clear() : null;
}
Insert cell
Plot.plot({
marks: [
Plot.ruleY([0]),
Plot.lineY(observations, {
filter: (d) => d.population_density > 0,
x: "time",
y: "population_density"
})
]
})
Insert cell
import { localStorage } from "@mbostock/safe-local-storage"
Insert cell
observations = (position ? JSON.parse(localStorage.getItem("data")) : []).map(
({ time, ...d }) => ({
time: new Date(time),
...d
})
)
Insert cell
{
const observation = {
time: new Date(),
population_density: population_density,
household_size: average_household_size
};
let data = localStorage.getItem("data");
if (data === null) {
data = [observation];
} else {
data = [...JSON.parse(data), observation];
}
localStorage.setItem("data", JSON.stringify(data));
}
Insert cell
Insert cell
median_household = {
const half = d3.sum(household_income.map((d) => d.value)) / 2;
let accumulator = 0;
for (const bracket of household_income) {
accumulator += bracket.value;
if (accumulator > half) {
return bracket;
}
}
}
Insert cell
median_age = {
const half = d3.sum(ages.map((d) => d.value)) / 2;
let accumulator = 0;
for (const bracket of ages) {
accumulator += bracket.value;
if (accumulator > half) {
return bracket;
}
}
}
Insert cell
Insert cell
ages.map((d) => ({ ...d, normal: d.value / (d.upper - d.lower) }))
Insert cell
Insert cell
average_household_size = d3.sum(
filtered_census_blockgroup.map(
([geoid, variables]) =>
variables.find((d) => d.variable === "B25010_001E").value *
bg_household_weights.get(geoid)
)
)
Insert cell
owner_occupied = d3.sum(
filtered_census_blockgroup.map(([geoid, variables]) => {
const owner_occupied = variables.find(
(d) => d.variable === "B25003_002E"
).value;
const renter_occupied = variables.find(
(d) => d.variable === "B25003_003E"
).value;
return (
(owner_occupied / (owner_occupied + renter_occupied)) *
bg_household_weights.get(geoid)
);
})
)
Insert cell
Insert cell
Insert cell
Plot.plot({
projection: { type: "mercator", domain: tracts },
marks: [Plot.geo(blockgroups), Plot.geo(current_buffer)]
})
Insert cell
bg_weights = {
const bg_pops = blockgroups.features.map((feature) => {
const intersection = turf.intersect(feature, current_buffer);
const intersection_area = intersection ? turf.area(intersection) : 0;
const block_group_pop_slice =
(intersection_area / turf.area(feature)) *
bg_census.find(
(d) =>
d.geoid === feature.properties.GEOID && d.variable == "B01001_001E"
).value;
return [feature.properties.GEOID, block_group_pop_slice];
});
const total = d3.sum(bg_pops.map(([geoid, pop]) => pop));
return new Map(bg_pops.map(([geoid, pop]) => [geoid, pop / total]));
}
Insert cell
bg_household_weights = {
const bg_pops = blockgroups.features.map((feature) => {
const intersection = turf.intersect(feature, current_buffer);
const intersection_area = intersection ? turf.area(intersection) : 0;
const block_group_pop_slice =
(intersection_area / turf.area(feature)) * feature.properties.HU100;
return [feature.properties.GEOID, block_group_pop_slice];
});
const total = d3.sum(bg_pops.map(([geoid, pop]) => pop));
return new Map(bg_pops.map(([geoid, pop]) => [geoid, pop / total]));
}
Insert cell
bg_area_weights = {
const buffer_area = turf.area(current_buffer);
return new Map(
blockgroups.features.map((feature) => {
const intersection = turf.intersect(feature, current_buffer);
const intersection_area = intersection ? turf.area(intersection) : 0;

return [feature.properties.GEOID, intersection_area / buffer_area];
})
);
}
Insert cell
Insert cell
Insert cell
tract_weights = {
const tract_pop = d3.rollup(
tract_census,
(v) => v.find((d) => d.variable === "B01001_001E").value,
(d) => d.state,
(d) => d.county,
(d) => d.tract
);
const bg_pop_weight = bg_census
.filter((d) => d.variable === "B01001_001E")
.map((d) => ({
...d,
weight:
(d.value / tract_pop.get(d.state).get(d.county).get(d.tract)) *
bg_weights.get(d.geoid)
}));
const z = d3.sum(bg_pop_weight, (d) => d.weight);
return new Map(
d3
.flatRollup(
bg_pop_weight,
(v) => d3.sum(v, (d) => d.weight) / z,
(d) => d.state,
(d) => d.county,
(d) => d.tract
)
.map(([state, county, tract, weight]) => [state + county + tract, weight])
);
}
Insert cell
bg_census = {
let data = [];
for (const tract of tracts.features) {
const results = await client.query(
[
"NAME",
"B25003_002E",
"B25003_003E",
"B01001_001E",
"B11001_001E",
"B25010_001E",
"B19013_001E",
...household_income_vars.keys(),
...age_vars.keys()
],
{
for: "block group:*",
in: `state:${tract.properties.STATE} county:${tract.properties.COUNTY} tract:${tract.properties.TRACT}`
},
census_vintage
);
data = [...data, ...results];
}
return data.map((d) => ({
...d,
geoid: d.state + d.county + d.tract + d["block group"]
}));
}
Insert cell
Insert cell
household_income_vars = new Map([
["B19001_002E", { lower: 0, upper: 10000 }],
["B19001_003E", { lower: 10000, upper: 15000 }],
["B19001_004E", { lower: 15000, upper: 20000 }],
["B19001_005E", { lower: 20000, upper: 25000 }],
["B19001_006E", { lower: 25000, upper: 30000 }],
["B19001_007E", { lower: 30000, upper: 35000 }],
["B19001_008E", { lower: 35000, upper: 40000 }],
["B19001_009E", { lower: 40000, upper: 45000 }],
["B19001_010E", { lower: 45000, upper: 50000 }],
["B19001_011E", { lower: 50000, upper: 60000 }],
["B19001_012E", { lower: 60000, upper: 75000 }],
["B19001_013E", { lower: 75000, upper: 100000 }],
["B19001_014E", { lower: 100000, upper: 125000 }],
["B19001_015E", { lower: 125000, upper: 150000 }],
["B19001_016E", { lower: 150000, upper: 200000 }],
["B19001_017E", { lower: 200000, upper: Infinity }]
])
Insert cell
age_vars = new Map([
["B01001_003E", { lower: 0, upper: 5, sex: "male" }],
["B01001_004E", { lower: 5, upper: 10, sex: "male" }],
["B01001_005E", { lower: 10, upper: 15, sex: "male" }],
["B01001_006E", { lower: 15, upper: 18, sex: "male" }],
["B01001_007E", { lower: 18, upper: 20, sex: "male" }],
["B01001_008E", { lower: 20, upper: 21, sex: "male" }],
["B01001_009E", { lower: 21, upper: 22, sex: "male" }],
["B01001_010E", { lower: 22, upper: 25, sex: "male" }],
["B01001_011E", { lower: 25, upper: 30, sex: "male" }],
["B01001_012E", { lower: 30, upper: 35, sex: "male" }],
["B01001_013E", { lower: 35, upper: 40, sex: "male" }],
["B01001_014E", { lower: 40, upper: 45, sex: "male" }],
["B01001_015E", { lower: 45, upper: 50, sex: "male" }],
["B01001_016E", { lower: 50, upper: 55, sex: "male" }],
["B01001_017E", { lower: 55, upper: 60, sex: "male" }],
["B01001_018E", { lower: 60, upper: 62, sex: "male" }],
["B01001_019E", { lower: 62, upper: 65, sex: "male" }],
["B01001_020E", { lower: 65, upper: 67, sex: "male" }],
["B01001_021E", { lower: 67, upper: 70, sex: "male" }],
["B01001_022E", { lower: 70, upper: 75, sex: "male" }],
["B01001_023E", { lower: 75, upper: 80, sex: "male" }],
["B01001_024E", { lower: 80, upper: 85, sex: "male" }],
["B01001_025E", { lower: 85, upper: 100, sex: "male" }],
["B01001_027E", { lower: 0, upper: 5, sex: "female" }],
["B01001_028E", { lower: 5, upper: 10, sex: "female" }],
["B01001_029E", { lower: 10, upper: 15, sex: "female" }],
["B01001_030E", { lower: 15, upper: 18, sex: "female" }],
["B01001_031E", { lower: 18, upper: 20, sex: "female" }],
["B01001_032E", { lower: 20, upper: 21, sex: "female" }],
["B01001_033E", { lower: 21, upper: 22, sex: "female" }],
["B01001_034E", { lower: 22, upper: 25, sex: "female" }],
["B01001_035E", { lower: 25, upper: 30, sex: "female" }],
["B01001_036E", { lower: 30, upper: 35, sex: "female" }],
["B01001_037E", { lower: 35, upper: 40, sex: "female" }],
["B01001_038E", { lower: 40, upper: 45, sex: "female" }],
["B01001_039E", { lower: 45, upper: 50, sex: "female" }],
["B01001_040E", { lower: 50, upper: 55, sex: "female" }],
["B01001_041E", { lower: 55, upper: 60, sex: "female" }],
["B01001_042E", { lower: 60, upper: 62, sex: "female" }],
["B01001_043E", { lower: 62, upper: 65, sex: "female" }],
["B01001_044E", { lower: 65, upper: 67, sex: "female" }],
["B01001_045E", { lower: 67, upper: 70, sex: "female" }],
["B01001_046E", { lower: 70, upper: 75, sex: "female" }],
["B01001_047E", { lower: 75, upper: 80, sex: "female" }],
["B01001_048E", { lower: 80, upper: 85, sex: "female" }],
["B01001_049E", { lower: 85, upper: 100, sex: "female" }]
])
Insert cell
Insert cell
{
if (
!turf.booleanContains(
dissolved_tract.features[0],
turf.buffer(current_point, 500, { units: "feet" })
)
) {
mutable new_tracts_required = true;
}
}
Insert cell
Insert cell
Insert cell
{
if (new_tracts_required) {
const params = new URLSearchParams({
where: "POP100 > 0",
geometry: `${position.coords.longitude},${position.coords.latitude}`,
geometryType: "esriGeometryPoint",
inSR: 4326,
spatialRel: "esriSpatialRelIntersects",
distance: 500,
units: "esriSRUnit_Foot",
outFields: "*",
returnGeometry: true,
outSR: 4326,
f: "geojson"
});
const to_proxy = `https://tigerweb.geo.census.gov/arcgis/rest/services/Census2020/tigerWMS_Census2020/MapServer/6/query?${params}`;
const result = await fetch(
`https://corsproxy.bunkum.us/corsproxy/?apiurl=${encodeURIComponent(
to_proxy
)}`
);
const data = await result.json();
mutable tracts = rewind(data);
mutable new_tracts_required = false;
}
}
Insert cell
mutable tracts = ({ type: "FeatureCollection", features: [] })
Insert cell
Insert cell
Insert cell
mutable use_geolocation = true
Insert cell
mutable can_use_geolocation = true
Insert cell
position = can_use_geolocation && use_geolocation
? geo_position
: debug_position
Insert cell
debug_position = ({
coords: {
latitude: Number(debug_latitude),
longitude: Number(debug_longitude)
}
})
Insert cell
function set(input, value) {
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
}
Insert cell
census_vintage = 2021
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