1. 關於 Geolocation 對象
隨著 Opera 10.6 和 Safari 5的相繼發布,截止到目前為止,W3C 中一個名為 Geolocation 的 API 規范在所有非 IE 浏覽器中都得到了實現。簡單地說,Geoloaction 的作用就是通過浏覽器感知用戶的地理位置。
事實上,對於用戶地理位置的探測在互聯網上早已經有了很多案例,其中一個最常見的應用就是當在地址欄輸入 google.com 並按下回車鍵的時候,google 並沒有把我們帶向剛才輸入的地址,而是轉向了 google 在我們所處地區的頁面,比如大陸和香港用戶會到達 google.com.hk,當然我們很懷念 google.cn,但這並不是今天所要討論的話題。
如果說地理位置的感知已經成為了一項成熟的技術,那麼緊接著的問題就是 Geolocation 存在的意義是什麼?就我目前對 Geolocation 的理解,我認為它存在的理由至少包含以下兩點:
1. 公共數據:目前的地理位置探測,其資料往往來源於服務商各自的數據,而使用浏覽器內置的統一接口,將高效整合這一信息。
2. 精確定位:無論是谷歌還是谷姐們,實現定位的原理無非是通過 IP 地址來探測用戶的所在位置,眾所周知,IP 的定位能力是比較差的,絕大多數情況下,它的精確度能達到數十公裡就已經不錯了,除此之外,沒有別的任何辦法,無論是服務器端語言還是客戶端的 JavaScript 腳本,都無法從用戶那裡獲取到更多對定位有所幫助的信息。然而作為用戶機器上更為底層的浏覽器,它將有權利支配其他有助於定位的設備,比如 GPS 和 WIFI。GPS 大家都已經很熟悉了,在信號良好的情況下,它可以精確到數米之內。而 WIFI 在國內基本上被廢了,對於這項技術普及的國家,WIFI 的定位基本上可以精確到百米左右,據說經常可以見到一些公司的人,整天什麼都不干,就開著小車到處晃,到一個熱點( Hot Spot ),就記錄下當前的地理位置,然後傳回到公司的數據庫。
該對象位於我們非常熟悉的 Navigator 對象下面,可以通過 navigator.geolocation 來訪問,當然前面也可以加上 window.。不支持 geolocation 的浏覽器並不包含這一對象,那麼可以通過下面的代碼來做能力檢測,對不同的浏覽器做不同的處理。
if (navigator.geolocation) {
alert( ' 你的浏覽器支持 geolocation ' );
}else{
alert( ' 你的浏覽器不支持 geolocation ' )
}
在訪問 geolocation 對象時,即調用 geolocation 下面的方法時,浏覽器會彈出提示,詢問用戶是否許可網站 提供的位置服務,只有在得到用戶許可過後,服務才會繼續,否則將被停止,在稍後你將會了解到,我們能夠捕獲到用戶拒絕服務的動作。下面這張圖分別是 Chrome , Firefox 和 Opera 在初次訪問 geolocation 時,給用戶的提示:
2. 獲取當前地理位置 --- getCurrentPosition
當獲得用戶的許可過後,便一切就緒。我們將通過 geolocation 下的 getCurrentPosition 方法來獲取用戶的信息,這個方法是今天整篇文章的核心部分,也是 geolocation 一個非常重要的方法。
navigator.geolocation.getCurrentPosition( getPositionSuccess , getPositionError );
在上面的代碼中,我們調用了 getCurrentPosition 方法,並為其傳遞了兩個參數,事實上這個方法可以接受三個參數,前兩個參數是函數,最後一個是對象:第一個參數是成功獲取位置信息的回調函數,它是方法唯一必須的參數;第二個參數用於捕獲獲取位置信息出錯的情況,第三個參數是配置項。
當浏覽器成功獲取到用戶的位置信息時,getCurrentPosition 的第一個函數類型的參數將被調用,一個 position 對象會被傳入到調用的函數中,這個對象中包含了浏覽器傳回的數據,這非常重要。
function getPositionSuccess( position ){
var lat = position.coords.latitude;
var lng = position.coords.longitude;
document.write( "您所在的位置: 經度" + lat + ",緯度" + lng );
}
是的,position 對象包含了用戶的地理位置信息,該對象下面的 coords 子對象包含了用戶所在的緯度和經度信息,通過 position.coords.latitude 可以訪問緯度,而 position.coords.longitude 中存放了經度的信息,用戶的位置信息越精確,這兩個數字後面的小數點越長。事實上,在 Firefox 中,position 對象下還附帶有另一個 address 對象,這個對象包含這個經緯度下的國家名,城市名甚至街道名。
function getPositionSuccess( position ){
var lat = position.coords.latitude;
var lng = position.coords.longitude;
alert( "您所在的位置: 經度" + lat + ",緯度" + lng );
if(typeof position.address !== "undefined"){
var country = position.address.country;
var province = position.address.region;
var city = position.address.city;
alert(' 您位於 ' + country + province + '省' + city +'市');
}
}
錯誤捕獲:上面都是成功獲取到用戶位置信息的處理,但是出現問題的情況在所難免,當獲取用戶的位置信息出錯時,傳遞到 getCurrentPosition 的第二個函數類型參數被調用,一個包含具體出錯信息的對象會被傳遞進去,錯誤將被捕獲。
function getPositionError(error){
switch(error.code){
case error.TIMEOUT :
alert( " 連接超時,請重試 " );
break;
case error.PERMISSION_DENIED :
alert( " 您拒絕了使用位置共享服務,查詢已取消 " );
break;
case error.POSITION_UNAVAILABLE :
alert( " 親愛的火星網友,非常抱歉,我們暫時無法為您所在的星球提供位置服務 " );
break;
}
}
error 對象下面,存放了3個常量:
TIMEOUT 表示獲取信息超時。
PERMISSION_DENIED 表示用戶選擇了拒絕了位置服務。
POSITION_UNAVAILABLE 表示位置不可知。
而每一次出錯時 error.code 將指向3個常量之中的一個。
配置項:getCurrentPosition 方法的第三個參數是一個對象,該對象影響了獲取位置時的一些細節。
enableHighAccuracy,它將告訴浏覽器是否啟用高精度設備,所謂的高精度設備包含但不局限於前面所提到的 GPS 和 WIFI,值為 true 的時候,浏覽器會嘗試啟用這些設備,默認指為 true,在這種情況下,浏覽器會盡可能地進行更為精確的查詢,簡單地說,如果用戶有可用的 GPS 設備,會返回 GPS 設備的查詢結果,IP 是最後的選擇,對於移動設備來說,網絡接入點(基站)或許成為另一個選擇,對此我還沒有完全了解,但根據測試,即時沒有任何額外功能的手機,也能夠得到更 為精確的查詢結果。
timeout,超時,獲取位置信息時超出設定的這個時長,將會觸發錯誤,捕獲錯誤的函數將被調用,並且錯誤碼指向TIMEOUT。
這樣我們嘗試修改調用 getCurrentPosition 時傳遞的參數
navigator.geolocation.getCurrentPosition( getPositionSuccess , getPositionError,{
timeout : 5000 // 5 秒超時
});
3. 持續追蹤位置 --- watchPosition
對於使用移動設備的用戶來說,位置並不是固定的,W3C 當然也考慮到了這一點,watchPosition 是一個專門用來處理這一情況的方法,watchPosition 被調用後,浏覽器會跟蹤設備的位置,每一次位置的變化,watchPosition 中的代碼都將會被執行。對於致力於移動設備 web 開發的同學來說,這個方法是及其重要的,它也許將會改變 web 移動客戶端的格局。
navigator.geolocation.watchPosition( refreshPosition );
上面的代碼表示,但設置位置發生改變時,refreshPosition 將會被調用。事實上 watchPosition 和 getCurrentPosition 幾乎一模一樣,同樣包含了一個成功獲取位置的回調函數,一個獲取失敗的回調函數和一個配置項,區別僅僅是在觸發的時機上。所以具體的方法就不再重復了。上 面的代碼表示在每一次位置發生改變時,調用 refreshPosition 函數。
4. 實例 --- 與 Google Map 交互
接下來,我們通過一個實例,來具體應用一下 Geolocation ,實例的最終效果是,獲取用戶的位置,並通過 Google Map 在地圖上標記出來,當用戶的位置發生改變後,更新之前在地圖上的標記到新的位置。首先我們准備好 DOM,並加載 Google Map
<!-- 按鈕,被點擊時開始獲取用戶位置信息 -->
<input type="button" id="getPos" value="獲取我的位置" />
<!-- 用來存放反饋給用戶信息的 div -->
<div id="info"></div>
<!-- 為 Google Map 准備 -->
<div id="map"></div>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false" ></script>
接下來我們在 JavaScript 中獲取這些 DOM,並為 Google Map 提供一個對象用來存放地圖和標記。這些都是准備工作,很煩,但是不得不做。
var dom = {
btn : document.getElementById('getPos'),
info : document.getElementById('info'),
map : document.getElementById('map')
};
var gmap = {
map : null,
marker : null
}
接下來監聽按鈕的事件,當按鈕被點擊的時候,將觸發獲取用戶的位置信息。在這裡我們對浏覽器進行了檢測,如果不支持 Geolocation 接口,將提示用戶,並停止進一步的動作。
dom.btn.onclick = function(){
if (navigator.geolocation) {
dom.info.innerHTML = "請等待查詢結果返回";
dom.info.className = "warn";
navigator.geolocation.getCurrentPosition(getPositionSuccess,getPositionError,{timeout:5000});
}else {
dom.info.innerHTML = "抱歉,您所使用的浏覽器不支持 Geolocation 接口";
dom.info.className = "warn";
}
}
在上面我們向 getCurrentPosition 傳遞了兩個函數,現在我們來具體寫這兩個函數,首先是 getPositionSuccess ,當成功獲取到用戶的位置信息時,這個函數被調用,我們將在這時更新頁面上的顯示值,並配置追蹤用戶的位置的函數,最後調用 Google Map 的 API 在 id 為 map 的 div 中顯示地圖,並在地圖上標記出用戶的當前位置。
function getPositionSuccess(position){
var lat = position.coords.latitude;
var lng = position.coords.longitude;
dom.info.innerHTML = "您所在的位置: 經度" + lat + ",緯度" + lng;
navigator.geolocation.watchPosition(refreshPosition);
// 載入 Google 地圖
var latlng = new google.maps.LatLng(lat, lng);
var myOptions = {
zoom : 16,
center : latlng,
mapTypeId : google.maps.MapTypeId.ROADMAP
};
gmap.map = new google.maps.Map(document.getElementById("map"), myOptions);
// 向地圖中添加標記
gmap.marker = new google.maps.Marker({
position: latlng,
map: gmap.map
});
}
對於錯誤處理的函數,則非常簡單,可以參看前面的內容。我們來關注一下追蹤位置的函數,在這個函數中,我們實時更新顯示信息,並更新地圖到用戶所處的位置。
function refreshPosition(position){
var lat = position.coords.latitude;
var lng = position.coords.longitude;
var latlng = new google.maps.LatLng(lat, lng);
// 重設地圖位置
gmap.map.setCenter(latlng);
dom.info.innerHTML = '您所在的位置: 經度' + lat ' + ',緯度' + lng;
// 重設標記位置
gmap.marker.setOptions({
position: new google.maps.LatLng(lat, lng)
});
}
以上關於 Google Map 的代碼不是很了解的同學可以到 Google Code 去看一下 手冊,這裡只是很簡單的應用,有興趣的同學可以進一步延伸。下面是這個 Demo 的最終效果。
這個 Demo 在具備 GPS 功能的手機上,能夠非常精確地定位,在我的測試中,在信號良好的地方,基本上能夠定位到所在地方,並且位置追蹤工作良好。下面是我在手機上浏覽本 Demo 的效果,平台是 S60v5 ,浏覽器 Opera Mobile 10.1 beta 1。需要說明一點的是,Opera Mobile 正是從這個版本開始支持 Geolocation 的。