Compare commits

..

5 Commits

  1. 80
      index.html
  2. 438
      js/bundle.js
  3. 242
      js/diagram.js
  4. 66
      js/hotfixes.js
  5. 139
      js/index.js
  6. 4
      js/matrices.js

@ -21,7 +21,7 @@
<blockquote> <blockquote>
Explore D3's chord diagrams using World Cup match data. Use Haskell to build JSON parsers. Explore D3's chord diagrams using World Cup match data. Use Haskell to build JSON parsers.
<br><br> <br><br>
Visualization should invite interaction to create and answer questions such as: The visualization should invite interaction to create and answer questions such as:
<ul> <ul>
<li>How many times has Iran competed in a World Cup?</li> <li>How many times has Iran competed in a World Cup?</li>
<li>Which teams played the in final in 1986?</li> <li>Which teams played the in final in 1986?</li>
@ -36,7 +36,7 @@
</p> </p>
<p> <p>
This visualization is created using D3 and Three.js libraries. This visualization is created using D3.js. Note that null-null relationships are not displayed on chord diagrams. As a result, matches with a score of 0-0 are not shown.
</p> </p>
<hr> <hr>
@ -53,40 +53,77 @@
</div> </div>
</div> </div>
<div class="notes"> <!-- <div class="notes">
<h5>TODO</h5>
1930: OK 1930: OK
1934: Patched. 1934: Patched.
Italy Spain replay 1-0! OK Italy Spain replay 1-0!
1938: 1938:
Switzerland Germany replay 4-2 OK Switzerland Germany 1-1 replay 4-2
Cuba Romania replay 3-2 OK Cuba Romania 3-3 replay 3-2
Brazil Czech replay 2-1 OK Brazil Czech 1-1 replay 2-1
1950: OK 1950: OK
1954: 1954:
Germany Turkey playoff 7-2 OK Germany Turkey 4-1 playoff 7-2
Switzerland Italy playoff 4-1 OK Switzerland Italy 2-1 playoff 4-1
Germany Hungary final 3-2 OK Germany Hungary 3-8 final 3-2
1958: 1958:
Northern Ireland Czech playoff 2-1
Wales Hungary replay 2-1
Sweden Wales 0-0 Sweden Wales 0-0
Brazil England 0-0 Brazil England 0-0
Russia England replay 1-0 OK Northern Ireland Czech 1-0 playoff 2-1
OK Wales Hungary 1-1 replay 2-1
OK Russia England 1-0 replay 2-2
1962: 1962:
Germany Italy 0-0
Brazil Czech Republic 0-0
Hungary Argentina 0-0
England Bulgaria 0-0
1966: 1966:
England Uruguay 0-0
Mexico Uruguay 0-0
Argentina Germany 0-0
1970: 1970:
Mexico Russia 0-0
Uruguay Italy 0-0
Israel Italy 0-0
1974: 1974:
Australia Chile 0-0
Brazil Yugoslavia 0-0
Scotland Brazil 0-0
Sweden Bulgaria 0-0
1978: 1978:
Germany Poland 0-0
Germany Tunisia 0-0
Brazil Spain 0-0
Netherlands Peru 0-0
Italy Germany 0-0
Argentina Brazil 0-0
1982: 1982:
Italy Poland 0-0
Peru Cameroon 0-0
Poland Cameroon 0-0
Serbia Northern Ireland 0-0
Russia Poland 0-0
Germany England 0-0
Spain England 0-0
1986: 1986:
Scotland Uruguay 0-0
Morocco Poland 0-0
1990: 1990:
Uruguay Spain 0-0
England Netherlands 0-0
Ireland Egypt 0-0
1994: 1994:
South Korea Bolivia 0-0
Ireland Norway 0-0
Brazil Sweden replay
1998: 1998:
Paraguay Bulgaria 0-0
Spain Paraguay 0-0
Netherlands Belgium 0-0
2002: 2002:
France Uruguay 0-0 France Uruguay 0-0
Nigeria England 0-0 Nigeria England 0-0
Brazil Turkey replay 1-0 OK Brazil Turkey replay 1-0
2006: 2006:
Trinidad Tobago Sweden 0-0 Trinidad Tobago Sweden 0-0
Netherlands Argentina 0-0 Netherlands Argentina 0-0
@ -106,18 +143,7 @@
Costa Rica England 0-0 Costa Rica England 0-0
Ecuador France 0-0 Ecuador France 0-0
Iran-Nigeria 0-0 Iran-Nigeria 0-0
Brazil Chile replay </div> -->
Costa Rica Greece replay
Netherlands Costa Rica replay
Argentina Netherlands replay
gremlins?
african dictator visualization
update gogs
update drone
tweet it!
</div>
</body> </body>
</html> </html>
` `

