MapLibre GL JS第21课:绘制GeoJSON点图标、注记

📌 学习目标

  • 掌握绘制GeoJSON点的实现方法
  • 理解相关API的使用
  • 能够独立完成类似功能开发

🎯 核心概念

在地图上绘制GeoJSON点。

💻 完 整 代 码

代码示例

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

map.on('load', async () => {
    // 添加图像作为自定义标记图标
    const image = await map.loadImage('https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png');
    map.addImage('custom-marker', image.data);
    // 添加一个GeoJSON源,包含15个点的
    map.addSource('conferences', {
        'type': 'geojson',
        'data': {
            'type': 'FeatureCollection',
            'features': [
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [100.4933, 13.7551]
                    },
                    'properties': {'year': '2004'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [6.6523, 46.5535]
                    },
                    'properties': {'year': '2006'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-123.3596, 48.4268]
                    },
                    'properties': {'year': '2007'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [18.4264, -33.9224]
                    },
                    'properties': {'year': '2008'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [151.195, -33.8552]
                    },
                    'properties': {'year': '2009'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [2.1404, 41.3925]
                    },
                    'properties': {'year': '2010'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-104.8548, 39.7644]
                    },
                    'properties': {'year': '2011'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-1.1665, 52.9539]
                    },
                    'properties': {'year': '2013'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-122.6544, 45.5428]
                    },
                    'properties': {'year': '2014'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [126.974, 37.5651]
                    },
                    'properties': {'year': '2015'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [7.1112, 50.7255]
                    },
                    'properties': {'year': '2016'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-71.0314, 42.3539]
                    },
                    'properties': {'year': '2017'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [39.2794, -6.8173]
                    },
                    'properties': {'year': '2018'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [26.0961, 44.4379]
                    },
                    'properties': {'year': '2019'}
                },
                {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-114.0879, 51.0279]
                    },
                    'properties': {'year': '2020'}
                }
            ]
        }
    });

    // 添加一个符号图层(自定义标记)
    map.addLayer({
        'id': 'conferences',
        'type': 'symbol',
        'source': 'conferences',
        'layout': {
            'icon-image': 'custom-marker',
            // 从源的year属性中获取年份
            'text-field': ['get', 'year'],
            'text-font': [ 'Noto Sans Regular' ],
            'text-offset': [0, 1.25],
            'text-anchor': 'top'
        }
    });
});

代码示例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Draw GeoJSON points</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://demotiles.maplibre.org/style.json',
        center: [0, 0],
        zoom: 1
    });

    map.on('load', async () => {
        // 添加一个图像作为自定义标记
        const image = await map.loadImage('https://maplibre.org/maplibre-gl-js/docs/assets/osgeo-logo.png');
        map.addImage('custom-marker', image.data);
        // 添加一个包含15个点的GeoJSON源
        map.addSource('conferences', {
            'type': 'geojson',
            'data': {
                'type': 'FeatureCollection',
                'features': [
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [100.4933, 13.7551]
                        },
                        'properties': {'year': '2004'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [6.6523, 46.5535]
                        },
                        'properties': {'year': '2006'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-123.3596, 48.4268]
                        },
                        'properties': {'year': '2007'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [18.4264, -33.9224]
                        },
                        'properties': {'year': '2008'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [151.195, -33.8552]
                        },
                        'properties': {'year': '2009'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [2.1404, 41.3925]
                        },
                        'properties': {'year': '2010'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-104.8548, 39.7644]
                        },
                        'properties': {'year': '2011'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-1.1665, 52.9539]
                        },
                        'properties': {'year': '2013'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-122.6544, 45.5428]
                        },
                        'properties': {'year': '2014'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [126.974, 37.5651]
                        },
                        'properties': {'year': '2015'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [7.1112, 50.7255]
                        },
                        'properties': {'year': '2016'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-71.0314, 42.3539]
                        },
                        'properties': {'year': '2017'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [39.2794, -6.8173]
                        },
                        'properties': {'year': '2018'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [26.0961, 44.4379]
                        },
                        'properties': {'year': '2019'}
                    },
                    {
                        'type': 'Feature',
                        'geometry': {
                            'type': 'Point',
                            'coordinates': [-114.0879, 51.0279]
                        },
                        'properties': {'year': '2020'}
                    }
                ]
            }
        });

        // 添加一个符号图层
        map.addLayer({
            'id': 'conferences',
            'type': 'symbol',
            'source': 'conferences',
            'layout': {
                'icon-image': 'custom-marker',
                // 从源的"year"属性获取年份
                'text-field': ['get', 'year'],
                'text-font': [ 'Noto Sans Regular' ],
                'text-offset': [0, 1.25],
                'text-anchor': 'top'
            }
        });
    });
