Published
Edited
Aug 14, 2020
Importers
5 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("class", "svgBackground");
// Yellow bars
svg.append("g")
.selectAll("rect")
.data(dataWiki)
.join("rect")
.attr("id", "highlight")
.attr("x",margin.left)
.attr("y",function(d,i) {return (i) * 20 + margin.top + 6})
.attr("height", 18)
.attr("width", d => textHighlight(d.Num_Watchers_Clean))
.attr("fill", "#f9ff77");
// Text init
const wiki = svg.append("g")
.selectAll("a")
.data(dataWiki)
.join("a")
.attr("transform", (d, i) => `translate(${margin.left} ${(i+1) * 20 + margin.top})`)
.attr("href", d => "https://en.wikipedia.org/" + d.Wiki_URL)
.attr("target", "_blank")
.append("text")
.attr("id", "para")
.attr("class", "init")
// margin gutter rectangle
svg.append("rect")
.attr("class", "svgBackground")
.attr("x", width - margin.right)
.attr("y", 0)
.attr("width", margin.right)
.attr("height", "100%")
// legend and title
svg.append("g")
.call(xAxis);
svg.append("g")
.call(axisTitle);
svg.append("g")
.call(legend);

yield svg.node();
format();
}
Insert cell
Insert cell
dataWiki = FileAttachment("Aug2019data@1.json").json()
Insert cell
tooltip = function(d){
return d.Article_Title + ": " +
"\n " + d.First_Para_Clean.replace(/\n/g, "") +
"\n Views: " + d3.format(",.0f")(d.Page_Views) +
"\n Words: " + d3.format(",.0f")(d.Page_Length) +
"\n Edits: " + d3.format(",.0f")(d.Total_Edits) +
"\n Wait: " + d3.format(",.1f")(d.Days_Wait/365) + " years" +
"\n Age: " + d3.format(",.1f")(d.Article_Age/365) + " years" +
"\n Watchers: " + d3.format(",.0f")(d.Num_Watchers_Clean)
}
Insert cell
format = function(){
const ch1 = d3.select("input#pageViews").property("checked"),
ch2 = d3.select("input#pageLength").property("checked"),
ch3 = d3.select("input#totalEdits").property("checked"),
ch4 = d3.select("input#yearsWait").property("checked"),
ch5 = d3.select("input#articleAge").property("checked"),
ch6 = d3.select("input#numWatcher").property("checked");
d3.selectAll("text#para")
.each(function(d){
let currentObj = d3.select(this);
currentObj.text("");
for (let i = 0; i < 150; i++){ // <= arbitrary value 150, just so long as the text covers the screen
let bold = (i < width2char(textBold(d.Page_Views)) && ch1)
let color = (i < width2char(textColor(d.Page_Length)) && ch2)
let uLine = (i < width2char(textUnderline(d.Total_Edits)) && ch3)
let ital = (i < width2char(textItalic(d.Days_Wait)) && ch4)
let serif = (i < width2char(textSerif(d.Article_Age)) || !ch5)
currentObj.append("tspan")
.attr("class", "init")
.attr("font-weight", bold? "bold":"normal")
.attr("fill", color? "#2166ac":"#b2182b")
.attr("text-decoration", uLine? "underline":"none")
.style("font-variation-settings", function(){
return `"slnt" ${ital? -30:0},
"wght" ${bold? 800:400},
"CASL" ${serif? 0:1},
"CRSV" ${serif? 0:1},
"MONO" 1`})
.text(d.First_Para_Clean.substring(i, i+1))
currentObj.append("title")
.text(tooltip)
}
})
d3.selectAll("rect#highlight")
.each(function(d){
d3.select(this)
.transition()
.duration(200)
.attr("opacity", ch6? 1:0)
})
}
Insert cell
Insert cell
textBold = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Page_Views)])
.range([0, width - margin.left - margin.right])
Insert cell
textColor = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Page_Length)])
.range([0, width - margin.left - margin.right])
Insert cell
textUnderline = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Total_Edits)])
.range([0, width - margin.left - margin.right])
Insert cell
textItalic = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Days_Wait)])
.range([0, width - margin.left - margin.right])
Insert cell
textSerif = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Article_Age)])
.range([0, width - margin.left - margin.right])
Insert cell
textHighlight = d3.scaleLinear()
.domain([0, d3.max(dataWiki, d => d.Num_Watchers_Clean)])
.range([0, width - margin.left - margin.right])
Insert cell
width2char = d3.scaleLinear()
.domain([0, width - margin.left - margin.right])
.range([0, 88]) // <= number of monospace characters that fit accross the visualization window
.interpolate(d3.interpolateRound)
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(d3.scaleLinear()
.domain([0,100])
.range([margin.left, width - margin.right])).ticks(width/100))
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", width)
.attr("y", margin.bottom)
.attr("fill", "#263c54")
.attr("text-anchor", "end"))
Insert cell
axisTitle = g => {
g.attr("transform", `translate(${margin.left}, ${height - margin.bottom + 40})`)
.attr("class", "other")
.style("font-size", "16px");
g.append("text")
.text("% of MAXIMUM")
.attr("font-weight", 700);
}
Insert cell
maxLegend = {
const view = d3.format(",.0f")(d3.max(dataWiki, d => d.Page_Views)),
lgth = d3.format(",.0f")(d3.max(dataWiki, d => d.Page_Length)),
edit = d3.format(",.0f")(d3.max(dataWiki, d => d.Total_Edits)),
wait = d3.format(",.1f")(d3.max(dataWiki, d => d.Days_Wait)/365),
age = d3.format(",.1f")(d3.max(dataWiki, d => d.Article_Age)/365),
num = d3.format(",.0f")(d3.max(dataWiki, d => d.Num_Watchers_Clean));
return [`Page Views: ${view}`,
`Word Count: ${lgth}`,
`Total Edits: ${edit}`,
`Years to Front-Page: ${wait}`,
`Article Age (years): ${age}`,
`Number of Watchers: ${num}`]
}
Insert cell
legend = g => {
g.attr("transform", `translate(${width - margin.right}, ${height - margin.bottom + 40})`)
.attr("class", "other")
.attr("text-anchor", "end")
.style("font-size", "16px");
g.append("text")
.text("max VALUE")
.attr("font-weight", 700);
g.append("rect")
.attr("x", -242)
.attr("y", 133)
.attr("height", 18)
.attr("width", 245)
.attr("fill", "#f9ff77");
g.selectAll("text#legend")
.data(maxLegend)
.join("text")
.attr("font-family", "Recursive")
.text(d => d)
.attr("y", (d, i) => `${i * 1.5 + 1.75}em`)
.attr("fill", (d, i) => (i == 1)? "#2166ac":"#b2182b")
.attr("text-decoration", (d, i) => (i == 2)? "underline":"none")
.style("font-variation-settings", function(d, i){
return `"slnt" ${(i == 3)? -30:0},
"wght" ${(i == 0)? 800:400},
"CASL" 0,
"CRSV" 0,
"MONO" 1`})
}
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
margin = ({top: 40, right: 20, bottom: 220, left: 36})
Insert cell
height = 900
Insert cell
//VARIABLE FONTS! If this visualization isn't working you might need to update or switch browsers (works best on firefox and chrome). I was able to get this running thanks to some example experiments with v-fonts done by (@jmahabal/variable-fonts-playground).

//Recursive (https://github.com/arrowtype/recursive) is a very cool variable font face that can switch between monospace and a typical sans-serif that is still in beta but is planned for release on Google Fonts sometime in the near future. Because its not currently hosted anywhere I had to upload the beta woff2 file. At the time of writing there's no support for Cyrillic or Kanji (which this dataset annoyingly had to have) so hopefully Cyrillic will be supported in the future.

html`
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:300,300i,400,400i,700,700i,900,900i&display=swap&subset=cyrillic,cyrillic-ext,greek,latin-ext,vietnamese" rel="stylesheet">


<style>
@font-face {
font-family: 'Recursive';
font-weight: 300 1000;
src: url(${await FileAttachment("Recursive_VF_1.52.woff2").url()}) format('woff2');
}

.svgBackground {fill: #fffbeb;}

.title {
font: 24px "Lato", sans-serif;
fill: #263c54;
font-weight: 700;
}

.other {
font: 20px "Lato", sans-serif;
fill: #263c54;
font-weight: 400;
}

.init {
font-family: Recursive, Courier, monospace;
font-variation-settings:
"wght" 350,
"MONO" 1,
"CASL" 0,
"slnt" 0,
"CRSV" 0;
}

</style>`
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