iT邦幫忙

2024 iThome 鐵人賽

DAY 1
1
JavaScript

可愛又迷人的 Web API系列 第 1

Day1. 用 Geolocation API 取得與追蹤使用者地理位置

  • 分享至 

  • xImage
  •  

Geolocation API 是 HTML5 Web API 中的一個強大功能,他可以讓網頁存取使用者裝置的地理位置,但安心的是 Web API 很重隱私,我們要經過使用者的同意,才能取得使用者的地理位置唷。

Geolocation API 主要提供以下功能:

  • 取得使用者現在的位置
  • 持續監控使用者位置的變化
  • 取得位置的精準度資訊

那麼,瀏覽器是如何確定使用者的地理位置呢?有下幾種方式,包括:

  • GPS (全球定位系統)
  • 網絡位置(如 Wi-Fi 或基地台定位)
  • IP 位置定位

取得使用者現在的位置

首先,我們要檢查瀏覽器是否支援這個 API:

if ("geolocation" in navigator) {
	console.log('瀏覽器支援 Geolocation');
} else {
	console.log('瀏覽器不支援 Geolocation');
}

支援的話,就可以使用 getCurrentPosition() 取得使用者當前的位置:

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);

這個方法接受三個參數:

  1. successCallback:成功取得位置時調用的函數
  2. errorCallback:取得位置失敗時調用的函數,選填
  3. options:相關設定參數,選填

以下是使用 getCurrentPosition() 的例子:

function successCallback(position) {
  const latitude = position.coords.latitude;
  const longitude = position.coords.longitude;
  console.log(`緯度:${latitude},經度:${longitude}`);
}

function errorCallback(error) {
  console.error(`錯誤:${error.message}`);
}

const options = {
  enableHighAccuracy: true, // 使用高精準度的資訊
  timeout: 5000, // 請求超時時間 (毫秒)
  maximumAge: 0 // 最長緩存時間
};

navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);

出現錯誤訊息:Only request geolocation information in response to a user gesture.

前面有提過,Geolocation API 必須要經過使用者的同意,才能取得地理位置。因此這個錯誤,表示我們在沒有經過使用者允許的情況下,就要使用 Geolocation API 取得使用者的地理位置。

那麼,怎樣算是經過使用者的同意呢?我們可以讓使用者進行明確的動作 (例如點選按鈕、點選連結) 後再要求 Geolocation 的權限。

if ("geolocation" in navigator) {
	console.log('瀏覽器支援 Geolocation');
	// 點選按鈕才會呼叫 getCurrentPosition()
	document.getElementById('geolocation').addEventListener('click', function () {
	function successCallback(position) {
		const latitude = position.coords.latitude;
		const longitude = position.coords.longitude;
		console.log(`緯度:${latitude},經度:${longitude}`);
	}
	function errorCallback(error) {
		console.error(`錯誤:${error.message}`);
	}
	const options = {
		enableHighAccuracy: true, // 使用高精準度的資訊
		timeout: 5000, // 請求超時時間 (毫秒)
		maximumAge: 0 // 快取時間
	};
	navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);
	});
} else {
	console.log('瀏覽器不支援 Geolocation');
}

此時就可以在 console 面板看到輸出的資訊

瀏覽器支援 Geolocation
緯度:15.0232011,經度:211.2709354

getCurrentPosition()options 參數說明

參數名稱 值的類型 預設值 說明
enableHighAccuracy Boolean false 使用 true 可以讓裝置提供更精準的位置,但相對的也會讓處理時間變長、需要更多的電量,或是讓 GPS 晶片耗能增加。
timeout 正整數 Infinity (單位:毫秒) 取得使用者地理位置時,超過這個時間就會停止解析。如果有在 errorCallback 顯示 error,會看到錯誤為 Timer expired(圖 1-1)
maximumAge 正整數 0 (單位:毫秒) 預設為 0 表示不進行快取,每次都要重新取得最新的使用者地理位置;假如設定為 60000,表示允許使用過去 60 秒內取得的地理位置。

更好的顯示錯誤訊息

取得地理位置時,可能會遇到各種問題,例如使用者拒絕授權,該裝置讀不到位置(本身不支援地理位置的功能),沒有網路或訊號不好… 等等。因此我們可以在 errorCallback 函數中,根據錯誤訊息顯示對應的文字:

function errorCallback(error) {
  switch(error.code) {
    case error.PERMISSION_DENIED:
      console.error("使用者拒絕了地理位置請求。");
      break;
    case error.POSITION_UNAVAILABLE:
      console.error("無法取得地理位置");
      break;
    case error.TIMEOUT:
      console.error("請求超時。");
      break;
    case error.UNKNOWN_ERROR:
      console.error("發生未知錯誤。");
      break;
  }
}

拒絕權限後該如何重新啟用

再次提醒,Geolocation API 非常注重隱私 XD,所以如果使用者拒絕分享地理位置,我們是無法使用 JavaScript 重新觸發請求的對話框唷。

但我們可以引導使用者,讓他們修改瀏覽器設定分享地理位置,以 Chrome 為例,選擇網址左側的 icon,再選擇「位置」 → 「重設權限」,然後重新載入頁面,就會跳出請求的對話框了。

https://ithelp.ithome.com.tw/upload/images/20240830/20120631ry8KDGoqrc.png

即時追蹤位置變化

除了取得使用者當前的地理位置外,我們還能使用 Geolocation API 監控地理位置的變化,可用於導航類的 APP。

使用 watchPosition() 方法來監控變化:

const watchId = navigator.geolocation.watchPosition(successCallback, errorCallback, options);

這個方法會返回一個 watchId,我們可以用它來停止監控:

navigator.geolocation.clearWatch(watchId);

結合地圖與模擬移動

Geolocation API 本身並不提供地圖顯示的功能,所以我們會將取得的位置資訊與第三方地圖服務結合使用,常見的就是 Google Maps 或 OpenStreetMap。

鑒於 Googel Mpas 的設定較為繁瑣,我們就用 OpenStreetMap 和 Leaflet 來做一個追蹤位置變化的地圖功能吧。

在地圖上顯示自己的地理位置

<script src="<https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js>"></script>
<link rel="stylesheet" href="<https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css>" />

<script>
let map;
let marker;

function initMap(lat, lon) {
  map = L.map('map').setView([lat, lon], 13);
  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
  }).addTo(map);
  marker = L.marker([lat, lon]).addTo(map);
}

if ("geolocation" in navigator) {
  console.log('瀏覽器支援 Geolocation');
  document.getElementById('geolocation').addEventListener('click', function () {
    function successCallback(position) {
      const latitude = position.coords.latitude;
      const longitude = position.coords.longitude;
      initMap(latitude, longitude);
      console.log(`緯度:${latitude},經度:${longitude}`);
    }

    function errorCallback(error) {
      console.error(`錯誤:${error.message}`);
    }
    navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);
  });
} else {
  console.log('瀏覽器不支援 Geolocation');
}
</script>

按下 Get Location 按鈕後,就會在地圖上顯示自己的所在位置

https://ithelp.ithome.com.tw/upload/images/20240830/20120631aqZUhequ2y.png

使用 watchPosition() 追蹤使用者的地理位置

再來放三個按鈕,分別是追蹤與模擬移動的功能

<div id="map"></div>
<!-- status 顯示當前經緯度 -->
<div id="status"></div>
<!-- 加入三個按鈕 -->
<div>
  <button onclick="startTracking()">開始追蹤</button>
  <button onclick="stopTracking()">停止追蹤</button>
  <button onclick="simulateMovement()">模擬移動</button>
</div>

原本是按下「Get Location」按鈕才會顯示位置,現在改成按下「startTracking」按鈕,才會在地圖上顯示位置。

watchPosition() 取代 getCurrentPosition(),用 watchPosition()successCallback 將經緯度傳入 initMap()

// watchPosition 的 successCallback
function updatePosition(position) {
  const { latitude, longitude } = position.coords;
  if (!map) {
    initMap(latitude, longitude);
  } else {
    map.setView([latitude, longitude], 13);
    marker.setLatLng([latitude, longitude]);
  }
  document.getElementById('status').textContent = `緯度: ${latitude}, 經度: ${longitude}`;
}

// watchPosition 的 errorCallback
function handleError(error) {
  document.getElementById('status').textContent = `錯誤: ${error.message}`;
}

function startTracking() {
  if ("geolocation" in navigator) {
    watchId = navigator.geolocation.watchPosition(updatePosition, handleError);
    document.getElementById('status').textContent = "正在追蹤位置...";
  } else {
    document.getElementById('status').textContent = "您的瀏覽器不支援地理位置功能。";
  }
}

模擬移動功能

開發中可以自己寫一個模擬移動的功能,隨機修改經緯度來模擬裝置的移動:

function simulateMovement() {
  if (simulationInterval) {
    clearInterval(simulationInterval);
    simulationInterval = null;
    document.getElementById('status').textContent = "模擬移動已停止。";
  } else {
    let lat = 48.860611; // 起始緯度
    let lon = 2.3327785; // 起始經度
    simulationInterval = setInterval(() => {
      lat += (Math.random() - 0.5) * 0.001;
      lon += (Math.random() - 0.5) * 0.001;
      updatePosition({ coords: { latitude: lat, longitude: lon } });
    }, 1000);
    document.getElementById('status').textContent = "正在模擬移動...";
  }
}

範例程式碼

線上範例網址:https://mukiwu.github.io/web-api-demo/geo.html
請先點選「開始追蹤」並允許瀏覽器取得你的地理位置。

小結

Geolocation API 是 HTML5 Web API 中的一個強大功能,允許網頁存取使用者裝置的地理位置,但需要經過使用者的同意以保護隱私,建議大家在取得使用者的地理位置前,應清楚解釋需求,並能提供如何修改瀏覽器設定,以便重新授權地理位置存取,避免日後爭議。

以上就是 Geolocation API 的介紹,有任何問題歡迎留言討論唷。


下一篇
Day2. 用 Geolocation API 實作車子導航追蹤
系列文
可愛又迷人的 Web API20
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言