|
!(function(){ |
|
var tPrs = d3.timeParse("%Y年%m月%d日"); |
|
var dateStart = new Date(1889, 1, 1); |
|
var dateEnd = new Date(2019, 1, 1); |
|
|
|
var index = { election: 0, wareki: 1, event: 2, seats: "比較第一党議席率", vote: "投票率" }; |
|
var chartMargin = { top: 60, left: 40, bottom: 120, right: 20 }; |
|
|
|
|
|
d3 |
|
.queue() |
|
.defer(d3.tsv, "election.tsv") |
|
.defer(d3.tsv, "wareki.tsv") |
|
.defer(d3.tsv, "event.tsv") |
|
.awaitAll(main); |
|
|
|
function main(error, data) { |
|
data[index.election].forEach(castNumber); |
|
data[index.wareki].forEach(castNumber); |
|
data[index.event].forEach(castNumber); |
|
|
|
data[index.election].forEach(castElectionData); |
|
data[index.wareki].forEach(castDate); |
|
data[index.event].forEach(castDate); |
|
|
|
var stage = d3.select("#stage").datum(data); |
|
|
|
var addVotePinhead = createPinhead(index.vote); |
|
var addSeatsPinhead = createPinhead(index.seats); |
|
|
|
stage |
|
.call(initStage) |
|
.call(initScale) |
|
.call(addLegend) |
|
.call(addYAxis) |
|
.call(addXAxis) |
|
.call(addPinNeck) |
|
.call(addVotePinhead) |
|
.call(addSeatsPinhead) |
|
.call(addWareki) |
|
.call(addPointEvent) |
|
.call(addRangeEvent) |
|
.call(addAdministration) |
|
.call(addZoom); |
|
} |
|
|
|
function castNumber(d) { |
|
Object.keys(d).forEach(function(key) { |
|
if (!isNaN(+d[key])) d[key] = +d[key]; |
|
}); |
|
} |
|
function castDate(d) { |
|
d.sdate = tPrs(d.sdate); |
|
d.edate = tPrs(d.edate); |
|
} |
|
function castElectionData(d) { |
|
var dateObje = tPrs(d["投票年"] + d["投票日"]); |
|
var adminEndDate = tPrs(d["任期満了西暦"] + d["任期満了日"]); |
|
d.sdate = dateObje; |
|
d.edate = adminEndDate ? adminEndDate : null; |
|
} |
|
|
|
function initStage(_selection) { |
|
var svg = _selection.append("svg"); |
|
|
|
var clipRect = svg |
|
.append("defs") |
|
.append("clipPath") |
|
.attr("id", "clip") |
|
.append("rect"); |
|
|
|
var legendLayer = svg.append("g").classed("legendLayer", true); |
|
var axisLayer = svg.append("g").classed("axisLayer", true); |
|
var charLayer = svg.append("g").classed("chartLayer", true); |
|
|
|
var parentNode = _selection.node(); |
|
var parentWidth = parentNode.clientWidth; |
|
var parentHeight = parentNode.clientHeight; |
|
|
|
var chartWidth = parentWidth - (chartMargin.left + chartMargin.right); |
|
var chartHeight = parentHeight - (chartMargin.top + chartMargin.bottom); |
|
|
|
svg.attr("width", parentWidth).attr("height", parentHeight); |
|
|
|
clipRect.attr("width", chartWidth).attr("height", parentHeight); |
|
|
|
axisLayer |
|
.attr("width", chartWidth) |
|
.attr("height", chartHeight) |
|
.attr( |
|
"transform", |
|
"translate(" + [chartMargin.left, chartMargin.top] + ")" |
|
); |
|
charLayer |
|
.attr("width", chartWidth) |
|
.attr("height", chartHeight) |
|
.attr("transform", "translate(" + [chartMargin.left, chartMargin.top] + ")") |
|
.attr("clip-path", "url(#clip)"); |
|
} |
|
|
|
function initScale(_selection) { |
|
var chartWidth = _selection.select(".chartLayer").attr("width"); |
|
var chartHeight = _selection.select(".chartLayer").attr("height"); |
|
|
|
var xScale = d3 |
|
.scaleTime() |
|
.domain([dateStart, dateEnd]) |
|
.range([0, chartWidth]); |
|
|
|
var yScale = d3 |
|
.scaleLinear() |
|
.domain([0, 100]) |
|
.range([chartHeight, 0]); |
|
|
|
_selection._xScale = xScale; |
|
_selection._yScale = yScale; |
|
} |
|
|
|
function addLegend(_selection) { |
|
var legendLayer = _selection.select(".legendLayer"); |
|
|
|
var g = legendLayer |
|
.selectAll("g") |
|
.data([index.vote, index.seats]) |
|
.enter() |
|
.append("g") |
|
.attr("transform", function(d, i) { |
|
return ( |
|
"translate(" + [chartMargin.left + i * 80, chartMargin.top / 2] + ")" |
|
); |
|
}); |
|
|
|
g |
|
.append("circle") |
|
.attr("r", 5.5) |
|
.attr("y", -4) |
|
.attr("class", function(d) { |
|
return d; |
|
}); |
|
|
|
g |
|
.append("text") |
|
.attr("class", "legendLabel") |
|
.attr("dominant-baseline", "middle") |
|
.attr("x", 6 + 3) |
|
.text(function(d) { |
|
return d; |
|
}); |
|
} |
|
|
|
function addZoom(_selection) { |
|
var xScale = _selection._xScale; |
|
var yScale = _selection._yScale; |
|
var chartWidth = _selection.select(".chartLayer").attr("width"); |
|
|
|
var zoom = d3 |
|
.zoom() |
|
.scaleExtent([1, 32]) |
|
.on("zoom", zoomed); |
|
|
|
zoom |
|
.translateExtent([[0, 0], [chartWidth, 0]]) |
|
.extent([[0, 0], [chartWidth, 0]]); |
|
|
|
_selection |
|
.select("svg") |
|
.call(zoom) |
|
.transition() |
|
.duration(1500) |
|
.call( |
|
zoom.transform, |
|
d3.zoomIdentity |
|
.scale(chartWidth / (xScale(dateEnd) - xScale(dateStart))) |
|
.translate(xScale(dateStart), 0) |
|
); |
|
|
|
function zoomed() { |
|
var t = d3.event.transform; |
|
var xt = t.rescaleX(xScale); |
|
|
|
_selection._xScale = xt; |
|
_selection._update(); |
|
} |
|
} |
|
|
|
function addXAxis(_selection) { |
|
var axisLayer = _selection.select(".axisLayer"); |
|
var g = axisLayer.append("g").attr("class", "axis--x"); |
|
|
|
var height = axisLayer.attr("height"); |
|
|
|
_selection._xAxis = d3.axisTop(_selection._xScale); |
|
|
|
g.attr("transform", "translate(" + [0, 0] + ")"); |
|
g.call(_selection._xAxis); |
|
g.selectAll(".domain").style("display", "none"); |
|
|
|
var update = function() { |
|
var t = d3.event.transform; |
|
var tFmt = d3.timeFormat("%Y-%m"); |
|
if (t.k < 30) tFmt = d3.timeFormat("%Y"); |
|
_selection._xAxis.scale(_selection._xScale).tickFormat(function(d) { |
|
return tFmt(d); |
|
}); |
|
g.call(_selection._xAxis); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function addYAxis(_selection) { |
|
var axisLayer = _selection.select(".axisLayer"); |
|
var g = axisLayer.append("g").attr("class", "axis--y"); |
|
|
|
var width = axisLayer.attr("width"); |
|
|
|
_selection._yAxis = d3 |
|
.axisLeft(_selection._yScale) |
|
.tickSizeOuter(0) |
|
.tickSizeInner(-width) |
|
.tickValues([30, 50, 70]) |
|
.tickFormat(function(d) { |
|
return d + "%"; |
|
}); |
|
|
|
g.attr("transform", "translate(" + [0, 0] + ")"); |
|
|
|
g.call(_selection._yAxis); |
|
|
|
g.selectAll(".domain").style("display", "none"); |
|
} |
|
|
|
function createPinhead(_index) { |
|
return function(_selection) { |
|
var data = _selection.datum(); |
|
var election = data[index.election]; |
|
|
|
var chartLayer = _selection.select(".chartLayer"); |
|
var pinChart = chartLayer.selectAll(".pinChart").data([election]); |
|
|
|
var enterPinChart = pinChart |
|
.enter() |
|
.append("g") |
|
.classed("pinChart", true); |
|
|
|
var g = d3 |
|
.select(".pinChart") |
|
.append("g") |
|
.attr("class", "heads " + _index); |
|
|
|
var chartHeight = chartLayer.attr("height"); |
|
|
|
var yProp = function(d) { |
|
return _selection._yScale(d[_index]); |
|
}; |
|
var hFn = function(d) { |
|
return chartHeight - yProp(d); |
|
}; |
|
var yFn = function(d) { |
|
return yProp(d); |
|
}; |
|
|
|
var neckWidth = 1; |
|
|
|
var selectHeads = g.selectAll("circle").data(function(d) { |
|
return d; |
|
}); |
|
|
|
var enterHeads = selectHeads.enter().append("circle"); //.style("opacity", 0); |
|
|
|
var heads = selectHeads |
|
.merge(enterHeads) |
|
.attr("r", 6) |
|
.attr("cy", yFn); |
|
|
|
heads |
|
.transition() |
|
.duration(1000) |
|
.delay(function(d, i) { |
|
return i * 100; |
|
}) |
|
.style("opacity", 1); |
|
|
|
var update = function() { |
|
heads.attr("cx", function(d) { |
|
return _selection._xScale(d.sdate); |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
}; |
|
} |
|
|
|
function addPinNeck(_selection) { |
|
var data = _selection.datum(); |
|
var election = data[index.election]; |
|
|
|
var chartLayer = _selection.select(".chartLayer"); |
|
var pinChart = chartLayer.selectAll(".pinChart").data([election]); |
|
|
|
var enterPinChart = pinChart |
|
.enter() |
|
.append("g") |
|
.classed("pinChart", true); |
|
|
|
d3 |
|
.select(".pinChart") |
|
.append("g") |
|
.attr("class", "necks"); |
|
|
|
var neckWidth = 1; |
|
var chartHeight = chartLayer.attr("height"); |
|
|
|
var voteProp = function(d) { |
|
return +_selection._yScale(d[index.vote]); |
|
}; |
|
var seatsProp = function(d) { |
|
return +_selection._yScale(d[index.seats]); |
|
}; |
|
|
|
var yFn = function(d) { |
|
var extent = d3.extent([voteProp(d), seatsProp(d)]); |
|
return extent[0]; |
|
}; |
|
|
|
var hFn = function(d) { |
|
var extent = d3.extent([voteProp(d), seatsProp(d)]); |
|
return extent[1] - extent[0]; |
|
}; |
|
|
|
var selectNecks = pinChart |
|
.merge(enterPinChart) |
|
.select(".necks") |
|
.selectAll(".neck") |
|
.data(function(d) { |
|
return d; |
|
}); |
|
|
|
var enterNecks = selectNecks |
|
.enter() |
|
.append("rect") |
|
.attr("class", "neck"); //.style("opacity", 0); |
|
|
|
var necks = selectNecks |
|
.merge(enterNecks) |
|
.attr("width", neckWidth) |
|
.attr("height", hFn) |
|
.attr("y", yFn); |
|
|
|
necks |
|
.transition() |
|
.duration(1000) |
|
.delay(function(d, i) { |
|
return i * 100; |
|
}) |
|
.style("opacity", 1); |
|
|
|
var update = function() { |
|
necks.attr("x", function(d) { |
|
return _selection._xScale(d.sdate) - neckWidth / 2; |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function addWareki(_selection) { |
|
var chartLayer = _selection.select(".chartLayer"); |
|
var chartHeight = chartLayer.attr("height"); |
|
var chartWidth = chartLayer.attr("width"); |
|
var Y = 0; |
|
var data = _selection.datum(); |
|
var wareki = data[index.wareki]; |
|
|
|
var chartLayer = _selection.select(".chartLayer"); |
|
var g = chartLayer.append("g").attr("class", "warekiLayer"); |
|
|
|
var line = g |
|
.selectAll(".warekiLine") |
|
.data(wareki) |
|
.enter() |
|
.append("path") |
|
.attr("class", "warekiLine") |
|
.attr("stroke", "black") |
|
.attr("fill", "none"); |
|
|
|
var pathGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = d.edate == null ? chartWidth : _selection._xScale(d.edate); |
|
var str = "M "; |
|
str += [sx, Y].join(" "); |
|
str += "L "; |
|
str += [ex, Y].join(" "); |
|
if (d.edate) { |
|
str += "L "; |
|
str += [ex, Y + 12].join(" "); |
|
} |
|
return str; |
|
}; |
|
|
|
var label = g |
|
.selectAll(".warekiLabel") |
|
.data(wareki) |
|
.enter() |
|
.append("text") |
|
.attr("class", "warekiLabel") |
|
.attr("text-anchor", "middle") |
|
.attr("y", 12); |
|
|
|
var labelGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
return "translate(" + [sx, Y] + ")"; |
|
}; |
|
|
|
var labelDx = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = d.edate == null ? chartWidth : _selection._xScale(d.edate); |
|
var center = (ex - sx) / 2; |
|
return center; |
|
}; |
|
|
|
var update = function() { |
|
line.attr("d", pathGen); |
|
|
|
label |
|
.attr("transform", labelGen) |
|
.attr("x", labelDx) |
|
.text(function(d) { |
|
return d["name"]; |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function addPointEvent(_selection) { |
|
var chartLayer = _selection.select(".chartLayer"); |
|
var Y = chartLayer.attr("height") ; |
|
var data = _selection.datum(); |
|
var event = data[index.event]; |
|
var eventPoint = event.filter(function(d) { |
|
return d.edate == null; |
|
}); |
|
|
|
var g = chartLayer.append("g").attr("class", "pointEventLayer"); |
|
|
|
var circle = g |
|
.selectAll(".pointEventLayer") |
|
.data(eventPoint) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "eventCircle") |
|
.attr("r", 2) |
|
.attr("cy", function(d) { |
|
return d.paragraph; |
|
}); |
|
|
|
var circleGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
return "translate(" + [sx, Y] + ")"; |
|
}; |
|
|
|
var label = g |
|
.selectAll(".eventiLabel") |
|
.data(eventPoint) |
|
.enter() |
|
.append("text") |
|
.attr("class", "eventLabel") |
|
.attr("dominant-baseline", "middle") |
|
.attr("text-anchor", "start") |
|
.attr("x", 6) |
|
.attr("y", function(d, i) { |
|
return d.paragraph; |
|
}); |
|
|
|
var labelGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
return "translate(" + [sx, Y] + ")"; |
|
}; |
|
|
|
var update = function() { |
|
circle.attr("transform", circleGen); |
|
label.attr("transform", labelGen).text(function(d) { |
|
return d["event"]; |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function addRangeEvent(_selection) { |
|
var chartLayer = _selection.select(".chartLayer"); |
|
var Y = chartLayer.attr("height") - 42; |
|
var data = _selection.datum(); |
|
var event = data[index.event]; |
|
var eventRange = event.filter(function(d) { |
|
return d.edate != null; |
|
}); |
|
|
|
var g = chartLayer.append("g").attr("class", "rangeEventLayer"); |
|
|
|
var line = g |
|
.selectAll(".warekiLine") |
|
.data(eventRange) |
|
.enter() |
|
.append("path") |
|
.attr("class", "warekiLine") |
|
.attr("stroke", "black") |
|
.attr("fill", "none"); |
|
|
|
var pathGen = function(d, i) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = _selection._xScale(d.edate); |
|
var str = "M "; |
|
var y = Y + d.paragraph; |
|
|
|
str += [sx, y].join(" "); |
|
str += "L "; |
|
str += [ex, y].join(" "); |
|
return str; |
|
}; |
|
|
|
var sCircle = g |
|
.selectAll(".eventSCircle") |
|
.data(eventRange) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "eventSCircle") |
|
.attr("r", 2); |
|
|
|
var eCircle = g |
|
.selectAll(".eventECircle") |
|
.data(eventRange) |
|
.enter() |
|
.append("circle") |
|
.attr("class", "eventECircle") |
|
.attr("r", 2); |
|
|
|
var sCircleGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var y = Y + d.paragraph; |
|
return "translate(" + [sx, y] + ")"; |
|
}; |
|
|
|
var eCircleGen = function(d, i) { |
|
var sx = _selection._xScale(d.edate); |
|
var y = Y + d.paragraph; |
|
return "translate(" + [sx, y] + ")"; |
|
}; |
|
|
|
var label = g |
|
.selectAll(".eventLabel") |
|
.data(eventRange) |
|
.enter() |
|
.append("text") |
|
.attr("class", "eventLabel") |
|
.attr("text-anchor", "middle") |
|
.attr("y", 12); |
|
|
|
var labelGen = function(d, i) { |
|
var sx = _selection._xScale(d.sdate); |
|
var y = Y + d.paragraph; |
|
return "translate(" + [sx, y] + ")"; |
|
}; |
|
|
|
var labelDx = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = _selection._xScale(d.edate); |
|
var center = (ex - sx) / 2; |
|
return center; |
|
}; |
|
|
|
var update = function() { |
|
line.attr("d", pathGen); |
|
sCircle.attr("transform", sCircleGen); |
|
eCircle.attr("transform", eCircleGen); |
|
|
|
label |
|
.attr("transform", labelGen) |
|
.attr("x", labelDx) |
|
.text(function(d) { |
|
return d["event"]; |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function addAdministration(_selection) { |
|
var chartLayer = _selection.select(".chartLayer"); |
|
var Y = chartLayer.attr("height") - 60; |
|
var data = _selection.datum(); |
|
var election = data[index.election]; |
|
|
|
var g = chartLayer.append("g").attr("class", "adminLayer"); |
|
|
|
var line = g |
|
.selectAll(".adminLine") |
|
.data(election) |
|
.enter() |
|
.append("path") |
|
.attr("class", "adminLine") |
|
.attr("stroke", "black") |
|
.attr("fill", "none"); |
|
|
|
var pathGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = _selection._xScale(d.edate); |
|
var str = "M "; |
|
str += [sx, Y - 10].join(" "); |
|
str += "L "; |
|
str += [sx, Y].join(" "); |
|
str += "L "; |
|
str += [ex, Y].join(" "); |
|
str += "L "; |
|
str += [ex, Y - 10].join(" "); |
|
return str; |
|
}; |
|
|
|
var label = g |
|
.selectAll(".adminLabel") |
|
.data(election) |
|
.enter() |
|
.append("text") |
|
.attr("class", "adminLabel") |
|
.attr("text-anchor", "middle") |
|
.attr("y", 12); |
|
|
|
var labelGen = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
return "translate(" + [sx, Y] + ")"; |
|
}; |
|
|
|
var labelDx = function(d) { |
|
var sx = _selection._xScale(d.sdate); |
|
var ex = _selection._xScale(d.edate); |
|
var center = (ex - sx) / 2; |
|
return center; |
|
}; |
|
|
|
var update = function() { |
|
var t = d3.event.transform; |
|
line.attr("d", pathGen); |
|
label |
|
.attr("transform", labelGen) |
|
.attr("x", labelDx) |
|
.text(function(d) { |
|
if (t.k > 3) return d["内閣"]; |
|
}); |
|
}; |
|
|
|
bindUpdate(_selection, update); |
|
} |
|
|
|
function bindUpdate(_selection, update) { |
|
if (!_selection._updateFanctions) _selection._updateFanctions = []; |
|
|
|
_selection._updateFanctions.push(update); |
|
|
|
_selection._update = function(data) { |
|
_selection._updateFanctions.forEach(function(f) { |
|
f(data); |
|
}); |
|
}; |
|
} |
|
|
|
}()); |
|
|