Skip to content

获取位置信息

Geolocation API 获取位置后返回 Position 对象,其中包含 Coordinates 坐标信息和时间戳。本节将详细解析 PositionCoordinates 的所有属性,以及 getCurrentPosition 方法的配置选项如何影响获取结果的精度和效率。

前置知识

阅读本节前,建议先了解:Geolocation API 基础

基础概念

Position 对象结构

getCurrentPositionwatchPosition 成功回调接收一个 Position 对象:

javascript
function onSuccess(position) {
  // Position 对象包含两个属性:
  console.log(position.coords);     // Coordinates 对象
  console.log(position.timestamp);  // 时间戳
}

Coordinates 属性一览

属性类型说明可用性
latitudedouble纬度(度)始终可用
longitudedouble经度(度)始终可用
accuracydouble精度半径(米)始终可用
altitude`doublenull`海拔(米)
altitudeAccuracy`doublenull`海拔精度(米)
heading`doublenull`行进方向(度,正北为 0)
speed`doublenull`速度(m/s)

语法与配置

Position 对象详解

javascript
function handlePosition(position) {
  const coords = position.coords;
  const timestamp = position.timestamp;

  console.log('===== Position 对象 =====');
  console.log('时间戳:', new Date(timestamp).toISOString());

  console.log('\n===== Coordinates 对象 =====');
  console.log('纬度:', coords.latitude);
  console.log('经度:', coords.longitude);
  console.log('精度:', coords.accuracy, '米');
  console.log('海拔:', coords.altitude);
  console.log('海拔精度:', coords.altitudeAccuracy);
  console.log('方向:', coords.heading);
  console.log('速度:', coords.speed);
}

配置选项详解

javascript
const options = {
  // 是否请求高精度(启用 GPS)
  enableHighAccuracy: true,

  // 请求超时时间(毫秒)
  // 超过此时间未获取到位置则触发 TIMEOUT 错误
  timeout: 15000,

  // 缓存位置的最大年龄(毫秒)
  // 如果缓存位置年龄小于此值,直接返回缓存
  maximumAge: 0
};

navigator.geolocation.getCurrentPosition(onSuccess, onError, options);
选项默认值推荐场景
enableHighAccuracy: truefalse导航、地图应用
enableHighAccuracy: false-附近推荐、天气服务
timeout: 5000Infinity需要快速响应
timeout: 30000-搜索 GPS 信号
maximumAge: 00需要最新位置
maximumAge: 300000-5 分钟内可接受旧位置
maximumAge: Infinity-任何缓存位置都可接受

详细说明

精度理解

accuracy 表示设备位置与真实位置的最大可能偏差半径:

javascript
function analyzeAccuracy(position) {
  const accuracy = position.coords.accuracy;

  if (accuracy < 10) {
    console.log('极高精度 - 可能使用了 GPS');
  } else if (accuracy < 100) {
    console.log('高精度 - 可能使用了 WiFi');
  } else if (accuracy < 1000) {
    console.log('中精度 - 可能使用了基站三角定位');
  } else {
    console.log('低精度 - 可能仅使用了 IP 定位');
  }

  // 根据精度决定是否使用位置
  if (accuracy > 1000) {
    console.warn('位置精度不足,不建议用于精确位置服务');
  }
}

不同精度的对比

定位方式典型精度耗时耗电适用场景
GPS3-10 米5-30 秒导航、户外运动
WiFi20-100 米1-5 秒城市定位、室内
基站100-1000 米1-3 秒粗略定位
IP 地址1-50 公里< 1 秒极低国家/城市级

maximumAge 缓存策略

javascript
// 每次都获取最新位置(最慢但最准)
const options1 = { maximumAge: 0 };

// 5 分钟内的缓存位置可接受(更快)
const options2 = { maximumAge: 300000 };

// 接受任何缓存位置(最快)
const options3 = { maximumAge: Infinity };

// 智能策略:根据场景选择
function getBestOptions(requireFresh = false) {
  return {
    enableHighAccuracy: requireFresh,
    timeout: requireFresh ? 15000 : 5000,
    maximumAge: requireFresh ? 0 : 300000
  };
}

经纬度实用工具

