使用Google Map API(Directions Service)獲取及顯示最佳路徑

之前文章有介紹過如何使用Google Map API(Geocoding API, Distance Matrix Service)去取得點位縣市鄉鎮資訊或是兩點之間的距離及移動所需時間資訊(有興趣可以看這兩篇: Geocoding API, Distance Matrix Service),這邊接著要介紹如何運用Google Map API取得兩點之間移動的最佳路徑資訊,並且把路徑顯示在地圖上,強大的Google Map目前提供了Google Map Javascript API :Directions Service讓我們可以獲取兩點之間的最佳路徑資訊,以下會依序說明如何使用Directions Service以及如何將結果繪製在地圖上。

首先,跟使用其他的Google Map Javascript API一樣,我們要先有Google Map 的API key才可以使用相關服務,API key的申請流程Google的線上說明文件Get an API Key有很詳細的說明,基本上流程就是使用google 帳號直接到Google Cloud Platform上面去創立一個專案,然後選擇要開啟的Google Map API,要使用Directions Service,就要開啟Directions API,創立專案的時候,Google也會產生一組API key,這個key就是我們之後需要用到的。拿到API key並啟用Directions API之後,就可以將Directions Service載入到程式中,載入方式為將下方程式碼放入放入html的body中

<script async defer 
src=”https://maps.googleapis.com/maps/api/js?key=Your API key&libraries=places,drawing,geometry&v=3&callback=initMap”>
</script>

接著就可以在程式中使用Directions Service送出request了,程式碼大概會像下面這樣,要注意的是,Directions Service是使用route()這個方法來傳入request的參數及callback的function

let directionsService = new google.maps.DirectionsService();let request = {
origin: haight,
destination: oceanBeach,
travelMode: 'WALKING'
};
directionsService.route(request, function(response) {
if (status == 'OK') {
console.log(response);
}
});

request可以傳送的參數有以下這些,以下一一簡單說明

{
origin: LatLng | String | google.maps.Place,
destination: LatLng | String | google.maps.Place,
travelMode: TravelMode,
transitOptions: TransitOptions,
drivingOptions: DrivingOptions,
unitSystem: UnitSystem,
waypoints[]: DirectionsWaypoint,
optimizeWaypoints: Boolean,
provideRouteAlternatives: Boolean,
avoidFerries: Boolean,
avoidHighways: Boolean,
avoidTolls: Boolean,
region: String
}

origin(必傳):起點資訊,傳的格式可以用經緯度、地址或者google.maps.Place的資訊,例如也可以傳 龍山寺。

destination(必傳):終點資訊,傳的格式可以用經緯度、地址或者google.maps.Place的資訊。

travelMode(必傳):從起點到終點移動的方式,包括:BICYCLING(腳踏車)、DRIVING(開車)、TRANSIT(轉運)、WALKING(走路)等四種,如果設定是DRIVING的話,可以另外傳送drivingOptions,設定交通時間計算模式,後面會再介紹drivingOptions,另外比較特別的是TRANSIT,如果傳送這個設定,表示起點跟終點間是有轉運點的,像搭飛機會在某個地方轉機那樣,不會直接抵達終點,所以如果設定TRANSIT的話,還會需要另外傳送transitOptions參數,下面會介紹transitOptions。

transitOptions(選填)travelModeTRANSIT時才需要傳送,可填的選項包括arrivalTime(抵達時間)departureTime(出發時間)modes(中繼點,有像是RAIL、SUBWAY、TRAIN等選項)、routingPreference(路徑偏好,可以設定FEWER_TRANSFERSLESS_WALKING這種轉運方式的偏好)。

drivingOptions(選填)travelModeDRIVING時才需要傳送,可傳送的參數有departureTime(出發時間)trafficModel(交通時間計算模式,有bestguess(最佳模式)、pessimistic(悲觀模式)、optimistic(樂觀模式)等三個模式來計算到達終點所需時間,預設值為bestguess,bestguess是用最貼近實際交通狀況來估計時間,pessimistic是用最糟的情況來預估時間而optimistic適用最好的情況來預估,所以一般情況,得到的時間長度應該是:pessimistic > bestguess > optimistic)

