地図をクリックすると円グラフに変化します。
Built with blockbuilder.org
license: mit |
地図をクリックすると円グラフに変化します。
Built with blockbuilder.org
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; | |
}; |
<!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> |