Skip to content

Instantly share code, notes, and snippets.

@shimizu
Last active March 23, 2018 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shimizu/3f4099cf411b7e8bd0f7b3210db9df63 to your computer and use it in GitHub Desktop.
Save shimizu/3f4099cf411b7e8bd0f7b3210db9df63 to your computer and use it in GitHub Desktop.
地図変形・円グラフ
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>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment