topnChart = (years, merges, topn, colorLookup) => {
const svg = d3.create("svg")
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("width", width)
.attr("height", height)
let domain = _.range(years[0], years[1])
const xScale = d3.scaleBand()
.domain(domain)
.range([0, width])
.paddingInner(0.4).paddingOuter(0.1).round(true)
const marginBottom = 20
const marginTop = 20
const yPadding = 4
const yScale = (v, i) => {
const fullY = height-marginBottom-marginTop-(yPadding*topn)
return Math.floor(height - marginBottom - marginTop - (fullY*v) - yPadding*i) + marginTop
}
const xAxis = d3.axisBottom(xScale).tickSizeOuter(0)
const steer = Math.floor((xScale.step() - xScale.bandwidth())/2)
const yTop = d => yScale(d.h + d.vp, d.i)
const yBottom = d => yScale(d.h, d.i)
const yHeight = d => yBottom(d) - yTop(d)
const yMid = d => Math.floor(yBottom(d) - (0.5*yHeight(d)))
const xRight = d => xScale(d.index) + xScale.bandwidth()
const xLeft = d => xScale(d.index)
const xMid = d => Math.floor(xScale(d.index) + 0.5*xScale.bandwidth())
const gRay = (d) => {
return ["M" + xRight(d[0]) + " " + yTop(d[0]),
"C" + (xRight(d[0])+steer) + " " + yTop(d[0]) + " " +
(xLeft(d[1])-steer) + " " + yTop(d[1]) + " " +
xLeft(d[1]) + " " + yTop(d[1]),
"L" + xLeft(d[1]) + " " + yBottom(d[1]),
"C" + (xLeft(d[1])-steer) + " " + yBottom(d[1]) + " " +
(xRight(d[0])+steer) + " " + yBottom(d[0]) + " " +
xRight(d[0]) + " " + yBottom(d[0]),
"Z"].join(" ")
}
//let x = pd.entries()
const title = svg.append("g").append("text")
.attr("x", width/2)
.attr("y", 10)
.attr("text-anchor", "middle")
.text("Antall studenter")
const bars = svg.append("g")
.attr("fill", "steelBlue")
const rays = svg.append("g")
.attr("opacity", 0.75)
const text = svg.append("g")
function numberWithSpaces(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ");
}
//return links.map(d => gRay(d))
svg.append("g")
.attr("fill", "#ccc")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(xAxis);
function percent(x) {
return (Math.round(x*1000.0)/10).toFixed(1) + "%"
}
return Object.assign(svg.node(), {
update(data) {
const pd = new TopNProcessor(data, merges, topn)
bars.selectAll("rect").data(pd.entries(), d => d.index + "_" + d.name)
.join(
enter => enter.append("rect")
.on("mouseover", (e, d) => {
if (d.other) {
title.text(d.name + " institusjoner hadde tilsammen " + percent(d.vp) + " av studentene i " + d.index)
} else {
title.text(d.title + " hadde " + percent(d.vp) + " av studentene i " + d.index)
}
})
.on("mouseout", (e, d) => {
title.text("Antall studenter")
})
//.attr("opacity", 0.1)
.attr("x", d => xScale(d.index))
.attr("y", d => yTop(d))
.attr("width", d => xScale.bandwidth())
.attr("height", d => yHeight(d))
.attr("fill", d => d.other ? "#777" : colorLookup.background(d.name))
.style('opacity', 0)
.transition().duration(500)
.style('opacity', 1)
.selection()
,
//.attr("title", d => d["Institusjonsnavn"] ? d["Institusjonsnavn"] : "")
update => update.transition().duration(500)
.attr("x", d => xScale(d.index))
.attr("y", d => yTop(d))
.attr("width", d => xScale.bandwidth())
.attr("height", d => yHeight(d))
.selection()
)
rays.selectAll("path").data(pd.links(), d => d[0].index + "_" + d[0].name + "_" + d[1].index + "_" + d[1].name)
.join(
enter => enter.append("path")
.attr("fill", d => colorLookup.background(d[1].name))
.attr("d", d => gRay(d)),
update => update.transition().duration(500)
.attr("d", d => gRay(d))
.selection()
)
text.selectAll("text").data(pd.entries(), d => d.index + "_" + d.name)
.join(
enter => enter.append("text")
.attr("fill", d => colorLookup.foreground(d.name))
.attr("x", d => d.other ? 0 : xMid(d))
.attr("y", d => d.other ? 0 : yMid(d))
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.attr("transform", d => d.other ? "translate(" +xMid(d) + "," + yMid(d)+ ") rotate(270)" : null)
.text(d => d.name),
update => update.transition().duration(500)
.attr("x", d => d.other ? 0 : xMid(d))
.attr("y", d => d.other ? 0 : yMid(d))
.selection()
)
return null
}
})
}