iT邦幫忙

2021 iThome 鐵人賽

DAY 20
1
Modern Web

從原料到燃料,從資料到預料—資料駕馭網頁之理科的美學系列 第 20

D3JsDay20筆畫面量彩色圖 塗色彩亮面畫筆—地理面量圖(上)

  • 分享至 

  • xImage
  •  

面量圖介紹

面量圖又稱分層設色圖、區域密度圖(Choropleth map),高中地理課本的說明是在界限明確的區域平均分布的地理現象以色彩或網紋來代表其數量大小,例如台北市的人口數用某個顏色代表,並且在台北市的地圖區域範圍呈現出該顏色。
以下圖片為範例
https://ithelp.ithome.com.tw/upload/images/20211005/20125095BhVPtqWCpP.png

圖片取自維基百科
更多知識點可以參考維基百科Choropleth map Wiki(英文)

下載原始資料

這次範例預計實作一個臺南市的面量圖,每個區的顏色表示該平均土地房屋的價格高低,因此我們要先有地圖資料和房屋土地的價格資料

我們用的資料是實價登錄網因此先到該網址找非本期下載如下圖
https://ithelp.ithome.com.tw/upload/images/20211005/20125095trPzbL2bkh.png

預計使用109第四季的臺南市CSV資料如下圖
https://ithelp.ithome.com.tw/upload/images/20211005/20125095KVm4XA0dqW.png

鄉鎮市區界線(TWD97經緯度)
實價登路的網址

加入dbf作為geojson的properties屬性

由於dbf含有地圖區域的資料,因此這次我們到mapshaper的網站添加shp副檔名之外還有添加dbf資料以便之後要進行資料篩選的時候把台南市給選取起來
https://ithelp.ithome.com.tw/upload/images/20211005/201250952x5S124AMz.png
之後步驟與先前一樣輸出成topojson格式,再使用d3.json把引入的資料轉換成geojson的格式後使用consolo.log可以發現這時候properites的欄位多了欄位

https://ithelp.ithome.com.tw/upload/images/20211005/20125095SlOOGsQkJ5.png

如上圖我們可以先看console.log(geojson)能夠發現每個陣列底下的properites都有正確被載入,裡面包含了縣市名稱、鄉鎮市名稱和英文名字等等的資訊,因此可以篩選COUNTYNAME為臺南市,然後重新組合成一個陣列來當作預備繪製我們台南市地圖的geojson資料
具體程式碼如下

設置地圖中心點和投影

與先前一樣設置地圖中心點和投影轉換函式
程式碼如下

let width = 1200;
let height = 675;
const projection = d3.geoMercator()
    .center([120.24,23.18 ])
    .scale(50000);
const svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
let path = d3.geoPath()
    .projection(projection);
const g = svg.append("g");
d3.json("taiwanDistrict.json")
.then(function(topology) {
    const geojson =  topojson
                .feature(topology, topology.objects.TOWN_MOI_1100415)
                .features;
    console.log(geojson);}
    )

篩選縣市做為新的geojson資料

const tainanGeojson = [];
geojson.forEach(function(el) {
    if(el.properties.COUNTYNAME=="臺南市"){
        tainanGeojson.push(el);
    }
})
console.log(tainanGeojson);

我們先創建一個陣列叫做tainanGeojson,然後對原本轉換完的geojson遍歷然後把元素裡面的properties.COUNTYNAME臺南市給添加到tainanGeojson的陣列裡面最後
這時候可以用console.log檢查裡面的東西是不是都為臺南市

https://ithelp.ithome.com.tw/upload/images/20211005/201250956pHUJTO5cV.png

如上圖裡面的properties內容物都是臺南市的資料,另外臺南市的行政區有37個,因此對應到陣列個數剛好也是37筆。

