parent
60b5c18add
commit
cd8a02b60b
6 changed files with 288 additions and 139 deletions
@ -1 +1,4 @@ |
||||
data |
||||
world-cup |
||||
node_modules |
||||
tmp |
||||
|
@ -0,0 +1,89 @@ |
||||
GEM |
||||
remote: http://rubygems.org/ |
||||
specs: |
||||
activemodel (4.2.7.1) |
||||
activesupport (= 4.2.7.1) |
||||
builder (~> 3.1) |
||||
activerecord (4.2.7.1) |
||||
activemodel (= 4.2.7.1) |
||||
activesupport (= 4.2.7.1) |
||||
arel (~> 6.0) |
||||
activerecord-utils (0.4.0) |
||||
activerecord |
||||
logutils |
||||
activesupport (4.2.7.1) |
||||
i18n (~> 0.7) |
||||
json (~> 1.7, >= 1.7.7) |
||||
minitest (~> 5.1) |
||||
thread_safe (~> 0.3, >= 0.3.4) |
||||
tzinfo (~> 1.1) |
||||
arel (6.0.3) |
||||
builder (3.2.2) |
||||
datafile (0.2.5) |
||||
fetcher |
||||
logutils |
||||
textutils |
||||
fetcher (0.4.5) |
||||
logutils (>= 0.6) |
||||
gli (2.14.0) |
||||
i18n (0.7.0) |
||||
json (1.8.3) |
||||
logutils (0.6.1) |
||||
logutils-activerecord (0.2.1) |
||||
activerecord |
||||
logutils (>= 0.6.1) |
||||
minitest (5.9.1) |
||||
persondb-models (0.5.0) |
||||
worlddb-models |
||||
props (1.1.2) |
||||
props-activerecord (0.1.0) |
||||
activerecord |
||||
props (>= 1.1.2) |
||||
rubyzip (1.2.0) |
||||
sportdb (1.11.0) |
||||
datafile (>= 0.1.1) |
||||
fetcher (>= 0.4.4) |
||||
gli (>= 2.12.2) |
||||
sportdb-models (>= 1.10.1) |
||||
sportdb-service |
||||
sportdb-update |
||||
sqlite3 |
||||
sportdb-models (1.16.2) |
||||
persondb-models (>= 0.4.2) |
||||
worlddb-models (>= 2.1.0) |
||||
sportdb-service (0.4.0) |
||||
sportdb-update (0.1.1) |
||||
sqlite3 (1.3.11) |
||||
tagutils (0.3.0) |
||||
activerecord |
||||
logutils (>= 0.6.1) |
||||
props (>= 1.1.2) |
||||
textutils (>= 0.10.0) |
||||
textutils (1.4.0) |
||||
activesupport |
||||
logutils (>= 0.6.1) |
||||
props (>= 1.1.2) |
||||
rubyzip (>= 1.0.0) |
||||
thread_safe (0.3.5) |
||||
tzinfo (1.2.2) |
||||
thread_safe (~> 0.1) |
||||
worlddb-models (2.3.4) |
||||
activerecord |
||||
activerecord-utils (>= 0.2.0) |
||||
logutils (>= 0.6.1) |
||||
logutils-activerecord (>= 0.2.0) |
||||
props (>= 1.1.2) |
||||
props-activerecord (>= 0.1.0) |
||||
rubyzip |
||||
tagutils (>= 0.3.0) |
||||
textutils (>= 1.3.1) |
||||
|
||||
PLATFORMS |
||||
ruby |
||||
|
||||
DEPENDENCIES |
||||
activerecord (= 4.2.7.1) |
||||
sportdb |
||||
|
||||
BUNDLED WITH |
||||
1.13.1 |
File diff suppressed because one or more lines are too long
@ -0,0 +1,188 @@ |
||||
<!DOCTYPE html> |
||||
<meta charset="utf-8"> |
||||
<style> |
||||
|
||||
body { |
||||
font: 10px sans-serif; |
||||
} |
||||
|
||||
.notes { |
||||
font-size:14px; |
||||
} |
||||
|
||||
.group-tick line { |
||||
stroke: #ddd; |
||||
} |
||||
|
||||
.groups, |
||||
.ribbons { |
||||
fill-opacity: 0.8; |
||||
} |
||||
|
||||
</style> |
||||
<svg width="1000" height="1000"></svg> |
||||
<script src="http://d3js.org/d3.v4.0.0-alpha.50.min.js"></script> |
||||
<script src="http://d3js.org/d3-chord.v0.0.min.js"></script> |
||||
<script> |
||||
|
||||
const get = (url) => new Promise((resolve, reject) => { |
||||
const req = new XMLHttpRequest(); |
||||
req.addEventListener('load', listener.bind(null, resolve, reject)); |
||||
req.open('GET', url); |
||||
req.send(); |
||||
}); |
||||
|
||||
const listener = (resolve, reject, { srcElement: req }) => { |
||||
req.status === 200 ? resolve(req.responseText) : reject("busted"); |
||||
}; |
||||
|
||||
const parseAndSet = (endpoints, result) => { |
||||
const lookup = {}; |
||||
|
||||
result.map((v, i) => { |
||||
const json = JSON.parse(v); |
||||
const address = endpoints[i].split('.').shift(); |
||||
lookup[address] = json; |
||||
}); |
||||
|
||||
return lookup; |
||||
}; |
||||
|
||||
const buildMatrix = (games) => { |
||||
const max = games.reduce((acc, v) => Math.max(acc, v.t1, v.t2), 1) + 1; |
||||
|
||||
const empty = Array.apply(null, Array(max)); |
||||
|
||||
const result = empty.map( |
||||
() => empty.map(() => null) |
||||
); |
||||
|
||||
games.forEach(v => { |
||||
if (v.rId < 77) { |
||||
return; |
||||
} |
||||
|
||||
result[v.t1][v.t2] = v.s1 + v.s1e + v.s1p; |
||||
result[v.t2][v.t1] = v.s2 + v.s2e + v.s2p; |
||||
}, []); |
||||
|
||||
return result; |
||||
}; |
||||
|
||||
const buildMeta = (data) => { |
||||
// console.warn(data) |
||||
return data; |
||||
}; |
||||
|
||||
const endpoints = ['teams.json', 'rounds.json', 'games.json']; |
||||
|
||||
const buildMain = (data) => { |
||||
const lookup = parseAndSet(endpoints, data); |
||||
const meta = buildMeta(lookup); |
||||
const matrix = buildMatrix(lookup.games) |
||||
foo(matrix) |
||||
}; |
||||
|
||||
Promise.all(endpoints.map(get)).then(buildMain); |
||||
|
||||
function foo(matrix2) { |
||||
const matrix = [ |
||||
[ null, 0, 2, 3, 0, 1], |
||||
[ 1, null, 2, null, 1, 3 ], |
||||
[ 2, 3, null, 5, 0, 2 ], |
||||
[ 0, null, 1, null, 2, 3 ], |
||||
[ 2, 4, 2, 5, null, 5 ], |
||||
[ 4, 3, 9, 3, 6, null ], |
||||
]; |
||||
|
||||
const svg = d3.select("svg"), |
||||
width = +svg.attr("width"), |
||||
height = +svg.attr("height"), |
||||
outerArcThickness = 5, |
||||
outerRadius = Math.min(width, height) * 0.5 - 40, |
||||
innerRadius = outerRadius - outerArcThickness; |
||||
|
||||
const chord = d3.chord() |
||||
.padAngle(0.02) |
||||
.sortSubgroups(d3.descending); |
||||
const arc = d3.arc() |
||||
.innerRadius(innerRadius) |
||||
.outerRadius(outerRadius); |
||||
|
||||
const ribbon = d3.ribbon() |
||||
.radius(innerRadius); |
||||
|
||||
const color = d3.scaleOrdinal(d3.schemeCategory10); |
||||
// d3.scaleOrdinal() |
||||
// .range(["#f0f9e8", "#bae4bc", "#7bccc4", "#2b8cbe"]); |
||||
|
||||
const g = svg.append("g") |
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") |
||||
.datum(chord(matrix2)); |
||||
|
||||
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); |
||||
|
||||
const groupTick = group.selectAll(".group-tick") |
||||
.data(function(d) { return groupTicks(d, 1); }) |
||||
.enter().append("g") |
||||
.attr("class", "group-tick") |
||||
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; }); |
||||
|
||||
groupTick.append("line") |
||||
.attr("x2", 6); |
||||
|
||||
groupTick |
||||
.filter(function(d) { return d.value % 3 === 0; }) |
||||
.append("text") |
||||
.attr("x", 8) |
||||
.attr("dy", ".35em") |
||||
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; }) |
||||
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) |
||||
.text(function(d) { return d.value; }); |
||||
|
||||
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(); }); |
||||
|
||||
// Returns an array of tick angles and values for a given group and step. |
||||
function groupTicks(d, step) { |
||||
const k = (d.endAngle - d.startAngle) / d.value; |
||||
return d3.range(0, d.value, step).map(function(value) { |
||||
return {value: value, angle: value * k + d.startAngle}; |
||||
}); |
||||
} |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<div class="notes"> |
||||
<h5>Lessons learned</h5> |
||||
scaleOrdinal vs scaleLinear (only 2 colors!) vs interpolateCool vs d3.schemeColor20c<br> |
||||
ribbon source vs index (change fill them differently) |
||||
|
||||
<a href="https://github.com/d3/d3-scale-chromatic">https://github.com/d3/d3-scale-chromatic</a> |
||||
|
||||
https://github.com/jokecamp/sportdb-build-scripts |
||||
https://groups.google.com/forum/#!topic/opensport/593H1O7yIdE |
||||
https://github.com/openfootball/datafile/blob/master/worldcup.rb |
||||
https://openfootball.github.io/questions.html |
||||
|
||||
<h5>TODO</h5> |
||||
add team name to each arc |
||||
build dataset |
||||
tweet it! |
||||
</div> |
@ -1,134 +0,0 @@ |
||||
<!DOCTYPE html> |
||||
<meta charset="utf-8"> |
||||
<style> |
||||
|
||||
body { |
||||
font: 10px sans-serif; |
||||
} |
||||
|
||||
.notes { |
||||
font-size:14px; |
||||
} |
||||
|
||||
.group-tick line { |
||||
stroke: #ddd; |
||||
} |
||||
|
||||
.groups, |
||||
.ribbons { |
||||
fill-opacity: 0.8; |
||||
} |
||||
|
||||
</style> |
||||
<svg width="500" height="500"></svg> |
||||
<script src="http://d3js.org/d3.v4.0.0-alpha.50.min.js"></script> |
||||
<script src="http://d3js.org/d3-chord.v0.0.min.js"></script> |
||||
<script src="teams.json"></script> |
||||
<script src="rounds.json"></script> |
||||
<script src="games.json"></script> |
||||
<script> |
||||
|
||||
// use lookup table to build this array: |
||||
// - traverse events, get team A and team B |
||||
// - put team A and team B IDs into lookup, assign them matrix array positions |
||||
// - for each event, go to lookupA and put in scoreB, then lookupB and scoreA |
||||
|
||||
var matrix = [ |
||||
[ null, 0, 2, 3, 0, 1], |
||||
[ 1, null, 2, null, 1, 3 ], |
||||
[ 2, 3, null, 5, 0, 2 ], |
||||
[ 0, null, 1, null, 2, 3 ], |
||||
[ 2, 4, 2, 5, null, 5 ], |
||||
[ 4, 3, 9, 3, 6, null ], |
||||
]; |
||||
|
||||
|
||||
var svg = d3.select("svg"), |
||||
width = +svg.attr("width"), |
||||
height = +svg.attr("height"), |
||||
outerArcThickness = 15, |
||||
outerRadius = Math.min(width, height) * 0.5 - 40, |
||||
innerRadius = outerRadius - outerArcThickness; |
||||
|
||||
var chord = d3.chord() |
||||
.padAngle(0.25) |
||||
.sortSubgroups(d3.descending); |
||||
var arc = d3.arc() |
||||
.innerRadius(innerRadius) |
||||
.outerRadius(outerRadius); |
||||
|
||||
var ribbon = d3.ribbon() |
||||
.radius(innerRadius); |
||||
|
||||
var color = d3.scaleOrdinal(d3.schemeCategory10); |
||||
// d3.scaleOrdinal() |
||||
// .range(["#f0f9e8", "#bae4bc", "#7bccc4", "#2b8cbe"]); |
||||
|
||||
var g = svg.append("g") |
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")") |
||||
.datum(chord(matrix)); |
||||
|
||||
var 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); |
||||
|
||||
var groupTick = group.selectAll(".group-tick") |
||||
.data(function(d) { return groupTicks(d, 1); }) |
||||
.enter().append("g") |
||||
.attr("class", "group-tick") |
||||
.attr("transform", function(d) { return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)"; }); |
||||
|
||||
groupTick.append("line") |
||||
.attr("x2", 6); |
||||
|
||||
groupTick |
||||
.filter(function(d) { return d.value % 3 === 0; }) |
||||
.append("text") |
||||
.attr("x", 8) |
||||
.attr("dy", ".35em") |
||||
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180) translate(-16)" : null; }) |
||||
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) |
||||
.text(function(d) { return d.value; }); |
||||
|
||||
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(); }); |
||||
|
||||
// Returns an array of tick angles and values for a given group and step. |
||||
function groupTicks(d, step) { |
||||
var k = (d.endAngle - d.startAngle) / d.value; |
||||
return d3.range(0, d.value, step).map(function(value) { |
||||
return {value: value, angle: value * k + d.startAngle}; |
||||
}); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<div class="notes"> |
||||
<h5>Lessons learned</h5> |
||||
scaleOrdinal vs scaleLinear (only 2 colors!) vs interpolateCool vs d3.schemeColor20c<br> |
||||
ribbon source vs index (change fill them differently) |
||||
|
||||
<a href="https://github.com/d3/d3-scale-chromatic">https://github.com/d3/d3-scale-chromatic</a> |
||||
|
||||
https://github.com/jokecamp/sportdb-build-scripts |
||||
https://groups.google.com/forum/#!topic/opensport/593H1O7yIdE |
||||
https://github.com/openfootball/datafile/blob/master/worldcup.rb |
||||
https://openfootball.github.io/questions.html |
||||
|
||||
<h5>TODO</h5> |
||||
add team name to each arc |
||||
build dataset |
||||
</div> |
Loading…
Reference in new issue