Published
Edited
Aug 23, 2021
Insert cell
md`# Centri Tech Foundation Vizualization`
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
Insert cell
Insert cell
Insert cell
Insert cell
chart4 = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width +80, height/1.8]);
svg
.append('defs')
.append('pattern')
.attr('id', 'diagonalHatch')
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', 4)
.attr('height', 4)
.append('path')
.attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
.attr('stroke', '#ccc')
.attr('stroke-width', 0.6);
const ks = Object.keys(categories);
const totals = [0, d3.sum(categories["education"][0].map(d => test[d])), d3.sum(categories["socioeconomic"][0].map(d => test[d])),d3.sum(categories["housing"][0].map(d => test[d])),d3.sum(categories["technology"][0].map(d => test[d])), 100];
var minibar = svg.append('g').attr('transform', 'translate(0,80)').selectAll(".minibar")
.data(Object.keys(categories))
.enter();
svg.append("text")
.attr("x", 240)
.attr("y", (d, i) => 100)
.style("fill", "black")
.attr("text-anchor", "end")
.text(test.name);
minibar.append("text")
.attr("x", xmod3(100)+10)
.attr("y",20)
.style("fill", "black")
.attr("text-anchor", "start")
.text(test.score);
minibar.append("rect")
.attr("x", (d, i) => xmod3(0))
.attr("y",0)
.attr("width", (d, i) => xmod3(100) - 250)
.style("fill", 'url(#diagonalHatch)')
.attr("height", 30);
minibar.append("rect")
.attr("x", (d, i) => xmod3(d3.sum(totals.slice(0,i+1))))
.attr("width", (d, i) => d === "null" ? xmod3(100 - test.score) - 250 : xmod3(totals[i+1]) - 250)
.style("fill", d => d === "null" ? 'url(#diagonalHatch)' : colorCats(d))
.attr("height", 30)
.on("click", onTabClick);
minibar.append("text")
.attr("x", (d, i) => d === "null" ? xmod3(d3.sum(totals.slice(0,i+1))) + ((xmod3(100 - test.score) - 250)/2) : xmod3(d3.sum(totals.slice(0,i+1))) + ((xmod3(totals[i+1]) - 250)/2))
.attr("y", -10)
.style("fill", "black")
.text(d => d === "null" ? "" : categories[d][1])
.style("text-anchor", "end")
.attr("transform", function (d) {
var xRot = d3.select(this).attr("x");
var yRot = d3.select(this).attr("y");
return `rotate(35, ${xRot}, ${yRot} )` //ES6 template literal to set x and y rotation points
});
var header = svg.append('g').attr('transform', 'translate(0,120)').selectAll(".header")
.data([test])
.enter();
header.append("rect")
.attr("width", width / 4)
.attr("y", 0)
.attr("x", 80)
.attr("height", 60)
.attr("fill", colorCats(cats))
.style("stroke-dasharray", "5,5")
.style("stroke", "#ccc")
.attr("class", "header");
header.append('text')
.attr('x', 110)
.attr('y', 40)
.style("fill", "#fff")
.text(d => `Goal: ${categories[cats][1]}`);
var score = svg.append('g').attr('transform', 'translate(80,180)').selectAll(".score")
.data([test])
.enter();
score.append("rect")
.attr("width", width / 4)
.attr("y", 0)
.attr("height", 120)
.attr("fill", "#fff")
.style("stroke-dasharray", "5,5")
.style("stroke", "#ccc")
.attr("class", "header");
const other = svg.append('g').attr('transform', `translate(${(width / 4) + 80},120)`).selectAll('.other')
.data(Object.keys(categories).filter(x => x !== cats))
.enter()
other.append("rect")
.attr("width", width / 5.5)
.attr("x", 0)
.attr("y", (d, i) => i * (180/3))
.attr("height", 60)
.attr("fill", d => colorCats(d))
.style("stroke-dasharray", "5,5")
.style("stroke", "#ccc")
.attr("class", "header")
.on("click", onTabClick);
other.append('text')
.attr('x',20)
.attr('y', (d, i) => (i * (180/3)) + 30)
.attr("text-anchor", "left")
.text(d => `${categories[d][1]}`);
other.append('text')
.attr('x', (width / 5.5) - 8)
.attr('y', (d, i) => (i * (180/3)) + 30)
.attr("style", "font-size: 18px; font-weight: 700")
.attr("text-anchor", "end")
.text(d => `${d3.sum(categories[d][0].map(e => test[e]))}`);
var totalscore = svg.append('g').attr('transform', `translate(${(3 * width) / 4},120)`).selectAll(".total-score")
.data([test])
.enter()
totalscore.append("rect")
.attr("width", width / 4)
.attr("x", 0)
.attr("y", 0)
.attr("height", 180)
.attr("fill", "#fff")
.style("stroke-dasharray", "5,5")
.style("stroke", "#ccc")
.attr("class", "header");
const total = d3.sum(categories[cats][0].map(d => test[d]))
const sumlist = mod2.map(x => d3.sum(categories[cats][0].map(y => x[y])))
const max = d3.max(sumlist)
const min = d3.min(sumlist)
const avg = d3.mean(sumlist)


