block by shimizu 3f4099cf411b7e8bd0f7b3210db9df63

地図変形・円グラフ

Full Screen

地図をクリックすると円グラフに変化します。

Built with blockbuilder.org

index.html

<!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>

d3.geo2circle.js

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;
};