D3JsDay20笔画面量彩色图涂色 彩亮面画笔—地理面量图(上)

面量图介绍

面量图又称分层设色图、区域密度图(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(),明天将会开始着手绘图并且加入一些动画效果吸引阅听人的目光。


<<:  RDS汇入汇出

>>:  Day-21 : devise 安装 part 2

[Day 28] HDFS

欢迎来到第 28 天,昨天提到 MapReduce 的观念,今天要提到另一个 Hadoop 中的重点...

谁比谁长,回圈和 reduce 用法,Ruby 30 天刷题修行篇第十话

嗨~我是 A Fei,又到了我们愉快的解题时间,让我们马上来看看今天的题目: (题目来源:Codew...

Day13【Web】网路攻击:DDoS 之 DNS 递回查询攻击

递回查询 Recursive Query 『递回查询』(Recursive Query)是指 当某个...

day2_arm 与 x86 的意思与特色

cpu 的架构是指什麽? cpu 是电脑作爲逻辑处理的重要核心,而我们会需要特殊的语言与 cpu 对...

[DAY-07] 强化人才密度 拿出业界最高薪资

从 WFH 的 三个半月多 ( 2021- 05, 06, 07, 08) 再回到 办公室 &am...