score.selectAll(".score-label")
.data([test])
.enter()
.append("text")
.text(d => `${total}`)
.attr('style', "font-size: 80px;")
.attr("x", width / 8)
.attr("y", 80)
.attr("text-anchor", "middle");
score.selectAll(".score-label")
.data([test])
.enter()
.append("text")
.text(`min: ${Number(min).toFixed(2)} | avg: ${Number(avg).toFixed(2)} | max: ${Number(max).toFixed(2)} `)
.attr('class', "stats")
.attr("x", width / 8)
.attr("y", 100)
.attr("text-anchor", "middle");
totalscore.selectAll(".totalscore-label-overall")
.data([test])
.enter()
.append("text")
.text("OVERALL SCORE")
.attr("x", width / 8)
.attr("y", 50)
.attr("class", "data-head")
.attr("text-anchor", "middle");
totalscore.selectAll(".totalscore-label")
.data([test])
.enter()
.append("text")
.text(d => d["score"])
.attr('style', "font-size: 80px;")
.attr("x", width / 8)
.attr("y", 120)
.attr("text-anchor", "middle");
totalscore.selectAll(".totalscore-label-city")
.data([test])
.enter()
.append("text")
.text(test["name"])
.attr("class", "data-head")
.attr("x", width / 8)
.attr("y", 160)
.attr("text-anchor", "middle");
const innerSVG = svg.append('g').attr('transform', 'translate(0,330)');
innerSVG.append('g').call(xAxisMod);
innerSVG.append('g').call(yAxisMod);
const indicatorBars = innerSVG.append('g').selectAll('g').data(categories[cats][0]).join('g');
indicatorBars.append("rect")
.attr('rx', 6)
.attr('ry', 6)
.attr("x", d => xmod(0))
.attr('y', (d, i) => ymod(d))
.attr("stroke", "#aaa")
.attr('height', 15)
.attr('width', width - margin.right - xmod(0))
.attr('fill', 'url(#diagonalHatch)');
indicatorBars.append('rect')
.attr('rx', 6)
.attr('ry', 6)
.attr("width", (d, i) => xmod(test[d]) - xmod(0))
.attr("x", d => xmod(0))
.attr('y', (d, i) => ymod(d))
.style("fill", colorCats(cats))
.attr('class', 'scoring')
.attr("height", 15);
return svg.node();
}
Insert cell
mutable check = "k"
Insert cell
onTabClick = (event, d) => {
viewof cats.value = d;
viewof cats.dispatchEvent(new CustomEvent("input"));
}
Insert cell
changeCity = (event, d) => {
const i = cities.filter(x => String(x).includes(d[1].name))[0]
viewof city.value = i;
viewof city.dispatchEvent(new CustomEvent("input"));
}
Insert cell
function click(event, name) {
mutable check = name;
}
Insert cell
check
Insert cell
categories[cats][0]
Insert cell
Insert cell
chart2 = {
const svg = d3.create("svg")
.attr("viewBox", [-20, 0, width, height]);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);

