如何使用 Google Maps API 动态刷新标记位置

本文介绍如何通过定时轮询 json 数据并更新地图标记,实现车辆 gps 位置的实时刷新,重点解决数据未同步更新、旧标记残留等问题。

在基于 Google Maps JavaScript API 构建实时车辆追踪系统时,仅定时拉取新数据(fetchAPI)是不够的——若不将响应结果赋值回全局 data 变量,后续 setMarker() 函数始终渲染的是初始快照,导致地图标记“卡死”在首次加载的位置。

✅ 正确的数据流设计

首先,修正 fetchAPI:移除 return,改为直接更新共享变量 data:

let data = null; // 初始化为 null,避免未定义引用

const fetchAPI = async () => {
  try {
    const response = await fetch('./cradlepoint.json');
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const locations = await response.json();
    data = locations; // ✅ 关键:实时覆盖 data
  } catch (err) {
    console.error('Failed to fetch vehicle data:', err);
  }
};

接着,必须清理旧标记,否则每 5 秒都会叠加新标记,造成内存泄漏与视觉混乱。推荐使用 Marker 实例数组集中管理:

let markers = []; // 存储所有活跃标记的引用

function setMarker() {
  // ✅ 清除所有现有标记
  markers.forEach(marker => marker.setMap(null));
  markers = [];

  // ✅ 确保 data 已就绪且包含有效数据
  if (!data || !Array.isArray(data.data) || data.data.length === 0) return;

  const busicon = "https://maps.google.com/mapfiles/ms/icons/bus.png";
  const infoWindow = new google.maps.InfoWindow();

  data.data.forEach(vehicle => {
    const { id, latitude, longitude } = vehicle;

    // ✅ 创建新标记并保存引用
    const marker = new google.maps.Marker({
      map,
      position: { lat: latitude, lng: longitude },
      icon: busicon,
      title: id,
    });

    // ✅ 绑定点击事件(注意:infoWindow 复用,避免重复创建)
    marker.addListener('click', () => {
      infoWindow.close();
      infoWindow.setContent(`Vehicle ID: ${id}`);
      infoWindow.open(map, marker);
    });

    markers.push(marker); // 记录引用,便于后续清除
  });
}

? 启动逻辑优化(避免竞态与重复初始化)

  • 不要在 initMap() 内部直接调用 setInterval(setMarker, 5000),而应在地图初始化完成、且首次数据加载成功后启动;
  • 使用 Promise.all 确保地图库与初始数据并行加载:
async function initMap() {
  const { Map } = await google.maps.importLibrary("maps");
  const { Marker } = await google.maps.importLibrary("marker");

  map = new Map(document.getElementById("map"), {
    mapId: "YOUR_MAP_ID",
    center: { lat: 37.7749, lng: -122.4194 }, // 示例坐标
    zoom: 14,
  });

  // 首次加载数据并渲染
  await fetchAPI();
  setMarker();

  // ✅ 启动轮询:先拉取新数据,再刷新标记
  setInterval(async () => {
    await fetchAPI(); // 等待数据更新完成
    setMarker();     // 再渲染最新状态
  }, 5000);
}

// 页面加载完成后初始化
window.addEventListener('load', initMap);

⚠️ 注意事项与最佳实践

  • 错误处理不可省略:网络失败或 JSON 格式错误会导致 data 为 undefined,setMarker() 必须做防御性检查(如 if (!data?.data?.length) return;);
  • 性能考量:若车辆数量庞大(>100),考虑使用 MarkerClusterer 聚合标记;
  • 服务端优化建议:生产环境应改用 WebSocket 或 Server-Sent Events(SSE)替代轮询,降低延迟与服务器压力;
  • 图标与样式:busicon URL 应托管在同源或配置 CORS,否则标记可能不显示。

通过以上重构,你的地图将真正实现「数据驱动」的动态刷新——每次轮询后,旧标记被精准清除,新位置被实时呈现,为车队监控提供可靠可视化支持。