Public
Edited
Apr 16, 2024
Insert cell
Insert cell
Insert cell
{
const data = [
{ dummy_date: new Date("2000-01-01"), ano_pi: 0.5, decade: "2000" },
{ dummy_date: new Date("2000-02-01"), ano_pi: 1.0, decade: "2000" },
{ dummy_date: new Date("2000-03-01"), ano_pi: 1.2, decade: "2000" },
{ dummy_date: new Date("2000-04-01"), ano_pi: 0.8, decade: "2000" },
{ dummy_date: new Date("2000-01-01"), ano_pi: 0.3, decade: "2010" },
{ dummy_date: new Date("2000-02-01"), ano_pi: 0.7, decade: "2010" },
{ dummy_date: new Date("2000-03-01"), ano_pi: 0.9, decade: "2010" },
{ dummy_date: new Date("2000-04-01"), ano_pi: 0.6, decade: "2010" }
];

const margin = { top: 40, right: 40, bottom: 30, left: 25 };
const totalWidth = 960;
const height = 350 - margin.top - margin.bottom;

const svg = d3
.create("svg")
.attr("viewBox", `0 0 ${totalWidth} ${height + margin.top + margin.bottom}`)
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

const dataByDecade = d3.group(data, (d) => d.decade);

let facetWidth =
(totalWidth - margin.left - margin.right) / dataByDecade.size;

const y = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.ano_pi)])
.range([height, 0]);

const x = d3
.scaleTime()
.domain(d3.extent(data, (d) => d.dummy_date))
.range([0, facetWidth - margin.right]);

const xSuperimposed = d3
.scaleTime()
.domain(d3.extent(data, (d) => d.dummy_date))
.range([0, totalWidth - margin.left - margin.right]);

const lineGenerator = d3
.line()
.x((d) => x(d.dummy_date)) // Initially set to use the 'x' scale
.y((d) => y(d.ano_pi));

let isMerged = false;

function updateFacets() {
facetWidth = isMerged
? totalWidth - margin.left - margin.right
: (totalWidth - margin.left - margin.right) / dataByDecade.size;

const currentXScale = isMerged ? xSuperimposed : x;
lineGenerator.x((d) => currentXScale(d.dummy_date));

svg
.selectAll(".facet")
.transition()
.duration(750)
.attr("transform", (_, i) =>
isMerged
? `translate(${margin.left}, ${margin.top})`
: `translate(${margin.left + i * facetWidth}, ${margin.top})`
);

svg
.selectAll(".x-axis")
.transition()
.duration(750)
.call(
d3
.axisBottom(currentXScale)
.tickFormat(d3.timeFormat("%b"))
.tickValues([
new Date("2000-01-01"),
new Date("2000-02-01"),
new Date("2000-03-01"),
new Date("2000-04-01")
])
)
.call((g) => g.select(".domain").remove())
.call((g) => g.selectAll(".tick line").attr("stroke", "#777"));

svg
.selectAll(".line")
.transition()
.duration(750)
.attr("d", (d) => lineGenerator(d));

// Debugging console.log statements
console.log("Facet Width:", facetWidth);
console.log("Is Merged:", isMerged);
}

svg.on("click", function () {
isMerged = !isMerged;
updateFacets();
});

dataByDecade.forEach((values, decade, i) => {
const xOffset = margin.left + i * facetWidth;

const facetGroup = svg
.append("g")
.attr("class", "facet")
.attr("transform", `translate(${xOffset}, ${margin.top})`);

facetGroup
.append("text")
.attr("class", "decade-label")
.attr("x", facetWidth / 2) // Center the label
.attr("y", 0 - margin.top / 2) // Adjust y position
.text(decade + "s")
.style("font-size", "16px") // Adjust font size
.attr("text-anchor", "middle");

facetGroup
.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0, ${height})`)
.call(
d3
.axisBottom(x)
.tickFormat(d3.timeFormat("%b"))
.tickValues([
new Date("2000-01-01"),
new Date("2000-02-01"),
new Date("2000-03-01"),
new Date("2000-04-01")
])
)
.call((g) => g.select(".domain").remove())
.call((g) => g.selectAll(".tick line").attr("stroke", "#777"));

facetGroup
.append("g")
.call(d3.axisLeft(y).tickValues([0, 0.5, 1, 1.5, 2]))
.call((g) => g.select(".domain").remove())
.call((g) => g.selectAll(".tick line").attr("stroke", "#777"))
.call((g) =>
g
.selectAll("line")
.attr("x2", facetWidth - margin.right)
.attr("stroke", "#ddd")
);

facetGroup
.selectAll(".line")
.data([values]) // Bind the data for each decade
.enter()
.append("path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "steelblue") // Adjust stroke color for visibility
.attr("stroke-width", 2) // Adjust stroke width
.attr("d", lineGenerator);
});

updateFacets();
return svg.node();
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more