MapLibre GL JS第44课:生成并添加缺失图标

📌 学习目标

  • 掌握生成并添加缺失图标的实现方法
  • 理解相关API的使用
  • 能够独立完成类似功能开发

🎯 核心概念

生成并添加缺失的图标到地图,即样式中配置的图标缺失时,用map的styleimagemissing事件监听并重新定义图标。

💻 完 整 代 码

代码示例

js 复制代码
const map = new maplibregl.Map({
    container: 'map',
    style: 'https://demotiles.maplibre.org/style.json',
});

map.on('styleimagemissing', (e) => {
    const id = e.id; // 缺失图像的ID

    // 检查这个缺失的图标是否是此函数可以生成的
    const prefix = 'square-rgb-';
    if (id.indexOf(prefix) !== 0) return;

    // 从ID中提取颜色
    const rgb = id.replace(prefix, '').split(',').map(Number);

    const width = 64; // 图像将是64x64像素
    const bytesPerPixel = 4; // 每个像素由4个字节表示:红、绿、蓝和透明度
    const data = new Uint8Array(width * width * bytesPerPixel);

    for (let x = 0; x < width; x++) {
        for (let y = 0; y < width; y++) {
            const offset = (y * width + x) * bytesPerPixel;
            data[offset + 0] = rgb[0]; // 红色
            data[offset + 1] = rgb[1]; // 绿色
            data[offset + 2] = rgb[2]; // 蓝色
            data[offset + 3] = 255; // 透明度
        }
    }

    map.addImage(id, {width, height: width, data});
});

map.on('load', () => {
    map.addSource('points', {
        'type': 'geojson',
        'data': {
            'type': 'FeatureCollection',
            'features': [
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [0, 0]
                    },
                    'properties': {
                        'color': '255,0,0'
                    }
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [50, 0]
                    },
                    'properties': {
                        'color': '255,209,28'
                    }
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-50, 0]
                    },
                    'properties': {
                        'color': '242,127,32'
                    }
                }
            ]
        }
    });

    map.addLayer({
        'id': 'points',
        'type': 'symbol',
        'source': 'points',
        'layout': {
            'icon-image': ['concat', 'square-rgb-', ['get', 'color']]
        }
    });
});

代码示例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Generate and add a missing icon to the map</title>
    <meta property="og:description" content="在运行时动态生成缺失的图标并将其添加到地图。" />
    <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://demotiles.maplibre.org/style.json',
    });

    map.on('styleimagemissing', (e) => {
        const id = e.id; // 缺失图像的ID

        // 检查这个缺失的图标是否是此函数可以生成的
        const prefix = 'square-rgb-';
        if (id.indexOf(prefix) !== 0) return;

        // 从ID中提取颜色
        const rgb = id.replace(prefix, '').split(',').map(Number);

        const width = 64; // 图像将是64像素的正方形
        const bytesPerPixel = 4; // 每个像素由4个字节表示:红、绿、蓝和透明度。
        const data = new Uint8Array(width * width * bytesPerPixel);

        for (let x = 0; x < width; x++) {
            for (let y = 0; y < width; y++) {
                const offset = (y * width + x) * bytesPerPixel;
                data[offset + 0] = rgb[0]; // 红色
                data[offset + 1] = rgb[1]; // 绿色
                data[offset + 2] = rgb[2]; // 蓝色
                data[offset + 3] = 255; // 透明度
            }
        }

        map.addImage(id, {width, height: width, data});
    });

    map.on('load', () => {
        map.addSource('points', {
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [0, 0]
                        },
                        'properties': {
                            'color': '255,0,0'
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [50, 0]
                        },
                        'properties': {
                            'color': '255,209,28'
                        }
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-50, 0]
                        },
                        'properties': {
                            'color': '242,127,32'
                        }
                    }
                ]
            }
        });

        map.addLayer({
            'id': 'points',
            'type': 'symbol',
            'source': 'points',
            'layout': {
                'icon-image': ['concat', 'square-rgb-', ['get', 'color']]
            }
        });
    });
</script>
</body>
</html>

🔍 代码解析

初始化地图

使用 new maplibregl.Map() 创建地图实例,配置基本参数。本示例的核心特色是展示如何利用 styleimagemissing 事件动态生成缺失的图标。

关键配置项

  • container: 地图容器的 DOM 元素 ID
  • style : 使用 MapLibre 官方样式 https://demotiles.maplibre.org/style.json

styleimagemissing 事件处理

javascript 复制代码
map.on('styleimagemissing', (e) => {
    const id = e.id; // 缺失图像的 ID
    
    // 检查是否是可生成的图标
    const prefix = 'square-rgb-';
    if (id.indexOf(prefix) !== 0) return;
    
    // 从 ID 中提取颜色值
    const rgb = id.replace(prefix, '').split(',').map(Number);
    
    // 生成 64x64 像素的纯色图像
    const width = 64;
    const bytesPerPixel = 4;
    const data = new Uint8Array(width * width * bytesPerPixel);
    
    for (let x = 0; x < width; x++) {
        for (let y = 0; y < width; y++) {
            const offset = (y * width + x) * bytesPerPixel;
            data[offset + 0] = rgb[0]; // 红色
            data[offset + 1] = rgb[1]; // 绿色
            data[offset + 2] = rgb[2]; // 蓝色
            data[offset + 3] = 255;   // 透明度
        }
    }
    
    map.addImage(id, {width, height: width, data});
});

动态图标引用

javascript 复制代码
map.addLayer({
    'id': 'points',
    'type': 'symbol',
    'source': 'points',
    'layout': {
        'icon-image': ['concat', 'square-rgb-', ['get', 'color']]
    }
});

⚙️ 参数说明

参数 类型 必填 默认值 说明
container string - 地图容器元素的 ID
style string/object - 地图样式 URL 或内联样式对象

styleimagemissing 事件

属性 类型 说明
id string 缺失图像的 ID

图标 ID 格式

格式 示例 说明
square-rgb-r,g,b square-rgb-255,0,0 红色方块图标

🎨 效果说明

运行代码后,地图上会显示三个彩色方块图标:

  • 红色方块 : 坐标 [0, 0],颜色 255,0,0(纯红色)
  • 黄色方块 : 坐标 [50, 0],颜色 255,209,28(金黄色)
  • 橙色方块 : 坐标 [-50, 0],颜色 242,127,32(橙色)

工作流程:

  1. 图层引用图标 square-rgb-{color}
  2. 地图发现图标不存在,触发 styleimagemissing 事件
  3. 事件处理器解析颜色值,生成对应颜色的方块图像
  4. 将生成的图像添加到地图,图层正常显示

💡 常 见 问 题

Q1: styleimagemissing 事件何时触发?

A: 当地图尝试使用一个未注册的图像时触发,通常在渲染符号图层时。

Q2: 如何避免重复生成?

A: 事件只会为每个缺失的图像 ID 触发一次,MapLibre 会缓存已注册的图像。

Q3: 可以生成复杂图像吗?

A: 可以。除了纯色方块,还可以生成渐变、图案等任意像素数据。

Q4: 性能影响如何?

A: 生成图像会占用 CPU 资源,但只在缺失时生成一次,后续会缓存。

📝 练习任务

  1. 基础练习:修改颜色值,创建不同颜色的方块
  2. 进阶挑战:生成渐变色方块,而不是纯色方块
  3. 拓展思考:如何生成带边框的方块图标?

🌟 最佳实践

  1. 图标命名规范: 使用统一的前缀标识可生成的图标
  2. 参数验证: 在生成前验证 ID 格式和颜色值范围
  3. 性能优化: 限制生成图像的最大尺寸
  4. 错误处理: 添加颜色值越界的处理
  5. 缓存策略: 考虑缓存常用颜色的图标
  6. 测试覆盖: 测试各种颜色值和边界情况

🔗 延伸阅读


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

相关推荐
snow@li1 小时前
前端:下拉框里边的数据叫啥 / 怎么称呼
前端
禁默1 小时前
数字人切入,我用魔珐星云搭建政务大厅咨询数字人,低成本落地便民接待
前端·人工智能·政务
小二·1 小时前
React 18 新特性与 Hooks 进阶实战
前端·react.js·前端框架
GoogleDocs1 小时前
ProbeKit Lite 是什么?一款用于检测 AI 中转站真实度的工具
javascript·人工智障
li-xun1 小时前
2026年6月10日博客精选
javascript·人工智能·ui
Shadow(⊙o⊙)1 小时前
QT常用控件3.0,font字体设置,toolTip提示,focusPolicy焦点定位原则,中型控件StyleSheet样式表。
服务器·开发语言·前端·c++·qt
六月的可乐2 小时前
【干货】小程序虚拟瀑布流探索小结
前端·react.js·小程序
techdashen2 小时前
Rust 项目管理动态 — 2026 年 3 月
前端·人工智能·rust
Ajie'Blog2 小时前
2026年AI安全与治理:从幻觉到系统性欺骗的攻防之战
javascript·人工智能·安全·rpc·json·rag