const g = svg.append("g")
.selectAll("g")
.data(series)
.join("g")
.attr("fill", d => color(d.key));
g.append("g")
.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", (d) => x(d[0]))
.attr("y", (d, i) => y_barpoint(d.data.name))
.attr("width", d => x(d[1]) - x(d[0]))
.attr("height", y_bar.bandwidth());
Object.assign(svg.node(), {
update2(names) {
y_barpoint.domain(names);
g.transition()
.delay((d, i) => i * 10)
.attr("transform", d => `translate(0,${y_barpoint(d.name)})`)
}});
}
Insert cell
keys = data.columns.slice(5)
Insert cell
keys
Insert cell
keys
Insert cell
keys2 = data.columns.slice(5).concat(["name"])
Insert cell
key_idx = d3.zip(keys, d3.range(6))
Insert cell
key_idx.filter(d => d[0] === primary2)
Insert cell
mod2
Insert cell
proto_data[1]
Insert cell
updater = {
const index = d3.range(302);
const order = primary === "name" ? d3.ascending : d3.descending;
const on_key = primary === "rank" ? "total" : primary;
index.sort((i, j) => order(proto_data[i][on_key], proto_data[j][on_key]));
chart3.updater(d3.permute(proto_data.map(s => s.name), index));
}
Insert cell
rank_range = d3.range(302).sort((i, j) => d3.ascending(proto_data[i]["total"], proto_data[j]["total"]));
Insert cell
rank = new Map(d3.zip(d3.permute(proto_data.map(s => s.name), d3.range(302)), d3.range(1, 303)));
Insert cell
proto_data = mod3
Insert cell
update2 = {
const index = d3.range(30);
const order = primary2 === "name" ? d3.ascending : d3.descending;
index.sort((i, j) => order(data[i][primary2], data[j][primary2]));
chart2.update2(d3.permute(data.map(s => s.name), index));
}
Insert cell
index = d3.range(series[0].length);

