鐵人賽Day 27- WebGIS 加入D3.js圖表互動

web
  1. 1. 前言
  2. 2. D3.js
  3. 3. 在地圖中增加互動
  4. 4. 後記

前言

今天要在WebGIS中加入一些資訊圖表,利用D3.js來實作圖表並與地圖互動,D3.js非常具有彈性且多樣,能與地圖結合有錦上添花的感覺,let’s go!

本文是參加鐵人賽的文章,同步發表於 “2018鐵人賽-30天打造我的WebGIS系列”

D3.js

D3.js是資料視覺化的利器,光從官方網站範例就琳瑯滿目,例如我們要畫一個bar chart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//設定畫布及x,y軸比例尺等

var svg = d3.select("#svg"),
margin = { top: 20, right: 20, bottom: 30, left: 40 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1);
var y = d3.scaleLinear().rangeRound([height, 0]);

var column = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");


//設定資料範圍
x.domain(data.map(function (d) { return d.letter; }));
y.domain([0, d3.max(data, function (d) { return d.frequency; })]);

//x軸
column.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));

//y軸
column.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency");

//bar chart部分
column.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d) { return x(d.letter); })
.attr("y", function (d) { return y(d.frequency); })
.attr("width", x.bandwidth())
.attr("height", function (d) { return height - y(d.frequency); })
}

在地圖中增加互動

WebGIS的圖表當然就是圖層的屬性,在讀入geojson後,我們會需要把資料做些轉換,去產生圖表需要的資訊。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$.getJSON("./dist/assets/data/map.geojson", function (data) {
featchdata(data);
});

function featchdata(d) {
//資料

var data = [];
var tmp_type;
var tmp_count = 0;
$.each(d.features, function (k, v) {
if (k == 0) {
tmp_type = v.properties.surface;
tmp_count++;
}
else if (k == d.length - 1) {
tmp_count++;
data.push({ "letter": tmp_type, "frequency": tmp_count })
}
else {
if (tmp_type == v.properties.surface) {
tmp_count++;
} else {
data.push({ "letter": tmp_type, "frequency": tmp_count })
tmp_count = 1;
tmp_type = v.properties.surface;
}

}


});

上面圖表串接,我們加入一些地圖事件讓圖表動起來,圖表會跟著地圖實際涵蓋的範圍做變化

首先,加入地圖事件:

1
2
3
4
5
6
7
8
9
map.on('zoomend', function () {
var d = pois.toGeoJSON();
featchdata(d);
});

map.on('dragend', function () {
var d = pois.toGeoJSON();
featchdata(d);
});

接著,在前面的featchdata加入bounds判斷,讓圖表會根據bounds範圍呈現資料
使用的函式為turf.booleanPointInPolygon(point,polygon)
如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function featchdata(d0) {
var ext = map.getBounds()
//資料

var poly = turf.polygon([[
[ext.getSouthWest().lng, ext.getSouthWest().lat],
[ext.getNorthWest().lng, ext.getNorthWest().lat],
[ext.getNorthEast().lng, ext.getNorthEast().lat],
[ext.getSouthEast().lng, ext.getSouthEast().lat],
[ext.getSouthWest().lng, ext.getSouthWest().lat]
]]);
console.log(poly);
var d = [];

$.each(d0.features, function (k, v) {
var pt = turf.point([v.geometry.coordinates[0], v.geometry.coordinates[1]]);

if (turf.booleanPointInPolygon(pt, poly)) {
d.push(v);
}

});
var data = [];
var tmp_type;
var tmp_count = 0;

$.each(d, function (k, v) {
if (k == 0) {
tmp_type = v.properties.surface;
tmp_count++;
}
else if (k == d.length - 1) {
tmp_count++;
data.push({ "letter": tmp_type, "frequency": tmp_count })
}
else {
if (tmp_type == v.properties.surface) {
tmp_count++;
} else {
data.push({ "letter": tmp_type, "frequency": tmp_count })
tmp_count = 1;
tmp_type = v.properties.surface;
}
}
});
...

...

省略
}

成果略圖

後記

在D3.js我們還加入一些click事件及css調整,可以直接看程式碼喔~,另外,除了D3.js以外,也可以使用單純圖表的C3.js或是highchart等較直覺的工具喔。