Public
Edited
Nov 2, 2024
Insert cell
Insert cell
svg = {
// Conform to D3's margin convention
const { marginLeft, marginTop, width, height } = sizes;

// create SVG container
const svg = d3.create("svg").attr("viewBox", `0, 0, ${width}, ${height}`);

// create lineChart selection
const lineChart = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

const xScale = appendTimeAxis(lineChart);

const yScale = appendTemperatureAxis(lineChart);

stylingLineChartAxes(lineChart);

appendAxisLabel(svg);


const aubergine = "#75485E";
const xPos = ({ date }) => xScale(date);
const yPos = ({ avg_temp_F: t }) => yScale(t);
lineChart
.selectAll("circle")
.data(data)
.join("circle")
.attr("r", 4)
.attr("cx", xPos)
.attr("cy", yPos)
.attr("fill", aubergine);

const lineGen = d3.line().x(xPos).y(yPos).curve(d3.curveCatmullRom);

lineChart
.append("path")
.attr("d", lineGen(data))
.attr("fill", "none")
.attr("stroke", aubergine);

return svg.node();
}
Insert cell
function appendTimeAxis(lineChart) {
const { innerWidth, innerHeight } = sizes;
// create xScale
const firstDate = new Date(2021, 0, 1, 0, 0, 0);
const lastDate = d3.max(data, (d) => d.date);
const xScale = d3
.scaleTime()
.domain([firstDate, lastDate])
.range([0, innerWidth]);

// create bottom axis
const bottomAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat("%b"));

// draw the bottom time axis
lineChart
.append("g")
.attr("class", "axis-x")
.attr("transform", `translate(0, ${innerHeight})`)
.call(bottomAxis);

// center the tick labels
lineChart
.selectAll(".axis-x text")
.attr("x", (curr) => {
const nextMonth = curr.getMonth() + 1;
const nextDate = new Date(2021, nextMonth, 1);
return (xScale(nextDate) - xScale(curr)) / 2;
})
.attr("y", "10px");

return xScale;
}
Insert cell
function appendTemperatureAxis(lineChart) {
const { innerHeight } = sizes;
// create yScale
const yScale = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.max_temp_F)])
.range([innerHeight, 0]);
const leftAxis = d3.axisLeft(yScale);

// append vertical axis
lineChart
.append("g")
.attr("class", "axis-y")
.attr("x", "-5px")
.call(leftAxis);

return yScale;
}
Insert cell
function stylingLineChartAxes(lineChart) {
lineChart
.selectAll(".axis-x text, .axis-y text")
.style("font-family", "Roboto, sans-serif")
.style("font-size", "14px");
}
Insert cell
function appendAxisLabel(svg) {
svg.append("text").attr("y", "20px").text("Temperature (°F)");
}
Insert cell
Insert cell
data = await FileAttachment("weekly_temperature.csv").csv({ typed: true });
Insert cell
Insert cell
Insert cell
suit = {
const suit = new Mocha(
"测试 D3.js 的日期格式化函数 d3.timeFormat(specifier):"
);
const describe = suit.describe.bind(suit);
const it = suit.it.bind(suit);

const monthArray = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];
const formatShortMonth = d3.timeFormat("%b");
const fn = (date) => monthArray[date.getMonth()];

describe("当标识符 specifier 为 '%b':", () => {
monthArray.forEach((expected, index) => {
const iDate = new Date(2024, index, 1, 0, 0, 0);
// check d3.timeFormat
it(`给定日期 2024-${index + 1}-1, 应该得到 "${expected}"`, () => {
expect(formatShortMonth(iDate)).to.equal(expected);
});
// check customized fn function
// it(` the same goes for fn() function (date = 2024-${
// index + 1
// }-1, expected "${expected}")`, () => {
// expect(fn(iDate)).to.equal(expected);
// });
});
});

return await suit.showResults();
}
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