使用D3 Geo模塊畫澳大利亞地圖

數據
數據可視化主要旨在藉助於圖形化手段,清晰有效地傳達與溝通訊息。所以作數據可視化前須要想明白2件事:
  • 你有什麼數據?
  • 你要傳達什麼信息?

本文中的示例中,將以不一樣的顏色顯示澳大利亞不一樣地區的客戶數量。

所以,首先須要澳大利亞的地圖數據,D3中的Geo模塊能夠處理GeoJSON格式的地理數據。(GeoJSON是一種對各類地理數據結構進行編碼的格式。GeoJSON對象能夠表示幾何、特徵或者特徵集合。GeoJSON支持下面幾何類型:點、線、面、多點、多線、多面和幾何集合。GeoJSON裏的特徵包含一個幾何對象和其餘屬性,特徵集合表示一系列特徵。參見:http://www.oschina.net/translate/geojson-spec?cmp
開發者能夠從Natural Earth(http://www.naturalearthdata.com/)獲取到全球全部的地理數據,使用其地理數據須要 注意2點:
  • 其有3種比例的數據1:10m,1:50m和1:110m。1:10m比例的數據擁有更細節的數據,只有它纔有州(省)的信息。
  • 其數據不是GeoJSON格式的(Shapefile),須要經過GDAL(Geospatial Data Abstraction Library)庫轉換爲GeoJSON格式。

在Mac下安裝GDAL很是方便,感謝Homebrew:
Shell代碼   收藏代碼
  1. brew install gdal  

而後經過以下命令就能夠Shapefile中的澳大利亞的數據提取出來。
Shell代碼   收藏代碼
  1. ogr2ogr -f GeoJSON -where "sr_adm0_a3 = 'AUS'" aus.states.json 10m_cultural/ne_10m_admin_1_states_provinces_lakes_shp.shp  

是一個相似下文這樣的一個GeoJSON格式數據。
Javascript代碼   收藏代碼
  1. {  
  2.     "type": "FeatureCollection",  
  3.     "features": [  
  4.         {  
  5.             "type": "Feature",  
  6.             "properties": {  
  7.                 ...  
  8.             },  
  9.             "geometry": {  
  10.                 "type": "Polygon",  
  11.                 "coordinates": [  
  12.                     [  
  13.                         ....  
  14.                     ]  
  15.                 ]  
  16.             }  
  17.         },  
  18.         ......  

順道提一下,Geo數據通常都比較大,尤爲是GeoJSON格式下的數據,像上面生成的數據就有741KB,這對於Web應用來講已是很大的一個數值。開發者能夠經過Topojson(https://github.com/mbostock/topojson/wiki)壓縮數據。Topojson是GeoJSON的一個擴展,使用方式大體相同,這兒就不講Topojson了,下圖可讓開發者大體瞭解一下三種格式下數據的大小:


畫圖
有了數據,接下來就開始畫圖。D3畫圖都有必定的套路,首先須要肯定把矢量圖SVG放到那兒,以及圖的大小
Javascript代碼   收藏代碼
  1. var width  = 960;  
  2. var height = 580;  
  3. var svg = d3.select("#geo_distribution").append("svg")  
  4.         .attr("width", width)  
  5.         .attr("height", height)  
  6.         .append("g")  
  7.         .attr("transform", "translate(0,0)");  
接着,須要建立一個路徑生成器,路徑生成器能夠接收一個投射函數,該投射函數存在的目的是把圓形地球上的經緯度投射到平面的Web界面上。D3自帶了各類各樣的投射函數(https://github.com/mbostock/d3/wiki/Geo-Projections),本例中使用的是墨卡託投影(http://baike.baidu.com/view/301981.htm?fr=aladdin)。
Javascript代碼   收藏代碼
  1. var projection = d3.geo.mercator()  
  2.        .center([132, -28])  
  3.        .scale(850)  
  4.        .translate([width/2, height/2]);  
  5.   
  6.    var path = d3.geo.path()  
  7.        .projection(projection);  

而後,根據讀取的GeoJSON數據繪製路徑:
Javascript代碼   收藏代碼
  1. var color = d3.scale.category20();  
  2. var states = svg.append("svg:g")  
  3.         .attr("id", "states");  
  4. d3.json("data/aus.states.json", function(error, root) {  
  5.         if (error)  
  6.             return console.error(error);  
  7.   
  8.         states.selectAll("path")  
  9.             .data( root.features)  
  10.             .enter()  
  11.             .append("path")  
  12.             .attr("stroke","#000")//路徑線顏色  
  13.             .attr("stroke-width",1)//路徑線寬度  
  14.             .attr("fill", function(d,i){  
  15.                 return color(i);//color函數可根據數據設置每一個州板塊的顏色,示例中使用的是D3自帶的顏色函數。  
  16.             })  
  17.             .attr("d", path )  
  18.             .on("mouseover",function(d,i){//添加鼠標事件  
  19.                 d3.select(this)  
  20.                     .attr("fill","yellow");  
  21.             })  
  22.             .on("mouseout",function(d,i){  
  23.                 d3.select(this)  
  24.                     .attr("fill",color(i));  
  25.             });  
  26.     });  

畫到這兒一個澳大利亞的地圖就是下面這個樣子了:


加點佐料
畫了地區以後,純屬我的樂趣,還想畫點城市在上面,作法也是同樣的, 首先獲取Geo數據,仍是能夠從Natural Earth的地理數據中轉換獲得(注:轉換數據時,開發者能夠根據我的愛好過濾掉一些數據,比方說下面的命令中我過濾掉了規模上第四等級之後的小城市):
Shell代碼   收藏代碼
  1. ogr2ogr -f GeoJSON -where "ADM0_A3 = 'AUS' and SCALERANK <=4" aus.big.cities.json 10m_cultural/ne_10m_populated_places.shp  

接着,把用於描述城市的小圓點和城市名字的SVG添加到底層SVG上:
Javascript代碼   收藏代碼
  1. var circles = svg.append("svg:g")  
  2.     .attr("id", "circles");  
  3.   
  4. var texts = svg.append("svg:g")  
  5.     .attr("id", "texts");  

而後,根據前面獲得的數據在建立的SVG上畫圖
Javascript代碼   收藏代碼
  1. d3.json("data/aus.cities.json", function(error, root) {  
  2.         circles.selectAll("circle")  
  3.             .data(root.features)  
  4.             .enter().  
  5.             append("svg:circle")  
  6.             .attr("cx", function(d){return projection([d.properties['LONGITUDE'],d.properties['LATITUDE']])[0];})//根據城市的經緯度投射肯定圓點座標  
  7.             .attr("cy",function(d){return projection([d.properties['LONGITUDE'],d.properties['LATITUDE']])[1];})  
  8.             .attr("r", 3)  
  9.             .attr('fill','#29FF57');  
  10.   
  11.         texts.selectAll("text")  
  12.             .data(root.features)  
  13.             .enter()  
  14.             .append("svg:text")  
  15.             .text(function(d){return d.properties['NAME'];})  
  16.             .attr("x", function(d){  
  17.                 return projection([ d.properties['LONGITUDE'],d.properties['LATITUDE']])[0];})  
  18.             .attr("y",function(d){  
  19.                 return projection([d.properties['LONGITUDE'],d.properties['LATITUDE']])[1];  
  20.             })  
  21.             .attr('fill','#000')  
  22.             .attr('font-size','9px');  
  23.   
  24.     });  
最後獲得的結果以下:


參考:

http://www.tnoda.com/blog/2013-12-07
https://github.com/mbostock/d3/wiki/Geo-Paths#path
相關文章
相關標籤/搜索