unitSystem(選填):距離單位,有google.maps.UnitSystem.METRIC及google.maps.UnitSystem.IMPERIAL兩個選項,沒有傳送unitSystem參數的話,預設為METRIC,也就是公制,得到的距離單位會是公尺、公里,如果設定是IMPERIAL,回傳值的距離單位就會是英尺、英里。

waypoints(選填):路徑中間是否有經過哪些特定的地點,可以傳送多個地點,每個地點要傳送的參數包括location(點位資訊,傳的格式可以用經緯度、地址或者google.maps.Place的資訊)及stopover(是否停留,傳送格式為Boolean,若為TRUE,則回傳的路徑會被此點為切分為兩條路徑)。

optimizeWaypoints(選填):若有傳送waypoints資訊,則可傳送TRUE or FALSE 來設定是否讓Directions Service決定路徑中的waypoints的最佳順序。

provideRouteAlternatives(選填):是否提供多個路徑的資訊,可傳送TRUE or FALSE,若傳送TRUE,則Directions Service會回傳多個建議的路徑資訊,不會只回傳一個最佳路徑資訊。

avoidFerries(選填):是否避開渡輪,可傳送TRUE or FALSE,要求Directions Service回傳的結果是否要避開使用渡輪的路徑。

avoidHighways(選填):是否避開高速公路,可傳送TRUE or FALSE,要求Directions Service回傳的結果是否要避開高速公路的路徑。

avoidTolls(選填):是否避開收費站,可傳送TRUE or FALSE,要求Directions Service回傳的結果是否要避開收費站的路徑。

region(選填):限定回傳結果是要特定區域顯示的資訊,可傳送像是GB, US這樣的地區代碼,因為Google Map在不同地區顯示的結果可能會有些微不同,例如在https://maps.google.com/ (the United States)跟在http://maps.google.es/ (Spain)搜尋”San Francisco”結果可能會不一樣,要查詢各地區應該傳送的代碼可參考這邊

看完以上可傳送參數說明,可以知道能根據不同的需求去傳送特定的參數給Directions Service得到特定情況下的路徑資訊,這邊簡單示範傳送的參數及得到的回傳結果:

以下圖為例,設定所在位置為起點,咖啡廳為終點,移動方式為走路

傳送的request參數如下,地址的部份因為太長就以…省略

{
travelMode: 'WALKING',
origin: {lat: 25.0388994, lng: 121.5018781},
destination: "No. 32, Lane 96, Section 2, ...., Taiwan 108"
}

而得到的response像下面這樣,可以看到Directions Service回傳了不少資訊,主要包含了四個部分:geocoded_waypoint, routes, status, request

{
geocoded_waypoints:
[
{
geocoder_status: "OK",
place_id: "ChIJF7o8ZaipQjQRfptaql_xRbA",
types: ["street_address"]
},
{
geocoder_status: "OK",
place_id: "ChIJf_uO2aepQjQR0g_GxilqXC4",
types: ["street_address"]
}
]
routes:
[
{
bounds: {
Va: {i: 121.50185, j: 121.50307},
Za: {i: 25.03873, j: 25.03894}
},
copyrights: "Map data ©2020 Google",
legs:
[
{
distance: {text: "0.1 km", value: 138}
duration: {text: "1 min", value: 46}
end_address: "No. 32, ...., Taiwan 108"
end_location: _.I {lat: ƒ, lng: ƒ}
start_address: "No. 131, ...., Taiwan 108"
start_location: _.I {lat: ƒ, lng: ƒ}
steps: (2) [{…}, {…}]
traffic_speed_entry: []
via_waypoint: []
via_waypoints: []
}
],
overview_path: (4) [_.I, _.I, _.I, _.I],
overview_polyline: "qlywCyyqdVd@wEDO]I",
summary: "永福街",
warnings: ["Walking directions are in beta. Use caution – This route may be missing sidewalks or pedestrian paths."],
waypoint_order: []
}
]
status: "OK"
request {...}
}

以下大致說明一下得到回傳的結果:

geocoded_waypoint:傳送的起點跟終點的地理定位資訊,主要回傳有沒有定到起點跟終點的位置,然後這兩個點位的一些補充資訊,像是點位的類別是屬於什麼,以上範例,起點跟終點的位置都是屬於門牌地址,所以都是street_address,有興趣深入了解geocoded_waypoint還有哪一些類別,可以參考這邊

