顯示具有 route 標籤的文章。 顯示所有文章
顯示具有 route 標籤的文章。 顯示所有文章

2015年8月21日 星期五

Javascript 開發筆記 - 透過 Google Maps Routing API 畫路徑 (Directions Service)

Google Maps Directions Service

使用 GPS Location 畫圖時,點太多太精細,容易畫很慢,點太少太粗糙時,發現畫出的線會很糟,例如截彎取直的現象。雖然正解應該是先把精細路徑畫出圖來用,例如每個 zoom level 都畫好路線圖,而非動態畫線、畫點。但就是想偷懶透過 Google Maps API 來畫圖 XD 因此就研究了一下 Directions Service:https://developers.google.com/maps/documentation/javascript/directions

2000個點

假設原路線共有 2000 個精細點,接著取 200 個 sample 點出來,接著想想看怎樣用 Google Maps API 來畫圖。如果單純把這兩百個點畫連線,就會出現截彎取直的現象,因此,想到 Google 導航,透過導航機制自動幫你把路線畫得服服貼貼,畢竟 GPS 收集時也還有誤差問題,真正要做的好,還得把 GPS 位置修到路徑上,有許多眉眉角角。

在使用 Google Maps Directions Service 時,要留意限制,例如有 OVER_QUERY_LIMIT、MAX_WAYPOINTS_EXCEEDED 等限制,不是你想做就可以做:

  • OVER_QUERY_LIMIT indicates the webpage has sent too many requests within the allowed time period.
  • MAX_WAYPOINTS_EXCEEDED indicates that too many DirectionsWaypoints were provided in the DirectionsRequest. The maximum allowed waypoints is 8, plus the origin, and destination. Google Maps API for Work customers are allowed 23 waypoints, plus the origin, and destination. Waypoints are not supported for transit directions.

因此,需要將 200 個點,切成若干次 request ,每一個 request 含頭尾只能有 8 個座標點,其中給予頭尾就可以導航了,而其中的 6 個中繼點只是可以精細導航路線不會飄走。大概是這樣的概念處理資料,200個點,以 8 個點為單位 = 25 次 requests ,會踩到 OVER_QUERY_LIMIT 。儘管可以再透過 call api 的頻率來解決,但我又偷懶把 200 個點限縮成 5~6 次的 requests,然後再連續的 GPS 中,透過平均個數抽 6 個點出來,讓一個 request 還是有八個點,只是更不精細。

總之,程式碼大概長這樣:

<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?signed_in=true&callback=initMap" async defer></script>
<script>
var routes = [];
var points = [];
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 15,
center: {lat: 23.900422, lng: 121.603807}
});

var prev = null;
for (var i=0, cnt=data['results'].length; i<cnt ; ++i) {
var point = new google.maps.LatLng(data['results'][i]['location'].lat,data['results'][i]['location'].lng);
points.push({
location: point,
stopover: false, // 是否顯示
});

// add marker
if (0) {
var marker = new google.maps.Marker({
map: map,
position: point,
});
}
if (prev) {
// use multiple routes
routes.push({
origin: prev,
destination: point,
travelMode: google.maps.TravelMode.DRIVING
});
}
prev = point;
}

var directionsService = new google.maps.DirectionsService;
// multiple routes
// https://developers.google.com/maps/documentation/javascript/directions
if (0) {
for (var i=0, cnt=routes.length ; i<cnt ; ++i) {
console.log(i);
directionsService.route(routes[i], function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
var directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true
});
directionsDisplay.setMap(map);
directionsDisplay.setDirections(result);
}
} );
}
}

// use multiple stops
// https://developers.google.com/maps/documentation/javascript/directions#Waypoints
var max_steps = 36;
for (var i=0, cnt=points.length; i < cnt ; i += max_steps) {
var stops = []; // max should be 8
var next_stop = Math.floor(max_steps / (8-2) );
for (var j=i+next_stop ; j<(max_steps - next_stop) && j < (cnt - 1) ; j += next_stop)  // 去頭去尾, 頭擺在 origin
stops.push(points[j]);
var request = {
origin: points[i].location,
destination: i+(max_steps - 1) < cnt ? points[i+max_steps-1].location : points[cnt-1].location,
waypoints: stops,
travelMode: google.maps.TravelMode.DRIVING,
};
directionsService.route(request, function(result, status) {
if (status == google.maps.DirectionsStatus.OK) {
var directionsDisplay = new google.maps.DirectionsRenderer({
suppressMarkers: true // 單純畫路線,不要顯示 marker
});
directionsDisplay.setMap(map);
directionsDisplay.setDirections(result);
} else {
console.log( status ); // OVER_QUERY_LIMIT, MAX_WAYPOINTS_EXCEEDED
}
} );
}
}

