地図をクリックすると円グラフに変化します。
Built with blockbuilder.org
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>地図・円グラフ遷移</title>
<style>
html, body {
width: 100%;
height: 100%;
}
#stage {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div id="stage"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.1.1/d3.min.js"></script>
<script src="d3.geo2circle.js"></script>
<script>
!(function(){
"use strict"
var margin = { top: 40, right: 20, bottom: 40, left: 50}
var width = document.querySelector("#stage").clientWidth
var height = document.querySelector("#stage").clientHeight
var stageW = width - (margin.left + margin.right)
var stageH = height - (margin.top + margin.bottom)
var radius = (stageH < stageW) ? stageH/2 : stageW/2
var svg = d3.select("#stage").append("svg")
var overLayer = svg.append("g").attr("class", "overLayer")
.attr("width", stageW)
.attr("height", stageH)
.attr("transform", "translate("+[margin.left, margin.top]+")")
var mapLayer = svg.append("g").attr("class", "mapLayer")
.attr("width", stageW)
.attr("height", stageH)
.attr("transform", "translate("+[margin.left, margin.top]+")")
var pieColor = ["red", "blue"]
var pieBg = overLayer.append("circle").attr("class", "pieBg").attr("r", radius)
.attr("stroke", "green")
.attr("fill", "#ccffcc")
.attr("opacity", 0)
var pieGroup = overLayer.append("g").attr("class", "pieGroup").attr("opacity", 0)
pieBg.attr("transform", "translate("+[stageW/2, stageH/2]+")")
pieGroup.attr("transform", "translate("+[stageW/2, stageH/2]+")")
var xScale = d3.scaleLinear().domain([0, 100]).range([0, stageW])
var yScale = d3.scaleLinear().domain([0, 100]).range([stageH, 0])
var projection = d3.geoMercator()
.scale(stageW*1.4)
.translate([stageW/2,stageH/1.7])
.center([139.0032936, 36.3219088]);
var geoPath = d3.geoPath().projection(projection);
var arcPath = d3.arc()
.outerRadius(radius)
.innerRadius(radius/2)
var arcs = d3.pie()
.sort(null)
.value(function(d) { return d.value })
d3.json('japan.geojson', main)
function main(json) {
json.features.forEach(function(d){
d.properties.gender = [
{type:"male", value:(Math.random()*100)},
{type:"female", value:(Math.random()*100)},
]
})
var data = convert(json)
drawMaps(data)
var toggleFn = toggle(
function(that) {
drawPie(d3.select(that).datum())
changeScatter(that)
},
function (that) {
changeMap(that)
}
)
mapLayer.selectAll(".land").on("click", function(d){
toggleFn(this)
})
}
function drawPie(data) {
var genderData = data.properties.gender
pieGroup.data([genderData])
var block = pieGroup.selectAll(".arc")
.data(arcs(genderData))
var newBlock = block.enter().append("g").classed("arc", true)
newBlock.append("path")
.attr("d", function(d){ return arcPath({startAngle:0, endAngle:0})})
.attr("id", function(d, i) { return "arc-" + i })
.attr("stroke-width", 2)
.attr("stroke", "green")
.attr("fill", function(d,i){ return pieColor[i] })
.attr("fill-opacity", 0.5)
}
function drawMaps(data) {
var selector = mapLayer.selectAll(".land")
.data(data, function(d){ return d.properties.ObjName})
var newSelector = selector.enter().append("path")
newSelector.merge(selector)
.attr("class", function(d){return "land " + d.properties.pref })
.attr("d", function(d){ return d.geoPath })
.attr("stroke", "green")
.attr("fill", "#ccffcc")
.attr("opacity", 1)
}
function changeMap(that) {
var lands = mapLayer.selectAll(".land")
var filtered = lands.filter(function(){ return this != that })
var selected = lands.filter(function(){ return this == that })
pieGroup.selectAll("path")
.transition() //円グラフ閉じる
.duration(800)
.attrTween("d", function(b) {
var i = d3.interpolate(b, {startAngle: 0, endAngle: 0});
return function(t) { return arcPath(i(t)); };
})
.on("end", function(){
pieGroup.attr("opacity", 0)
pieBg.attr("opacity", 0)
selected.attr("opacity", 1)
selected
.transition() //縮小
.duration(600)
.attr("d", function(d){ return d.scatterPath })
.transition() //元の位置に戻る
.duration(800)
.attr('transform', "translate(0, 0)")
.transition() //地形に変形
.duration(600)
.attr("d", function(d){ return d.geoPath })
.on("end", function(d){
lands.each(function(d){
d.order = d.properties["JIS-CODE"]
})
.sort(function(a,b){ return a.order - b.order})
filtered.classed("hidden", false)
.transition() //日本地図をフェードイン
.duration(600)
.attr("opacity", 1)
})
})
}
function changeScatter(that) {
var lands = mapLayer.selectAll(".land")
lands.each(function(d){
if(this == that) d.order = 999
})
.sort(function(a,b){ return a.order - b.order})
var filtered = lands.filter(function(){ return this != that })
var selected = lands.filter(function(){ return this == that })
filtered.transition()
.duration(600) //日本地図をフェードアウト
.attr("opacity", 0)
.on("end", function(){
lands.filter(function(){ return this != that }).classed("hidden", true)
})
selected
.transition() //円に変形
.duration(600)
.attr("d", function(d){ return d.scatterPath })
.transition() //中心へ移動
.duration(800)
.attr('transform', function(d, i){
return 'translate('+(0-d.center.x)+','+(0-d.center.y)+'),translate('+d.center.nx+','+d.center.ny+')';
})
.transition() //円を拡大
.duration(600)
.attr("d", function(d){ return d.piePath })
.on("end", function(){
selected.attr("opacity", 0)
pieBg.attr("opacity", 1)
pieGroup.attr("opacity", 1)
.selectAll("path")
.transition()
.duration(800)
.attrTween("d", function(b) {
var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) { return arcPath(i(t)); };
})
})
}
function convert(json) {
var calcCoords = function(polygon, properties) {
var geoCoords = polygon.map(projection);
var scatterCoords = d3.geo2circle(geoCoords, 6);
var scatterPathString = 'M' + scatterCoords.join('L') + 'Z';
var pieCoords = d3.geo2circle(geoCoords, radius);
var piePathString = 'M' + pieCoords.join('L') + 'Z';
var geoPathString = 'M' + geoCoords.join('L') + 'Z';
return { scatterPath: scatterPathString, piePath:piePathString,geoPath: geoPathString , properties:properties};
};
var reslut = [];
for (var i = 0; i < json.features.length; i++) {
var geometry = json.features[i].geometry;
var properties =json.features[i].properties;
properties.geometry = geometry;
if (geometry.type == 'Polygon') {
reslut.push(calcCoords(geometry.coordinates[0], properties));
} else if (geometry.type == 'MultiPolygon') {
geometry.coordinates.forEach(function(coordinates){
reslut.push(calcCoords(coordinates[0], properties));
});
}
}
reslut.forEach(function(d){
var center = geoPath.centroid(d.properties.geometry);
var x = ~~center[0];
var y = ~~center[1];
var nx = stageW/2
var ny = stageH/2
d.center = {x:x, y:y, nx:nx, ny:ny}
})
reslut.sort(function(a,b){ return a.properties["JIS-CODE"] - b.properties["JIS-CODE"]})
.forEach(function(d){ d.order = d.properties["JIS-CODE"]})
return reslut
}
function toggle(){
var fn = arguments;
var l = arguments.length;
var i = 0;
return function(that){
if(l <= i) i=0;
fn[i++](that);
}
}
function endall(transition, callback) {
var n = 0;
transition
.each(function() { ++n; })
.each('end', function() { if (!--n) callback.apply(this, arguments); });
}
}());
</script>
</body>
</html>
if(!d3.geo2circle) d3.geo2circle = function(coordinates, radius) {
radius = (radius) ? radius : Math.sqrt(Math.abs(area) / Math.PI)
var circle = []
var length = 0
var lengths = [length]
var p0 = coordinates[0]
var p1
var x
var y
var i = 0
var n = coordinates.length
while (++i < n) {
p1 = coordinates[i]
x = p1[0] - p0[0]
y = p1[1] - p0[1]
lengths.push(length += Math.sqrt(x * x + y * y))
p0 = p1;
}
var centroid =d3.polygonCentroid(coordinates)
var angle
var i = -1
var k = 2 * Math.PI / lengths[lengths.length - 1];
while (++i < n) {
angle = lengths[i] * k;
circle.push([
centroid[0] + radius * Math.cos(angle),
centroid[1] + radius * Math.sin(angle)
]);
}
return circle;
};