Insert cell
series.map(d => d3.permute(d.map(s => s.data.name), index))
Insert cell
update = {
const index = d3.range(data.length);
const order = primary === "name" ? d3.ascending : d3.descending;
index.sort((i, j) => order(data[i][primary], data[j][primary]));
chart.update(d3.permute(data.map(d => d.name), index));
}
Insert cell
series = d3.stack()
.keys(keys)
(data)
.map(d => (d.forEach(v => v.key = d.key), d))
Insert cell
data = d3.csvParse(await FileAttachment("test_data_2.csv").text(), (d, i, columns) => (d3.autoType(d)))
Insert cell
mod = d3.csvParse(await FileAttachment("modtest@1.csv").text(), (d, i, columns) => (d3.autoType(d)))
Insert cell
categories = ({
education : [mod2.columns.slice(2,5), "Education Access"],
socioeconomic: [mod2.columns.slice(5,8), "Socioeconomic"],
housing: [mod2.columns.slice(8,11), "Housing"],
technology: [mod2.columns.slice(11, 14), "Technology"],
})
Insert cell
mod2 = d3.csvParse(await FileAttachment("modtest_2_v2.csv").text(), (d, i, columns) => (d3.autoType(d)))
Insert cell
mod3 = d3.csvParse(await FileAttachment("full_ctf_v2.csv").text(), (d, i, columns) => (d3.autoType(d)))
.map(x => {x.name = x.name.split(", ")[0]; return x})
Insert cell
test = mod2.filter(d => d.name == city)[0]
Insert cell
x = d3.scaleLinear()
.domain([0, 100])
.rangeRound([margin.left, width - margin.right])
Insert cell
x2 = d3.scaleLinear()
.domain([0, 100])
.rangeRound([100, 700])
Insert cell
xmod3 = d3.scaleLinear()
.domain([0, 100])
.rangeRound([250, width - 100])
Insert cell
xmod= d3.scaleLinear()
.domain([0, 25])
.rangeRound([100, width - margin.right])
Insert cell
y = d3.scalePoint()
.domain(data.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(1)
Insert cell
testcols = Object.keys(test).slice(2,5)
Insert cell
ymod = d3.scalePoint()
.domain(categories[cats][0])
.range([0, categories[cats][0].length/0.04])
.padding(0.1)
Insert cell
ymod2 = d3.scalePoint()
.domain(cities.sort())
.range([0, cities.length/0.04])
.padding(0.1)
Insert cell
250/0.04
Insert cell
ymod3 = d3.scalePoint()
.domain(proto_data.map(x => x.name))
.range([30, 250/0.04])
.padding(0)
Insert cell
proto_data
Insert cell
categories[cats]
Insert cell
y_barpoint = d3.scalePoint()
.domain(data.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(1)
Insert cell
y_bar = d3.scaleBand()
.domain(data.map(d => d.name))
.range([margin.top, height - margin.bottom])
.padding(0.1)
Insert cell
color = d3.scaleOrdinal()
.domain(keys)
.range(d3.schemeSpectral[keys.length])
.unknown("#ccc")
Insert cell
Object.keys(categories)
Insert cell
keys
Insert cell
colorCats = d3.scaleOrdinal()
.domain(Object.keys(categories))
.range(d3.schemeTableau10.slice(0,4))
.unknown("#ccc");
Insert cell
colorCatsTwo = d3.scaleOrdinal()
.domain(clusters)
.range(d3.schemeTableau10.slice(0,4))
.unknown("#ccc");
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(null))
.call(g => g.selectAll(".tick line").clone().attr("stroke-opacity", 0.1).attr("y2", height - margin.bottom))
.call(g => g.selectAll(".domain").remove())
Insert cell
xAxisTwo = g => g
.attr("transform", `translate(30,${margin.top})`)
.call(d3.axisTop(x2).ticks(5))
.call(g => g.selectAll(".tick line").clone().attr("stroke-opacity", 0.1).attr("y2", height - margin.bottom))
.call(g => g.selectAll(".domain").remove())
Insert cell
xAxisModTwo = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(null))
.call(g => g.selectAll(".tick line").clone().attr("stroke-opacity", 0.1).attr("y2", height - margin.bottom))
.call(g => g.selectAll(".domain").remove())
Insert cell
xAxisMod = g => g
.attr("transform", `translate(0,${0})`)
.call(d3.axisTop(xmod).ticks(5))
.call(g => g.selectAll(".domain").remove())
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_barpoint).tickSizeOuter(0))
.call(g => g.select(".domain").remove());
/**.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.style("font-size", "14px")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.name))*/
Insert cell
yAxisMod = g => g
.attr("transform", `translate(${100},5)`)
.call(d3.axisLeft(ymod).tickPadding(10))
.call(g => g.select(".domain").remove());
Insert cell
yAxisBar = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y_bar).tickSizeOuter(0))
.call(g => g.select(".domain").remove())
/**.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.style("font-size", "14px")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text(data.name))*/
Insert cell
height = 3000
Insert cell
formatValue = x => isNaN(x) ? "N/A" : x.toLocaleString("en")
Insert cell
margin=({top: 20, right: 10, bottom:10, left:10})
Insert cell
async function addWebFont(selection, fontName, fontURL, fontType = 'woff2') {
const fontData = await toDataURL(fontURL);
return selection.append('style').text(`
@font-face {
font-family: '${fontName}';
src: url(${fontData}) format('${fontType}');
}
`);
};
Insert cell
import {palettes} from "@tomshanley/cheysson-color-palettes"
Insert cell
import {toDataURL} from '@mootari/embedding-fonts-into-an-svg@171';
Insert cell
d3 = require("d3@6", "d3-array@3", "d3-color@3", "d3-scale-chromatic@3")
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