1. 程式人生 > >【 D3.js 入門系列 --- 10.2 】 可拖動的地圖

【 D3.js 入門系列 --- 10.2 】 可拖動的地圖

    轉載請註明出處,謝謝。

    本節是結合9.2節10節 的內容製作的一個可力學導向的中國地圖,使用者可以拖動中國的各個省份。

    1. 定義各函式

	var projection = d3.geo.mercator()
						.center([107, 31])
						.scale(850)
    					.translate([width/2, height/2]);
	
	var path = d3.geo.path()
					.projection(projection);
	
	var force = d3.layout.force().size([width, height]);
	
	var color = d3.scale.category20();
    projection 函式是用於將三維地圖的座標投影到二維所用的投影函式。具體可見: 10節

    path 函式用於繪製地圖路徑,裡面要傳入投影函式 projection 。具體可見: 10節

    force 是定義力學圖的 layout 。具體可見: 9.2節

    color 是顏色函式。

    2. 讀取資料

d3.json("china_simplify.json", function(error, root) {
		
		if (error) 
			return console.error(error);
		console.log(root.features);
}
    和前幾節一樣,用 d3.json() 讀取檔案,後面兩句是用於檢測錯誤,以及輸出錯誤資訊。

    3. 轉換資料

		var nodes = [];
		var links = [];
		
		root.features.forEach(function(d, i) {
			var centroid = path.centroid(d);
			centroid.x = centroid[0];
			centroid.y = centroid[1];
			centroid.feature = d;
			nodes.push(centroid);
		});
		
		var triangles = d3.geom.voronoi().triangles(nodes);
		
		triangles.forEach(function(d,i){
			links.push( edge( d[0] , d[1] ) );
			links.push( edge( d[1] , d[2] ) );
			links.push( edge( d[2] , d[0] ) );
		});
    讀取後的檔案資訊都存在變數 root 中,上面的程式碼是將 root 中的資料分別轉換為力學圖所需要的點和線,存在變數 nodes 和 links 中。
    第1-2行: 定義變數 nodes 和 links

    第4-10行: 對於 root.features 中存有每一個省的資料, root.features.forEach() 即對每一個省的資料,執行後面的無名函式,函式裡面是計算出各省的中點,儲存在 centroid.x 和 centroid.y 中,再把其他資訊賦值給 centroid.feature,最後插入到 nodes 中。

    第12行: 對 nodes 中的頂點進行三角剖分,即用三角形來連線各頂點,結果儲存在 triangles 中。

    第14-18行: 將三角形的各邊存到 links 變數中。其中的 edge 函式的實現為:

	function edge(a, b) {
		var dx = a[0] - b[0], dy = a[1] - b[1];
		return {
			source: a,
			target: b,
			distance: Math.sqrt(dx * dx + dy * dy)
		};
	}

    4. 繪製地圖

		force.gravity(0)
			.charge(0)
			.nodes(nodes)
			.links(links)
			.linkDistance(function(d){ return d.distance; })
			.start();
		
		var node = svg.selectAll("g")
						.data(nodes)
						.enter().append("g")
						.attr("transform", function(d) { return "translate(" + -d.x + "," + -d.y + ")"; })
      					.call(force.drag)
				        .append("path")
				        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
				        .attr("stroke","#000")
				        .attr("stroke-width",1)
				        .attr("fill", function(d,i){
				            return color(i);
				        })
				        .attr("d", function(d){
				        	return path(d.feature);
				        } );
		
		var link = svg.selectAll("line")
						.data(links)
						.enter()
						.append("line")
						.attr("class","link")
						.attr("x1",function(d) { return d.source.x; } )
						.attr("y1",function(d) { return d.source.y; } )
						.attr("x2",function(d) { return d.target.x; } )
						.attr("y2",function(d) { return d.target.y; } );
    第1-6行: 設定 force 的各引數進行設定。
    第8-22行: 繪製各頂點,即中國各省。其中要注意,第11行和第14行,是完全相反的兩個平移函式,不錯,這麼做就是為了移過去,再移回來,即初始時顯示的是各省拼成的完整的地圖且顯示在最初設定的位置,因為拖拽的過程中變化的量是 d.x 和 d.y ,所以要這麼做。這裡有點難理解,請好好體會一下,如有疑問,請在下面留言。另外,第12行是呼叫 force.drag 函式。

    第24-32行: 繪製連線各省的線條。

    5. 力學圖的結合

		 force.on("tick", function() {
			    link.attr("x1", function(d) { return d.source.x; })
			        .attr("y1", function(d) { return d.source.y; })
			        .attr("x2", function(d) { return d.target.x; })
			        .attr("y2", function(d) { return d.target.y; });

			   node.attr("transform", function(d) {
			      return "translate(" + d.x + "," + d.y + ")";
			   });
		});
    這裡和 9.2節 一樣,tick 指的是時間間隔,也就是每一個時間間隔之後就重新整理一遍畫面,重新整理的內容寫在後面的無名函式 function 中, function 函式中寫上作圖的內容。這裡看到了吧,第7-9行裡是用於平移的,平移的引數為 d.x 和 d.y 。

    結果圖:


    拖動試試吧,哈哈:


    自己用滑鼠試試吧,點選下面的連結,完整程式碼請右鍵點選瀏覽器後選擇檢視:

    謝謝閱讀。