読者です 読者をやめる 読者になる 読者になる

データ分析エンジニアが気まぐれに更新するブログ

勉強してきた技術などを適当に書いていければと思います。

D3.jsとThree.jsで日本地図を可視化してみた

D3.jsとThree.jsを使って、日本地図を3D的に描画してみました。


D3.js : http://d3js.org/

Three.js : http://threejs.org/


※画像をクリックするとページを移動します。


3Dといっても、日本地図は平べったいままです。

D3.jsで日本地図をHTML5Canvasに描画して、CanvasをThree.jsの平面オブジェクトに貼り付けて描画しています。

ここからデータをどのように乗っけていけるか、さらに研究ですね。

var renderer, scene, camera, light, canvas;
function init(){
	$("#target").css("width", window.innerWidth).css("height", window.innerHeight);
	init3DMap("target");
	initPanel();
}
function init3DMap(targetId){

	var target = document.getElementById(targetId);
	var mapSize = (target.clientWidth < target.clientHeight ? target.clientWidth : target.clientHeight);

	// init three
	renderer = new THREE.WebGLRenderer();
	if (!renderer) console.log("three.js init error");
	renderer.setSize(target.clientWidth, target.clientHeight);
	target.appendChild(renderer.domElement);
	renderer.setClearColor(0x000000, 1.0);
	scene = new THREE.Scene();

	// init camera
	camera = new THREE.PerspectiveCamera(45, target.clientWidth / target.clientHeight, 1, 10000);

	// init canvas
	var projection, path, zoom, g;
	d3.json("geodata/japan/topojson/japan.topojson", function(error, json){ // d3 topojson data load
		if (error) return console.error(error);

		canvas = document.createElement("canvas") // create canvas element
		var ctx = d3.select(canvas)
			.attr("width", mapSize)
			.attr("height", mapSize)
			.node()
			.getContext("2d");
		projection = d3.geo.mercator()
			.center(d3.geo.centroid(topojson.feature(json, json.objects.japan)))
			.translate([mapSize / 2, mapSize / 2])
			.scale(1200);
		path = d3.geo.path().projection(projection).context(ctx);

		var features = topojson.feature(json, json.objects.japan).features;
		var renderCanvas = function(){ // init canvas rendering
			var start = new Date();
			ctx.clearRect(0, 0, mapSize, mapSize);
			ctx.fillStyle = "#3cb371";
			for(var i = 0; i < features.length; i++){
				ctx.beginPath();
				path(features[i]);
				ctx.fill();
				ctx.closePath();
			}
			var end = new Date();
			$("#canvasRenderingTime font").html(end.getTime() - start.getTime() + " ms");
			requestAnimationFrame(renderCanvas);
		}
		renderCanvas();

		// init three object
		var texture = new THREE.Texture(canvas); // canvas texture
		texture.needsUpdate = true;
		var geometry = new THREE.PlaneGeometry(400, 400, 50, 50);
		var material = new THREE.MeshPhongMaterial({
			color: 0xffffff,
			map: texture,
		});
		var plane = new THREE.Mesh(geometry, material);
		plane.position.set(0, 0, 0);
		scene.add(plane);
	
		light = new THREE.DirectionalLight(0xffffff, 5.0); // light
		scene.add(light);

		// init rendering
		var step = 0;
		var render = function(){
			var start = new Date();
			step++;
			var cameraX = 400 * Math.cos(step / 500);
			var cameraY = 400 * Math.sin(step / 500);
			camera.position.set(cameraX, cameraY, 200);
			camera.up.set(0, 0, 1);
			camera.lookAt({x: 0, y: 0, z: 0});
			var lightX = 500 * Math.cos(step / 500);
			var lightY = 500 * Math.sin(step / 500);
			light.position.set(lightX, lightY, 200);
			
			renderer.clear();
			renderer.render(scene, camera);

			var end = new Date();
			$("#threeRenderingTime font").html(end.getTime() - start.getTime() + " ms");

			requestAnimationFrame(render);
		}
		render();

	});

}
function initPanel(){
	var html = '<div style="font-size:large; font-weight:bold; color:#7fffd4; margin-bottom:10px;">D3.js and Three.js Japan Map Test</div>'
		+ 'javascript library : '
		+ '<a href="http://d3js.org/">D3.js [http://d3js.org/]</a>, '
		+ '<a href="http://threejs.org/">Three.js [http://threejs.org/]</a><br>'
		+ 'data : '
		+ '<a href="http://www.esrij.com/">ESRI Japan [http://www.esrij.com/]</a>'
		+ '<div id="canvasRenderingTime" style="margin-top:10px;">canvas rendering time : <font></font></div>'
		+ '<div id="threeRenderingTime">three rendering time : <font></font></div>';
	$("#panel").css("position", "absolute")
		.css("top", "0px")
		.css("left", "0px")
		.css("padding", "10px")
		.css("opacity", "0.5")
		.append(html);
}


追記:さらに更新しました。