Published
Edited
Nov 2, 2021
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
fixedWeekNo = 96 // 94 = data cut off date oct 9
Insert cell
// viewof z = Scrubber(Array.from({length: desiredData.length}, (_, z) => z), {delay: 200}, {autoplay: false})
viewof z = Scrubber(Array.from({length: fixedWeekNo}, (_, z) => z), {delay: 200}, {autoplay: false})
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]); // min-x, min-y, width , height

var slicedData = desiredData.map(d=>d[regions.indexOf(xxx)]).slice(0,z+1)

svg
.append('g')
.selectAll("ellipse")
.data(slicedData)
.join("ellipse")
.attr("class", (d, i) => "petal")
.attr("visibility", function (d){
return (hideCheckbox.length ===0 & d.deaths ==0)? "hidden": "visible"
})
.attr("cy", d => rScale(maxDeaths) - rScale(d.deaths))
.attr("ry", d => rScale(d.deaths))
.attr("rx", d => rScale(d.deaths)/3)
.attr("fill", function (d, i){
return (i < indexOf2021)? colorByContinent(d.region, 1, colors_):colorByContinent(d.region, 1)
})
.attr("stroke", "white")
.attr("stroke-width", strokeWidth)
.attr('transform', function(d, i) {
var rx = Math.cos(xcos(i)) * outerRadius;
var ry = Math.sin(ysin(i)) * outerRadius;
return `translate(${rx},${ry})
rotate(${xcos(i) * 180 / Math.PI + 90})
scale(${0.8})`;
})

// to add 1st year's ellipse on top of 2nd year's
if(z > indexOf2021){
svg
.append('g')
.selectAll("ellipse")
.data(slicedData.splice(0,indexOf2021))
.join("ellipse")
.attr("visibility", function (d){
return (hideCheckbox.length ===0 & d.deaths ==0)? "hidden": "visible"
})
.attr("cy", d => rScale(maxDeaths) - rScale(d.deaths))
.attr("ry", d => rScale(d.deaths))
.attr("rx", d => rScale(d.deaths) / 3)
.attr("fill", function (d, i){
return (i < indexOf2021)? colorByContinent(d.region, 0.1, colors_):colorByContinent(d.region, 0.1)
})
.attr("stroke", "white")
.attr("stroke-width", strokeWidth)
.attr('transform', function(d, i) {
var rx = Math.cos(xcos(i)) * outerRadius;
var ry = Math.sin(ysin(i)) * outerRadius;
return `translate(${rx},${ry})
rotate(${xcos(i) * 180 / Math.PI + 90})
scale(${0.8})`;
})
}

// color the last ellipse
// svg.selectAll(".ellipse:nth-last-child(1)")
// .attr("fill", d => colorByContinent3(d.region, 1))
// .attr("stroke", "white")
// .attr("stroke-width", 1.5)

let displayedYearWeek = desiredData.map(d=>d[regions.indexOf(xxx)])[z]["yearWeek"];
svg.append("text")
.attr("class", "label")
.attr("x", 0)
.attr("y", -15)
.text(formatter(yearWeekLookUp(displayedYearWeek)))
.attr("transform", "rotate(90)");

svg.append("text")
.attr("class", "label")
.attr("x", 0)
.attr("y", 0)
.text(desiredData.map(d=>d[regions.indexOf(xxx)])[0].region)
.attr("transform", "rotate(90)");

svg.append("text")
.attr("class", "label")
.attr("x", 0)
.attr("y", 15)
.text(desiredData.map(d=>d[regions.indexOf(xxx)])[z]["cumuDeaths"])
.attr("transform", "rotate(90)");
svg.append("text")
.attr("class", "label")
.attr("x", 0)
.attr("y", 30)
.text(`+${desiredData.map(d=>d[regions.indexOf(xxx)])[z]["deaths"]}`)
.attr("transform", "rotate(90)");

svg.attr("transform", "rotate(270)");
svg.append("g")
.call(yAxis);