routes:主要是起點到終點要行經的路徑資訊,包括路徑中的最大跟最小經緯度是多少(記錄在bounds中)、路徑的一些基本資訊像是多長要花多久時間到終點(記錄在legs中,若一開始傳送多個waypoint,則response的legs就會是記錄有多個hash的array,沒有waypoint就是回傳只有一個hash的資訊),還有比較特別的是可能會有警告訊息回傳,提醒使用者要注意,像上面範例是使用走路做為移動的方法,在response的結果,routes這邊warning的參數就紀錄了這段文字

Walking directions are in beta. Use caution — This route may be missing sidewalks or pedestrian paths.

status:這次request的結果是不是成功,或是有什麼錯誤,一般來說成功會回傳OK,如果有一些特殊狀況像是地點找不到就可能會回傳NOT_FOUND,詳細的回傳結果類型可以看這邊

request:基本上就是把一開始傳送的request參數再回傳回來。

根據上面的範例及說明,已經可以使用Directions Service並且得到reponse結果了,接著就是想辦法把結果呈現在地圖上,這樣才可以一目瞭然Google Map幫我們找的最佳路線是長什麼樣,Google Map這邊提供了一個方法叫DirectionsRenderer(),可以把路徑畫到地圖上,接下來就直接看一下使用的方法(如下方程式碼),基本上,只要把得到的response結果帶入directions這個參數,然後帶入要畫的地圖object給map參數,在整包hash傳給DirectionsRenderer()就可以把路徑畫出來了

let directionsService = new google.maps.DirectionsService();let request = {
origin: haight,
destination: oceanBeach,
travelMode: 'WALKING'
};
directionsService.route(request, function(response) {
if (status == 'OK') {
let directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
directions: response,
});

}
});

畫出來的結果像下面這樣,可以看到DirectionsRenderer()預設會把路徑的起點跟終點用紅色圖釘然後顯示A點跟B點標示出來,路徑的顏色預設會是藍色

路徑預設樣式

若想要調整預設的設定,DirectionsRenderer()也提供了不少參數讓我們傳送來客製化顯示的路徑樣式,以下簡單加上一些設定做示範:

調整路徑線條樣式DirectionsRenderer()可以傳送polylineOptions參數來對路徑的樣式做一些調整,像是線條的顏色、透明度及寬度等,詳細的燦數設定可以參考這裡

let directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
directions: response,
polylineOptions: {
strokeColor: 'yellow'
}

});
調整路徑線條樣式

隱藏終點和起點圖釘標示:想要隱藏終點和起點的圖釘,可以傳送suppressMarkers這個參數。

let directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
directions: response,
polylineOptions: {
strokeColor: 'yellow'
}
suppressMarkers: true

});
隱藏終點和起點圖釘標示

設定路徑可拖拉:如果想手動拖拉路徑,則可以加上draggable這個參數。

let directionsDisplay = new google.maps.DirectionsRenderer({
map: map,
directions: response,
polylineOptions: {
strokeColor: 'yellow'
}
suppressMarkers: true,
draggable: true
});
手動拖拉路徑

以上主要簡單展示幾個繪製路徑時可以做的調整設定,DirectionsRenderer()還有提供很多設定參數做顯示路徑客製化的調整,有興趣可以參考這邊

最後這邊做一個小分享,給大家看同樣的起點終點但移動的方式如果不同的話,其實路徑還是會有些微的差別,這邊就以移動模式為走路(WALKING)跟開車(DRIVING)來做比較,兩種模式的路徑如下面兩張圖

走路模式
開車模式

從圖上可以看到,走路模式的路徑,是真的沿著人行道在走的,然後開車模式,路徑都是在道路正中間,Google連這麼細節的部分都有呈現,真的是很令人佩服,看得出來他們相當認真的要呈現出符合現實的結果。

以上介紹的Directions Service以及前兩篇文章介紹的Distance Matrix Service 及Geocoding API,若有興趣參考更多使用的範例及程式碼,可以參考我的github專案coffee_project

參考資料:

Google Map Platform — Directions Service

Udacity 線上課程 — Google Map API

--

--

Programming Skill learner and Sharer | Ruby on Rails | Golang | Vue.js | Web Map API

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
icelandcheng

Programming Skill learner and Sharer | Ruby on Rails | Golang | Vue.js | Web Map API