2015年4月12日執行の北海道知事選挙の市区町村ごとの得票状況です。
市区町村の色分け
全道の得票率を基準に、高橋候補の得票率が高い地域をオレンジ、佐藤候補の得票率が高い地域をグリーンにしています。
棒グラフの見方
オレンジ・グリーンの帯の高さが投票率です。つまり、グレーの面積が大きいほど棄権者数の比率が高いことをあらわしています。
Source:北海道選挙管理委員会
xxxxxxxxxx
<meta charset="utf-8">
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<!--
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<script src="crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.min.js"></script>
<style src="https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.css" />
-->
<style type="text/css">
.label {
fill: #333;
font-size: 9px;
text-anchor: middle;
}
svg text.legend, text.area-desc, text.legend-choson {
font-size: 14px;
font-weight: bold;
}
svg text.result {
font-size: 11px;
fill: #fff;
}
svg text.bar-label {
font-size: 11px;
fill: #f00;
}
svg path.map-path {
stroke: #fff;
stroke-width: 1px;
}
svg text::selection { background: none; }
</style>
<body>
<script>
var width = 960,
height = 640;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//.append("g");
var g = svg.append("g");
//var mapfilepath = 'hokkaido.topojson';
var mapfilepath = '/sugi2000/raw/0c17316fee7dba5f7187/hokkaido.topojson';
var csvpath = 'hokkaido.csv';
var hokkaido;
var votes = [];
var candidates = [];
var results = [];
var candidateColors = d3.scale.ordinal().range(["hsla(30, 90%, 50%, 0.8)", "hsla(160, 90%, 40%, 0.8)"]);
var resultSizes = [];
for (var i = 0; i < results.length; i++) {
resultSizes.push(Math.sqrt(results[i]) * 0.2);
}
var zoom = d3.behavior.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
svg.call(zoom)
.call(zoom.event);
var color = d3.scale.ordinal()
.range(['hsla(0, 70%, 50%, 1.0)', 'hsla(210, 70%, 50%, 1.0)', 'hsla(60, 70%, 50%, 1.0)', 'hsla(90, 0%, 50%, 1.0)']);
g.selectAll('circle.legend')
.data(candidates)
.enter()
.append('circle')
.attr('class', 'legend')
.attr('cx', 100)
.attr('cy', function(d,i) {
var margin = 100;
for (var j = 0; j < i; j++) {
margin += resultSizes[j];
}
return margin;
})
.attr('r', function(d,i) { return resultSizes[i] / 2; })
.style('fill', function(d) { return color(d); });
var areaColors = ['hsla(30, 70%, 90%, 1.0)', 'hsla(160, 70%, 90%, 1.0)'];
var threshold = 0.5662651012601533;
var alpha = d3.scale.linear()
.domain([0.0, 0.3, threshold, 0.7, 1.0])
.range([1.0, 1.0, 0.2, 1.0, 1.0]);
var areaColor = function(d) {
var total = +d.properties[candidates[0]] + +d.properties[candidates[1]];
var ratio = +d.properties[candidates[0]] / total;
if (ratio >= threshold) {
return 'hsla(30, 100%, 50%, ' + alpha(ratio).toFixed(1) + ')';
} else {
return 'hsla(160, 100%, 40%, ' + alpha(ratio).toFixed(1) + ')';
}
}
queue(1)
.defer(d3.json, mapfilepath)
.defer(d3.csv, csvpath)
.await(ready);
function ready(error, map, rows) {
if (error) {
return console.warn(error);
}
renderMap(map);
renderRows(rows);
updateMap(candidates[0]);
}
function initCandidates(rows) {
// init candidates and results
for (key in rows[rows.length-1]) {
if (key !== '市区町村' && key !== '有権者数' && key !== '投票率') {
candidates.push(key);
}
}
results = rows;
candidateColors.domain(d3.keys(results[0]).filter(function(key) { return (key !== "市区町村" && key !== '有権者数' && key !== '投票率'); }));
results.forEach(function(d) {
var y0 = 0;
d.entries = candidateColors.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name], votes: +d[name]}; });
d.entries.forEach(function(d) { d.y0 /= y0; d.y1 /= y0; });
});
bardata = results[results.length - 1].entries;
var x = d3.scale.linear().rangeRound([0, width]);
var g2 = svg.append('g')
.attr('class', 'legend');
g2.append('rect')
.attr('class', 'total')
.attr('x', 0 )
.attr('y', 0)
.attr('height', 50)
.attr('width', width)
.style('fill', 'hsla(0, 0%, 0%, 0.2)');
g2.selectAll('rect.result')
.data(bardata)
.enter()
.append('rect')
.attr('class', 'result')
.attr('x', function(d) { return x(d.y0);} )
.attr('y', 25)
.attr('height', 50 - 25)
.attr('width', function(d) { return x(d.y1 - d.y0);})
.style('fill', function(d) { return candidateColors(d.name); });
g2.append('line')
.attr('class','split')
.attr('x1', width / 2)
.attr('y1', 0)
.attr('x2', width / 2)
.attr('y2', 50)
.style('stroke', 'white')
.style('stroke-width', 1);
g2.append('line')
.attr('class','split-ex')
.attr('x1', width / 2)
.attr('y1', 50)
.attr('x2', width / 2)
.attr('y2', 55)
.style('stroke', 'red')
.style('stroke-width', 1);
g2.append('text')
.attr('class', 'bar-label')
.attr('x', width / 2)
.attr('y', 65)
.style('text-anchor', 'middle')
.text('50%');
g2.selectAll('text.legend')
.data(candidates)
.enter()
.append('text')
.attr('class', 'legend')
.attr('x', function(d, i) { return (i === 0) ? 20 : width - 20;})
.attr('y', 25)
.style('text-anchor', function(d, i){ return (i === 0) ? 'start' : 'end';})
.text(function(d){ return d;});
g2.append('text')
.attr('class', 'legend-choson')
.attr('x', width / 2)
.attr('y', 15)
.style('text-anchor', 'middle')
.text('合計');
g2.selectAll('text.result')
.data(bardata)
.enter()
.append('text')
.attr('class', 'result')
.attr('x', function(d, i) { return (i === 0) ? 20 : width - 20;})
.attr('y', 40)
.style('text-anchor', function(d, i){ return (i === 0) ? 'start' : 'end';})
.text(function(d){ return d.votes.toLocaleString() + '票';});
updateBarChart(rows.length-1);
}
function updateBarChart(index) {
var x = d3.scale.linear().rangeRound([0, width]);
bardata = results[index].entries;
var g2 = d3.select('g.legend');
d3.select('text.legend-choson')
.text(function(d) { return results[index]['市区町村'].replace(/(.+)区$/, '札幌市$1区')
+ '(投票率:' + (results[index]['投票率'] * 100).toFixed(2) + '%)'; });
g2.selectAll('rect.result')
.data(bardata)
.transition()
.duration(300)
.attr('y', function(d) {
return 50 - results[index]['投票率'] * 50;
})
.attr('height', function(d) {
return results[index]['投票率'] * 50;
})
.attr('x', function(d) { return x(d.y0);} )
.attr('width', function(d) { return x(d.y1 - d.y0);});
g2.selectAll('text.result')
.data(bardata)
.text(function(d){ return d.votes.toLocaleString() + '票';});
g2.selectAll('line.split')
.transition()
.duration(300)
.attr('y1', 50 - results[index]['投票率'] * 50);
g2.selectAll('line.split-ex')
.transition()
.duration(300)
.attr('x1', x(bardata[0].y1))
.attr('x2', x(bardata[0].y1));
g2.selectAll('text.bar-label')
.transition()
.duration(300)
.attr('x', x(bardata[0].y1))
.text((bardata[0].y1 * 100).toFixed(2) + '%');
}
function renderMap(map) {
// setup map
hokkaido = topojson.feature(map, map.objects.hokkaido).features;
updateMap(hokkaido);
}
function updateMap(candidate) {
var projection = d3.geo.mercator()
.center([142.5,43.6])
.scale(6000)
//.center(d3.geo.centroid(hokkaido))
.translate([width / 2, height / 2]);
var path = d3.geo.path().projection(projection);
var update = g.selectAll('path.map-path').data(hokkaido);
var enter = update.enter();
for (var i = 0; i < candidates.length; i++) {
update.attr('candidate-' + (i+1), function(d){
return d.properties[candidates[i]];
});
}
enter
.append('path')
.filter(function(d){
return (d.properties.SIKUCHOSON === null);
})
.attr('class', 'map-path-disabled')
.attr('d', path)
.style('fill', '#eee');
enter
.append('path')
.filter(function(d){
return (d.properties.SIKUCHOSON !== null);
})
.attr('class', 'map-path')
.attr('d', path)
.attr('vector-effect', 'non-scaling-stroke')
.attr('JCODE', function(d) {return d.properties.JCODE;})
.attr('KENCODE', function(d) {
if (d.properties.JCODE) {
return d.properties.JCODE.substr(0, 2);
} else {
return 'null';
}
})
.attr('shikuchoson', function(d) {
return d.properties.SIKUCHOSON;
})
.style('fill', '#eee')
.style('cursor', 'pointer')
/*.on('mouseup', function(){
var self = d3.select(this);
//alert(self.attr('shikuchoson'));
var index = searchIndex(results, '市区町村', self.attr('shikuchoson'));
updateBarChart(index);
})*/
.on('mouseover', function(){
var self = d3.select(this);
self.moveToFront()
.style('fill', '#fff')
.style('stroke', '#f00');
//d3.select('text.label-' + self.attr('JCODE')).attr('visibility', 'visible');
var index = searchIndex(results, '市区町村', self.attr('shikuchoson'));
updateBarChart(index);
})
.on('mouseout', function(){
var self = d3.select(this);
self.transition()
//.duration(300)
.style('fill', function(d) {
return areaColor(d);
})
.style('stroke', '#fff');
updateBarChart(results.length - 1);
})
;
update
.transition()
.duration(300)
.style('fill', function(d) {
return areaColor(d);
});
var centroids = [];
for (var i = 0; i < hokkaido.length; i++) {
centroids.push(path.centroid(hokkaido[i]));
}
//console.log(centroids);
g.selectAll('text.label')
.data(hokkaido)
.enter()
.append('text')
.attr('class', function(d) { return 'label label-' + d.properties.JCODE; })
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.attr('dy', function(d) {
return '.35em';
})
.attr('pointer-events', 'none')
.attr('visibility', 'hidden')
.text(function(d) { return d.properties.SIKUCHOSON; });
/*
for (var i = 0; i < votes.length; i++) {
var entries = d3.entries(votes[i]);
entries.shift(); // remove first town-name
//console.log(entries);
var totalVotes = 0;
for (var j = 0; j < entries.length; j++) {
totalVotes += +entries[j].value;
}
var radius = Math.sqrt(totalVotes) * 0.125;
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius * 0.5);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return +d.value; });
var garc = g.selectAll('g.arc-' + i)
.data(pie(entries))
.enter()
.append('g')
.attr('class', function(d) { return "arc arc-" + i; })
.attr("transform", 'translate(' + centroids[i] + ')');
garc.append('path')
.attr('d', arc)
.style('fill', function(d) { return candidateColors(+d.value); });
}
*/
}
function searchIndex (rows, key, value) {
var index = -1;
for (var i = 0; i < rows.length; i++) {
if (rows[i][key] === value) {
index = i;
break;
}
}
return index;
}
function renderRows(rows) {
initCandidates(rows);
// add properties
for (var i = 0; i < hokkaido.length; i++) {
var choson = hokkaido[i].properties.SIKUCHOSON;
var index = searchIndex(rows, '市区町村', choson);
if (index >= 0) {
for (var j = 0; j < candidates.length; j++) {
hokkaido[i].properties[candidates[j]] = rows[index][candidates[j]];
}
votes.push(rows[index]);
}
}
for (var i = 0; i < results.length; i++) {
var vote = results[i];
var totalVote = 0;
for (var j = 0; j < candidates.length; j++) {
totalVote += +vote[candidates[j]];
}
//vote['投票率'] = totalVote / vote['有権者数'];
vote['棄権者数'] = vote['有権者数'] - totalVote;
}
updateBarChart(results.length -1);
}
function zoomed() {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
d3.select(self.frameElement).style("height", height + "px");
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
</script>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://d3js.org/topojson.v1.min.js to a secure url
Modified http://d3js.org/queue.v1.min.js to a secure url
Modified http://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.min.js to a secure url
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js
https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js
https://d3js.org/d3.v3.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/queue.v1.min.js
https://cdnjs.cloudflare.com/ajax/libs/dc/1.7.0/dc.min.js