本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
繼之前介紹向量模式的點資料圖徵後,今天要來介紹線資料圖徵及面資料圖徵。線及面資料圖徵其實都是一群點所組成,並且有方向性,唯一的差別在於面資料的頭尾會相連,線則頭尾不會相連。
<div id="hmap"></div>
↑ 建個地圖的div。
var platform = new H.service.Platform({
'apikey': yourkey
});
var defaultLayers = platform.createDefaultLayers();
var HMap = new H.Map(
document.getElementById('hmap'),
defaultLayers.vector.normal.map,
{
zoom: 7,
center: { lat: 23.5, lng: 121 },
pixelRatio: 1
});
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(HMap));
var ui = H.ui.UI.createDefault(HMap, defaultLayers);
↑ Here Maps API 起手式,初始化地圖。
var lineString = new H.geo.LineString();
lineString.pushPoint({ lat: 23, lng: 120 });
lineString.pushPoint({ lat: 23.2, lng: 120.5 });
lineString.pushPoint({ lat: 23.4, lng: 120.7 });
lineString.pushPoint({ lat: 24, lng: 121 });
lineString.pushPoint({ lat: 24.5, lng: 121.8 });
HMap.addObject(new H.map.Polyline(
lineString, { style: { lineWidth: 4 } }
));
↑ new一個LineString物件,並且把每個點座標以物件的方式push進去,再把LineString傳入Polyline中,最後把新建的Polyline加入地圖中就大功告成啦!注意!這裡的pushPoint並不是Array.prototype.push方法,而是Here Maps的LineString的方法!
↑ 成功建立一條線在地圖上。
然而,我們不可能每次呼叫都把一個一個點輸入LineString,因此我們把它改寫成function吧!這次用ES6語法。
var ShowLine = (pointList = [], map, style) => {
let lineString = new H.geo.LineString();
pointList.forEach(item => {
lineString.pushPoint({ lat: item.y, lng: item.x });
});
map.addObject(new H.map.Polyline( // 新增Polyline並加入地圖
lineString, { style: style }
));
map.getViewModel().setLookAtData({ // 設定視線畫面為線的邊界
bounds: lineString.getBoundingBox()
});
};
↑ 預設傳入pointList陣列,並且假設陣列格式為每一筆都是帶有x、y屬性的物件,EX:
var pointList = [{ x: 121.5, y: 24 }, { x: 121.2, y: 23.8 }, { x: 121, y: 23.5 }];
我們只要傳入點座標陣列、地圖物件,並設定屬性,就可以畫出線圖徵。
ShowLine(pointList, HMap, { lineWidth: 8, strokeColor: '#E488AE' });
↑ 呼叫ShowLine
↑ style作為參數的好處,每次呼叫時都可以重新決定它的樣式,不必寫死在程式中。
var lineString = new H.geo.LineString();
lineString.pushPoint({ lat: 23, lng: 120 });
lineString.pushPoint({ lat: 23.2, lng: 120.5 });
lineString.pushPoint({ lat: 23.4, lng: 120.7 });
lineString.pushPoint({ lat: 24, lng: 121 });
lineString.pushPoint({ lat: 24.5, lng: 121.8 });
HMap.addObject(
new H.map.Polygon(lineString, {
style: {
fillColor: '#FFFFCC',
strokeColor: '#829',
lineWidth: 8
}
})
);
↑ Polygon與PolyLine的寫法大同小異,都是先建立一個LineString物件,並且把點資料都加入LineString中,最後新建Polygon並加入地圖。
↑ 成功畫出Polygon,這就是我現在的表情!(誤
var ShowPolygon = (pointList = [], map, style) => {
let lineString = new H.geo.LineString();
pointList.forEach(item => {
lineString.pushPoint({ lat: item.y, lng: item.x });
});
map.addObject(new H.map.Polygon(
lineString, { style: style }
));
};
ShowPolygon(pointList, HMap, { // 呼叫,建立一個面資料圖徵
fillColor: '#99CEFF',
strokeColor: '#E5A596',
lineWidth: 3
});
↑ 一樣可以把它改寫成function。
Here Maps API有很多很有趣的加值功能,當時看到這個Routing功能,就躍躍欲試,也是我最近一直玩Here Maps API的原因之一。今天用的功能是Map with Driving Route from A to B,也就是汽車導航,只要輸入起始座標點及終點座標點,就可以進行路網分析,找出最佳的路線。先來看看Routing Service要怎麼使用吧!
function calculateRouteFromAtoB(platform) {
var router = platform.getRoutingService(null, 8),
routeRequestParams = {
routingMode: 'fast',
transportMode: 'car',
origin: '25.031,121.543', // 大安
destination: '25.041,121.566', // 市政府
return: 'polyline,turnByTurnActions,actions,instructions,travelSummary'
};
router.calculateRoute(routeRequestParams, function (result) {
console.log(arguments);
});
}
↑ 建立getRoutingService後,並且設定它的參數,包括車速快慢、交通工具類型、起始點、終點等等,是不是很有趣呢?最後呼叫calculateRoute來計算,讓我們看看它的回調函式傳給我們什麼。
看到這裡的瞬間,心理OS:它callback arguments結構也太複雜了吧!
不光是結構複雜,我找半天它的點座標,竟然找不到!後來查了文件之後才知道,竟然是那一長串的polyline,而且座標組竟是加密過後的!
第一次看到有座標還有加密之後傳遞的,優勢除了資訊安全提高外,也避免座標組過多時傳遞很長的陣列。
但說真的不直覺!
↓ 知道它回傳結構後,重新修改calculateRoute的callback function
router.calculateRoute(routeRequestParams, function (result) {
let poly = H.geo.LineString.fromFlexiblePolyline( // 解密
result.routes[0].sections[0].polyline).getLatLngAltArray();
let arr = [];
for (i = 0; i < poly.length; i += 3) { // 陣列重組
arr.push({ y: poly[i], x: poly[i + 1] });
}
// 畫出導航路線
ShowLine(arr, HMap, { lineWidth: 8, strokeColor: 'rgba(0, 128, 255, 0.7)' });
});
↑ 可以利用fromFlexiblePolyline方法把加密過後的字串,解密為座標組合
,再利用for迴圈提取資料存成陣列,最後呼叫ShowLine把導航的路線畫出來!
↑ 大致是從大安附近一路到東區,沿途都是走大馬路,還算OK的導航路線。導航功能還有每個轉彎點的方向及下個轉彎點的距離,不過過程太繁瑣了就先不列上來,因為都再拆解參數。期待未來能加入車流量分析,透過監視器即時影像辨識判斷車流擁擠度,並找尋最佳的路線。
今天既然講了面資料結構,那怎麼可以沒有面資料結構的例子呢?因此找來了TGOS API行政區定位,並且同樣以Here Maps的圖台呈現。
<script type="text/javascript" src="https://api.tgos.tw/TGOS_API/tgos?ver=2&AppID=yourID&APIKey=yourkey" charset="utf-8"></script>
↑ 引入TGOS API
var LocateByDistrict = (district, callback) => {
var locator = new TGOS.TGLocateService();
var callback = callback || function () { }
var result = {
pointList: [],
errorMessage: ''
}
locator.locateWGS84({ district: district }, function (e, status) {
if (status != TGOS.TGLocatorStatus.OK) {
console.log('查無行政區');
result.errorMessage = '查無行政區';
} else {
result.pointList = e[0].geometry.geometry
.rings_[0].linestring.path.map(function (item) {
return {
x: item.x,
y: item.y
}
});
}
callback(result);
});
this.result = result;
}
↑ 這邊直接附上寫好的function,透過TGOS的定位服務TGLocateService,並呼叫locateWGS84方法,傳入行政區,做簡單的資料重組後,即能在callback function中回傳點資料陣列。
LocateByDistrict('台北市', (result) => {
ShowPolygon(result.pointList, HMap, {
fillColor: 'rgba(224, 197, 83, 0.3)',
strokeColor: '#4439E7',
lineWidth: 3,
});
});
↑ 呼叫行政區定位,並在呼叫結束時用剛剛寫好的ShowPolygon以面資料的方式,顯示在Here Maps上。
↑ 這樣就能繪製出行政區邊界囉!
今日簡單介紹了線資料圖徵以及面資料圖徵,用Here Maps API Routing來呈現線資料,
以及TGOS API行政區定位示範呈現面資料。
大家有沒有發現一個共通點,不管是線還是面都要先new H.geo.LineString(),
一樣的function寫兩次,造成程式碼重複外,奇檬子還不太爽,身為programmer的我們,沒事就是要一直重構阿!
明天,會講解用原型鏈(prototype chain)的方式,把它們重新包裝,並且用ES5語法實作繼承,讓重複的程式碼只寫一次,其它通通都用繼承的!
是不是很期待呀?!
大大好:
請問Here Maps API 可以用地址查經緯度嗎?
目前都看到用經緯度查地址,但我希望使用者輸入後我可以轉成經緯度顯示在Map上
您好:
地址轉坐標關鍵字一般為geocoding或geocoder API,如下為官網範例的連結,v7版為新版的(推薦使用),但是token要加在request的header中,要用ajax/fetch/axios的方式取得;v6版的則是直接把token加在網址後方queryString即可,但v6版已不再更新,實際效果還要您自己試試看有沒有符合需求。
另外,也可以使用其他API做地址轉坐標後,再於Here Maps上面顯示,彼此是不衝突的。
希望有幫助到您,還有其他問題也歡迎提出!
HERE Geocoding & Search API v7
https://developer.here.com/documentation/geocoding-search-api/dev_guide/topics-api/code-geocode-address.html
HERE Geocoder API v6
https://developer.here.com/documentation/geocoder/dev_guide/topics/quick-start-geocode.html