Published
Edited
Aug 17, 2021
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round");

svg
.append("path")
.datum(blossoms.filter(radial.defined()))
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("stroke-width", "0.5")
.attr("d", radial);

let areaGradient = svg
.append("defs")
.append("radialGradient")
.attr("id", "areaGradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "100%");

areaGradient
.append("stop")
.attr("offset", "0%")
.attr("stop-color", "#edd1cb")
.attr("stop-opacity", 1);
areaGradient
.append("stop")
.attr("offset", "60%")
.attr("stop-color", "#c3819c")
.attr("stop-opacity", 1);
areaGradient
.append("stop")
.attr("offset", "100%")
.attr("stop-color", "#2d1e3e")
.attr("stop-opacity", 1);

svg
.append("path")
.datum(blossoms)
.attr("fill", "none")
.attr("stroke", "url(#areaGradient)")
.attr("stroke-width", "0.5")
.attr("d", radial);

svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

return svg.node();
}
Insert cell
xAxis = (g) =>
g.attr("text-anchor", "middle").call((g) =>
g
.selectAll("g")
.data([
"800",
"900",
"1000",
"1100",
"1200",
"1300",
"1400",
"1500",
"1600",
"1700",
"1800",
"1900",
"2000"
])

.join("g")
.attr(
"transform",
(d, i, arr) => `
rotate(${((i - 3) * 360) / arr.length})
translate(${innerRadius + 20},0)
`
)
.call((g) =>
g
.append("text")
.attr("transform", (d, i, arr) =>
(((i - 3) * 360) / arr.length) % 360 > 180
? "rotate(90)translate(0,16)"
: "rotate(-90)translate(0,-9)"
)
.style("font-family", "sans-serif")
.style("font-size", 10)
.attr("fill", "#2d1e3e")
.text((d) => d)
)
)
Insert cell
yAxis = (g) =>
g
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.call((g) =>
g
.selectAll("g")
.data(scaleRadius.ticks())
.join("g")
.attr("fill", "none")
.call((g) =>
g
.append("circle")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.1)
.attr("r", scaleRadius)
)
.call((g) =>
g
.append("text")
.attr("y", (d) => -scaleRadius(d))
.attr("dy", "0.35em")
.attr("stroke", "#fff")
.attr("stroke-width", 5)
.data(yTicks)
.join("g")
.text((d) => d)
.clone(true)
.attr("fill", "#2d1e3e")
.attr("stroke", "none")
)
)
Insert cell
Insert cell
Insert cell
innerRadius = height / 2.35
Insert cell
scaleAngle.range()
Insert cell
scaleAngle.domain()
Insert cell
scaleAngle = d3
.scaleLinear()
.domain(d3.extent(blossoms, (d) => d.year))
.range([0, 2 * Math.PI])
Insert cell
scaleRadius.domain()
Insert cell
scaleRadius = d3
.scaleTime()
.domain([d3.min(blossoms, (d) => d.date), d3.max(blossoms, (d) => d.date)])
.range([0, 200])
Insert cell
radial = d3
.lineRadial()
.angle((d) => scaleAngle(d.year))
.radius((d) => scaleRadius(d.date))
.curve(d3.curveCatmullRomOpen)
// .defined((d) => d.date > 0)
.defined((d) => !isNaN(d.date))
Insert cell
Insert cell
blossoms = Object.assign(
d3
.csvParse(await FileAttachment("peak-bloom.csv").text())
.map(({ year, date, ratio }) => ({
year: d3.timeFormat("%Y")(new Date(year)),
date: parseDate("0" + date) ? parseDate("0" + date) : undefined
})),
{ title: "Cherry Blossom Peak-bloom Date", y: " date" },
{ x: "Year →", y: "↑ Number of peak-bloom date" }
)
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