@ -58,13 +58,12 @@
var _ui2 = _interopRequireDefault(_ui); var _ui2 = _interopRequireDefault(_ui);
var _hotfixes = __webpack_require__(13); var _hotfixes = __webpack_require__(5);
var _hotfixes2 = _interopRequireDefault(_hotfixes); var _hotfixes2 = _interopRequireDefault(_hotfixes);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
__webpack_require__(5);
__webpack_require__(6); __webpack_require__(6);
__webpack_require__(7); __webpack_require__(7);
__webpack_require__(8); __webpack_require__(8);
@ -72,6 +71,7 @@
__webpack_require__(10); __webpack_require__(10);
__webpack_require__(11); __webpack_require__(11);
__webpack_require__(12); __webpack_require__(12);
__webpack_require__(13);
var main = { var main = {
changeEvent: function changeEvent(e) { changeEvent: function changeEvent(e) {
@ -105,22 +105,46 @@
main.updateUI(); main.updateUI();
}, },
getRounds: function getRounds(eventKey) { // DEPRECATED? 161120
var rounds = {}; // getRounds: (eventKey) => {
// const rounds = {};
//
// main.json.tourneys[eventKey].games.forEach(game => {
// const name = UI.getRoundName(main.json.rounds[game.rId]);
// if (rounds[name] === undefined) {
// rounds[name] = [];
// }
//
// rounds[name].push({
// id: game.rId,
// name: name,
// });
// });
//
// return rounds;
// },
getDuplicates: function getDuplicates(eventKey) {
var games = {};
main.json.tourneys[eventKey].games.forEach(function (game) { main.json.tourneys[eventKey].games.forEach(function (game) {
var name = _ui2.default.getRoundName(main.json.rounds[game.rId]); var teams = [game.t1, game.t2].sort();
if (rounds[name] === undefined) { var addr = teams[0] + '-' + teams[1];
rounds[name] = [];
if (games[addr] === undefined) {
games[addr] = [];
} }
rounds[name].push({ games[addr].push(game);
id: game.rId,
name: name
});
}); });
return rounds; return Object.keys(games).reduce(function (acc, k) {
if (games[k].length > 1) {
acc.push(games[k]);
}
return acc;
}, []);
}, },
generateUI: function generateUI() { generateUI: function generateUI() {
@ -135,10 +159,103 @@
updateUI: function updateUI() { updateUI: function updateUI() {
var state = main.getState(); var state = main.getState();
var eventKey = state.eventKey;
var matrix = _matrices2.default.buildMatrix(main.json, eventKey);
var duplicates = main.getDuplicates(eventKey);
var tmp = _diagram2.default.buildChords({
data: main.json,
sort: state.sort,
eventKey: eventKey,
matrix: matrix,
SORT_TYPES: _ui2.default.SORT_TYPES
});
var getScore = function getScore(game) {
var s1 = game.s1;
var s2 = game.s2;
if (game.sp1 !== null) {
s1 = game.sp1;
} else if (game.se1 !== null) {
s1 = game.se1;
}
if (game.sp2 !== null) {
s2 = game.sp2;
} else if (game.se2 !== null) {
s2 = game.se2;
}
return { s1: s1, s2: s2 };
};
var chords = tmp.reduce(function (acc, d) {
var tSrc = main.json.tourneys[eventKey].teams[d.source.index].tId;
var tTgt = main.json.tourneys[eventKey].teams[d.target.index].tId;
d.game = main.json.tourneys[eventKey].games.find(function (g) {
return g.t1 === tSrc && g.t2 === tTgt || g.t1 === tTgt && g.t2 === tSrc;
});
duplicates.forEach(function (games) {
if (games[0] === d.game || games[1] === d.game) {
var gameNew = games[0] === d.game ? games[1] : games[0];
var sourceNew = Object.assign({}, d.source);
var targetNew = Object.assign({}, d.target);
var sourceAngle = d.source.endAngle - d.source.startAngle;
var targetAngle = d.target.endAngle - d.target.startAngle;
var _getScore = getScore(games[0]),
s1g0 = _getScore.s1,
s2g0 = _getScore.s2;
var _getScore2 = getScore(games[1]),
s1g1 = _getScore2.s1,
s2g1 = _getScore2.s2;
var totals = { src: 0, tgt: 0 };
var offset = { src: 0, tgt: 0 };
totals.src += games[0].t1 === tSrc ? s1g0 : s2g0;
totals.tgt += games[0].t1 === tTgt ? s1g0 : s2g0;
totals.src += games[1].t1 === tSrc ? s1g1 : s2g1;
totals.tgt += games[1].t1 === tTgt ? s1g1 : s2g1;
offset.src = games[0].t1 === tSrc ? s1g0 : s2g0;
offset.tgt = games[0].t1 === tTgt ? totals.tgt - s1g0 : totals.tgt - s2g0;
sourceNew.startAngle = d.source.startAngle + sourceAngle * (offset.src / totals.src);
d.source.endAngle = d.source.startAngle + sourceAngle * (offset.src / totals.src);
targetNew.endAngle = d.target.startAngle + targetAngle * (offset.tgt / totals.tgt);
d.target.startAngle = d.target.startAngle + targetAngle * (offset.tgt / totals.tgt);
acc.push({ source: sourceNew, target: targetNew, game: gameNew });
}
});
acc.push(d);
return acc;
}, []);
chords.groups = tmp.groups;
var matrix = _matrices2.default.buildMatrix(main.json, state.eventKey);
_diagram2.default.clear(); _diagram2.default.clear();
_diagram2.default.build(main.json, state.eventKey, state.sort, state.scheme, _ui2.default.SORT_TYPES, matrix);
var color = _diagram2.default.buildColorScheme({
scheme: parseInt(state.scheme),
len: main.json.tourneys[state.eventKey].teams.length
});
var container = _diagram2.default.buildContainer(chords);
_diagram2.default.buildArcs({ container: container, color: color, eventKey: eventKey, data: main.json });
_diagram2.default.buildRibbons({ container: container, color: color, chords: chords, eventKey: eventKey, data: main.json });
_ui2.default.updateTourneyPane(state.eventKey); _ui2.default.updateTourneyPane(state.eventKey);
_ui2.default.updateEventsPane(state.eventKey); _ui2.default.updateEventsPane(state.eventKey);
@ -150,7 +267,7 @@
fetch: function fetch(url) { fetch: function fetch(url) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var listener = function listener(_ref) { var listener = function listener(_ref) {
var req = _ref.srcElement; var req = _ref.target;
req.status === 200 ? resolve(req.responseText) : reject("busted"); req.status === 200 ? resolve(req.responseText) : reject("busted");
}; };
@ -267,8 +384,8 @@
s2 += g.s2; s2 += g.s2;
} }
matrix[i1][i2] = g.s1 + g.se1 + g.sp1; matrix[i1][i2] = s1;
matrix[i2][i1] = g.s2 + g.se2 + g.sp2; matrix[i2][i1] = s2;
}, []); }, []);
return matrix; return matrix;
@ -295,6 +412,8 @@
value: true value: true
}); });
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _sorter = __webpack_require__(3); var _sorter = __webpack_require__(3);
var _sorter2 = _interopRequireDefault(_sorter); var _sorter2 = _interopRequireDefault(_sorter);
@ -422,82 +541,81 @@
return team.ga; return team.ga;
}, },
build: function build(data, eventKey, sort, scheme, SORT_TYPES, matrix) { buildChords: function buildChords(_ref) {
var svg = d3.select("svg"), var matrix = _ref.matrix,
width = svg.attr("width"), data = _ref.data,
height = svg.attr("height"), eventKey = _ref.eventKey,
outerArcThickness = 5, sort = _ref.sort,
outerRadius = Math.min(width, height) * 0.5 - 130, SORT_TYPES = _ref.SORT_TYPES;
innerRadius = outerRadius - outerArcThickness;
var chords = d3.chord().padAngle(0.05).call(null, matrix); var chords = d3.chord().padAngle(0.05).call(null, matrix);
var sortedChords = chords;
switch (sort) { switch (sort) {
case SORT_TYPES.COUNTRY: case SORT_TYPES.COUNTRY:
sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getCountryName.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); return _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getCountryName.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey));
break;
case SORT_TYPES.GOALS: case SORT_TYPES.GOALS:
sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getGoalsFor.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); return _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getGoalsFor.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey));
break;
case SORT_TYPES.POPULATION: case SORT_TYPES.POPULATION:
sortedChords = _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getPopulation.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey)); return _sorter2.default.sort(chords, 0, chords.groups.length - 1, Diagram.getPopulation.bind(null, data, eventKey), Diagram.swapGroups.bind(null, data, eventKey));
break;
} }
var arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius); return chords;
},
var ribbon = d3.ribbon().radius(innerRadius); buildDimensions: function buildDimensions() {
var svgW = d3.select("svg").attr("width");
var svgH = d3.select("svg").attr("height");
var arcT = 5;
var outerR = Math.min(svgW, svgH) * 0.5 - 130;
var innerR = outerR - arcT;
var len = data.tourneys[eventKey].teams.length; return { svgW: svgW, svgH: svgH, arcT: arcT, outerR: outerR, innerR: innerR };
var color = d3.scaleOrdinal(d3.schemeCategory20); },
(function () { buildColorScheme: function buildColorScheme(_ref2) {
switch (parseInt(scheme)) { var scheme = _ref2.scheme,
case 1: len = _ref2.len;
color = d3.scaleLinear().domain([0, len]).range(["#fff", "green"]).interpolate(d3.interpolateRgb);
break;
case 2:
var colors = ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"];
color = function color(i) {
return colors[i % colors.length];
};
break;
case 3:
color = d3.scaleLinear().domain([0, len]).range(["red", "blue"]).interpolate(d3.interpolateRgb);
break;
case 4:
color = d3.scaleOrdinal(d3.schemeCategory10);
break;
}
})();
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")").datum(sortedChords); if (scheme === 1) {
return d3.scaleLinear().domain([0, len]).range(["#fff", "green"]).interpolate(d3.interpolateRgb);
} else if (scheme === 2) {
var _ret = function () {
var colors = ["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"];
var group = g.append("g").attr("class", "groups").selectAll("g").data(function (chords) { return {
return chords.groups; v: function v(i) {
}).enter().append("g"); return colors[i % colors.length];
}
};
}();
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
} else if (scheme === 3) {
return d3.scaleLinear().domain([0, len]).range(["red", "blue"]).interpolate(d3.interpolateRgb);
} else if (scheme === 4) {
return d3.scaleOrdinal(d3.schemeCategory10);
}
group.append("path").style("fill", function (d) { return d3.scaleOrdinal(d3.schemeCategory20);
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) { buildRibbons: function buildRibbons(_ref3) {
return chords; var container = _ref3.container,
}).enter().append("path").attr("d", ribbon).attr("data-round-id", function (d) { color = _ref3.color,
var t1 = data.tourneys[eventKey].teams[d.source.index]; chords = _ref3.chords,
var t2 = data.tourneys[eventKey].teams[d.target.index]; data = _ref3.data,
eventKey = _ref3.eventKey;
var game = data.tourneys[eventKey].games.find(function (v) { var dimensions = Diagram.buildDimensions();
return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId); var ribbon = d3.ribbon().radius(dimensions.innerR);
});
return game.rId; container.append("g").attr("class", "ribbons").selectAll("path").data(function (chords) {
}).style("fill", function (d) { return chords;
}).enter().append("path").attr("d", ribbon).attr('data-round-id', function (d) {
return d.game.rId;
}).style('fill', function (d) {
return color(d.target.index); return color(d.target.index);
}).style("stroke", function (d) { }).style('stroke', function (d) {
return d3.rgb(color(d.target.index)).darker(); return d3.rgb(color(d.target.index)).darker();
}).classed("ribbon", true).append("title").text(function (d) { }).classed("ribbon", true).append("title").text(function (d) {
var t1 = data.tourneys[eventKey].teams[d.source.index]; var t1 = data.tourneys[eventKey].teams[d.source.index];
@ -506,60 +624,74 @@
var c1 = data.countries[t1.cId]; var c1 = data.countries[t1.cId];
var c2 = data.countries[t2.cId]; var c2 = data.countries[t2.cId];
var game = data.tourneys[eventKey].games.find(function (v) { var s1 = d.game.t1 === t1.tId ? d.game.s1 : d.game.s2;
return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId); var s2 = d.game.t2 === t2.tId ? d.game.s2 : d.game.s1;
});
var s1 = game.t1 === t1.tId ? game.s1 : game.s2;
var s2 = game.t2 === t2.tId ? game.s2 : game.s1;
if (game.sp1 !== null && game.sp2 !== null) { if (d.game.sp1 !== null && d.game.sp2 !== null) {
s1 = game.t1 === t1.tId ? game.sp1 : game.sp2; s1 = d.game.t1 === t1.tId ? d.game.sp1 : d.game.sp2;
s2 = game.t2 === t2.tId ? game.sp2 : game.sp1; s2 = d.game.t2 === t2.tId ? d.game.sp2 : d.game.sp1;
s1 += " (Penalties)"; s1 += " (Penalties)";
s2 += " (Penalties)"; s2 += " (Penalties)";
} else if (game.se1 !== null && game.se2 !== null) { } else if (d.game.se1 !== null && d.game.se2 !== null) {
s1 = game.t1 === t1.tId ? game.se1 : game.se2; s1 = d.game.t1 === t1.tId ? d.game.se1 : d.game.se2;
s2 = game.t2 === t2.tId ? game.se2 : game.se1; s2 = d.game.t2 === t2.tId ? d.game.se2 : d.game.se1;
s1 += " (Extended time)"; s1 += " (Extended time)";
s2 += " (Extended time)"; s2 += " (Extended time)";
} }
// const s1 = game.t1 === t1.tId ? game.s1 : game.s2; return c1 + ': ' + s1 + '\n' + c2 + ': ' + s2 + '\n' + data.rounds[d.game.rId];
// const s2 = game.t2 === t2.tId ? game.s2 : game.s1;
//
// const e1 = game.se1 ? `(+${game.se1} in extended time)` : '';
// const e2 = game.se2 ? `(+${game.se2} in extended time)` : '';
//
// const p1 = game.sp1 ? `(+${game.sp1} in penalties)` : '';
// const p2 = game.sp2 ? `(+${game.sp2} in penalties)` : '';
return c1 + ': ' + s1 + '\n' + c2 + ': ' + s2 + '\n' + data.rounds[game.rId];
}); });
},
buildArcs: function buildArcs(_ref4) {
var container = _ref4.container,
color = _ref4.color,
eventKey = _ref4.eventKey,
data = _ref4.data;
var dimensions = Diagram.buildDimensions();
var arc = d3.arc().innerRadius(dimensions.innerR).outerRadius(dimensions.outerR);
var group = container.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);
group.append("text").each(function (d) { group.append("text").each(function (d) {
d.angle = (d.startAngle + d.endAngle) / 2; d.angle = (d.startAngle + d.endAngle) / 2;
}).attr("dy", ".35em").attr("transform", function (d) { }).attr("dy", ".35em").attr("transform", function (d) {
return "rotate(" + (d.angle * 180 / Math.PI - 91) + ")" + "translate(" + (innerRadius + 26) + ")" + (d.angle > Math.PI ? "rotate(180)" : ""); return "rotate(" + (d.angle * 180 / Math.PI - 91) + ")" + "translate(" + (dimensions.innerR + 26) + ")" + (d.angle > Math.PI ? "rotate(180)" : "");
}).style("text-anchor", function (d) { }).style("text-anchor", function (d) {
return d.angle > Math.PI ? "end" : null; return d.angle > Math.PI ? "end" : null;
}).text(function (d) { }).text(function (d) {
var team = data.tourneys[eventKey].teams[d.index]; var team = data.tourneys[eventKey].teams[d.index];
var metric = ''; var metric = '';
switch (sort) { // switch (sort) {
case SORT_TYPES.GOALS: // case SORT_TYPES.GOALS:
metric = '(' + team.gf + ')'; // metric = `(${team.gf})`;
break; // break;
// case SORT_TYPES.POPULATION: // // case SORT_TYPES.POPULATION:
// metric = `(${Number(team.p).toLocaleString()})`; // // metric = `(${Number(team.p).toLocaleString()})`;
// break; // // break;
} // }
return data.countries[team.cId] + ' ' + metric; return data.countries[team.cId] + ' ' + metric;
}); });
},
buildContainer: function buildContainer(chords) {
var svg = d3.select("svg");
var dimensions = Diagram.buildDimensions();
return svg.append("g").attr("transform", 'translate(' + dimensions.svgW / 2 + ',' + dimensions.svgH / 2 + ')').datum(chords);
} }
}; };
@ -1000,54 +1132,6 @@
/***/ }, /***/ },
/* 5 */ /* 5 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 6 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 7 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 8 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 9 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 10 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 11 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 12 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 13 */
/***/ function(module, exports) { /***/ function(module, exports) {
"use strict"; "use strict";
@ -1122,5 +1206,53 @@
exports.default = hotfixes; exports.default = hotfixes;
/***/ },
/* 6 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 7 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 8 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 9 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 10 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 11 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 12 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 13 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ } /***/ }
/******/ ]); /******/ ]);