</script>
</body>
</html>

🔍 代码解析

1. 初始化地图

使用 new maplibregl.Map() 创建地图实例,配置基本参数。本示例中心点设置在全球视图(0, 0)。

2. 加载自定义图标

使用 map.loadImage() 异步加载远程图片,然后通过 map.addImage() 将其注册为自定义标记图标。

3. 添加GeoJSON数据源

通过 map.addSource() 添加包含15个点的GeoJSON数据源,每个点代表一个会议举办地点,包含年份属性。

4. 添加符号图层

使用 symbol 类型图层,配置:

  • icon-image: 使用自定义图标
  • text-field: 使用表达式 ['get', 'year'] 从属性获取年份
  • text-offset: 文字偏移,避免与图标重叠

⚙️ 参数说明

参数 类型 必填 说明
container string 地图容器ID
style string 地图样式URL
center number, number 初始中心点,默认0, 0
zoom number 初始缩放级别,默认0

symbol 图层 layout 属性

属性 类型 说明
icon-image string 使用的图标名称
text-field string/expression 显示的文字内容
text-font array 字体名称数组
text-offset number, number 文字偏移 x, y
text-anchor string 文字锚点位置

🎨 效果说明

运行代码后:

  • 地图显示全球视图(中心点 0, 0,缩放级别1)
  • 15个会议地点以自定义图标标记显示
  • 每个标记下方显示对应的年份文字
  • 用户可正常交互(拖拽、缩放、旋转)

💡 常 见 问 题

Q1: 如何使用自定义图标?

A: 先使用 map.loadImage() 加载图片,再用 map.addImage() 注册,最后在图层中引用:

javascript 复制代码
const image = await map.loadImage('path/to/icon.png');
map.addImage('custom-marker', image.data);

Q2: 如何从GeoJSON属性获取值?

A: 使用表达式 ['get', 'propertyName']

javascript 复制代码
'text-field': ['get', 'year']

Q3: 如何调整文字位置?

A: 使用 text-offset 属性调整偏移,使用 text-anchor 设置锚点。

📝 练习任务

  1. 基础练习:修改自定义图标的URL,更换为其他图标
  2. 进阶挑战:根据年份属性设置不同颜色的标记
  3. 拓展思考:如何添加点击事件显示详细信息?

🌟 最佳实践

  1. 图标预加载: 在地图加载完成后再加载图标,避免异步问题
  2. 表达式使用: 使用表达式动态获取属性值,提高代码灵活性
  3. 性能优化: 大量点数据时考虑使用聚类(cluster)功能
  4. 响应式设计: 根据缩放级别调整图标大小和文字显示

🔗 延伸阅读


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

相关推荐
LCG元2 小时前
现代Web应用高可用架构设计与性能调优实战
前端·wpf
丷丩2 小时前
MapLibre GL JS第20课:更新GeoJSON多边形
前端·javascript·gis·mapbox·maplibre gl js
swipe2 小时前
DeepAgents middleware 工程实战:把复杂 Agent 的运行时基建交给可组合中间件
前端·面试·llm
丷丩2 小时前
MapLibre GL JS第33课:渲染世界副本
javascript·gis·map·mapbox·maplibre gl js
前端环境观察室2 小时前
别让 Agent 浏览器任务无限重试:失败分类、RetryPolicy 与人工复核
前端
bonechips2 小时前
深入理解 JavaScript的历史包袱——变量提升(Hoisting)
javascript·深度学习
喵个咪2 小时前
Headless 后端实践:基于Go的企业级多栈管理系统脚手架
前端·vue.js·react.js
m0_738120722 小时前
渗透测试基础——黑盒测试下的Web漏洞挖掘与利用解析(一)
服务器·前端·网络·安全·php
丷丩3 小时前
MapLibre GL JS第31课:添加实时数据
javascript·gis·map·mapbox·maplibre gl js