parent
0819057c79
commit
aefdade7ea
5 changed files with 170 additions and 194 deletions
@ -0,0 +1,104 @@ |
|||||||
|
const Diagram = { |
||||||
|
build: (eventIndex, data, metaMatrix, chordMatrix) => { |
||||||
|
const svg = d3.select("svg"), |
||||||
|
width = +svg.attr("width"), |
||||||
|
height = +svg.attr("height"), |
||||||
|
outerArcThickness = 5, |
||||||
|
outerRadius = Math.min(width, height) * 0.5 - 100, |
||||||
|
innerRadius = outerRadius - outerArcThickness; |
||||||
|
|
||||||
|
const chord = d3.chord() |
||||||
|
.padAngle(0.05); |
||||||
|
|
||||||
|
const arc = d3.arc() |
||||||
|
.innerRadius(innerRadius) |
||||||
|
.outerRadius(outerRadius); |
||||||
|
|
||||||
|
const ribbon = d3.ribbon() |
||||||
|
.radius(innerRadius); |
||||||
|
|
||||||
|
// const color = d3.scaleOrdinal(d3.schemeCategory20);
|
||||||
|
// const color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||||
|
// const color = d3.scaleOrdinal(d3.interpolateCool);
|
||||||
|
// const color = d3.scaleSequential(d3.interpolateRainbow);
|
||||||
|
|
||||||
|
const color = d3.scaleLinear().domain([0,10]).range(["red", "blue"]).interpolate(d3.interpolateRgb) |
||||||
|
|
||||||
|
// const colors = ["#f1eef6","#d4b9da","#c994c7","#df65b0","#e7298a","#ce1256","#91003f"];
|
||||||
|
// const colors = ["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#0c2c84"];
|
||||||
|
// const colors = ["#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"];
|
||||||
|
// const colors = ["#1B9E77", "#D95F02", "#7570B3", "#E7298A", "#66A61E", "#E6AB02", "#A6761D", "#666666"];
|
||||||
|
// const color = i => colors[i % colors.length];
|
||||||
|
|
||||||
|
const g = svg.append("g") |
||||||
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") |
||||||
|
.datum(chord(chordMatrix)); |
||||||
|
|
||||||
|
const group = g.append("g") |
||||||
|
.attr("class", "groups") |
||||||
|
.selectAll("g") |
||||||
|
.data(function(chords) { return chords.groups; }) |
||||||
|
.enter().append("g"); |
||||||
|
|
||||||
|
group.append("path") |
||||||
|
.style("fill", function(d) { return color(d.index); }) |
||||||
|
.style("stroke", function(d) { return d3.rgb(color(d.index)).darker(); }) |
||||||
|
.attr("d", arc); |
||||||
|
|
||||||
|
g.append("g") |
||||||
|
.attr("class", "ribbons") |
||||||
|
.selectAll("path") |
||||||
|
.data(function(chords) { return chords; }) |
||||||
|
.enter().append("path") |
||||||
|
.attr("d", ribbon) |
||||||
|
.style("fill", function(d) { return color(d.target.index); }) |
||||||
|
.style("stroke", function(d) { return d3.rgb(color(d.target.index)).darker(); }) |
||||||
|
.attr("class", "ribbon") |
||||||
|
// .append("title")
|
||||||
|
// .text(function(d) {
|
||||||
|
// const meta1 = metaMatrix[d.target.index][d.source.index];
|
||||||
|
// const meta2 = metaMatrix[d.source.index][d.target.index];
|
||||||
|
//
|
||||||
|
// const g = meta1.game;
|
||||||
|
//
|
||||||
|
// const t1 = lookup.teams[g.t1];
|
||||||
|
// const t2 = lookup.teams[g.t2];
|
||||||
|
//
|
||||||
|
// const e1 = g.s1e ? `(+${g.s1e} in extended time)` : '';
|
||||||
|
// const e2 = g.s2e ? `(+${g.s2e} in extended time)` : '';
|
||||||
|
//
|
||||||
|
// const p1 = g.s1p ? `(+${g.s1p} in penalties)` : '';
|
||||||
|
// const p2 = g.s2p ? `(+${g.s2p} in penalties)` : '';
|
||||||
|
//
|
||||||
|
// return `${t1}: ${g.s1} ${e1} ${p1}\n${t2}: ${g.s2} ${e2} ${p2}`;
|
||||||
|
// });
|
||||||
|
|
||||||
|
group.append("text") |
||||||
|
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; }) |
||||||
|
.attr("dy", ".35em") |
||||||
|
.attr("transform", function(d) { |
||||||
|
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" |
||||||
|
+ "translate(" + (innerRadius + 26) + ")" |
||||||
|
+ (d.angle > Math.PI ? "rotate(180)" : ""); |
||||||
|
}) |
||||||
|
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) |
||||||
|
.text(function(d) { |
||||||
|
// SORT BY GOALS SCORED
|
||||||
|
// SORT BY ALPHABETICAL
|
||||||
|
// SORT BY COUNTRY SIZE
|
||||||
|
// SORT BY COUNTRY POPULATION
|
||||||
|
// COLOR BY CONTINENT
|
||||||
|
// STRANGE EXTENDED TIME CHILE-BRAZIL - FIX BY HAND? IS BECAUE se1 IS SCORE __GOING INTO__ EXTENDED TIME
|
||||||
|
// console.info(metaMatrix[d.index])
|
||||||
|
const t = data.tourneys[eventIndex].teams[d.index]; |
||||||
|
const c = data.countries[data.teams[t]]; |
||||||
|
return data.countries[data.teams[t]].n; |
||||||
|
}); |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
// const main = (data) => {
|
||||||
|
// const lookup = buildLookup(endpoints, data);
|
||||||
|
// const chordMatrix = buildChordMatrix(lookup.games, lookup.reducedTeams)
|
||||||
|
// const metaMatrix = buildMetaMatrix(lookup.games, lookup.reducedTeams)
|
||||||
|
// };
|
@ -0,0 +1,19 @@ |
|||||||
|
const fetch = (url) => new Promise((resolve, reject) => { |
||||||
|
const listener = ({ srcElement: req }) => { |
||||||
|
req.status === 200 ? resolve(req.responseText) : reject("busted"); |
||||||
|
}; |
||||||
|
|
||||||
|
const req = new XMLHttpRequest(); |
||||||
|
req.addEventListener('load', listener); |
||||||
|
req.open('GET', url); |
||||||
|
req.send(); |
||||||
|
}); |
||||||
|
|
||||||
|
const main = function(strData) { |
||||||
|
const json = JSON.parse(strData); |
||||||
|
const metaMatrix = Matrices.buildMetaMatrix(json, "1930"); |
||||||
|
const chordMatrix = Matrices.buildChordMatrix(json, "1930"); |
||||||
|
Diagram.build("1930", json, metaMatrix, chordMatrix); |
||||||
|
} |
||||||
|
|
||||||
|
fetch('worldcup.json').then(main); |
@ -0,0 +1,39 @@ |
|||||||
|
const Matrices = { |
||||||
|
// A x A array of nulls; null-null relationships are omitted in chord diagram.
|
||||||
|
createEmptyMatrix: (len) => { |
||||||
|
const empty = Array.apply(null, Array(len)); |
||||||
|
return empty.map(() => empty.map(() => null)); |
||||||
|
}, |
||||||
|
|
||||||
|
// Identical structure of chord matrix but with { game, team }.
|
||||||
|
buildMetaMatrix: (json, eventKey) => { |
||||||
|
const teams = json.tourneys[eventKey].teams; |
||||||
|
const matrix = Matrices.createEmptyMatrix(teams.length); |
||||||
|
|
||||||
|
json.tourneys[eventKey].games.forEach(g => { |
||||||
|
const i1 = teams.indexOf(g.t1); |
||||||
|
const i2 = teams.indexOf(g.t2); |
||||||
|
|
||||||
|
matrix[i1][i2] = { game: g, team: g.t1 }; |
||||||
|
matrix[i2][i1] = { game: g, team: g.t2 }; |
||||||
|
}, []); |
||||||
|
|
||||||
|
return matrix; |
||||||
|
}, |
||||||
|
|
||||||
|
// Scalar data points (sum of goals scored).
|
||||||
|
buildChordMatrix: (json, eventKey) => { |
||||||
|
const teams = json.tourneys[eventKey].teams; |
||||||
|
const matrix = Matrices.createEmptyMatrix(teams.length); |
||||||
|
|
||||||
|
json.tourneys[eventKey].games.forEach(g => { |
||||||
|
const i1 = teams.indexOf(g.t1); |
||||||
|
const i2 = teams.indexOf(g.t2); |
||||||
|
|
||||||
|
matrix[i1][i2] = g.s1 + g.se1 + g.sp1; |
||||||
|
matrix[i2][i1] = g.s2 + g.se2 + g.sp2; |
||||||
|
}, []); |
||||||
|
|
||||||
|
return matrix; |
||||||
|
}, |
||||||
|
}; |
Loading…
Reference in new issue