|
<!DOCTYPE html> |
|
<html> |
|
|
|
<head> |
|
<title>Norwegian political party voting similarity</title> |
|
<style type="text/css"> |
|
.title { |
|
font-weight: bold; |
|
font-family: sans-serif; |
|
} |
|
.x-axis .y-axis line { |
|
fill: none; |
|
stroke: none; |
|
stroke-opacity:.2; |
|
shape-rendering: crispEdges; |
|
} |
|
.x-axis path { |
|
stroke: white; |
|
fill: none; |
|
} |
|
.y-axis path { |
|
stroke: white; |
|
fill: none; |
|
} |
|
.x-axis text { |
|
font-size: 9px; |
|
} |
|
.y-axis text { |
|
font-size: 9px; |
|
font-family: sans-serif; |
|
} |
|
.legend-y-axis path { |
|
fill: none; |
|
} |
|
.legend-y-axis text { |
|
font-size: 9px; |
|
font-family: sans-serif; |
|
} |
|
.labels { |
|
font-size: 9px; |
|
} |
|
.domain { |
|
fill-opacity: none; |
|
stroke-opacity: none; |
|
} |
|
#ten { |
|
fill: #a50026; |
|
} |
|
#nine { |
|
fill: #d73027; |
|
} |
|
#eight { |
|
fill: #f46d43; |
|
} |
|
#seven { |
|
fill: #fdae61; |
|
} |
|
#six { |
|
fill: #fee090; |
|
} |
|
#five { |
|
fill: #ffffbf; |
|
} |
|
#four { |
|
fill: #e0f3f8; |
|
} |
|
#three { |
|
fill: #abd9e9; |
|
} |
|
#two { |
|
fill: #74add1; |
|
} |
|
#one { |
|
fill: #4575b4; |
|
} |
|
#zero { |
|
fill: #313695; |
|
} |
|
</style> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script> |
|
</head> |
|
|
|
<body> |
|
<script type="text/javascript"> |
|
|
|
var createHolderDeOrdViz = (function() { |
|
|
|
var assignColour = function(data) { |
|
var roundedData = d3.round(data/10); |
|
var colourId; |
|
switch (roundedData) { |
|
case 10: |
|
colourId = "ten"; |
|
break; |
|
case 9: |
|
colourId = "nine"; |
|
break; |
|
case 8: |
|
colourId = "eight"; |
|
break; |
|
case 7: |
|
colourId = "seven"; |
|
break; |
|
case 6: |
|
colourId = "six"; |
|
break; |
|
case 5: |
|
colourId = "five"; |
|
break; |
|
case 4: |
|
colourId = "four"; |
|
break; |
|
case 3: |
|
colourId = "three"; |
|
break; |
|
case 2: |
|
colourId = "two"; |
|
break; |
|
case 1: |
|
colourId = "one"; |
|
break; |
|
case 0: |
|
colourId = "zero"; |
|
break; |
|
default: |
|
colourId = ""; |
|
} |
|
return colourId; |
|
} |
|
|
|
var createLegendSpec = function(margin, widthInit, heightInit) { |
|
var xInit = widthInit - margin.right, |
|
yInit = margin.top; |
|
return { |
|
"xPos": xInit + margin.top, |
|
"yPos": yInit + margin.top, |
|
"width": margin.right - (margin.top*2), |
|
"height": heightInit/2 |
|
} |
|
} |
|
|
|
var createLegendSvg = function(spec) { |
|
var newPosition = "translate(" + spec.xPos + "," + spec.yPos + ")"; |
|
return svg |
|
.append("g") |
|
.attr("class", "legend-svg") |
|
.attr("transform", newPosition); |
|
} |
|
|
|
var parseClassValues = function(classValue) { |
|
var vals = classValue.split(","); |
|
var parsed = []; |
|
for (i = 0; i < vals.length; i++) { |
|
parsed.push(parseInt(vals[i])); |
|
} |
|
return parsed; |
|
} |
|
|
|
var buildExplanation = function(classValue, data, issues, partyCombinations) { |
|
var classInfo = parseClassValues(classValue); |
|
var rowNum = classInfo[0]; |
|
var colNum = classInfo[1]; |
|
return "".concat(partyCombinations[colNum], " voted ", data[rowNum].values[colNum], |
|
"% in agreement on ", issues[rowNum], "."); |
|
} |
|
|
|
var drawLegend = function(margin, widthInit, heightInit) { |
|
var spec = createLegendSpec(margin, widthInit, heightInit) |
|
var legendSvg = createLegendSvg(spec) |
|
var legendData = { |
|
"labels": [ |
|
"[95%, 100%]", "[85%, 95%)", "[75%, 85%)", |
|
"[65%, 75%)", "[55%, 65%)", "[45%, 55%)", |
|
"[35%, 45%)", "[25%, 35%)", "[15%, 25%)", |
|
"[5%, 15%)", "[0%, 5%)"], |
|
"mappingData": [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0] |
|
} |
|
|
|
var lyScale = d3.scale.ordinal() |
|
.domain(legendData.labels.map(function(d) {return d; })) |
|
.rangeRoundBands([0, spec.height], 0, 0); |
|
|
|
var ly = legendSvg |
|
.append("g") |
|
.attr("class", "legend-y-axis") |
|
.attr("transform", "translate(" + spec.width/6 + " ,0)"); |
|
|
|
var lyAxis = d3.svg.axis().scale(lyScale).orient("right"); |
|
ly.call(lyAxis); |
|
var legend = legendSvg |
|
.append("g") |
|
.attr("class", "legend") |
|
.selectAll("rect") |
|
.data(legendData.mappingData) |
|
.enter() |
|
.append("rect") |
|
.attr("x", "0") |
|
.attr("y", function(d, i) { |
|
return lyScale(legendData.labels[i]); |
|
}) |
|
.attr("width", spec.width/6) |
|
.attr("height", spec.height/legendData.mappingData.length) |
|
.attr("id", function(d) { return assignColour(d); }) |
|
.attr("stroke", "black"); |
|
return legend; |
|
} |
|
|
|
var margin = {top: 70, bottom: 30, left: 200, right: 200}, |
|
widthInit = 1000; |
|
heightInit = 450; |
|
width = widthInit - margin.left - margin.right, |
|
height = heightInit - margin.top - margin.bottom; |
|
|
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("class", "base-svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.attr("viewBox", "0 0 " + widthInit + " " + heightInit) |
|
.attr("preserveAspectRatio", "xMidYMid"); |
|
|
|
var xScale = d3.scale.ordinal().rangeRoundBands([0, width], 0, 0), |
|
yScale = d3.scale.ordinal().rangeRoundBands([0, height], 0, 0); |
|
|
|
var legend = drawLegend(margin, widthInit, heightInit); |
|
|
|
var graphSvg = svg.append("g").attr("class", "graph-svg") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
var x = graphSvg.append("g").attr("class", "x-axis"), |
|
y = graphSvg.append("g").attr("class", "y-axis"); |
|
|
|
var dataUrl = "data.json"; |
|
|
|
d3.json("testdata.json", function(sourceData) { |
|
|
|
var partyCombinations = sourceData[0].partyCombinations, |
|
issues = sourceData[0].issues, |
|
data = sourceData[0].data; |
|
|
|
xScale.domain(partyCombinations.map(function(d) { return d; })); |
|
yScale.domain(issues.map(function(d) {return d; })); |
|
|
|
d3.select(".base-svg").append("text") |
|
.attr("x", margin.left) |
|
.attr("y", margin.top/2) |
|
.attr("text-anchor", "start") |
|
.attr("class", "title") |
|
.text("Party agreement (% vote similarity per issue)"); |
|
|
|
var row = 0; |
|
data.forEach(function(issue) { |
|
|
|
var currentIssue = issue.values; |
|
|
|
var blocks = graphSvg |
|
.append("g") |
|
.attr("class", function(d, i) { return issue.issue; }) |
|
.selectAll("rect") |
|
.data(currentIssue) |
|
.enter() |
|
.append("rect") |
|
.attr("x", function(d, i) { return xScale(partyCombinations[i]); }) |
|
.attr("y", function() { return yScale(issue.issue); }) |
|
.attr("id", function(d) { return assignColour(d); }) |
|
.attr("width", width/currentIssue.length) |
|
.attr("height", height/issues.length) |
|
.attr("stroke", "black") |
|
.attr("class", function(d, i) { return row + "," + i; }); |
|
|
|
blocks |
|
.on("mouseover", function() { |
|
d3.select(this).attr("fill-opacity", "0.3"); |
|
var classVals = d3.select(this).attr("class"); |
|
var explanation = buildExplanation(classVals, data, issues, partyCombinations); |
|
var explained = d3.select(".base-svg").append("text") |
|
.attr("x", margin.left) |
|
.attr("y", margin.top + height + (margin.bottom / 2)) |
|
.attr("text-anchor", "start") |
|
.attr("id", "data-explanation") |
|
.text(explanation); |
|
}) |
|
.on("mouseout", function() { |
|
d3.select(this).attr("fill-opacity", "1"); |
|
d3.selectAll("#data-explanation").remove(); |
|
}); |
|
|
|
row += 1; |
|
|
|
}); |
|
|
|
var xAxis = d3.svg.axis().scale(xScale).orient("top"); |
|
var yAxis = d3.svg.axis().scale(yScale).orient("left"); |
|
x.call(xAxis); |
|
y.call(yAxis); |
|
|
|
}); |
|
|
|
var aspect = widthInit/heightInit, |
|
graph = $(".base-svg"); |
|
$(window).on("resize", function() { |
|
var targetWidth = graph.parent().width(); |
|
graph.attr("width", targetWidth); |
|
graph.attr("height", targetWidth/aspect) |
|
}); |
|
|
|
})(); |
|
|
|
</script> |
|
</body> |
|
|
|
</html> |