javascript
// 经纬度工具函数
const GeoUtils = {
  // 计算两点之间的距离(Haversine 公式)
  distance(lat1, lon1, lat2, lon2) {
    const R = 6371000; // 地球半径(米)
    const dLat = (lat2 - lat1) * Math.PI / 180;
    const dLon = (lon2 - lon1) * Math.PI / 180;
    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
              Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
              Math.sin(dLon / 2) * Math.sin(dLon / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
  },

  // 格式化经纬度
  formatLat(lat) {
    const direction = lat >= 0 ? 'N' : 'S';
    return `${Math.abs(lat).toFixed(6)}° ${direction}`;
  },

  formatLng(lng) {
    const direction = lng >= 0 ? 'E' : 'W';
    return `${Math.abs(lng).toFixed(6)}° ${direction}`;
  },

  // 判断是否在中国境内(粗略判断)
  isChina(lat, lng) {
    return lat >= 3.86 && lat <= 53.55 && lng >= 73.66 && lng <= 135.05;
  },

  // 生成地图链接
  getMapLink(lat, lng) {
    return `https://www.google.com/maps?q=${lat},${lng}`;
  },

  // 生成静态地图图片 URL
  getStaticMapUrl(lat, lng, zoom = 14, size = '400x300') {
    return `https://maps.googleapis.com/maps/api/staticmap?center=${lat},${lng}&zoom=${zoom}&size=${size}&markers=${lat},${lng}`;
  }
};

// 使用示例
const distance = GeoUtils.distance(39.9042, 116.4074, 31.2304, 121.4737);
console.log(`北京到上海: ${(distance / 1000).toFixed(1)} 公里`);

实战示例

详细位置信息展示

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>位置信息详情</title>
  <style>
    body { font-family: -apple-system, sans-serif; padding: 20px; background: #f5f7fa; }

    .info-card {
      max-width: 500px;
      margin: 0 auto;
      background: white;
      border-radius: 12px;
      padding: 24px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }

    h2 { text-align: center; color: #1a1a2e; margin-bottom: 20px; }

    .coords-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 12px;
      margin-top: 16px;
    }

    .coord-item {
      padding: 12px;
      background: #f8f9fa;
      border-radius: 8px;
      text-align: center;
    }

    .coord-label { font-size: 12px; color: #888; margin-bottom: 4px; }
    .coord-value { font-size: 18px; font-weight: 700; color: #333; font-family: monospace; }
    .coord-unit { font-size: 12px; color: #aaa; }

    .full-width { grid-column: 1 / -1; }

    .accuracy-bar {
      margin-top: 16px;
      height: 8px;
      background: #e0e0e0;
      border-radius: 4px;
      overflow: hidden;
    }

    .accuracy-fill {
      height: 100%;
      border-radius: 4px;
      transition: width 0.5s;
    }

    .accuracy-fill.high { background: #4CAF50; }
    .accuracy-fill.medium { background: #FF9800; }
    .accuracy-fill.low { background: #F44336; }

    .actions {
      margin-top: 20px;
      display: flex;
      gap: 10px;
    }

    .btn {
      flex: 1;
      padding: 12px;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 600;
    }

    .btn-primary { background: #1a73e8; color: white; }
    .btn-secondary { background: #e0e0e0; color: #333; }
    .btn-secondary:disabled { opacity: 0.5; cursor: not-allowed; }

    .precision-selector {
      margin-top: 16px;
      display: flex;
      gap: 8px;
    }

    .precision-btn {
      flex: 1;
      padding: 8px;
      border: 2px solid #e0e0e0;
      background: white;
      border-radius: 6px;
      cursor: pointer;
      font-size: 13px;
      text-align: center;
    }

    .precision-btn.active {
      border-color: #1a73e8;
      background: #e3f2fd;
      color: #1a73e8;
    }
  </style>
</head>
<body>
  <div class="info-card">
    <h2>位置信息详情</h2>

    <div class="precision-selector">
      <div class="precision-btn" onclick="getLocation(false)">低精度</div>
      <div class="precision-btn active" onclick="getLocation(true)">高精度</div>
    </div>

    <div class="coords-grid">
      <div class="coord-item">
        <div class="coord-label">纬度 Latitude</div>
        <div class="coord-value" id="lat">--</div>
      </div>
      <div class="coord-item">
        <div class="coord-label">经度 Longitude</div>
        <div class="coord-value" id="lng">--</div>
      </div>
      <div class="coord-item full-width">
        <div class="coord-label">精度 Accuracy</div>
        <div class="coord-value" id="accuracy">--</div>
        <div class="accuracy-bar"><div class="accuracy-fill" id="accuracyBar"></div></div>
      </div>
      <div class="coord-item">
        <div class="coord-label">海拔 Altitude</div>
        <div class="coord-value" id="altitude">--</div>
      </div>
      <div class="coord-item">
        <div class="coord-label">速度 Speed</div>
        <div class="coord-value" id="speed">--</div>
      </div>
      <div class="coord-item">
        <div class="coord-label">方向 Heading</div>
        <div class="coord-value" id="heading">--</div>
      </div>
      <div class="coord-item">
        <div class="coord-label">获取时间</div>
        <div class="coord-value" id="timestamp" style="font-size:14px;">--</div>
      </div>
    </div>

    <div class="actions">
      <button class="btn btn-primary" onclick="refreshLocation()">刷新位置</button>
      <button class="btn btn-secondary" id="mapBtn" onclick="openMap()" disabled>打开地图</button>
    </div>
  </div>

  <script>
    let currentLat = null, currentLng = null;

    function getLocation(highAccuracy) {
      // 更新精度选择器样式
      document.querySelectorAll('.precision-btn').forEach((btn, i) => {
        btn.classList.toggle('active', (i === 0 && !highAccuracy) || (i === 1 && highAccuracy));
      });

      navigator.geolocation.getCurrentPosition(
        (position) => displayPosition(position),
        (error) => console.error('获取失败:', error),
        {
          enableHighAccuracy: highAccuracy,
          timeout: 15000,
          maximumAge: 0
        }
      );
    }

    function displayPosition(position) {
      const c = position.coords;
      currentLat = c.latitude;
      currentLng = c.longitude;

      document.getElementById('lat').textContent = c.latitude.toFixed(6);
      document.getElementById('lng').textContent = c.longitude.toFixed(6);
      document.getElementById('accuracy').textContent = c.accuracy.toFixed(0) + ' 米';
      document.getElementById('altitude').textContent = c.altitude !== null ? c.altitude.toFixed(0) + ' 米' : 'N/A';
      document.getElementById('speed').textContent = c.speed !== null ? c.speed.toFixed(1) + ' m/s' : 'N/A';
      document.getElementById('heading').textContent = c.heading !== null ? c.heading.toFixed(0) + '°' : 'N/A';
      document.getElementById('timestamp').textContent = new Date(position.timestamp).toLocaleTimeString();

      // 精度条
      const bar = document.getElementById('accuracyBar');
      const pct = Math.min(100, Math.max(5, 100 - (c.accuracy / 10)));
      bar.style.width = pct + '%';
      bar.className = `accuracy-fill ${c.accuracy < 50 ? 'high' : c.accuracy < 500 ? 'medium' : 'low'}`;

      // 启用地图按钮
      document.getElementById('mapBtn').disabled = false;
    }

    function refreshLocation() {
      getLocation(true);
    }

    function openMap() {
      if (currentLat && currentLng) {
        window.open(`https://www.google.com/maps?q=${currentLat},${currentLng}`);
      }
    }

    // 初始加载
    getLocation(true);
  </script>
</body>
</html>

注意事项

  1. altitudealtitudeAccuracyheadingspeed 可能为 null,使用前必须检查
  2. accuracy 始终存在,应作为位置数据可信度的参考
  3. 在室内环境中,GPS 信号弱,精度会显著降低

最佳实践

  1. 根据场景选择合适的精度:不需要米级精度时不开启高精度
  2. 设置合理的 timeout 避免用户长时间等待
  3. 利用 maximumAge 减少重复定位,节省电量和流量
  4. 始终检查 accuracy 值,过滤低精度结果
  5. null 属性做好降级处理

下一节

继续学习:持续追踪

参考链接