載入土地房屋資料

  d3.csv("taiwan109s4.csv")
            .then(function(csvData){
                console.log(csvData);
            }

接下來我們再載入109年第四季台南的土地資料,使用console.log可以看到有八千多筆的資料有如期的被正確載入如下圖
https://ithelp.ithome.com.tw/upload/images/20211005/20125095c8rimGE0FX.png

分類區域—d3.group群組化

這邊可以使用d3的一個API叫做group先行群組化,我們將用行政區來分組
具體說明可以看到官方API文件的範例group的第一個參數帶入該陣列,第二個參數可以是一個function此時的d參數是陣列裡面的資料,使用callback函數的方式把你要依據的分組方式給對應出來,官方的範例是取陣列中的資料key是name的值
https://ithelp.ithome.com.tw/upload/images/20211005/20125095Q4GUkVD1o8.png

另外官方網站有提到關於group使用後轉換是InternMap的資料類型
,簡單的說他轉換後的資料會變成像是Map的資料格式,但比原生的Javascript又多了一些功能其說明可以參考下面

https://ithelp.ithome.com.tw/upload/images/20211005/20125095Yr64lKrtI3.png

Interning介紹
d3.group官方API

因此撰寫程式碼如下

 d3.csv("taiwan109s4.csv")
.then(function(csvData){
    console.log(csvData);
    const districtMap = d3.group(csvData, d => d["鄉鎮市區"]);
    console.log(districtMap);

轉換完畢就可以使用使用console可以看到如下圖
https://ithelp.ithome.com.tw/upload/images/20211005/20125095QE61bhLRrC.png

新增geojson的properties欄位

我們希望新增一個資料欄位是房屋土地的平均價格以便之後再進行繪製地圖的時候可以使用d3的data()函數得到該筆資料,由於資料有8千多筆,這裡也會使用到d3內建計算統計的mean()函數這裡先簡單說明一下

d3.mean()計算平均數

官方提到可以計算陣列裡面的平均數,另外也可以輸入一個函數來指定要訪問陣列的元素裡,物件的某一個屬性。
https://ithelp.ithome.com.tw/upload/images/20211005/20125095VCDGtU2pTT.png

官方範例如下圖

https://ithelp.ithome.com.tw/upload/images/20211005/20125095YQX4RUNL9L.png

d3.mean()官方說明
d3.mean()範例

因此我們帶入回原始資料,程式碼如下

tainanGeojson.forEach(function(el){
    for (let [key, value] of districtMap) {
        if(key===el.properties.TOWNNAME){
            el.properties.HOUSEPRICE= d3.mean(value, d=>d["單價元平方公尺"]);
        }
    }
});

這邊主要先遍歷整個tainanGeojsongeojson,每次執行到某個台南市行政區域的時候再使用for of 遍歷整個map,在裡面的大括號判斷如果districtMapkeytainanGeojson元素的properties.TOWNNAM一樣的話就把districtMap的value透過d3.mean()這個方法再遍歷得出平均值存入geojson該行政區的properties的HOUSEPRICE屬性裡面。

如果看不懂的話可以看下圖理解

https://ithelp.ithome.com.tw/upload/images/20211005/20125095T6PhGEiADz.jpg

使用console.log(tainanGeojson),應當會出現這個圖解的左邊的樣貌,可以看得出我們插入了HOUSEPRICE
不熟for of 可以參考MDN

for of的MDN

小總結

今天主要先做資料預處理,先將shpdbf轉成topojson後轉成geojson,將其台南市的geojson過濾出來並加入行政區的[單位元平方公尺]的平均值至該properties,透過網路中得到的資料做資料預處理幾乎是不可避免的情況,很少的情況是拿到資料剛好完全符合你所需要的內容,例如某一類的平均值又或者沒有多餘的資料,即便是拿到一份報表也可能擁有產品編號、成本、售價、分類、進口商等等,也許你僅需要某個分類的成本和售價製作成圖表也是需要進行資料預處理,今天額外補充了兩個函式d3.mean()d3.group(),明天將會開始著手繪圖並且加入一些動畫效果吸引閱聽人的目光。


上一篇
D3JsDay19 地圖加入了事件,地點資料就呈現—為地圖加入互動事件
下一篇
D3JsDay21筆畫面量彩色圖,塗色彩亮面畫筆—地理面量圖(下)
系列文
從原料到燃料,從資料到預料—資料駕馭網頁之理科的美學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
金金
iT邦新手 1 級 ‧ 2021-10-05 12:37:04

好仔細!!很棒的文~

DannyChen iT邦研究生 5 級 ‧ 2021-10-06 12:23:16 檢舉

謝謝稱讚~~/images/emoticon/emoticon37.gif

我要留言

立即登入留言