Published
Edited
Nov 2, 2021
2 forks
Importers
9 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
smallMultiples = {
const num_cols = 3;
const num_rows = Math.round(countries.length / 3);
const width = 1000;
const height = Math.round(countries.length / 3) * 300;
const cardwidth = 250;
const cardheight = 250;

const svg = d3.create("svg").attr("viewBox", [0, 0, width + 2, height]);

let i = 0;

const dimensions = {
width: cardwidth,
height: cardheight,
margin: {
top: 50,
right: 20,
bottom: 20,
left: 30
}
};
dimensions.boundedWidth =
dimensions.width - dimensions.margin.left - dimensions.margin.right;
dimensions.boundedHeight =
dimensions.height - dimensions.margin.top - dimensions.margin.bottom;

const wrapper = svg
.append("g")
.attr(
"transform",
`translate(${dimensions.margin.left}, ${dimensions.margin.top})`
);
let yScale;

yScale = d3
.scaleLog()
.domain([
1,
d3.max(allCountries, d => d3.max(d.data, dd => dd["75pct_cumulative"]))
])
.range([dimensions.boundedHeight, 0])
.nice();

// if (type === "death") {
// yScale = d3
// .scaleLinear()
// .domain([1, d3.max(dataCanada, d => d.total)])
// .range([dimensions.boundedHeight, 0])
// .nice();
// } else {
// yScale = d3
// .scaleLog()
// .domain([1, d3.max(dataCanada, d => d.total)])
// .range([dimensions.boundedHeight, 0])
// .nice();
// }

const xScale = d3
.scaleUtc()
.domain([new Date("2020-02-01"), new Date("2020-09-01")])
.range([0, dimensions.boundedWidth])
.nice();

array(num_rows).forEach(row => {
array(num_cols).forEach(col => {
if (i < countries.length) {
// console.log(i, row, col);
// console.log(row, col, i);
let bounds = wrapper
.append("g")
.attr(
"transform",
`translate(${col * (width / num_cols)}, ${row *
(height / num_rows)})`
);

let data = [
{
province: countries[i],
color: "steelblue",
data: allCountries.filter(d => d.country === countries[i])[0].data
}
];
// console.log(data[0].data);

if (allCountries.filter(d => d.country === countries[i])) {
bounds
.selectAll('text')
.data([null])
.join('text')
.style("text-anchor", "middle")
.attr("x", dimensions.boundedWidth - dimensions.boundedWidth / 2)
.attr("y", -10)
.attr("fill", "black")
.attr("font-weight", "bold")
.attr("font-size", "1.2em")
// .style('text-align', 'center')
.text(data[0].province);
bounds
.selectAll('stats')
.data([null])
.join('text')
.style("text-anchor", "left")
.attr("x", 20)
.attr("y", 10)
.attr("fill", "hsla(7, 100%, 50%,100%)")
.attr("id", data[0].province.replace(/[- )(]/g, ''))
// .attr("id", data[0].province.replace(/[- )(]/g, ''))
// .attr("font-weight", "bold")
.attr("font-size", "0.8em")
// .style('text-align', 'center')
.text(d3.format(",d")(last(data[0].data)["50pct_cumulative"]));

// );

const stats = bounds
.selectAll('stats')
.data([null])
.join('text')
.style("text-anchor", "left")
.attr("x", 20)
.attr("y", 20)
.attr("fill", "black")
.attr("id", data[0].province.replace(/[- )(]/g, '') + "range")
// .attr("font-weight", "bold")
.attr("font-size", "0.6em")
.text(
d3.format(",d")(last(data[0].data)["2_5pct_cumulative"]) +
"-" +
d3.format(",d")(last(data[0].data)["97_5pct_cumulative"])
);
// .style('text-align', 'center')

bounds
.append("defs")
.append("clipPath")
.attr("id", "bounds-clip-path1" + i)
.append("rect")
.attr("width", dimensions.boundedWidth)
.attr("height", dimensions.boundedHeight);

const clip = bounds
.append("g")
.attr("clip-path", "url(#bounds-clip-path1" + i + ")");

const yAxisGenerator = d3
.axisLeft()
.scale(yScale)
.ticks(...[6, "~s"]);

const yAccessor = d => {
// console.log(d.cumgoals);
return d["50pct_cumulative"];
};

const xAxisGenerator = d3.axisBottom().scale(xScale);

// Draw data
// const areas = bounds.append("g");

const lineGenerator = d3
.line()
.x(d => {
return xScale(xAccessor(d));
})
.y(d => {
// console.log(yScale(yAccessor(d)));
return yScale(yAccessor(d));
});

const areaGenerator = d3
.area()
.x(d => {
// console.log(yScale(d["25pct_cumulative"]));
return xScale(xAccessor(d));
})
.y(d => {
return yScale(d["25pct_cumulative"]);
})
.y1(d => {
// console.log(yScale(d["25pct_cumulative"]));
return yScale(d["75pct_cumulative"]);
});

const areaGenerator2 = d3
.area()
.x(d => {
return xScale(xAccessor(d));
})
.y(d => {
return yScale(d["2_5pct_cumulative"]);
})
.y1(d => {
return yScale(d["97_5pct_cumulative"]);
});

const areaGenerator3 = d3
.area()
.x(d => {
return xScale(xAccessor(d));
})
.y(d => {
return yScale(d["10pct_cumulative"]);
})
.y1(d => {
return yScale(d["90pct_cumulative"]);
});

const areaGenerator4 = d3
.area()
.x(d => {
return xScale(xAccessor(d));
})
.y(d => {
return yScale(d["1pct_cumulative"]);
})
.y1(d => {
return yScale(d["99pct_cumulative"]);
});

clip.append("path").attr("class", "area");

const area = clip.attr("class", "area").append("g");

area
.selectAll(".area")
.data(data)
.enter()
.append("path")
.attr("d", d => {
// console.log(areaGenerator(d.data));
return areaGenerator(d.data);
})
.style("fill", "hsla(7, 100%, 50%,.2)")
.style("stroke", d => "none")

.style("stroke-width", 2)
.style("stroke-linejoin", "round");
// .on("mousemove click touchmove", function() {
// const m = d3.mouse(this);
// hover(xScale.invert(m[0]));
// });

area
.selectAll(".area")
.data(data)
.enter()
.append("path")
.attr("d", d => {
// console.log(areaGenerator(d.data));
return areaGenerator2(d.data);
})
.style("fill", "hsla(7, 100%, 50%,.2)")
.style("stroke", d => "none")

.style("stroke-width", 2)
.style("stroke-linejoin", "round");
// .on("mousemove click touchmove", function() {
// const m = d3.mouse(this);
// hover(xScale.invert(m[0]));
// });

area
.selectAll(".area")
.data(data)
.enter()
.append("path")
.attr("d", d => {
return areaGenerator3(d.data);
})
.style("fill", "hsla(7, 100%, 50%,.2)")
.style("stroke", d => "none")

.style("stroke-width", 2)
.style("stroke-linejoin", "round");
// .on("mousemove click touchmove", function() {
// const m = d3.mouse(this);
// hover(xScale.invert(m[0]));
// });

area
.selectAll(".area")
.data(data)
.enter()
.append("path")
.attr("d", d => {
// console.log(areaGenerator(d.data));
return areaGenerator4(d.data);
})
.style("fill", "hsla(7, 100%, 50%,.2)")
.style("stroke", d => "none")

.style("stroke-width", 2)
.style("stroke-linejoin", "round")
.on("mousemove click touchmove", function() {
const m = d3.mouse(this);
hover(xScale.invert(m[0]));
});

const lines = clip.append("g");

lines
.selectAll(".line")
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr("d", d => {
return lineGenerator(d.data);
})
.style("fill", "none")
.style("stroke", d => "hsla(7, 100%, 50%,1)")
.attr("stroke-dasharray", "3,3")
.style("stroke-width", 2)
.style("stroke-linejoin", "round");
// .on("mousemove click touchmove", function() {
// const m = d3.mouse(this);
// hover(xScale.invert(m[0]));
// });

// const lineMeas = clip
// .attr("class", "line")
// .append("g")
// .selectAll(".line")
// .data(data)
// .enter()
// .append("path")
// .attr("d", d => {
// return lineGenerator(d.data.slice(0, observedIndex + 1));
// })
// .style("fill", "none")
// .style("stroke", d => "hsla(7, 100%, 50%,1)")
// .style("stroke-width", 2)
// .style("stroke-linejoin", "round");
// .on("mousemove click touchmove", function() {
// const m = d3.mouse(this);
// hover(xScale.invert(m[0]));
// });

const xAxis = bounds
.append("g")
.attr('opacity', .5)
.attr("class", "x-axis")
.call(xAxisGenerator)
.style("transform", `translateY(${dimensions.boundedHeight}px)`)
.call(g =>
g
.selectAll("text")
.attr("transform", "rotate(-65)")
.attr("dx", "-2.23em")
.attr("dy", "-.1em")
);

const yAxis = bounds
.append("g")
.attr('opacity', .5)
.call(yAxisGenerator)
.call(g => g.select(".domain").remove())
.call(g =>
g
.select(".tick:last-of-type text")
.clone()
.attr("x", 5)
.attr("fill", "black")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("")
);

// console.log(last(data[0].data)["50pct_cumulative"]);
const marker = bounds
.append("circle")
.attr("r", 4)
.attr("cx", -10)
.attr("fill", "hsla(7, 100%, 50%,45%)")
.attr("stroke", "none")
.attr("class", "marker-" + data[0].province.replace(/[- )(]/g, ''))
.attr("opacity", 1)
.attr("cx", xScale(last(data[0].data).time))
.attr("cy", yScale(last(data[0].data)["50pct_cumulative"]));
// .attr("opacity", 0);

// .attr("fill", "hsla(7, 100%, 50%,100%)");

const bisect = d3.bisector(d => d.time);

function hover(time) {
// console.log(date);
countries.forEach(p => {
let dataT = [
{
province: p,
data: allCountries.filter(d => d.country === p)[0].data
}
];

const j = bisect.right(dataT[0].data, time);
// console.log(dataT[0]);
d3.select('.marker-' + p.replace(/[- )(]/g, ''))
.attr("stroke", "hsla(7, 100%, 50%,100%)")
.attr("fill", "hsla(7, 100%, 50%,45%)")
.attr("opacity", 1)
.attr("cx", xScale(dataT[0].data[j].time))
.attr("cy", yScale(dataT[0].data[j]["50pct_cumulative"]));

d3.select('#' + p.replace(/[- )(]/g, '')).text(
d3.format(",d")(dataT[0].data[j]["50pct_cumulative"])
);

d3.select('#' + p.replace(/[- )(]/g, '') + "range").text(
d3.format(",d")(dataT[0].data[j]["2_5pct_cumulative"]) +
"-" +
d3.format(",d")(dataT[0].data[j]["97_5pct_cumulative"])
);
});
}
}
i++;
}
});
});

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cardwidth = 450
Insert cell
height = 550
Insert cell
width = 1000
Insert cell
cardheight = 500
Insert cell
Insert cell
Insert cell
covidraw = d3.csv(
"https://raw.githubusercontent.com/cghr-toronto/public/master/covid/intervals.csv",
// "https://raw.githubusercontent.com/cghr-toronto/public/master/covid/data/intervals.csv",
d3.autoType
)
Insert cell
// covidraw = d3.csvParse(await FileAttachment("data.csv").text())
Insert cell
covidFiltered = covidraw.filter(d => d.central_intensity !== "NA")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { legend } from "@d3/color-legend"
Insert cell
import {
chart,
europeBubbs,
asianBubbs,
NABubbs,
viewof date as dateSlide
} from "289d8504bdfb4e28"
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