Published
Edited
Mar 31, 2021
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("id", "chart")
svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);

keys.forEach((key,i) => {
svg.append("path")
.datum(keyData[key])
.attr("fill", "none")
.attr("stroke", colors[i])
.attr("stroke-width", 2)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", lineGenerators[key])
svg
.append("circle")
.attr("cy", (d) => {
return y(keyData[key][keyData[key].length - 1][key])
})
.attr("fill", colors[i])
.attr("cx", (d) => {
return x(keyData[key][keyData[key].length - 1][xVar])
})
.attr("r", 4)
.style("opacity", 1)
svg
.append("text")
.attr("class", "lineLabels")
.style("font-family", "'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif")
.style("font-weight", "bold")
.style("font-size",14)
.attr("y", (d) => {
return (
y(keyData[key][keyData[key].length - 1][key]) +
4
)
})
.attr("x", (d) => {
return (
x(keyData[key][keyData[key].length - 1][xVar]) + 5
)
})
.style("opacity", 1)
.attr("fill", colors[i])
.text((d) => {
return key
})
})
svg.append("circle")
.attr("r",20)
.attr("cx",100)
.attr("cy",50)
.attr("fill","lightgreen")
.style("cursor", "pointer")
.attr("id", "playButton")
.on("click", noiseLoop)
svg.append("text")
.attr("x",100)
.attr("y",52)
.style("font-size","12px")
.style("font-family","sans-serif")
.style("pointer-events","none")
.attr("text-anchor","middle")
.text("play")
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", margin.left + 15)
.attr("x", -20)
.attr("fill", "#767676")
.style("font-family", "'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif")
.style("font-weight", "bold")
.style("font-size",12)
.attr("text-anchor", "end")
.text("Cumulative doses");
svg.append("circle")
.attr("r",5)
.attr("stroke", "red")
.attr("cx",x(data[0].Date))
.attr("cy",y(data[0]['Original goal']))
.attr("fill","none")
.attr("id", "playHead")
return svg.node();
}
Insert cell
margin = ({top: 20, right: 120, bottom: 30, left: 35})
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickFormat(function (d) { return numberFormat(d)}))
Insert cell
line1 = d3.line()
.x(d => x(d.Date))
.y(d => y(d['Original goal']))
Insert cell
colors = ["#d10a10", "#cccccc", "#ea5a0b"];
Insert cell
function numberFormat(num) {
if ( num > 0 ) {
if ( num > 1000000000 ) { return ( num / 1000000000 ) + 'bn' }
if ( num >= 1000000 ) { return ( num / 1000000 ) + 'm' }
if ( num > 1000 ) { return ( num / 1000 ) + 'k' }
if (num % 1 != 0) { return num.toFixed(2) }
else { return num.toLocaleString() }
}
if ( num < 0 ) {
var posNum = num * -1;
if ( posNum > 1000000000 ) return [ "-" + String(( posNum / 1000000000 )) + 'bn'];
if ( posNum > 1000000 ) return ["-" + String(( posNum / 1000000 )) + 'm'];
if ( posNum > 1000 ) return ["-" + String(( posNum / 1000 )) + 'k'];
else { return num.toLocaleString() }
}
return num;
}
Insert cell
lineGenerators = {
var lineGenTemp = {}
keys.forEach(function(key,i) {
lineGenTemp[key] = d3.line()
.x(function(d) {
return x(d[xVar]);
})
.y(function(d) {
return y(d[key]);
})
})
return lineGenTemp
}
Insert cell
blah = y.domain()
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(data, d => d['Original goal'])])
.range([height - margin.bottom, margin.top])
Insert cell
x = d3.scaleUtc()
.domain(d3.extent(data, d => d.Date))
.range([margin.left, width - margin.right])
Insert cell
keyData = {

var tempData = {}
keys.forEach(function(key,i) {
var temp = []
data.forEach(function(d) {
if (d[key] != "") {
// console.log(temp)
var newData = {}
newData[xVar] = d[xVar]
newData[key] = d[key]
temp.push(newData)
}
})
tempData[key] = temp
})
return tempData
}
Insert cell
keys = {
var keysss = Object.keys(data[0])
keysss.splice(0, 1)
return keysss
}
Insert cell
xVar = Object.keys(data[0])[0]
Insert cell
data = {
json.sheets.data.forEach(d => {
if (typeof d.Date == 'string') {
d.Date = parseDate(d.Date)
}
})
var endDate = new Date(2021, 3, 1)
var temp = json.sheets.data.filter(d => {
return d.Date <= endDate
})
return temp
}
Insert cell
parseDate = d3.timeParse("%Y-%m-%d")
Insert cell
json = d3.json(`https://interactive.guim.co.uk/yacht-charter-data/Covid_oz_vac_gap_two_goals_feed.json`)
Insert cell
height = 500
Insert cell
d3 = require("d3@6")
Insert cell
function noiseLoop() {
var keyOrder = ['Original goal', 'Revised goal', keys[0]]
makeNoise('Date', keyOrder[0])
console.log(keyData[keyOrder[1]].length * note * 1000)
setTimeout(function() { makeNoise('Date', keyOrder[1])}, keyData[keyOrder[1]].length * note * 1000 + 2000)
setTimeout(function() { makeNoise('Date', keyOrder[2])}, (keyData[keyOrder[1]].length * note * 1000) + (keyData[keyOrder[2]].length * note * 1000) + 4000)

}
Insert cell
function makeNoise(xVar, yVar) {
var synth = new tone.Synth({
envelope: {
decay: 0,
sustain:1,
release:0.5
},
oscillator : {
count: 8,
spread: 30,
type : "sawtooth4"
}
}
).toDestination();
console.log("start", yVar)
keyData[yVar].forEach(function(d,i) {
if (i == 0) {
synth.triggerAttackRelease(scale(d[yVar]), keyData[yVar].length * note).onsilence(clearSynth())
d3.select("#playHead")
.attr("cx",x(keyData[yVar][i][xVar]))
.attr("cy",y(keyData[yVar][i][yVar]))
}
else {
tone.Transport.schedule(function(){
d3.select("#playHead").transition().duration(500)
.ease(d3.easeLinear)
.attr("cx",x(keyData[yVar][i][xVar]))
.attr("cy",y(keyData[yVar][i][yVar]))
synth.frequency.rampTo(scale(d[yVar]), note);
}, i*note);
}
})
tone.Transport.position = "0:0:0"
tone.Transport.start()
function clearSynth () {
console.log("finished")
}
}
Insert cell
Insert cell
bpm = 400
Insert cell
note = 60 / bpm
Insert cell
Insert cell
low = 130.81
Insert cell
high = 261.63
Insert cell
Insert cell
scale = d3.scaleLinear()
.domain([0, d3.max(data, d => d['Original goal'])])
.range([low,high])
Insert cell
tone = require("tone")
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