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();
}