var data =
{
  "results": [
      {
         "elevation" : 17.00912857055664,
         "location" : {
            "lat" : 23.900397,
            "lng" : 121.603762
         },
         "resolution" : 610.8129272460938
      },
      {
         "elevation" : 20.88261413574219,
         "location" : {
            "lat" : 23.898869,
            "lng" : 121.603903
         },
         "resolution" : 610.8129272460938
      },
      {
         "elevation" : 22.29166030883789,
         "location" : {
            "lat" : 23.896661,
            "lng" : 121.60384
         },
         "resolution" : 610.8129272460938
      },
      {
         "elevation" : 20.24537467956543,
         "location" : {
            "lat" : 23.895185,
            "lng" : 121.60387
         },
         "resolution" : 610.8129272460938
      },
      {
         "elevation" : 30.49811744689941,
         "location" : {
            "lat" : 23.891678,
            "lng" : 121.603371
         },
         "resolution" : 610.8129272460938
      },
      // ...
   ]
};
</script>
</head>
<body>
<div id="map" style="width:100%; height:100%;"></div>
</body>
</html>

2014年6月20日 星期五

AWS 筆記 - Amazon Route 53 GeoDNS 用法 (Routing Policy: Latency)



想要讓不同 client 查詢統一個 Domain 時,回應離使用者近的 Data Center 的機器嗎?恰好 AWS 有提供這個功能,用法:
  • 將各個 Data Center 的機器,透過 A Record 指定一個 Domain Name
  • 將使用者真正查詢對象(DomainName)設定多筆 CName Record 對應,在新增時,選擇 Latency 並挑選區域即可,例如服務亞洲可以挑日本(ap-northeast-1), 其他地方挑美國(us-west-1)等,更多區域資訊請查詢:AWS Regions and Endpoints

接著,測試時,可以透過 nslookup 來指定要查詢的 DNS Server 來模擬不同區域的查詢結果,如 Google 8.8.8.8 和最近阿里雲對外公佈的新服務 ALiDNS 235.5.5.5,就可以看到因為地域的不同產生的變化。

連續動作:
  1. 新增 A Record: www-geo-jp.changyy.org/1.1.1.1
  2. 新增 A Record: www-geo-us.changyy.org/2.2.2.2
  3. 新增 CName Record: www-geo.changyy.org/www-geo-jp.changyy.org/Latency/ap-northest-1
  4. 新增 CName Record: www-geo.changyy.org/www-geo-us.changyy.org/Latency/us-west-1
查詢:

$ nslookup www-geo.changyy.org 223.5.5.5
Server:         223.5.5.5
Address:        223.5.5.5#53

Non-authoritative answer:
www-geo.changyy.org       canonical name = www-geo-jp.changyy.org.
Name:   www-geo-jp.changyy.org
Address: 1.1.1.1

$ nslookup www-geo.changyy.org 8.8.8.8
Server:         8.8.8.8
Address:        8.8.8.8#53

Non-authoritative answer:
www-geo.changyy.org       canonical name = www-geo-us.changyy.org.
Name:   www-geo-us.changyy.org
Address: 2.2.2.2


別忘了,這種功能是需要額外收費的。

Amazon Route 53 Pricing
  • Standard Queries
    • $0.500 per million queries – first 1 Billion queries / month
    • $0.250 per million queries – over 1 Billion queries / month
  • Latency Based Routing Queries
    • $0.750 per million queries – first 1 Billion queries / month
    • $0.375 per million queries – over 1 Billion queries / month

AWS 筆記 - 從 Godaddy 搬到 AWS Route 53



AWS Route 53 有很多收費機制,但為了 GeoDNS 的使用,開始來搬遷。原先購買的 Domain name 是在 Godaddy 上購買以維護的,整個匯出流程還滿容易的。

  1. AWS Route 53: Create Hosted Zone

  2. AWS Route 53: Import Zone File

  3. Godaddy: Export DNS Setttings

  4. AWS Route 53: Import Zone File 貼上需要的,如 A系列、CNAME系列、MX系列等

  5. Godaddy: Set NameServer,改用 AWS Route 53 提供的清單



最後,可以用 dig 去追蹤看看,是不是真的改用 AWS Route 53 提供的 NS 囉

$ dig +trace example.com

2014年3月18日 星期二

[PHP] CodeIgniter 處理多層子目錄 Routing 問題

[PHP] CodeIgniter 處理多層子目錄 Routing 問題

過去使用 CodeIgniter 一直以為在 application/controller 裡的目錄結構可以無限延伸使用,如:

application/conotrollers/service/dashboard/product.php
application/conotrollers/service/api/product.php
application/conotrollers/service/welcome.php
application/conotrollers/welcome.php


當瀏覽 hostname/ 可以由 conotrollers/welcome.php 處理,瀏覽 hostname/service/welcome 則由 conotrollers/service/welcome.php 處理,一切正常。

但瀏覽 hostname/service/api/product 和 hostname/service/dashboard/product 時,卻噴 404 Page Not Found 訊息。

一開始以為 nginx rules 設定錯誤,追一下 CodeIgniter 的 source code 後,發現 CodeIgniter 的程式碼沒有用遞迴或等價方式去搜尋子目錄,再透過相關關鍵字才發現,關於多層子目錄的需求,則只能透過指定 routing 的設定方式進行,但對應到還是一層目錄結構:

例如 hostname/service/dashboard/product 用法:

$ vim application/config/routes.php

$route['service/(:any)/(:any)'] = 'service_$1/$2';


而目錄結構更新為:

application/conotrollers/service_dashboard/product.php
application/conotrollers/service_api/product.php
application/conotrollers/welcome.php


簡言之,就是以 application/controllers 為基準,頂多再加一層 subdir 而已,而想要 uri 有多層的含義,只能自定 route 來達到。