Last active
July 11, 2018 19:55
-
-
Save djbarnwal/da9cc47ec78e213d2cd9a5cc0f380305 to your computer and use it in GitHub Desktop.
Fifa Visualization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Fifa Visualization - iodide</title> | |
<link rel="stylesheet" type="text/css" href="iodide.dev.css"> | |
</head> | |
<body> | |
<script id="jsmd" type="text/jsmd"> | |
%% meta | |
{ | |
"title": "Fifa Visualization", | |
"lastSaved": "2018-07-11T19:44:50.532Z", | |
"lastExport": "2018-07-11T19:49:25.155Z" | |
} | |
%% md | |
# Country wise performance in Fifa World Cup | |
## by their wins, loses and draws | |
%% css | |
h1, h2 { | |
margin: 0 !important; | |
max-width: 1000px !important; | |
} | |
%% resource | |
https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.js | |
%% js | |
var countryGrid = { columns: 5, rows: 8 }; | |
var cellSize = { width: 190, height: 120 }; | |
var maxRadius = (Math.floor(cellSize.height / 2) - 30); | |
var scaleRadius = d3.scaleLinear() | |
.domain([0, 100]) | |
.range([10, maxRadius]); | |
var matchResults = ["Won", "Lost", "Draw"] | |
var scaleColor = d3.scaleOrdinal() | |
.domain(matchResults) | |
.range(["#F13C05", "#FF7747", "#35D4DA"]); | |
%% md | |
<div class="content"> | |
<div class="legend"> | |
<div class="column"> | |
<div class="title">Match<br/>Results</div> | |
<div class="legend-statuses"></div> | |
</div> | |
<div class="column"> | |
<div class="title">Total<br/>Matches</div> | |
<div class="legend-duration"></div> | |
</div> | |
</div> | |
<div class="container"></div> | |
</div> | |
%% js | |
d3.csv("https://gist.githubusercontent.com/djbarnwal/05b931c63251eb21b5b352349a94b6c2/raw/e4fd6ef546b283eda8715423a5464f7018a04a31/fifadata.csv", function(error, source) { | |
if (error) throw error; | |
var data = prepareData(source.slice(0,40)); | |
var svg = createContainer(); | |
appendGooeyFilter(svg); | |
generateChart(svg, data); | |
}); | |
function createContainer() { | |
var width = cellSize. width * countryGrid.columns; | |
var height = cellSize. height * countryGrid.rows; | |
var svg = d3.select(".container") | |
.append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
return svg; | |
}; | |
function prepareData(source) { | |
return source.map(function (item) { | |
var aggregated = { | |
team: item['Team'], | |
bestFinish: item['Best finish'], | |
results: [] | |
}; | |
aggregated.results = [ | |
{ status: "Won", duration: item['W'] }, | |
{ status: "Lost", duration: item['D'] }, | |
{ status: "Draw", duration: item['L'] } | |
]; | |
return aggregated; | |
}); | |
}; | |
function generateChart(svg, data) { | |
var countries = svg | |
.append("g") | |
.selectAll("g") | |
.data(data).enter() | |
.append("g") | |
.attr("class", "state") | |
.attr("transform", (d, i) => { | |
var x = (i % countryGrid.columns) * cellSize.width; | |
var y = Math.floor(i / countryGrid.columns) * (cellSize.height + 40); | |
return "translate(" + x + "," + y + ")"; | |
}); | |
var titleOffset = cellSize.width / 2; | |
countries | |
.append("text") | |
.attr("transform", "translate(" + titleOffset + ",15)") | |
.style("text-anchor", "middle") | |
.text(d => d.team); | |
countries | |
.append("text") | |
.attr("transform", "translate(" + titleOffset + ",100)") | |
.style("text-anchor", "middle") | |
.style("font-weight", "bold") | |
.text('Best Performance'); | |
countries | |
.append("text") | |
.attr("transform", "translate(" + titleOffset + ",120)") | |
.style("text-anchor", "middle") | |
.text(d => d.bestFinish); | |
var charts = countries | |
.append("g") | |
.style("filter", "url(#gooeyFilter)"); | |
charts | |
.selectAll("circle") | |
.data((d) => prepareSingleChart(d.results)).enter() | |
.append("circle") | |
.attr("r", (d) => d.radius ) | |
.attr("cx", (d) => d.offset ) | |
.style("fill", (d) => d.color) | |
.style("fill-opacity", "0.7") | |
charts | |
.attr("transform", function() { | |
var realSize = d3.select(this).node().getBoundingClientRect(); | |
var xOffset = (cellSize.width - realSize.width) / 2 ; | |
var yOffset = maxRadius + 15; | |
return "translate(" + xOffset + "," + yOffset +")" | |
}); | |
}; | |
function prepareSingleChart(data) { | |
var result = []; | |
var offset = 0; | |
for (var i = 0; i < data.length; i++) { | |
var item = data[i]; | |
if (item.duration == 0) { | |
continue; | |
} | |
var radius = scaleRadius(item.duration); | |
offset = offset + radius; | |
result.push({ | |
radius: radius, | |
offset: offset, | |
color: scaleColor(item.status) | |
}); | |
offset = offset + radius; | |
} | |
return result; | |
} | |
function appendGooeyFilter(svg) { | |
var filter = svg.append("defs").append("filter").attr("id","gooeyFilter"); | |
filter.append("feGaussianBlur") | |
.attr("in","SourceGraphic") | |
.attr("stdDeviation", 5) | |
.attr("color-interpolation-filters","sRGB") | |
.attr("result","blur"); | |
filter.append("feColorMatrix") | |
.attr("in","blur") | |
.attr("mode","matrix") | |
.attr("values","1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9") | |
.attr("result","gooey"); | |
} | |
%% css | |
.user-markdown { | |
max-width: 1000px; | |
margin: 0 auto; | |
text-align: center; | |
} | |
.content { | |
max-width: 1000px !important; | |
} | |
.container { | |
max-width: 1000px !important; | |
margin-top: 40px !important; | |
} | |
.legend { | |
display: flex; | |
flex-wrap: wrap; | |
margin: 10px 70px 50px 70px; | |
padding: 10px 50px; | |
border: 1px #dadada; | |
border-style: solid none; | |
} | |
.legend text { | |
text-anchor: left; | |
font-size: 12px; | |
fill: #333; | |
} | |
.legend .column { | |
display: flex; | |
flex-wrap: wrap; | |
flex: 1 1 auto; | |
align-items: center; | |
} | |
.legend .title { | |
padding-right: 30px; | |
color: #777; | |
font-size: 0.9em; | |
} | |
%% js | |
appendStatusLegend(); | |
appendDurationLegend(); | |
function appendStatusLegend() { | |
var svg = d3.select(".legend-statuses") | |
.append("svg") | |
.attr("width", 200) | |
.attr("height", 80); | |
var resultsLegend = svg.append("g").attr("class", "results"); | |
var results = resultsLegend.selectAll("g") | |
.data(matchResults).enter() | |
.append("g") | |
.attr("transform", function(d, i) { return "translate(0," + (30 * i)+ ")"}); | |
results | |
.append("circle") | |
.attr("cx", 10) | |
.attr("cy", 10) | |
.attr("r", 7) | |
.style("fill", function(d) { return scaleColor(d) }); | |
results | |
.append("text") | |
.attr("transform", "translate(25,14)") | |
.text(function(d) { return d;}); | |
} | |
function appendDurationLegend() { | |
var svg = d3.select(".legend-duration") | |
.append("svg") | |
.attr("width", 300) | |
.attr("height", 80); | |
var matchesLegend = svg | |
.append("g") | |
.attr("class", "duration") | |
.attr("transform", "translate(30, 35)"); | |
var matches = matchesLegend | |
.selectAll("g") | |
.data([5, 40, 75]).enter() | |
.append("g") | |
.attr("transform", function(d, i) { return "translate(" + (i * 85) + ", 0)"}); | |
matches | |
.append("circle") | |
.attr("cx", 0) | |
.attr("cy", 0) | |
.attr("r", function(d) { return scaleRadius(d) - 5; }) | |
.style("fill", "#dadada"); | |
matches | |
.append("text") | |
.attr("transform","translate(0, 40)") | |
.text(function(d) { return d + " matches"; }) | |
.style("text-anchor", "middle") | |
} | |
%% js | |
</script> | |
<div id='page'></div> | |
<script src='iodide.dev.js'></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment