MapLibre GL JS第31课:添加实时数据

📌 学习目标

  • 掌握添加实时数据的实现方法
  • 理解相关API的使用
  • 能够独立完成类似功能开发

🎯 核心概念

向地图添加实时数据流。

💻 完 整 代 码

代码示例

js 复制代码
const map = new maplibregl.Map({
    container: 'map',
    style: 'https://tiles.openfreemap.org/styles/bright',
    zoom: 2
});

map.on('load', () => {
    window.setInterval(() => {
        // 发送GET请求获取两个随机数0到1之间的数字,用于表示经度和纬度
        fetch('https://www.random.org/decimal-fractions/?num=2&dec=10&col=1&format=plain&rnd=new')
            .then(r => r.text())
            .then(text => {
                // 将两个随机数0到1之间的数字转换为度数
                const coordinates = text.split('\n').map(l => (Number(l) * 180) - 90);
                const json = {
                    type: 'Feature',
                    geometry: {
                        type: 'Point',
                        coordinates
                    }
                };
                // 更新地图上的无人机符号位置为新坐标
                map.getSource('drone').setData(json);

                // 飞行到无人机当前位置,速度为0.5秒
                map.flyTo({
                    center: json.geometry.coordinates,
                    speed: 0.5
                });
            });
    }, 2000);

    // 设置初始位置为(0,0)
    map.addSource('drone', {type: 'geojson', data: {
        type: 'Feature',
        geometry: {
            type: 'Point',
            coordinates: [0, 0]
        }
    }});
    map.addLayer({
        'id': 'drone',
        'type': 'symbol',
        'source': 'drone',
        'layout': {
            'icon-image': 'airport'
        }
    });
});

代码示例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Add live realtime data</title>
    <meta property="og:description" content="使用实时 GeoJSON 数据流在地图上移动符号。" />
    <meta property="og:created" content="2025-06-25" />
    <meta charset='utf-8'>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.css' />
    <script src='https://unpkg.com/maplibre-gl@5.24.0/dist/maplibre-gl.js'></script>
    <style>
        body { margin: 0; padding: 0; }
        html, body, #map { height: 100%; }
    </style>
</head>
<body>
<div id="map"></div>

<script>
    const map = new maplibregl.Map({
        container: 'map',
        style: 'https://tiles.openfreemap.org/styles/bright',
        zoom: 2
    });

    map.on('load', () => {
        window.setInterval(() => {
            // 发起GET请求获取两个随机数
            fetch('https://www.random.org/decimal-fractions/?num=2&dec=10&col=1&format=plain&rnd=new')
                .then(r => r.text())
                .then(text => {
                    // 将0到1之间的两个随机数转换为度数
                    const coordinates = text.split('\n').map(l => (Number(l) * 180) - 90);
                    const json = {
                        type: 'Feature',
                        geometry: {
                            type: 'Point',
                            coordinates
                        }
                    };
                    // 更新地图上无人机符号的位置
                    map.getSource('drone').setData(json);

                    // 让地图飞行到无人机的当前位置
                    map.flyTo({
                        center: json.geometry.coordinates,
                        speed: 0.5
                    });
                });
        }, 2000);

        // 设置初始位置为(0,0)。
        map.addSource('drone', {type: 'geojson', data: {
            type: 'Feature',
            geometry: {
                type: 'Point',
                coordinates: [0, 0]
            }
        }});
        map.addLayer({
            'id': 'drone',
            'type': 'symbol',
            'source': 'drone',
            'layout': {
                'icon-image': 'airport'
            }
        });
    });
</script>
</body>
</html>

🔍 代码解析

初始化地图

使用 new maplibregl.Map() 创建地图实例,配置基本参数。本示例的核心特色是实现实时数据更新,通过 setInterval 定时获取数据并更新地图上的符号位置。

javascript 复制代码
const map = new maplibregl.Map({
    container: 'map',
    style: 'https://tiles.openfreemap.org/styles/bright',
    zoom: 2
});

关键配置项

  • container: 地图容器的 DOM 元素 ID
  • style : 使用 OpenStreetMap 风格的样式 https://tiles.openfreemap.org/styles/bright
  • zoom: 初始缩放级别为 2,显示较大地理范围(适合全球视野)
  • setInterval: 每 2000ms(2秒)执行一次数据获取和更新
  • fetch : 从 https://www.random.org API 获取随机坐标数据
  • map.getSource('drone').setData(): 更新 GeoJSON 数据源,实现实时位置更新
  • map.flyTo(): 平滑飞行动画到新位置,提升用户体验

实时数据更新流程

  1. 定时触发 : 使用 setInterval 每 2 秒触发一次数据获取
  2. 数据请求 : 通过 fetch 请求外部 API 获取两个 0-1 范围的随机数
  3. 坐标转换: 将 0-1 范围的随机数转换为经纬度坐标(经度: -180 到 180,纬度: -90 到 90)
  4. 构造数据: 创建 GeoJSON Feature 对象,包含点几何坐标
  5. 更新数据源 : 使用 setData() 更新地图数据源
  6. 视图跟随 : 使用 flyTo() 让地图平滑移动到无人机新位置

数据源配置

javascript 复制代码
// 添加 GeoJSON 数据源,初始位置为 [0, 0]
map.addSource('drone', {
    type: 'geojson',
    data: {
        type: 'Feature',
        geometry: {
            type: 'Point',
            coordinates: [0, 0]
        }
    }
});

// 添加符号图层,使用机场图标标记无人机位置
map.addLayer({
    'id': 'drone',
    'type': 'symbol',
    'source': 'drone',
    'layout': {
        'icon-image': 'airport'  // 使用内置的机场图标
    }
});

⚙️ 参数说明

地图初始化参数

参数 类型 必填 默认值 说明
container string - 地图容器元素的 ID
style string/object - 地图样式 URL 或内联样式对象
zoom number 0 初始缩放级别,范围 0-22

setInterval 参数

参数 类型 必填 说明
callback function 定时执行的回调函数,每次执行时获取数据并更新地图
delay number 执行间隔(毫秒),本示例为 2000(2秒)

flyTo 配置项

参数 类型 必填 默认值 说明
center number, number - 目标中心点坐标,格式为 经度, 纬度
speed number 1.2 动画速度,范围 0.1-10,值越小越慢
easing function - 缓动函数,控制动画节奏
duration number - 动画持续时间(毫秒),与 speed 互斥

setData 方法

参数 类型 必填 说明
data object GeoJSON 数据对象,用于更新数据源

🎨 效果说明

运行代码后,页面显示一个交互式地图,地图上有一个表示无人机位置的动态符号:

  • 实时更新: 每 2 秒自动获取新的随机坐标并更新无人机位置
  • 平滑动画 : 使用 flyTo() 实现平滑的地图飞行动画,视角跟随无人机移动
  • 符号标记 : 使用内置的机场图标(airport)标记无人机位置,图标清晰可见
  • 初始位置 : 无人机从坐标原点 [0, 0](非洲附近)开始
  • 全球范围: 随机坐标覆盖全球(经度 -180 到 180,纬度 -90 到 90)
  • 交互功能: 支持鼠标拖拽、滚轮缩放、右键旋转等标准地图交互

无人机符号会在全球范围内随机移动,地图会自动平滑跟随无人机位置,呈现出追踪飞行目标的效果。

💡 常 见 问 题

Q1: 实时数据不更新怎么办?

A: 按以下步骤排查:

  1. 打开浏览器开发者工具(F12),检查 Console 面板是否有报错信息
  2. 在 Network 面板查看 random.org 的请求是否成功返回数据
  3. 确认 API 地址可正常访问(可直接在浏览器中打开测试)
  4. 检查 setInterval 的时间间隔是否合理设置
  5. 验证 GeoJSON 数据格式是否正确,确保 coordinates 数组有效

Q2: 如何调整更新频率?

A: 修改 setInterval 的第二个参数(毫秒数):

javascript 复制代码
// 每秒更新一次
window.setInterval(() => { /* ... */ }, 1000);

// 每5秒更新一次
window.setInterval(() => { /* ... */ }, 5000);

Q3: 如何处理数据加载失败?

A: 添加完善的错误处理逻辑:

javascript 复制代码
fetch(url)
    .then(r => {
        if (!r.ok) throw new Error('网络请求失败: ' + r.status);
        return r.text();
    })
    .then(text => { /* 处理数据 */ })
    .catch(error => {
        console.error('加载失败:', error);
        // 可选:显示用户友好的错误提示
        alert('数据更新失败,请稍后重试');
    });

Q4: 实时更新会影响性能吗?

A: 频繁更新可能影响性能。建议:

  • 根据实际需求设置合理的更新间隔(一般不小于 1000ms)
  • 使用 requestAnimationFrame 优化动画渲染
  • 在页面卸载或不需要更新时,使用 clearInterval 清除定时器
  • 避免在回调中执行复杂计算

Q5: 如何暂停和恢复实时更新?

A: 保存定时器 ID,通过 clearInterval 控制:

javascript 复制代码
let intervalId;

// 开始更新
intervalId = window.setInterval(() => { /* ... */ }, 2000);

// 暂停更新
clearInterval(intervalId);

📝 练习任务

  1. 基础练习 :修改 setInterval 的时间间隔为 5000ms(5秒),观察更新频率变化,并记录每次更新的时间戳
  2. 进阶挑战:添加一个按钮,点击后暂停/恢复实时更新,并显示当前状态(运行中/已暂停)
  3. 拓展练习:修改代码,让无人机沿固定路径移动(如正方形、圆形),而不是随机移动
  4. 拓展思考:如何实现基于真实 GPS 数据的实时追踪功能?需要哪些技术(WebSocket、GPS 设备、数据解析等)?

🌟 最佳实践

  1. 更新频率: 根据数据特性和用户体验设置合理的更新间隔(一般不小于 1000ms),避免过于频繁的更新导致性能问题
  2. 错误处理: 添加完善的错误处理机制,处理网络异常、数据格式错误和 API 限流
  3. 资源清理 : 在页面卸载(beforeunload)或不需要更新时,使用 clearInterval 清除定时器,避免内存泄漏
  4. 动画优化 : 使用 flyTo()easeTo() 实现平滑过渡,设置合适的速度参数提升用户体验
  5. 数据验证: 在更新地图前验证数据格式,确保 GeoJSON 结构正确
  6. 防抖节流: 对用户交互事件添加节流,避免在用户操作期间触发更新
  7. 降级方案: 网络不可用时提供离线数据或友好的提示信息
  8. 性能监控 : 使用 performance API 监控更新耗时,及时发现性能瓶颈

🔗 延伸阅读


本文是MapLibre GL JS实践课程系列的一部分,欢迎关注收藏

相关推荐
candyTong2 小时前
Claude Code 每次调用 API 时,上下文是怎么"拼"出来的?
javascript·后端·架构
小林ixn2 小时前
别再背“变量提升”了!深入编译执行,彻底搞懂 JavaScript 运行机制
javascript
用户852495071842 小时前
为什么变量能 未定义先使用?
javascript·程序员
Larcher2 小时前
JS 变量提升:代码没动,为什么执行顺序就变了?
前端·javascript·前端框架
就叫_这个吧3 小时前
JavaScript中常用事件示例展示附源码
开发语言·javascript·html
代码N年归来仍是新手村成员4 小时前
【AWS】Lambda 初识与服务部署
javascript·react.js·ai·node.js·云计算·ai编程·aws
云水一下4 小时前
JavaScript 从零基础到精通系列:流程控制、函数与作用域
前端·javascript
丷丩4 小时前
MapLibre GL JS第28课:PMTiles源和协议
javascript·gis·map·mapbox·maplibre gl js
之歆4 小时前
Day24_JavaScript正则表达式与性能优化实战:从入门到精通
javascript·性能优化·正则表达式