@ -120,68 +120,122 @@ const Diagram = {
return team.ga; return team.ga;
}, },
build: (data, eventKey, sort, scheme, SORT_TYPES, matrix) => { buildChords: ({ matrix, data, eventKey, sort, SORT_TYPES }) => {
const svg = d3.select("svg"),
width = (svg.attr("width")),
height = (svg.attr("height")),
outerArcThickness = 5,
outerRadius = Math.min(width, height) * 0.5 - 130,
innerRadius = outerRadius - outerArcThickness;
const chords = d3.chord() const chords = d3.chord()
.padAngle(0.05) .padAngle(0.05)
.call(null, matrix); .call(null, matrix);
let sortedChords = chords;
switch (sort) { switch (sort) {
case SORT_TYPES.COUNTRY: case SORT_TYPES.COUNTRY:
sortedChords = Sorter.sort(chords, 0, chords.groups.length - 1, return Sorter.sort(chords, 0, chords.groups.length - 1,
Diagram.getCountryName.bind(null, data, eventKey), Diagram.getCountryName.bind(null, data, eventKey),
Diagram.swapGroups.bind(null, data, eventKey)); Diagram.swapGroups.bind(null, data, eventKey));
break;
case SORT_TYPES.GOALS: case SORT_TYPES.GOALS:
sortedChords = Sorter.sort(chords, 0, chords.groups.length - 1, return Sorter.sort(chords, 0, chords.groups.length - 1,
Diagram.getGoalsFor.bind(null, data, eventKey), Diagram.getGoalsFor.bind(null, data, eventKey),
Diagram.swapGroups.bind(null, data, eventKey)); Diagram.swapGroups.bind(null, data, eventKey));
break;
case SORT_TYPES.POPULATION: case SORT_TYPES.POPULATION:
sortedChords = Sorter.sort(chords, 0, chords.groups.length - 1, return Sorter.sort(chords, 0, chords.groups.length - 1,
Diagram.getPopulation.bind(null, data, eventKey), Diagram.getPopulation.bind(null, data, eventKey),
Diagram.swapGroups.bind(null, data, eventKey)); Diagram.swapGroups.bind(null, data, eventKey));
break;
} }
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius); return chords;
},
const ribbon = d3.ribbon()
.radius(innerRadius); buildDimensions: () => {
const svgW = d3.select("svg").attr("width");
const len = data.tourneys[eventKey].teams.length; const svgH = (d3.select("svg").attr("height"));
let color = d3.scaleOrdinal(d3.schemeCategory20); const arcT = 5;
const outerR = Math.min(svgW, svgH) * 0.5 - 130;
switch (parseInt(scheme)) { const innerR = outerR - arcT;
case 1:
color = d3.scaleLinear().domain([0, len]).range(["#fff", "green"]).interpolate(d3.interpolateRgb); return { svgW, svgH, arcT, outerR, innerR};
break; },
case 2:
const colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]; buildColorScheme: ({ scheme, len }) => {
color = i => colors[i % colors.length]; if (scheme === 1) {
break; return d3.scaleLinear()
case 3: .domain([0, len])
color = d3.scaleLinear().domain([0, len]).range(["red", "blue"]).interpolate(d3.interpolateRgb); .range(["#fff", "green"])
break; .interpolate(d3.interpolateRgb);
case 4: } else if (scheme === 2) {
color = d3.scaleOrdinal(d3.schemeCategory10); const colors = [
break; "#ffffd9",
"#edf8b1",
"#c7e9b4",
"#7fcdbb",
"#41b6c4",
"#1d91c0",
"#225ea8",
"#253494",
"#081d58"
];
return (i => colors[i % colors.length]);
} else if (scheme === 3) {
return d3.scaleLinear()
.domain([0, len])
.range(["red", "blue"])
.interpolate(d3.interpolateRgb);
} else if (scheme === 4) {
return d3.scaleOrdinal(d3.schemeCategory10);
} }
const g = svg.append("g") return d3.scaleOrdinal(d3.schemeCategory20);
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") },
.datum(sortedChords);
const group = g.append("g") buildRibbons: ({ container, color, chords, data, eventKey }) => {
const dimensions = Diagram.buildDimensions();
const ribbon = d3.ribbon().radius(dimensions.innerR);
container.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(chords => chords)
.enter().append("path")
.attr("d", ribbon)
.attr('data-round-id', d => d.game.rId)
.style('fill', d => color(d.target.index))
.style('stroke', d => d3.rgb(color(d.target.index)).darker())
.classed("ribbon", true)
.append("title")
.text(function(d) {
const t1 = data.tourneys[eventKey].teams[d.source.index];
const t2 = data.tourneys[eventKey].teams[d.target.index];
const c1 = data.countries[t1.cId];
const c2 = data.countries[t2.cId];
let s1 = d.game.t1 === t1.tId ? d.game.s1 : d.game.s2;
let s2 = d.game.t2 === t2.tId ? d.game.s2 : d.game.s1;
if (d.game.sp1 !== null && d.game.sp2 !== null) {
s1 = d.game.t1 === t1.tId ? d.game.sp1 : d.game.sp2;
s2 = d.game.t2 === t2.tId ? d.game.sp2 : d.game.sp1;
s1 += " (Penalties)";
s2 += " (Penalties)";
}
else if (d.game.se1 !== null && d.game.se2 !== null) {
s1 = d.game.t1 === t1.tId ? d.game.se1 : d.game.se2;
s2 = d.game.t2 === t2.tId ? d.game.se2 : d.game.se1;
s1 += " (Extended time)";
s2 += " (Extended time)";
}
return `${c1}: ${s1}\n${c2}: ${s2}\n${data.rounds[d.game.rId]}`;
});
},
buildArcs: ({ container, color, eventKey, data }) => {
const dimensions = Diagram.buildDimensions();
const arc = d3.arc().innerRadius(dimensions.innerR).outerRadius(dimensions.outerR)
const group = container.append("g")
.attr("class", "groups") .attr("class", "groups")
.selectAll("g") .selectAll("g")
.data(chords => chords.groups) .data(chords => chords.groups)
@ -192,73 +246,12 @@ const Diagram = {
.style("stroke", d => d3.rgb(color(d.index)).darker()) .style("stroke", d => d3.rgb(color(d.index)).darker())
.attr("d", arc); .attr("d", arc);
g.append("g")
.attr("class", "ribbons")
.selectAll("path")
.data(function(chords) { return chords; })
.enter().append("path")
.attr("d", ribbon)
.attr("data-round-id", (d) => {
const t1 = data.tourneys[eventKey].teams[d.source.index];
const t2 = data.tourneys[eventKey].teams[d.target.index];
const game = data.tourneys[eventKey].games.find(v => {
return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId);
});
return game.rId;
})
.style("fill", (d) => color(d.target.index))
.style("stroke", (d) => d3.rgb(color(d.target.index)).darker())
.classed("ribbon", true)
.append("title")
.text(function(d) {
const t1 = data.tourneys[eventKey].teams[d.source.index];
const t2 = data.tourneys[eventKey].teams[d.target.index];
const c1 = data.countries[t1.cId];
const c2 = data.countries[t2.cId];
const game = data.tourneys[eventKey].games.find(v => {
return (v.t1 === t1.tId || v.t1 === t2.tId) && (v.t2 === t1.tId || v.t2 === t2.tId);
});
let s1 = game.t1 === t1.tId ? game.s1 : game.s2;
let s2 = game.t2 === t2.tId ? game.s2 : game.s1;
if (game.sp1 !== null && game.sp2 !== null) {
s1 = game.t1 === t1.tId ? game.sp1 : game.sp2;
s2 = game.t2 === t2.tId ? game.sp2 : game.sp1;
s1 += " (Penalties)";
s2 += " (Penalties)";
}
else if (game.se1 !== null && game.se2 !== null) {
s1 = game.t1 === t1.tId ? game.se1 : game.se2;
s2 = game.t2 === t2.tId ? game.se2 : game.se1;
s1 += " (Extended time)";
s2 += " (Extended time)";
}
// const s1 = game.t1 === t1.tId ? game.s1 : game.s2;
// const s2 = game.t2 === t2.tId ? game.s2 : game.s1;
//
// const e1 = game.se1 ? `(+${game.se1} in extended time)` : '';
// const e2 = game.se2 ? `(+${game.se2} in extended time)` : '';
//
// const p1 = game.sp1 ? `(+${game.sp1} in penalties)` : '';
// const p2 = game.sp2 ? `(+${game.sp2} in penalties)` : '';
return `${c1}: ${s1}\n${c2}: ${s2}\n${data.rounds[game.rId]}`;
});
group.append("text") group.append("text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; }) .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em") .attr("dy", ".35em")
.attr("transform", function(d) { .attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 91) + ")" return "rotate(" + (d.angle * 180 / Math.PI - 91) + ")"
+ "translate(" + (innerRadius + 26) + ")" + "translate(" + (dimensions.innerR + 26) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : ""); + (d.angle > Math.PI ? "rotate(180)" : "");
}) })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) .style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
@ -266,18 +259,29 @@ const Diagram = {
const team = data.tourneys[eventKey].teams[d.index]; const team = data.tourneys[eventKey].teams[d.index];
let metric = ''; let metric = '';
switch (sort) { // switch (sort) {
case SORT_TYPES.GOALS: // case SORT_TYPES.GOALS:
metric = `(${team.gf})`; // metric = `(${team.gf})`;
break; // break;
// case SORT_TYPES.POPULATION: // // case SORT_TYPES.POPULATION:
// metric = `(${Number(team.p).toLocaleString()})`; // // metric = `(${Number(team.p).toLocaleString()})`;
// break; // // break;
} // }
return `${data.countries[team.cId]} ${metric}`; return `${data.countries[team.cId]} ${metric}`;
}); });
}, },
buildContainer: (chords) => {
const svg = d3.select("svg");
const dimensions = Diagram.buildDimensions();
return svg.append("g")
.attr("transform", `translate(${ dimensions.svgW / 2 },${ dimensions.svgH / 2 })`)
.datum(chords);
},
}; };
export default Diagram; export default Diagram;

