鐵人賽Day 2-webGIS初步

web
  1. 1. webgis:從Client端及Server端談起
    1. 1.1. Clinet端:
    2. 1.2. Server端:Web service及資料庫
  2. 2. 先直接來個範例

webgis:從Client端及Server端談起

Clinet端:

跟一般網頁應用程式一樣,webgis需要前端程式與使用者互動,要開發一個webgis,開發者可以使用各種前端框架。
然而,要炒一盤webgis的菜,就必須要有專屬於gis/地圖的前端框架

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

這些框架主要功能包含提供地圖,圖層渲染,基本地圖互動、介接資料等功能,Javascript常見的webgis框架包含:

  • (1)leaflet
  • (2)openlayers
  • (3)google maps api
  • (4)esri javascript api

一個地圖框架提供我們基本地圖操作,把地圖元素(含空間坐標物件)轉換成前端可操作的元件(DOM等),而為使地圖互動更生動活潑,其它前端工具當然也是可以整合進來的,包含Jquery, Vue, React, Angularjs…甚至D3.js, C3.js等可能都需要整合在webGIS中。

Server端:Web service及資料庫

在webGIS應用程式中,可以透過Web service來進行資料操作,這之中最常使用到的是網路圖磚服務(WMTS),例如我們透過 Google maps api 在網頁之中建立一個地圖,這時可以看到Google maps的底圖,這個底圖服務就是透過類似WMTS的概念來實作的。

另一個面向是資料庫,具有空間資料的資料庫稱作「空間資料庫」,空間資料庫能夠執行各種空間查詢,目前包含MSSQL(SQL Spatial)Postgressql(PostGIS)Oracle(Oracel Spatial)MySQLMongoDB等各大資料庫系統都有支援空間資料庫的格式及模組。

先直接來個範例

上面長篇大論講了一堆,本次鐵人賽的目標是把這些事情說清楚,逐一擊破。

以下先快速開始一個範例:
我們使用leaflet.js new一個地圖並以線上WMTS作為底圖,並把一些資料放上圖台,
這樣其實就是最簡單的一個webGIS。

引入必要元件

1
2
3
4
5
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
<script src="https://code.jquery.com/jquery-1.12.4.min.js"
integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>

初始化一個地圖並設定中心經緯度為[25.0375928,121.5529563], 比例尺尺度為10的視角

1
2
<div id='map_div' style="height:100%"></div>
var map = L.map('map_div').setView([25.0375928,121.5529563], 10);

一個地圖應用都需要底圖,這邊我們使用官方維護的內政部國土測繪中心wmts

1
2
3
var basemap = L.tileLayer('https://wmts.nlsc.gov.tw/wmts/EMAP/default/GoogleMapsCompatible/{z}/{y}/{x}', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

接下來,webGIS都會有自己的資料,可能是靜態的,也可以是需要資料庫IO的
可能需要從Server side 寫一個api接口取資料
c#:(示意)

1
2
3
4
5
6
7
8
9
10
11
//取資料接口..
[Route("api/Poi")]
[httpget]
public JsonResult Poi()
{
//Bson collection example [{"name":"市府店","geo":[25.0375,121.5529]},{"name":"西門店","geo":[25.0401,121.509]}....]
List<PoiDataModel> pois = List<PoiDataModel>();
//取資料...
略..
return Json(...)
}

client side:
接上面的api,把資料放在地圖上

1
2
3
4
5
6
7
8
9
10
11
12
13
$.ajax({
url: "/api/Poi",
contentType: 'application/json',
type: 'GET',
contentType: 'application/json; charset=utf-8',
success: function(data) {
$.each(data.pois, function(k, v) {
L.marker([v[0], v[1]]).addTo(map)
.bindPopup(v.name)
.openPopup();
});
}
});

另外,可能需要介接其它api資料,我們的範例資料是一個展店的計畫,
在此,我們有需求是看看周圍的商家資料。
這邊我們介接經濟部開放資料
說明: A-3 鄰近商家查詢:透過坐標參數,查詢所在位置鄰近商家清冊資料。

接到的資料是geojson,把它們放到地圖上
並以不同符號表示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$.ajax({
url: "https://egis.moea.gov.tw/MoeaEGFxData_WebAPI_Inside/InnoServe/BusinessBUSM?resptype=GeoJson&x=121.509&y=25.0401&buffer=1000",
type: 'GET',
success: function (data) {
var symbol = {
radius: 3,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
};
L.geoJSON(data, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, symbol);
}
}).addTo(map);
}
});

完整程式碼

client side(map.html):

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
52
53
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
<script src="https://code.jquery.com/jquery-1.12.4.min.js"
integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>


<div id='map_div' style="height:100%"></div>
<script>
//利用leaflet初始化地圖
var map = L.map('map_div').setView([25.0375928,121.5529563], 10);

//加入內政部國土測繪中心wmts
var basemap=L.tileLayer('https://wmts.nlsc.gov.tw/wmts/EMAP/default/GoogleMapsCompatible/{z}/{y}/{x}', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

//從Server端API接口把POI展在地圖上,並binding一個popup
$.ajax({
url: "/api/Poi",
contentType: 'application/json',
type: 'GET',
contentType: 'application/json; charset=utf-8',
success: function(data) {
$.each(data.pois, function(k, v) {
L.marker([v[0], v[1]]).addTo(map)
.bindPopup(v.name)
.openPopup();
});
}
});

//介接經濟部商家查詢api
$.ajax({
url: "https://egis.moea.gov.tw/MoeaEGFxData_WebAPI_Inside/InnoServe/BusinessBUSM?resptype=GeoJson&x=121.509&y=25.0401&buffer=1000",
type: 'GET',
success: function (data) {
var symbol = {
radius: 3,
fillColor: "#ff7800",
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
};
L.geoJSON(data, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, symbol);
}
}).addTo(map);
}
});
</script>

server side(ApiController.cs):
API接口取資料庫中的據點資料,假設使用mongodb去Query到bson document..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//取資料接口..
[Route("api/Poi")]
[httpget]
public JsonResult Poi()
{
//Bson collection example [{"name":"市府店","geo":[25.0375,121.5529]},{"name":"西門店","geo":[25.0401,121.509]}....]
List<PoiDataModel> pois = List<PoiDataModel>();
//取資料...
略..
return Json(...)
}


//新增資料接口...
[Route("api/Poi")]
[httppost]
public JsonResult GetPois(PoiDataModel poi)
{
//新增資料進資料庫...
略..
}

上面範例,結果如下: