formatDate = d3.utcFormat("%b %d, %Y")
us = await d3.json("assets/us-census-counties.topojson")
counties = topojson.feature(us, us.objects.counties)
states = topojson.mesh(us, us.objects.states, (a, b) => a !== b)
data = FileAttachment("assets/fsa-normal-grazing-period-simple.csv").csv({typed: true})
romaO = FileAttachment("assets/colors.json").json()
color = d3.scaleSequential()
.domain([1, 366])
.interpolator(t => romaO[Math.floor(t * (romaO.length - 1))])filtered = data.filter(d => d.year === year && d.type === type)
countiesFiltered = {
const rowById = Object.fromEntries(
filtered.map(d => [String(d.id).padStart(5, "0"), d])
);
return {
type: "FeatureCollection",
features: counties.features.map(f => {
const row = rowById[String(f.id)];
return {
...f,
properties: {
id: f.id,
...f.properties,
...(row ?? {}),
value: row ? row[variable] : null
}
};
})
};
}// Reactive card dimensions — uses #map (created by Quarto from label: map) to find the card body
cardSize = Generators.observe(notify => {
let ro;
const check = setInterval(() => {
const cardBody = document.querySelector('#map')?.closest('.card-body');
if (cardBody) {
clearInterval(check);
ro = new ResizeObserver(([entry]) => {
const { width, height } = entry.contentRect;
if (width > 0 && height > 0) notify({ width, height });
});
ro.observe(cardBody);
}
}, 50);
return () => {
clearInterval(check);
if (ro) ro.disconnect();
};
})viewof year = Inputs.range(
d3.extent(data, d => d.year),
{
step: 1,
label: "Year",
value: 2026
}
)
viewof type = Inputs.select(
[...new Set(data.map(d => d.type))].sort(),
{
label: "Forage Type",
value: "Native Pasture"
}
)
viewof variable = Inputs.radio(
new Map([
["Season Start", "start_yday"],
["Season End", "end_yday"]
]),
{
label: "Color by",
value: "start_yday"
}
)Plot.plot({
width: cardSize.width,
height: cardSize.height,
projection: {
type: "albers",
domain: counties
},
grid: true,
color: {legend: false},
marks: [
Plot.geo(countiesFiltered, {
fill: d => d.properties.value != null ? color(d.properties.value) : "#ccc"
}),
Plot.dot(
countiesFiltered.features.map(f => {
const [x, y] = d3.geoCentroid(f);
return {
...f.properties,
x,
y
};
}),
{
x: "x",
y: "y",
r: 3,
fill: "transparent",
stroke: "none",
tip: true,
title: d => `${d.county} County, ${d.state}
FIPS: ${String(d.id).padStart(5, "0")}
Crop Type: ${type}
Start: ${d.start_date ? formatDate(d.start_date) : "None"}
End: ${d.end_date ? formatDate(d.end_date) : "None"}`
}
),
Plot.geo(states, {stroke: "white"}),
Plot.image([{}], { x: -76, y: 30,
width: 100,
sheight: 100,
src: () => "assets/legend.png"}),
]
})