block by shimizu 00138ff01086f1dfb98725315a60d581

地図変形・散布図

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: 900px;
    height: 500px;
}
svg {
    width: 100%;
    height: 100%;
}
</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 svg = d3.select("#stage").append("svg")
        
    var axisLayer = svg.append("g").attr("class", "axisLayer").attr("opacity", 0)
    
    
    var mapLayer = svg.append("g").attr("class", "mapLayer")
        .attr("width", stageW)
        .attr("height", stageH)
        .attr("transform", "translate("+[margin.left, margin.top]+")")
            
        
    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,250])
        .center([139.0032936, 36.3219088]); 
    
    
    var path = d3.geoPath().projection(projection); 
    
    d3.json('japan.geojson', main)
    
    
    function main(json) {
        
        var data = convert(json)
        drawAxis(data)
        drawMaps(data)
        
    
        var toggleFn = toggle(
            function() {
                axisLayer.transition().duration(1400).attr("opacity", 1)
                changeScatter()
            },
            function () {
                axisLayer.transition().duration(1400).attr("opacity", 0)
                changeMap()
            }
        )
    
        
        d3.select("body").on("click", toggleFn)
        d3.select("body").on("touchend", toggleFn)

    }
    
    function drawAxis() {
        axisLayer.append("g")
            .attr("transform", "translate("+[margin.left, margin.top]+")")
            .attr("class", "axis y")
            .call(d3.axisLeft(yScale))
    
        axisLayer.append("g")
            .attr("class", "axis x")
            .attr("transform", "translate("+[margin.left, stageH+margin.top]+")")
            .call(d3.axisBottom(xScale))
                
    }
    
    function drawMaps(data) {
         
        data.sort(function(a,b){ return a.properties["JIS-CODE"] - b.properties["JIS-CODE"]})
        
        var selector = mapLayer.selectAll(".land")
            .data(data, function(d){ return d.properties.ObjName})
            
        var newSelector = selector.enter().append("path")
            
        newSelector.merge(selector)
            .attr("class", "land")
            .attr("d", function(d){ return d.geoPath })
            .attr("stroke", "green")
            .attr("fill", "#ccffcc")
            .attr("fill-opacity", 0.5)
            
    }
    
    
    function changeMap() {
        mapLayer.selectAll(".land")
            .transition()
            .duration(1200)
            .attr("transform", function(){
                return "translate("+[0, 0]+")"
            })
            .call(endAll, function(){
                mapLayer.selectAll(".land")
                    .transition()
                    .delay(function(d, i){ return i * 100 })
                    .duration(1000)
                    .attr("d", function(d){ return d.geoPath })
            })
            
    }
    
    function changeScatter() {
        mapLayer.selectAll(".land")
            .transition()
            .delay(function(d, i){ return i * 100 })
            .duration(1000)        
            .attr("d", function(d){ return d.scatterPath })
            .call(endAll, function(){
                mapLayer.selectAll(".land")
                    .transition()
                    .duration(1200)
                    .attr('transform', function(d, i){
                        var center = path.centroid(d.properties.geometry);
                        var x = ~~center[0]; 
                        var y = ~~center[1];
                        var nx = xScale(Math.random()*100)  
                        var ny = yScale(Math.random()*100)  
                            return 'translate('+(0-x)+','+(0-y)+'),translate('+nx+','+ny+')';		    
                    });
                    
            })
    
    }
    
    function endAll(transition, callback) {
        var n = 0; 
        transition
            .each(function() { ++n; })     
            .on('end', function() { if (!--n) callback.apply(this, arguments); });
        
    }
    
    
    
    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 geoPathString = 'M' + geoCoords.join('L') + 'Z';
            return { scatterPath: scatterPathString, 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));
                });			
            }
        }
            
        return reslut
    
    }
    
    function toggle(){
        var fn = arguments;
        var l = arguments.length;
        var i = 0;
        return function(){
            if(l <= i) i=0;
            fn[i++]();            
        }
    }
    
    
    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)

	//circle 生成用の変数
	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

    
	// coordinatesの前後間の距離を求める
	while (++i < n) {
		p1 = coordinates[i]
		//console.log(p1);
		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;
};