地理位置 API(Geolocation API)
一、Geolocation API 概述
1.1 简介
Geolocation API 允许 Web 应用访问用户的地理位置信息。这个 API 通过 GPS、Wi-Fi、IP 地址等多种方式来确定用户位置。
// 检测支持
if ('geolocation' in navigator) {
console.log('Geolocation supported');
}
1.2 核心方法
// 获取当前位置
navigator.geolocation.getCurrentPosition(
successCallback,
errorCallback,
options
);
// 持续监听位置变化
navigator.geolocation.watchPosition(
successCallback,
errorCallback,
options
);
// 停止监听
navigator.geolocation.clearWatch(watchId);
二、获取当前位置
2.1 基本用法
function showPosition(position) {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
console.log(`Latitude: ${lat}, Longitude: ${lon}`);
}
function showError(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.error('User denied the request for Geolocation');
break;
case error.POSITION_UNAVAILABLE:
console.error('Position information is unavailable');
break;
case error.TIMEOUT:
console.error('The request to get user position timed out');
break;
case error.UNKNOWN_ERROR:
console.error('An unknown error occurred');
break;
}
}
navigator.geolocation.getCurrentPosition(showPosition, showError);
2.2 位置信息详解
function showPosition(position) {
const coords = position.coords;
// 纬度
console.log('Latitude:', coords.latitude);
// 经度
console.log('Longitude:', coords.longitude);
// 精度(米)
console.log('Accuracy:', coords.accuracy);
// 海拔(米)
console.log('Altitude:', coords.altitude);
// 海拔精度(米)
console.log('Altitude Accuracy:', coords.altitudeAccuracy);
// 方向(度,0-360)
console.log('Heading:', coords.heading);
// 速度(米/秒)
console.log('Speed:', coords.speed);
// 时间戳
console.log('Timestamp:', new Date(position.timestamp));
}
三、选项配置
3.1 位置选项
const options = {
// 是否使用高精度位置(消耗更多资源)
enableHighAccuracy: true,
// 超时时间(毫秒)
timeout: 10000,
// 缓存有效期(毫秒)
// 0 表示每次都获取新位置
// Infinity 表示使用缓存
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(error) => console.error(error),
options
);
3.2 高精度模式
// 高精度模式(通常使用 GPS)
const highAccuracyOptions = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 0
};
// 低精度模式(使用 IP/Wi-Fi,更快但不准)
const lowAccuracyOptions = {
enableHighAccuracy: false,
timeout: 5000,
maximumAge: 60000
};
四、持续监听位置
4.1 watchPosition
let watchId;
function startTracking() {
watchId = navigator.geolocation.watchPosition(
(position) => {
console.log('Position updated:', position.coords.latitude, position.coords.longitude);
},
(error) => {
console.error('Tracking error:', error);
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 5000
}
);
}
function stopTracking() {
if (watchId) {
navigator.geolocation.clearWatch(watchId);
watchId = null;
}
}
// 页面卸载时停止
window.addEventListener('beforeunload', stopTracking);
4.2 实际应用:实时位置追踪
class LocationTracker {
constructor() {
this.watchId = null;
this.positions = [];
this.onPositionUpdate = null;
}
start() {
if (!('geolocation' in navigator)) {
console.error('Geolocation not supported');
return;
}
this.watchId = navigator.geolocation.watchPosition(
(position) => this.handlePosition(position),
(error) => this.handleError(error),
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
}
handlePosition(position) {
const point = {
lat: position.coords.latitude,
lon: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp
};
this.positions.push(point);
if (this.onPositionUpdate) {
this.onPositionUpdate(point);
}
}
handleError(error) {
switch (error.code) {
case error.PERMISSION_DENIED:
console.error('Permission denied');
break;
case error.POSITION_UNAVAILABLE:
console.error('Position unavailable');
break;
case error.TIMEOUT:
console.error('Position timeout');
break;
}
}
stop() {
if (this.watchId) {
navigator.geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
getPath() {
return this.positions.map(p => [p.lat, p.lon]);
}
}
// 使用
const tracker = new LocationTracker();
tracker.onPositionUpdate = (point) => {
console.log('Moved to:', point.lat, point.lon);
updateMap(point.lat, point.lon);
};
tracker.start();
五、权限管理
5.1 Permissions API
// 查询权限状态
async function checkGeolocationPermission() {
const permission = await navigator.permissions.query({ name: 'geolocation' });
console.log('Permission state:', permission.state);
// 'granted' - 已授权
// 'prompt' - 待请求
// 'denied' - 已拒绝
permission.addEventListener('change', () => {
console.log('Permission changed:', permission.state);
});
}
5.2 权限请求最佳实践
async function requestGeolocation() {
// 检查是否已拒绝
const permission = await navigator.permissions.query({ name: 'geolocation' });
if (permission.state === 'denied') {
console.error('Geolocation permission denied');
// 引导用户手动开启权限
return;
}
if (permission.state === 'prompt') {
// getCurrentPosition 会自动触发权限请求
return navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(error) => console.error(error)
);
}
// 已授权
return navigator.geolocation.getCurrentPosition(
(position) => console.log(position),
(error) => console.error(error)
);
}
六、实际应用
6.1 计算距离
// Haversine 公式计算两点间距离
function calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371e3; // 地球半径(米)
const φ1 = lat1 * Math.PI / 180;
const φ2 = lat2 * Math.PI / 180;
const Δφ = (lat2 - lat1) * Math.PI / 180;
const Δλ = (lon2 - lon1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // 距离(米)
}
// 使用
const distance = calculateDistance(39.9042, 116.4074, 31.2304, 121.4737);
console.log(`Distance: ${distance.toFixed(2)} meters`);
6.2 附近地点查找
class NearbySearch {
constructor(places) {
this.places = places;
}
findNearby(lat, lon, radiusKm = 5) {
return this.places
.map(place => ({
...place,
distance: calculateDistance(lat, lon, place.lat, place.lon)
}))
.filter(place => place.distance <= radiusKm * 1000)
.sort((a, b) => a.distance - b.distance);
}
}
// 使用
const search = new NearbySearch([
{ name: 'Restaurant A', lat: 39.905, lon: 116.408 },
{ name: 'Restaurant B', lat: 39.906, lon: 116.409 },
{ name: 'Restaurant C', lat: 39.910, lon: 116.415 }
]);
const nearby = search.findNearby(39.9042, 116.4074, 2); // 2km 范围内
console.log(nearby);
七、安全与隐私
7.1 隐私保护
- 地理位置是敏感信息,必须获得用户明确同意
- HTTPS 是使用 Geolocation API 的前提条件(localhost 除外)
- 精确位置需要用户在系统级别授权
7.2 错误处理
function handleGeolocationError(error) {
const messages = {
[error.PERMISSION_DENIED]: '位置权限被拒绝。请在浏览器设置中允许位置访问。',
[error.POSITION_UNAVAILABLE]: '无法获取位置信息。请检查您的网络连接。',
[error.TIMEOUT]: '获取位置超时。请重试。'
};
console.error(messages[error.code] || '未知错误');
showErrorMessage(messages[error.code] || '获取位置失败');
}