地理位置API(Geolocation API)是HTML5提供的接口,允许Web应用获取用户的地理位置信息。通过这个API,可以开发基于位置的服务,如地图导航、周边搜索、位置分享等功能。
东巴文(db-w.cn) 认为:地理位置API让Web应用具备了感知位置的能力,为基于位置的服务打开了大门。
| 特点 | 说明 |
|---|---|
| 标准化 | W3C标准API,浏览器支持广泛 |
| 高精度 | 支持GPS、WiFi、基站等多种定位方式 |
| 隐私保护 | 需要用户授权才能获取位置 |
| 异步获取 | 不阻塞页面,用户体验好 |
| 保护机制 | 说明 |
|---|---|
| 用户授权 | 首次访问时需要用户明确授权 |
| HTTPS要求 | 现代浏览器要求HTTPS环境 |
| 权限管理 | 用户可以随时撤销授权 |
| 模糊定位 | 可以提供模糊的位置信息 |
东巴文点评:隐私保护是地理位置API的重要特性,开发者应该尊重用户隐私,只在必要时请求位置权限。
// 检查浏览器支持
if ('geolocation' in navigator) {
console.log('浏览器支持地理位置API');
} else {
console.log('浏览器不支持地理位置API');
}
// 获取当前位置
navigator.geolocation.getCurrentPosition(
// 成功回调
function(position) {
console.log('纬度:', position.coords.latitude);
console.log('经度:', position.coords.longitude);
console.log('精度:', position.coords.accuracy);
},
// 错误回调
function(error) {
console.error('获取位置失败:', error.message);
}
);
navigator.geolocation.getCurrentPosition(
successCallback,
errorCallback,
options
);
// 成功回调
function successCallback(position) {
console.log('位置信息:', {
latitude: position.coords.latitude, // 纬度
longitude: position.coords.longitude, // 经度
accuracy: position.coords.accuracy, // 精度(米)
altitude: position.coords.altitude, // 海拔(米)
altitudeAccuracy: position.coords.altitudeAccuracy, // 海拔精度
heading: position.coords.heading, // 方向(度)
speed: position.coords.speed, // 速度(米/秒)
timestamp: position.timestamp // 时间戳
});
}
// 错误回调
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;
default:
console.error('未知错误');
}
}
// 选项参数
const options = {
enableHighAccuracy: true, // 是否使用高精度定位
timeout: 5000, // 超时时间(毫秒)
maximumAge: 0 // 缓存时间(毫秒)
};
navigator.geolocation.getCurrentPosition(
successCallback,
errorCallback,
options
);
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enableHighAccuracy |
Boolean | false | 是否使用高精度定位(如GPS) |
timeout |
Number | Infinity | 获取位置的超时时间(毫秒) |
maximumAge |
Number | 0 | 缓存位置的最大时间(毫秒) |
东巴文点评:enableHighAccuracy会消耗更多电量,应该根据实际需求选择是否启用。
// 监听位置变化
const watchId = navigator.geolocation.watchPosition(
function(position) {
console.log('位置更新:', position.coords.latitude, position.coords.longitude);
},
function(error) {
console.error('监听失败:', error.message);
},
{
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
}
);
// 停止监听
navigator.geolocation.clearWatch(watchId);
class LocationTracker {
constructor() {
this.watchId = null;
this.positions = [];
}
// 开始跟踪
start(options = {}) {
const defaultOptions = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
this.watchId = navigator.geolocation.watchPosition(
(position) => {
this.positions.push({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
timestamp: position.timestamp
});
this.onPositionUpdate(position);
},
(error) => {
this.onError(error);
},
{ ...defaultOptions, ...options }
);
return this.watchId;
}
// 停止跟踪
stop() {
if (this.watchId !== null) {
navigator.geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
// 获取轨迹
getTrack() {
return this.positions;
}
// 清空轨迹
clearTrack() {
this.positions = [];
}
// 位置更新回调(可重写)
onPositionUpdate(position) {
console.log('位置更新:', position);
}
// 错误回调(可重写)
onError(error) {
console.error('位置错误:', error);
}
}
// 使用示例
const tracker = new LocationTracker();
tracker.onPositionUpdate = function(position) {
console.log('新位置:', position.coords.latitude, position.coords.longitude);
};
tracker.start();
| 错误码 | 错误名称 | 说明 |
|---|---|---|
| 1 | PERMISSION_DENIED | 用户拒绝了位置请求 |
| 2 | POSITION_UNAVAILABLE | 位置信息不可用 |
| 3 | TIMEOUT | 请求超时 |
function getLocation() {
return new Promise((resolve, reject) => {
if (!('geolocation' in navigator)) {
reject(new Error('浏览器不支持地理位置API'));
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
});
},
(error) => {
let message = '';
switch (error.code) {
case error.PERMISSION_DENIED:
message = '用户拒绝了位置请求,请在浏览器设置中允许位置访问';
break;
case error.POSITION_UNAVAILABLE:
message = '无法获取位置信息,请检查设备定位功能是否开启';
break;
case error.TIMEOUT:
message = '获取位置超时,请稍后重试';
break;
default:
message = '获取位置时发生未知错误';
}
reject(new Error(message));
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
});
}
// 使用
getLocation()
.then(location => {
console.log('位置:', location);
})
.catch(error => {
console.error('错误:', error.message);
// 显示友好的错误提示
alert(error.message);
});
东巴文点评:良好的错误处理能提升用户体验,应该为每种错误提供清晰的提示信息。
// 计算两点之间的距离(单位:米)
function calculateDistance(lat1, lon1, lat2, lon2) {
const R = 6371000; // 地球半径(米)
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 / 1000).toFixed(2)} 公里`);
function formatDistance(meters) {
if (meters < 1000) {
return `${Math.round(meters)} 米`;
} else if (meters < 10000) {
return `${(meters / 1000).toFixed(1)} 公里`;
} else {
return `${(meters / 1000).toFixed(0)} 公里`;
}
}
console.log(formatDistance(500)); // 500 米
console.log(formatDistance(1500)); // 1.5 公里
console.log(formatDistance(15000)); // 15 公里
地理位置API只提供坐标信息,要获取详细地址需要使用第三方服务(如高德地图、百度地图)。
// 使用高德地图逆地理编码API
async function reverseGeocode(latitude, longitude) {
const key = 'YOUR_AMAP_KEY'; // 替换为你的高德地图key
const url = `https://restapi.amap.com/v3/geocode/regeo?key=${key}&location=${longitude},${latitude}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.status === '1') {
return data.regeocode.formatted_address;
} else {
throw new Error('地理编码失败');
}
} catch (error) {
console.error('逆地理编码错误:', error);
return null;
}
}
// 使用示例
navigator.geolocation.getCurrentPosition(async (position) => {
const address = await reverseGeocode(
position.coords.latitude,
position.coords.longitude
);
console.log('当前位置:', address);
});
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>地理位置API示例 - 东巴文</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.section {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
margin: 5px;
border: none;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
button:hover {
opacity: 0.9;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.location-info {
margin: 15px 0;
padding: 15px;
background: #f5f5f5;
border-radius: 5px;
}
.location-info p {
margin: 8px 0;
}
.location-info strong {
display: inline-block;
width: 120px;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}
.status.info {
background: #d1ecf1;
color: #0c5460;
}
#map {
width: 100%;
height: 300px;
border: 1px solid #ddd;
border-radius: 5px;
margin: 15px 0;
}
.track-info {
margin: 10px 0;
padding: 10px;
background: #fff3cd;
border-radius: 5px;
}
</style>
</head>
<body>
<h1>地理位置API示例</h1>
<div class="section">
<h2>获取当前位置</h2>
<button onclick="getCurrentLocation()">获取位置</button>
<div id="currentLocation"></div>
</div>
<div class="section">
<h2>实时位置跟踪</h2>
<button onclick="startTracking()">开始跟踪</button>
<button onclick="stopTracking()">停止跟踪</button>
<button onclick="clearTrack()">清空轨迹</button>
<div id="trackingStatus" class="status info">未开始跟踪</div>
<div id="trackInfo" class="track-info"></div>
</div>
<div class="section">
<h2>距离计算</h2>
<div class="location-info">
<p><strong>地点1:</strong> 北京 (39.9042°N, 116.4074°E)</p>
<p><strong>地点2:</strong> 上海 (31.2304°N, 121.4737°E)</p>
</div>
<button onclick="calculateDistance()">计算距离</button>
<div id="distanceResult"></div>
</div>
<div class="section">
<h2>附近地点搜索</h2>
<p>获取当前位置后,搜索附近的兴趣点</p>
<button onclick="searchNearby()">搜索附近</button>
<div id="nearbyResult"></div>
</div>
<script>
// 位置跟踪器
class LocationTracker {
constructor() {
this.watchId = null;
this.positions = [];
}
start(options = {}) {
const defaultOptions = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
this.watchId = navigator.geolocation.watchPosition(
(position) => {
this.positions.push({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp
});
this.onPositionUpdate(position);
},
(error) => {
this.onError(error);
},
{ ...defaultOptions, ...options }
);
return this.watchId;
}
stop() {
if (this.watchId !== null) {
navigator.geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
getTrack() {
return this.positions;
}
clearTrack() {
this.positions = [];
}
onPositionUpdate(position) {
console.log('位置更新:', position);
}
onError(error) {
console.error('位置错误:', error);
}
}
const tracker = new LocationTracker();
let currentPosition = null;
// 获取当前位置
function getCurrentLocation() {
const container = document.getElementById('currentLocation');
container.innerHTML = '<div class="status info">获取位置中...</div>';
if (!('geolocation' in navigator)) {
container.innerHTML = '<div class="status error">浏览器不支持地理位置API</div>';
return;
}
navigator.geolocation.getCurrentPosition(
(position) => {
currentPosition = position;
const coords = position.coords;
container.innerHTML = `
<div class="status success">位置获取成功</div>
<div class="location-info">
<p><strong>纬度:</strong> ${coords.latitude.toFixed(6)}°</p>
<p><strong>经度:</strong> ${coords.longitude.toFixed(6)}°</p>
<p><strong>精度:</strong> ${coords.accuracy.toFixed(2)} 米</p>
${coords.altitude ? `<p><strong>海拔:</strong> ${coords.altitude.toFixed(2)} 米</p>` : ''}
${coords.speed ? `<p><strong>速度:</strong> ${coords.speed.toFixed(2)} 米/秒</p>` : ''}
<p><strong>时间:</strong> ${new Date(position.timestamp).toLocaleString()}</p>
</div>
`;
},
(error) => {
let message = '';
switch (error.code) {
case error.PERMISSION_DENIED:
message = '用户拒绝了位置请求';
break;
case error.POSITION_UNAVAILABLE:
message = '位置信息不可用';
break;
case error.TIMEOUT:
message = '请求超时';
break;
default:
message = '未知错误';
}
container.innerHTML = `<div class="status error">错误: ${message}</div>`;
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
}
// 开始跟踪
function startTracking() {
const statusEl = document.getElementById('trackingStatus');
const trackInfoEl = document.getElementById('trackInfo');
tracker.onPositionUpdate = function(position) {
const coords = position.coords;
const track = tracker.getTrack();
statusEl.className = 'status success';
statusEl.textContent = '跟踪中...';
trackInfoEl.innerHTML = `
<p><strong>当前纬度:</strong> ${coords.latitude.toFixed(6)}°</p>
<p><strong>当前经度:</strong> ${coords.longitude.toFixed(6)}°</p>
<p><strong>轨迹点数:</strong> ${track.length}</p>
<p><strong>精度:</strong> ${coords.accuracy.toFixed(2)} 米</p>
`;
};
tracker.onError = function(error) {
statusEl.className = 'status error';
statusEl.textContent = '跟踪错误: ' + error.message;
};
tracker.start();
}
// 停止跟踪
function stopTracking() {
tracker.stop();
const statusEl = document.getElementById('trackingStatus');
statusEl.className = 'status info';
statusEl.textContent = '已停止跟踪';
}
// 清空轨迹
function clearTrack() {
tracker.clearTrack();
document.getElementById('trackInfo').innerHTML = '';
}
// 计算距离
function calculateDistance() {
const resultEl = document.getElementById('distanceResult');
// 北京坐标
const beijing = { lat: 39.9042, lon: 116.4074 };
// 上海坐标
const shanghai = { lat: 31.2304, lon: 121.4737 };
const distance = haversineDistance(beijing.lat, beijing.lon, shanghai.lat, shanghai.lon);
resultEl.innerHTML = `
<div class="status success">
北京到上海的距离: ${formatDistance(distance)}
</div>
`;
}
// Haversine公式计算距离
function haversineDistance(lat1, lon1, lat2, lon2) {
const R = 6371000; // 地球半径(米)
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;
}
// 格式化距离
function formatDistance(meters) {
if (meters < 1000) {
return `${Math.round(meters)} 米`;
} else if (meters < 10000) {
return `${(meters / 1000).toFixed(1)} 公里`;
} else {
return `${(meters / 1000).toFixed(0)} 公里`;
}
}
// 搜索附近
function searchNearby() {
const resultEl = document.getElementById('nearbyResult');
if (!currentPosition) {
resultEl.innerHTML = '<div class="status error">请先获取当前位置</div>';
return;
}
// 模拟附近地点数据
const nearbyPlaces = [
{ name: '咖啡厅', distance: 150, type: '餐饮' },
{ name: '公园', distance: 300, type: '休闲' },
{ name: '超市', distance: 500, type: '购物' },
{ name: '医院', distance: 800, type: '医疗' },
{ name: '学校', distance: 1000, type: '教育' }
];
resultEl.innerHTML = `
<div class="status success">找到 ${nearbyPlaces.length} 个附近地点</div>
<div class="location-info">
${nearbyPlaces.map(place => `
<p><strong>${place.name}</strong> - ${place.type} - ${formatDistance(place.distance)}</p>
`).join('')}
</div>
`;
}
// 页面卸载时停止跟踪
window.addEventListener('beforeunload', function() {
tracker.stop();
});
</script>
</body>
</html>
// 推荐:先检查支持性
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(success, error);
} else {
// 提供降级方案
alert('您的浏览器不支持地理位置功能');
}
// 不推荐:直接调用
navigator.geolocation.getCurrentPosition(success, error);
// 推荐:完善的错误处理
navigator.geolocation.getCurrentPosition(
(position) => {
// 成功处理
updateLocation(position);
},
(error) => {
// 根据错误类型提供友好提示
switch (error.code) {
case error.PERMISSION_DENIED:
showTip('请在浏览器设置中允许位置访问');
break;
case error.POSITION_UNAVAILABLE:
showTip('无法获取位置,请检查定位服务是否开启');
break;
case error.TIMEOUT:
showTip('获取位置超时,请稍后重试');
break;
}
}
);
// 推荐:根据场景选择选项
// 高精度场景(如导航)
navigator.geolocation.getCurrentPosition(success, error, {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
});
// 低精度场景(如天气查询)
navigator.geolocation.getCurrentPosition(success, error, {
enableHighAccuracy: false,
timeout: 5000,
maximumAge: 300000 // 5分钟缓存
});
东巴文点评:高精度定位会消耗更多电量,应该根据实际需求选择合适的定位精度。
// 推荐:及时清理watchPosition
let watchId = null;
function startWatch() {
watchId = navigator.geolocation.watchPosition(callback);
}
function stopWatch() {
if (watchId !== null) {
navigator.geolocation.clearWatch(watchId);
watchId = null;
}
}
// 页面卸载时清理
window.addEventListener('beforeunload', stopWatch);
// 不推荐:不清理watchPosition
navigator.geolocation.watchPosition(callback);
// 忘记清理,导致持续消耗资源
问题1:地理位置API获取位置时,用户拒绝授权的错误码是?
A. 0 B. 1 C. 2 D. 3
<details> <summary>点击查看答案</summary>答案:B
东巴文解释:错误码1表示PERMISSION_DENIED,即用户拒绝了位置请求。
问题2:以下哪个选项用于设置获取位置的超时时间?
A. timeout
B. maximumAge
C. enableHighAccuracy
D. delay
答案:A
东巴文解释:timeout选项用于设置获取位置的超时时间(毫秒),maximumAge是缓存时间,enableHighAccuracy是高精度开关。
任务:创建一个位置分享应用,获取用户当前位置并显示坐标,可以复制位置信息分享给他人。
<details> <summary>点击查看参考答案</summary><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>位置分享</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.location-card {
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
margin: 20px 0;
}
.location-card h2 {
margin-top: 0;
}
.coords {
font-size: 24px;
font-weight: bold;
margin: 15px 0;
}
.coords small {
font-size: 14px;
opacity: 0.8;
}
button {
padding: 12px 24px;
margin: 5px;
border: none;
background: white;
color: #667eea;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
button:hover {
opacity: 0.9;
}
.share-link {
margin: 20px 0;
padding: 15px;
background: #f5f5f5;
border-radius: 5px;
word-break: break-all;
}
.tip {
padding: 10px;
background: #fff3cd;
border-radius: 5px;
margin: 10px 0;
}
</style>
</head>
<body>
<h1>位置分享</h1>
<button onclick="getLocation()">获取我的位置</button>
<div id="locationCard" class="location-card" style="display: none;">
<h2>我的位置</h2>
<div class="coords">
<div id="latDisplay"></div>
<div id="lonDisplay"></div>
</div>
<p id="timeDisplay"></p>
<button onclick="copyLocation()">复制位置信息</button>
<button onclick="shareLocation()">生成分享链接</button>
</div>
<div id="shareLink" class="share-link" style="display: none;"></div>
<div id="tip" class="tip" style="display: none;"></div>
<script>
let currentLocation = null;
function getLocation() {
if (!('geolocation' in navigator)) {
showTip('您的浏览器不支持地理位置功能', 'error');
return;
}
showTip('获取位置中...', 'info');
navigator.geolocation.getCurrentPosition(
(position) => {
currentLocation = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy,
timestamp: position.timestamp
};
displayLocation(currentLocation);
showTip('位置获取成功', 'success');
},
(error) => {
let message = '';
switch (error.code) {
case error.PERMISSION_DENIED:
message = '您拒绝了位置请求';
break;
case error.POSITION_UNAVAILABLE:
message = '位置信息不可用';
break;
case error.TIMEOUT:
message = '获取位置超时';
break;
default:
message = '获取位置失败';
}
showTip(message, 'error');
},
{
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
}
);
}
function displayLocation(location) {
document.getElementById('locationCard').style.display = 'block';
document.getElementById('latDisplay').innerHTML = `
${location.latitude.toFixed(6)}° <small>纬度</small>
`;
document.getElementById('lonDisplay').innerHTML = `
${location.longitude.toFixed(6)}° <small>经度</small>
`;
document.getElementById('timeDisplay').textContent = `
获取时间: ${new Date(location.timestamp).toLocaleString()}
`;
}
function copyLocation() {
if (!currentLocation) {
showTip('请先获取位置', 'error');
return;
}
const text = `我的位置: ${currentLocation.latitude.toFixed(6)}°N, ${currentLocation.longitude.toFixed(6)}°E`;
navigator.clipboard.writeText(text).then(() => {
showTip('位置信息已复制到剪贴板', 'success');
}).catch(() => {
showTip('复制失败,请手动复制', 'error');
});
}
function shareLocation() {
if (!currentLocation) {
showTip('请先获取位置', 'error');
return;
}
const url = `${window.location.origin}${window.location.pathname}?lat=${currentLocation.latitude}&lon=${currentLocation.longitude}`;
const shareLinkEl = document.getElementById('shareLink');
shareLinkEl.style.display = 'block';
shareLinkEl.innerHTML = `
<strong>分享链接:</strong><br>
<a href="${url}" target="_blank">${url}</a>
`;
showTip('分享链接已生成', 'success');
}
function showTip(message, type) {
const tipEl = document.getElementById('tip');
tipEl.style.display = 'block';
tipEl.textContent = message;
tipEl.style.background = type === 'success' ? '#d4edda' :
type === 'error' ? '#f8d7da' : '#fff3cd';
}
// 检查URL参数
const params = new URLSearchParams(window.location.search);
const lat = params.get('lat');
const lon = params.get('lon');
if (lat && lon) {
currentLocation = {
latitude: parseFloat(lat),
longitude: parseFloat(lon),
timestamp: Date.now()
};
displayLocation(currentLocation);
showTip('这是分享的位置', 'info');
}
</script>
</body>
</html>
</details>
东巴文(db-w.cn) - 让编程学习更有趣、更高效!