I am trying to convert observable notebook (https://observablehq.com/@d3/radial-area-chart) to vanilla Javascript using d3 SVG elements. You can find the ‘sfo-temperature.csv’ by clicking on paper clip symbol on the link above.
I am still beginner in HTML, JS and D3. I am stuck in an error. I would appreciate any alternative solutions as well. Below, you can find my attempt.
Here is my index.HTML file:
<html>
<head>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script src="d3Chart.js"></script>
<!-- <link rel="stylesheet" type="text/css" href="styles.css"> -->
</head>
<body>
<div id="chart-container"></div>
<div class="container"></div>
<script>
// Call the drawChart function with the container and data
const container = d3.select("#chart-container");
// Upload local CSV file
d3.csv("sfo-temperature.csv").then(function(data) {
drawChart('.container', data);
});
<!--drawChart('.container',data)-->
</script>
</body>
</html>
Here is my d3Chart.js file:
async function drawChart(container, data) {
const rawdata = await d3.csv("sfo-temperature.csv");
data = Array.from(d3.rollup(
rawdata,
v => ({
date: new Date(Date.UTC(2000, v[0].DATE.getUTCMonth(), v[0].DATE.getUTCDate())),
avg: d3.mean(v, d => d.TAVG || NaN),
min: d3.mean(v, d => d.TMIN || NaN),
max: d3.mean(v, d => d.TMAX || NaN),
minmin: d3.min(v, d => d.TMIN || NaN),
maxmax: d3.max(v, d => d.TMAX || NaN)
}),
d => `${d.DATE.getUTCMonth()}-${d.DATE.getUTCDate()}`
).values())
.sort((a, b) => d3.ascending(a.date, b.date))
const width = 954;
const height = width;
const margin = 10;
const innerRadius = width / 5;
const outerRadius = width / 2 - margin;
const x = d3.scaleUtc()
.domain([Date.UTC(2000, 0, 1), Date.UTC(2001, 0, 1) - 1])
.range([0, 2 * Math.PI]);
const y = d3.scaleLinear()
.domain([d3.min(data, d => d.minmin), d3.max(data, d => d.maxmax)])
.range([innerRadius, outerRadius]);
const xAxis = g => g
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.call(g => g.selectAll("g")
.data(x.ticks())
.join("g")
.each((d, i) => d.id = DOM.uid("month"))
.call(g => g.append("path")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.attr("d", d => `
M${d3.pointRadial(x(d), innerRadius)}
L${d3.pointRadial(x(d), outerRadius)}
`))
.call(g => g.append("path")
.attr("id", d => d.id.id)
.datum(d => [d, d3.utcMonth.offset(d, 1)])
.attr("fill", "none")
.attr("d", ([a, b]) => `
M${d3.pointRadial(x(a), innerRadius)}
A${innerRadius},${innerRadius} 0,0,1 ${d3.pointRadial(x(b), innerRadius)}
`))
.call(g => g.append("text")
.append("textPath")
.attr("startOffset", 6)
.attr("xlink:href", d => d.id.href)
.text(d3.utcFormat("%B"))));
const yAxis = g => g
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.call(g => g.selectAll("g")
.data(y.ticks().reverse())
.join("g")
.attr("fill", "none")
.call(g => g.append("circle")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.attr("r", y))
.call(g => g.append("text")
.attr("y", d => -y(d))
.attr("dy", "0.35em")
.attr("stroke", "#fff")
.attr("stroke-width", 5)
.text(y.tickFormat(5, "f")))
.call(g => g.append("text")
.attr("y", d => -y(d))
.attr("dy", "0.35em")
.text(y.tickFormat(5, "f"))));
const line = d3.lineRadial()
.angle(d => x(d.date))
.radius(d => y(d.avg));
const svg = d3.select(container)
.append("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("text-anchor", "middle");
svg.append("g")
.attr("fill", "none")
.attr("stroke-opacity", 0.6)
.selectAll("path")
.data(data)
.join("path")
.style("mix-blend-mode", "multiply")
.attr("stroke", "steelblue")
.attr("d", d => line(d.values));
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("g")
.selectAll("g")
.data(data)
.join("g")
.attr("transform", d => `
rotate(${((x(d.date) + x(d3.utcMonth.offset(d.date, 1))) / 2 * 180 / Math.PI - 90)})
translate(${innerRadius},0)
`)
.append("line")
.attr("x2", -5)
.attr("stroke", "#000");
svg.append("g")
.selectAll("g")
.data(data)
.join("g")
.attr("transform", d => `
rotate(${((x(d.date) + x(d3.utcMonth.offset(d.date, 1))) / 2 * 180 / Math.PI - 90)})
translate(${outerRadius},0)
`)
.append("line")
.attr("x2", 5)
.attr("stroke", "#000");
}
When I run my code, I encounter the following error: Uncaught (in promise) TypeError: d.DATE.getUTCMonth is not a function I tried replacing d.DATE.getUTCMonth with d.getUTCMonth, however, it still did not work. How should I modify my code so that I can create the radial area chart deined on obsrvable run using vanilla JS and d3 SVG elements?