@ -0,0 +1,66 @@
const hotfixes = {
patch1934: (data) => {
data.games.forEach(g => {
if (g.t1 === 149 && g.t2 === 2 && g.rId === 13) {
g.s1 = 4;
g.s2 = 2;
}
if (g.t1 === 223 && g.t2 === 146 && g.rId === 13) {
g.s1 = 2;
g.s2 = 1;
}
if (g.t1 === 134 && g.t2 === 223 && g.rId === 18) {
g.s1 = 2;
g.s2 = 1;
}
if (g.t1 === 223 && g.t2 === 153 && g.rId === 14) {
g.s1 = 3;
g.s2 = 2;
}
if (g.t1 === 223 && g.t2 === 127 && g.rId === 16) {
g.s1 = 3;
g.s2 = 1;
}
if (g.t1 === 134 && g.t2 === 129 && g.rId === 14) {
g.s1 = 1;
g.s2 = 1;
}
if (g.t1 === 134 && g.t2 === 191 && g.rId === 13) {
g.s1 = 7;
g.s2 = 1;
}
if (g.t1 === 153 && g.t2 === 137 && g.rId === 13) {
g.s1 = 3;
g.s2 = 2;
}
if (g.t1 === 129 && g.t2 === 211 && g.rId === 13) {
g.s1 = 3;
g.s2 = 1;
}
if (g.t1 === 127 && g.t2 === 125 && g.rId === 13) {
g.s1 = 5;
g.s2 = 2;
}
if (g.t1 === 124 && g.t2 === 131 && g.rId === 13) {
g.s1 = 3;
g.s2 = 2;
}
if (g.t1 === 147 && g.t2 === 210 && g.rId === 13) {
g.s1 = 3;
g.s2 = 2;
}
if (g.t1 === 127 && g.t2 === 124 && g.rId === 17) {
g.s1 = 3;
g.s2 = 2;
}
if (g.t1 === 124 && g.t2 === 149 && g.rId === 14) {
g.s1 = 2;
g.s2 = 1;
}
});
return data;
},
};
export default hotfixes;

