本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
本篇文章請搭配
[5-1] 環域與繪圖工具 - 以Leaflet Draw實現
[5-2] Callback & Promise - 解決request非同步的四種解法
[5-3] 點線面的接口 - 以配接器模式 Adapter Design Pattern 重構
今天要來把環域查詢剩下的程式寫完。
今天搭配的資料為桃園市政府 觀光旅遊局 開放資料。
使用的是景觀資料、飯店資料、店家資料。
把csv檔透過SSMS可以將資料匯入SQL Server,
或是自己開資料表組Insert語法,這部分不是今天的重點就不細講。
↑ 匯完的資料如上,有很多欄位資訊,我們要篩選我們需要使用的部分,如果還要保存原始資料的話,建議可以建view,之後只需要讀取view即可。
CREATE VIEW [dbo].[V_TY_Landscape] AS
SELECT [Name] AS '名稱'
,[Add] AS '地址'
,[Opentime] AS '開放時間'
,[Parkinginfo] AS '停車資訊'
,[Px] AS 'x'
,[Py] AS 'y'
,[Toldescribe] AS '簡介'
,[Remarks] AS '備註'
,[Tel] AS '電話'
,[Fax] AS '傳真'
,'/Build/img/TYTest/landscape.png' AS [icon]
FROM TY_Landscape
GO
↑ 這邊特別建立欄位[icon],用以讀取圖台顯示icon的路徑,如果不同的資料要用不同的icon,只需要修改view就好,不必去程式加判斷。
↑ 建好的view,欄位名稱改為我們想顯示的中文名稱,之後前端只需要做迭帶把它們通通顯示就好。如此一來,修改名稱也只要改view,不必改程式。
初始化地圖以及刻一個簡單的UI,提供checkbox可以選擇想要查詢的資訊。
<div id="lmap"></div>
<div class="search">
<label for="Landscape">景觀</label>
<input type="checkbox" name="searchType" value="Landscape" id="Landscape" />
<label for="Hotel">飯店</label>
<input type="checkbox" name="searchType" value="Hotel" id="Hotel" />
<label for="Store">店家</label>
<input type="checkbox" name="searchType" value="Store" id="Store" />
<button id="btnSearch">繪圖查詢</button>
</div>
↑ 刻好介面如上(css就不附了,大家可以自行發揮) 其實我有偷偷抄bootstrap
var typeArr = [];
$(function () {
$('input[name="searchType"]').click(function () {
let type = $(this).val();
if ($(this)[0].checked) {
typeArr.push(type);
typeArr.filter((item, index) =>
typeArr.indexOf(item) !== index);
} else {
typeArr = typeArr.filter((item) => item !== type);
}
});
});
↑ 這邊用JQ來寫,用陣列typeArr來儲存欲查詢資訊的類別。
如果勾選就把勾選項目加入陣列,
然後用filter去除重複(也可以用ES6 Set,有機會來介紹)。
如果取消勾選則把它從陣列中去除。
$('#btnSearch').click(function () {
var drawing = new Drawing(LMap, 'Circle', {});
drawing.StartWithAdapter(adaptee.LCircleToObject)
.then(res => GetTYTravelData(res.x, res.y, res.radius, typeArr))
.then(res => {
console.log(res)
ShowMultiPoint(res, LMap);
});
});
↑ 按鈕點擊事件,點擊後開啟我們昨天寫的繪圖事件drawing.StartWithAdapter並畫圓,
畫完圓後的回傳資訊加入參數呼叫GetTYTravelData(),
目的是跑一個axios去呼叫後端API,
最後則是跑ShowMultiPoint(),把回傳資訊秀在地圖上。
var GetTYTravelData = (x, y, radius, typeArr) => {
return new Promise(function (resolve, reject) {
axios.post('/WebService/BufferSearch.ashx', { method: 'GetTYData', longitude: x, latitude: y, radius: radius, type: JSON.stringify(typeArr) })
.then(function (response) {
let data = response.data.dataList;
resolve(data);
})
.catch(function (error) {
console.log(error);
reject(error);
})
.finally(function () {
console.log('finally');
});
});
};
↑ 使用axios呼叫API,並回傳Promise物件。
這邊API端使用ASP.NET的ashx泛型處理常式,
原本想要建.NET Core WebAPI的Controller的,只是我不熟只好先放棄QQ。
後端C#程式就不附上了,大家可以依自己熟悉的方式建API。
Declare @p geography
SET @p = geography::STGeomFromText('POINT(121.218235004344 25.0257789866682)', 4326)
SELECT * FROM (
SELECT * FROM V_TY_Hotel UNION
SELECT * FROM V_TY_Store UNION
SELECT * FROM V_TY_Landscape
) a WHERE @p.STDistance(geography::STGeomFromText('POINT(' + CAST(CAST([x] AS DECIMAL(20,7)) AS VARCHAR(20)) + ' ' + CAST(CAST([y] AS DECIMAL(20,7)) AS VARCHAR(20)) +')', 4326)) < 700
↑ SQL語法大致長這個樣子,由中心點到座標點的距離有沒有小於環域半徑來篩選資料,MSSQL空間查詢語法詳見[番外篇] MSSQL Spatial 地理空間資訊查詢。
var ShowMultiPoint = (dataList = [], map) => {
let layers = [];
if (dataList.length > 0) {
dataList.forEach(function (item) {
let marker = L.marker([item.y, item.x], {
icon: L.icon({
iconUrl: item.icon,
iconSize: [20, 20],
iconAnchor: [5, 5],
})
}).addTo(map);
SetInfoWindow(marker, item);
layers.push(marker);
});
}
}
↑ 建立ShowMultiPoint函式可以把所有點資料呈現在圖台上,並根據每個點資料的icon url,來顯示不同的icon,最後再用SetInfoWindow綁上資訊視窗。
var SetInfoWindow = (marker, data = {}) => {
let field = Object.keys(data);
let str = "";
field = field.filter((item) => !["icon"].includes(item));
field.forEach((item) => {
str += `<p>${item}: ${data[item]} </p>`;
});
marker.bindPopup(str);
}
↑ 利用Object.keys的方式找到物件中所有欄位的名稱,
再把所有欄位名稱及值顯示在資訊視窗中。
利用filter((item) => !["icon"].includes(item)); 額外篩掉不想顯示的欄位。
↑ 環域結果
可以找到環域範圍中的旅遊資訊,並且根據勾選的項目來顯示景觀、飯店、店家資料,並且點擊後可以把詳細資料秀出來。
↑ 也可以查詢桃園市中心密集的旅店分佈情形。
密集的點資料群聚,可以計算後用一個圖標去代替一群點,透過顏色、大小去顯示及區別,讓群聚效應更加視覺化。(之後會有一個單元特別講群聚)
今天終於把環域查詢完成啦!(撒花
不過今天做的是圓的環域,多邊形的環域大同小異,
可以依靠MSSQL的空間查詢方法做到,就交給大家實做看看啦!
不知不覺Day15了,痛苦的日子還剩一半
下一篇要開始介紹WebGIS的其他資料呈現格式。準備好放中秋連假啦!