クリックすると地形から散布図へ遷移します。
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) | |
//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; | |
}; |
<!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> |