在前面的博客中已經介紹了如何繪制地圖,這一節學習如何繪制地區分布圖。如果對繪制地圖還不熟悉的話可以了解一下之前我寫的博客:數據可視化【十】繪制地圖
Intergrating(整合) TopoJSON with tabular data(列表數據)
在前面的博客中沒有使用到tsv
文件,因為我找到的TopoJSON中本來就包含國家的名字。但是如果想要知道國家更多的數據僅僅知道國家的名字是不夠的,因此還需要導入關于國家其他數據的tsv
文件。當導入多個文件的時候最好使用Promis.all
,這樣就可以在文件全部都加載后再開始執行。需要將多個任務都放在一個數組里面,然后再在then
中用一個數組接收讀入的數據。
Promise.all([tsv('https://unpkg.com/world-atlas@1.1.4/world/50m.tsv'),json('https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-50m.json')
]).then(([tsvData, topsJSONdata]) => {}
為了快速處理tsv
文件,我們使用reduce
函數將其放到一個類中。其中iso_n3
與JSON
文件中的id
對應
const rowById = tsvData.reduce((accumulator, d)=>{accumulator[d.iso_n3] = d;return accumulator;}, {});//前面是reduce的執行,后面是accumulator的初始值
為了使得JSON
文件中的每個元素都包含這個國家更多的信息,我們使用assign
函數將同一個國家的信息進行合并
const countries = feature(topsJSONdata, topsJSONdata.objects.countries);// console.log(countries);countries.features.forEach(d => {Object.assign(d.properties, rowById[d.id]);});
Creating a loadAndProcessData module
上面載入數據的過程其實可以當作另一個模塊,因此我們把上面的代碼單獨放在一個用于加載數據的文件中
loadAndProcessData.js
import {json, tsv} from 'd3';
import {feature} from 'topojson';export const loadAndProcessData = () => Promise.all([tsv('https://unpkg.com/world-atlas@1.1.4/world/50m.tsv'),json('https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/countries-50m.json')
]).then(([tsvData, topsJSONdata]) => {const rowById = tsvData.reduce((accumulator, d)=>{accumulator[d.iso_n3] = d;return accumulator;}, {});const countries = feature(topsJSONdata, topsJSONdata.objects.countries);// console.log(countries);countries.features.forEach(d => {Object.assign(d.properties, rowById[d.id]);});return countries;});
并在index.js中將相應的代碼都放在loadAndProcessData
函數的then
中
Visualizing an ordered attribute with color
在得到國家的其他屬性之后,我們就可以根據國家的其他標簽給國家上色
我們可以首先觀察一下tsv
文件中都有什么。太多了這里就不顯示了,比如說里面有一個標簽是經濟,我們不妨使用經濟給不同國家不同的顏色。
為了方便修改標簽,我們把獲取數據屬性的操作放到一個函數里,并且在后面所有的地方都通過這個函數獲取屬性,這樣做的好處是如果我們不想要這個標簽,我們只需要在這一個地方進行修改就可以。
我們使用scaleOrdinal
來得到從標簽到顏色的映射,對于標簽我們需要進行一定的排序處理,而顏色區間d3
有現成的函數可以讓我們很方便的得到漸變的顏色從而區分不同的等級。
我們可以搜索d3-scale-chromatic
,里面有很多函數。在這里我們選擇schemeSpectral[k]
,這個k
是標簽分類的個數,我們可以直接得到:
const colorValue = d => d.properties.income_grpconst colorScale = scaleOrdinal().domain(countries.features.map(colorValue));colorScale.domain(colorScale.domain().sort().reverse()).range(schemeSpectral[colorScale.domain().length]);
制作好顏色標簽以后就是在添加path
的后面設置fill
屬性,我之前做的是隨機選擇一個顏色,這里就改為根據上面已經設置好的顏色尺得到對應的顏色。
Showing data from 2 attributes in tooltip
在得到好看的顏色以后,我們還希望對應的標簽的內容可以改變,這一點很容易再到,只需要在設置text
的時候加上對應的屬性即可。
g.selectAll('path').data(countries.features).enter().append('path').attr('class', 'country').attr('fill', d => colorScale(colorValue(d))) .attr('d', pathGenerator)
.append('title')//添加title,然后鼠標放在上面就可以出現標題.text(d => d.properties.name + ':' + colorValue(d));
初步效果圖如下:
Adding a color legend
我們還希望添加一個顏色圖例,這樣就可以很方便地知道什么顏色對應的是什么內容而不必要一一去看。根據上一節(數據可視化【十二】顏色圖例和尺寸圖例)我們制作的顏色圖例,可以直接進行使用。
index.js
colorLegendG.call(colorLegend, {colorScale,circleRadius: 8,spacing : 20,textOffset : 15,backgroundRectWidth: 250});
在colorLegend.js中,圖例有一個背景,通過添加一個矩形來實現
const backgroundRect = selection.selectAll('rect').data([null]);const n = colorScale.domain().length;backgroundRect.enter().append('rect').merge(backgroundRect).attr('x', -circleRadius * 2).attr('y', -circleRadius * 2).attr('rx', circleRadius * 2).attr('width', backgroundRectWidth).attr('height', spacing * n + circleRadius * 3).attr('fill', 'white').attr('opacity', 0.8);
最后效果圖:
代碼地址:https://vizhub.com/Edward-Elric233/635845fd4c8b4917b999b18cab5e9b09