參考資料 ----
行程含目的地的停靠點在 10 個點以下,是一種費率,11(含) 以上則會收較高的費用。
本筆記是自行計算及排序停靠點。
您可以服務全開,不過我只使用其中 2 項 -- 取得地理位置(經緯度)、導航。
建立 GMap 類別
<?php date_default_timezone_set('Asia/Taipei'); // 用來查詢地址的經、緯度 class GMap { function getPageData($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect: ')); curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_NOBODY, false); curl_setopt($ch, CURLOPT_FILETIME, true); curl_setopt($ch, CURLOPT_REFERER, $url); curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 4); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)"); $result['data'] = curl_exec($ch); curl_close($ch); return $result; } // 初始化 apikey 金鑰 function __construct() { $this->apikey = '向 Google map 申請 apikey'; } // 從 google map 取得地址經緯度 public function getLngLat($addr='',$sDebug='') { $apikey = $this->apikey; $url = "https://maps.googleapis.com/maps/api/geocode/json?address=$addr&key=$apikey"; $geocode = $this->getPageData($url); if($sDebug=='debug') var_dump($geocode); if(isset($geocode['data'])) { if(!$geocode["data"]) // 當 Google map 解析不了時,回應 經/緯度值都是 -1 的虛擬經緯度 $geocode = '{"results":[{"geometry":{"location":{"lat":-1,"lng":-1}}}]}'; else $geocode = $geocode['data']; } else // 當 Google map 解析不了時,回應 經/緯度值都是 -1 的虛擬經緯度 $geocode = '{"results":[{"geometry":{"location":{"lat":-1,"lng":-1}}}]}'; $output = json_decode($geocode); $latitude = $output->results[0]->geometry->location->lat; $longitude = $output->results[0]->geometry->location->lng; return array('lat'=>$latitude,'lng'=>$longitude); } } ?>
getroute.php,用來依次計算起點至每個目的地的距離,取最近的地址做為下個停靠點,並再以這地址做為下次計算的起點
<?php // 切換至 https if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off") { $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $location); exit; } include('gmap.php'); class MyDest { public $addr; public $len = 0; public $lng = -1; public $lat = -1; function __construct($aa) { $this->addr = $aa; } } if(isset($_GET['lat'])) { // 接收到起點 經/緯 度 // $sLat = 22.612058525444247; // 測試用 // $sLng = 120.3142669919977; // 測試用 $sLat = $_GET['lat']; $sLng = $_GET['lng']; $aDest = array(); // 存放上傳的目的地 $aSort = array(); // 排序後的目的地 $gmap = new GMap(); if(isset($_GET['dest1'])) { $obj = new MyDest($_GET['dest1']); $fLngLat = $gmap->getLngLat($obj->addr); // 上車地點經緯度 $obj->lat = $fLngLat["lat"]; // 緯度 $obj->lng = $fLngLat["lng"]; // 經度 array_push($aDest, $obj); } if(isset($_GET['dest2'])) { $obj = new MyDest($_GET['dest2']); $fLngLat = $gmap->getLngLat($obj->addr); // 上車地點經緯度 $obj->lat = $fLngLat["lat"]; // 緯度 $obj->lng = $fLngLat["lng"]; // 經度 array_push($aDest, $obj); } if(isset($_GET['dest3'])) { $obj = new MyDest($_GET['dest3']); $fLngLat = $gmap->getLngLat($obj->addr); // 上車地點經緯度 $obj->lat = $fLngLat["lat"]; // 緯度 $obj->lng = $fLngLat["lng"]; // 經度 array_push($aDest, $obj); } if(isset($_GET['dest4'])) { $obj = new MyDest($_GET['dest4']); $fLngLat = $gmap->getLngLat($obj->addr); // 上車地點經緯度 $obj->lat = $fLngLat["lat"]; // 緯度 $obj->lng = $fLngLat["lng"]; // 經度 array_push($aDest, $obj); } if(isset($_GET['dest5'])) { $obj = new MyDest($_GET['dest5']); $fLngLat = $gmap->getLngLat($obj->addr); // 上車地點經緯度 $obj->lat = $fLngLat["lat"]; // 緯度 $obj->lng = $fLngLat["lng"]; // 經度 array_push($aDest, $obj); } while(count($aDest)>0) { foreach($aDest as &$val) { // 注意:$val 前面有 & // 計算【各目的地】距離【起點】的長度 $url = "https://maps.googleapis.com/maps/api/directions/json?origin=$sLat,$sLng&destination=".urlencode($val->addr)."&key=$apiKey"; // 目的地 destination 參數也可以是經緯度 $url = "https://maps.googleapis.com/maps/api/directions/json?origin=$sLat,$sLng&destination=".urlencode($val->lat).",".urlencode($val->lng)."&key=$apiKey"; // 若希望路線避開上高速公路, 則可加 avoid=highways 參數 $url = "https://maps.googleapis.com/maps/api/directions/json?origin=$sLat,$sLng&destination=".urlencode($val->addr)."&avoid=highways&key=$apiKey"; // 此外, 還可避開 ---- // tolls:避開收費路段 // highways:避開高速公路 // ferries:避開渡輪 // indoor:避開室內路線(主要用於步行路線) // 初始化 cURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($ch); curl_close($ch); // 解析 json 響應 $data = json_decode($response, true); if ($data['status']=='OK') { $val->len = $data['routes'][0]['legs'][0]['distance']['value']; } else { $val->len = -1; } unset($val); } // 使用 usort 函數排序陣列,根據 $a 屬性值從小到大排序 usort($aDest, function($a, $b) { return $a->len <=> $b->len; }); // 排序後的第一個物件就是 距離最短的地址 $minObj = $aDest[0]; // print_r($aDest); array_push($aSort, ['addr' => ($minObj->addr)]); // 這個地址做為起點 $sLat = $minObj->lat; $sLng = $minObj->lng; array_splice($aDest, 0, 1); } echo json_encode($aSort); // 回傳依距離排序後的停靠點及目的地 } ?>
navi.php
<?php // 切換至 https if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off") { $location = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $location); exit; } ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html style="height:100%;"> <head> <meta charset="utf-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script> </head> <body> <br /> <button onclick="startNavigation()" style="font-size:40pt;">巡航</button><br /> <span style="font-size:40pt;">提醒:在 Android Chrome 開啟本網頁</span><br /> <br /> <br /> <script> function startNavigation() { var aRoute = [ "高雄市苓雅區四維三路2號", "高雄市前鎮區中華五路789號", "高雄市鼓山區濱海二路1號", "高雄市左營區博愛二路777號", "高雄市左營區高鐵路107號" ]; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { var latitude = position.coords.latitude; // 緯度 var longitude = position.coords.longitude; // 經度 var str = "https://您自己的網址/getroute.php?lat="+latitude+"&lng="+longitude+"&dest1="+aRoute[0]+"&dest2="+aRoute[1]+"&dest3="+aRoute[2]+"&dest4="+aRoute[3]+"&dest5="+aRoute[4]; // alert('str = '+str); // return; var destinations = new Array(); $.getJSON(str, function(res) { // alert("res = "+res); // 這段是從 client 端向 server 請求查詢 // server 回傳 json 型態的陣列 // 陣列內的元素是 addr res.forEach(function(element) { destinations.push(element.addr); }); var waypoints = destinations.map(function(destination) { // Google map 規定停靠點以【|】字元分隔 // 將 5 個地點以【|】字元分隔 return encodeURIComponent(destination); }).join("|"); destination = waypoints.split('|').pop(); // 取得最後【|】右邊的字串做為目的地 waypoints = waypoints.slice(0, waypoints.lastIndexOf('|')); // 取得最後【|】的左邊字串做為停靠點 // 構建導航 URL var mapsURL = "https://www.google.com/maps/dir/?api=1&origin=" + latitude + "," + longitude + "&destination=" + destination + "&waypoints=" + waypoints + "&travelmode=driving"; // alert(mapsURL); // return; // 跳轉到 Google Maps window.location.href = mapsURL; }); // return; }, function(error) { alert("無法取得地理位置: " + error.message); } ); } else { alert("瀏覽器不支援地理定位功能。"); } } </script> <br /> <br /> </body> </html>
排序後,程式呼叫 Android 手機上的 Google Map app 進行導航。
第一次執行這個網頁程式時,會詢問是否同意瀏覽器存取你的地理位置,選擇同意且記住您的選擇,之後執行就不會再跳出詢問視窗了。
測試 Google Chrome、Firefox、Microsoft Edge 這幾個主流瀏覽器
Chrome 效果最佳,直接啟動 Google Map,畢竟都是自家的產品;
Edge 雖可呼叫 Google Map,但詢問視窗一直開著,不會自動關閉
Firefox 最差,啟動了 Google Map,但要導航的地址全不見了!!
另一個有趣的發現:
如果只單純導航前往一個地點,Google Map 並不管目的地是否與您的行車方向順向,只要抵達目的地附近,Google Map 就認為任務完成了。ex:出發點是高雄市中山大學,目的地是高雄女中(雄女),雄女的對面是國軍英雄館,Google 導航只要帶您到國軍英雄館,導航就結束了。
但當設了多個停靠點時,Google Map 就會使命必達,規劃的路線即使繞一圈也會帶您順向抵達雄女。
然後...老人家又好奇了...這個網頁程式在 iPhone 上打開會是什麼結果?
答案是:在 Safari 直接 Deny!! 不給開
相關筆記 ----
沒有留言:
張貼留言