return svg.node()
}
Insert cell
Insert cell
roundedMaxDeaths = (maxDeaths / 1000).toFixed() * 1000
Insert cell
y = d3.scaleLinear()
.domain([minDeaths, roundedMaxDeaths])
// .range([20, height / rScaleNum])
.range([40, height / 2.05]) // magic number...
Insert cell
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", "lightgrey")
.attr("stroke-width", 0.5)
.attr("stroke-opacity", (d, i) => i%2==0 ? 0.8 :0.2)
.attr("r", y))
.call(g => g.append("text")
.attr("class", "label-small")
.attr("y", d => -y(d) + 3)
.attr("transform", "rotate(90)")
.text((x, i) => `${i % 2 == 0?(x.toFixed())/1000 : ""}${i== 0?"k deaths"
:i % 2 ==0? "k"
:""}`)
.clone(true)
.attr("y", d => y(d) + 3)
.attr("transform", "rotate(135)")
.selectAll(function() { return [this, this.previousSibling]; })
.clone(true)
.attr("fill", "currentColor")
.attr("stroke", "none")))
Insert cell
// indentify break of 1st and 2nd layer
indexOf2021 = 53
Insert cell
Insert cell
Insert cell
// sin cos 1.95
// magicNumber = 3 / 8
//magicNumber = 3 / 12 // ~50 weeks = one round; 1/4 circle = one season
magicNumber = 2.75/ 12 // ~50 weeks = one round; 1/4 circle = one season
Insert cell
xcos = d3.scaleLinear()
.domain([0, desiredData[0].length])
.range([Math.PI * 0, Math.PI * magicNumber]);
Insert cell
ysin = d3.scaleLinear()
.domain([0, desiredData[0].length])
.range([Math.PI * 0, Math.PI * magicNumber]);
Insert cell
Insert cell
// maxDeaths = d3.max(desiredData.map(d => d3.max(d)).map(d => d.deaths))
Insert cell
minDeaths = 1
Insert cell
outerRadius = 190
// outerRadius = 120
Insert cell
Insert cell
regions = desiredData[0].map(d=>d.region)
Insert cell
rScaleNum = 3.8
// rScaleNum = 9
Insert cell
rScale = d3.scaleLinear()
.domain([minDeaths, maxDeaths])
// .range([20, height / rScaleNum])
.range([10, height / rScaleNum])
Insert cell
height / rScaleNum
Insert cell
Insert cell
html`
<style type="text/css">

.label {
font-size: 15px;
font-weight: 800;
fill: gray;
text-anchor: middle;
font-family: monospace;
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
}

.label-small {
font-size: 12px;
fill: lightgrey;
opacity: 0.7;
text-anchor: middle;
font-family: monospace;
}


.line{
stroke: lightgray;
stroke-width: 0.3px;
}

.label:nth-last-child(1){
opacity: 0.5;
}
</style>
`
Insert cell
{
return html`<p style="font-family:sans-serif;font-size:small;color:grey;line-height:1.8em;">
${regions
.map(
c => `<span style="background:${opacityLegend(colorByContinent(c))}; padding:7px; color:white;"> ${c} </span>`
)
.join("")}`;
}
Insert cell
colors = ["#FFD400","#E08523","#C7246B","#24A299","#347FB2","#664B9A"]
Insert cell
// default // first year
colors_ = ["#F9E280","#D8A373","#C16893","#6DBCB5","#67A6DA","#7C7099"]
Insert cell
function colorByContinent(expr, op, palette = colors){
var c = palette.map(d => opacityTweak(d,op))
// var c = d3.schemePastel1.map(d => opacityTweak(d,op))
switch (expr) {
case regions[0]:
return c[0]
break;
case regions[1]:
return c[1]
break;
case regions[2]:
return c[2]
break;
case regions[3]:
return c[3]
break;
case regions[4]:
return c[4]
break;
case regions[5]:
return c[5]
break;
return c[6]
}}
Insert cell
function opacityTweak(color, number) {
const {l, c, h, opacity} = d3.lch(color);
return d3.lch(l, c, h,opacity* number);
}
Insert cell
Insert cell
Insert cell
formatter = d3.timeFormat("%d %b, %Y")
Insert cell
height = 700
Insert cell
// width = height
Insert cell
margin = 50
Insert cell
// desiredData = desiredData1
Insert cell
Insert cell
Insert cell
import {maxDeaths} from "@spepechen/dataprep-v2"
Insert cell
import {yearWeekLookUp} from "@spepechen/dataprep-v2-2" // countries data
Insert cell
desiredData1
Insert cell
Insert cell
desiredData = desiredDataRaw
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