@ -44,22 +44,46 @@ const main = {
main.updateUI(); main.updateUI();
}, },
getRounds: (eventKey) => { // DEPRECATED? 161120
const rounds = {}; // getRounds: (eventKey) => {
// const rounds = {};
//
// main.json.tourneys[eventKey].games.forEach(game => {
// const name = UI.getRoundName(main.json.rounds[game.rId]);
// if (rounds[name] === undefined) {
// rounds[name] = [];
// }
//
// rounds[name].push({
// id: game.rId,
// name: name,
// });
// });
//
// return rounds;
// },
getDuplicates: (eventKey) => {
const games = {};
main.json.tourneys[eventKey].games.forEach(game => { main.json.tourneys[eventKey].games.forEach(game => {
const name = UI.getRoundName(main.json.rounds[game.rId]); const teams = [game.t1, game.t2].sort();
if (rounds[name] === undefined) { const addr = `${teams[0]}-${teams[1]}`;
rounds[name] = [];
if (games[addr] === undefined) {
games[addr] = [];
} }
rounds[name].push({ games[addr].push(game);
id: game.rId,
name: name,
});
}); });
return rounds; return Object.keys(games).reduce((acc, k) => {
if (games[k].length > 1) {
acc.push(games[k]);
}
return acc;
}, []);
}, },
generateUI: () => { generateUI: () => {
@ -74,10 +98,99 @@ const main = {
updateUI: () => { updateUI: () => {
const state = main.getState(); const state = main.getState();
const eventKey = state.eventKey;
const matrix = Matrices.buildMatrix(main.json, eventKey);
const duplicates = main.getDuplicates(eventKey);
const tmp = Diagram.buildChords({
data: main.json,
sort: state.sort,
eventKey,
matrix,
SORT_TYPES: UI.SORT_TYPES,
});
const getScore = (game) => {
let s1 = game.s1;
let s2 = game.s2;
if (game.sp1 !== null) {
s1 = game.sp1;
} else if (game.se1 !== null) {
s1 = game.se1;
}
if (game.sp2 !== null) {
s2 = game.sp2;
} else if (game.se2 !== null) {
s2 = game.se2;
}
return { s1, s2 }
};
const chords = tmp.reduce((acc, d) => {
const tSrc = main.json.tourneys[eventKey].teams[d.source.index].tId;
const tTgt = main.json.tourneys[eventKey].teams[d.target.index].tId;
d.game = main.json.tourneys[eventKey].games
.find(g => {
return (g.t1 === tSrc && g.t2 === tTgt) ||
(g.t1 === tTgt && g.t2 === tSrc)
});
duplicates.forEach(games => {
if (games[0] === d.game || games[1] === d.game) {
const gameNew = (games[0] === d.game ? games[1] : games[0]);
const sourceNew = Object.assign({}, d.source);
const targetNew = Object.assign({}, d.target);
const sourceAngle = d.source.endAngle - d.source.startAngle;
const targetAngle = d.target.endAngle - d.target.startAngle;
const { s1: s1g0, s2: s2g0 } = getScore(games[0]);
const { s1: s1g1, s2: s2g1 } = getScore(games[1]);
const totals = { src: 0, tgt: 0 };
const offset = { src: 0, tgt: 0 }
totals.src += (games[0].t1 === tSrc ? s1g0 : s2g0);
totals.tgt += (games[0].t1 === tTgt ? s1g0 : s2g0);
totals.src += (games[1].t1 === tSrc ? s1g1 : s2g1);
totals.tgt += (games[1].t1 === tTgt ? s1g1 : s2g1);
offset.src = (games[0].t1 === tSrc ? s1g0 : s2g0);
offset.tgt = (games[0].t1 === tTgt ? (totals.tgt - s1g0) : (totals.tgt - s2g0));
sourceNew.startAngle = d.source.startAngle + sourceAngle * (offset.src / totals.src);
d.source.endAngle = d.source.startAngle + sourceAngle * (offset.src / totals.src);
targetNew.endAngle = d.target.startAngle + targetAngle * (offset.tgt / totals.tgt);
d.target.startAngle = d.target.startAngle + targetAngle * (offset.tgt / totals.tgt);
acc.push({ source: sourceNew, target: targetNew, game: gameNew });
}
});
acc.push(d);
return acc;
}, []);
chords.groups = tmp.groups;
const matrix = Matrices.buildMatrix(main.json, state.eventKey);
Diagram.clear(); Diagram.clear();
Diagram.build(main.json, state.eventKey, state.sort, state.scheme, UI.SORT_TYPES, matrix);
const color = Diagram.buildColorScheme({
scheme: parseInt(state.scheme),
len: main.json.tourneys[state.eventKey].teams.length,
});
const container = Diagram.buildContainer(chords);
Diagram.buildArcs({ container, color, eventKey, data: main.json });
Diagram.buildRibbons({ container, color, chords, eventKey, data: main.json });
UI.updateTourneyPane(state.eventKey); UI.updateTourneyPane(state.eventKey);
UI.updateEventsPane(state.eventKey); UI.updateEventsPane(state.eventKey);
@ -87,7 +200,7 @@ const main = {
}, },
fetch: (url) => new Promise((resolve, reject) => { fetch: (url) => new Promise((resolve, reject) => {
const listener = ({ srcElement: req }) => { const listener = ({ target: req }) => {
req.status === 200 ? resolve(req.responseText) : reject("busted"); req.status === 200 ? resolve(req.responseText) : reject("busted");
}; };

@ -37,8 +37,8 @@ const Matrices = {
s2 += g.s2; s2 += g.s2;
} }
matrix[i1][i2] = g.s1 + g.se1 + g.sp1; matrix[i1][i2] = s1;
matrix[i2][i1] = g.s2 + g.se2 + g.sp2; matrix[i2][i1] = s2;
}, []); }, []);
return matrix; return matrix;

Loading…
Cancel
Save