关于 maplibre-source-wiring
面向 MapLibre 开发者的地图数据源与样式连接指南,解释瓦片类型、图层结构和资源配置,并提供空白地图等问题的排查方法。
主要功能
- 01选择 TileJSON URL 或瓦片模板
- 02核对矢量瓦片 source-layer
- 03控制图层绘制顺序
- 04配置字形与 sprite 资源
- 05排查栅格瓦片与要素状态问题
- 06检查跨域请求配置
适用场景
- 01排查地图空白或图层未绘制
- 02将自定义数据叠加到样式中
- 03修复底图比例或图层顺序问题
- 04诊断图标、交互高亮或瓦片加载异常
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ maplibre/ maplibre-agent-skills --skill maplibre-source-wiring安装后在 Claude Code 中输入/maplibre-source-wiring确认已加载。
详细介绍
MapLibre 源连接
你有一个瓦片 URL 或数据文件,以及一个样式,但地图显示为空白、比例尺不正确,或绘制顺序不正确。本技能介绍如何正确地将源连接到样式,以及那些从外部看起来完全相同的故障模式。
何时使用本技能
源已配置,但地图出现了错误,而且没有报错。以下是你在尚未确定原因时可能观察到的症状:
- 地图为空白,或你添加的图层没有任何内容,且控制台中没有报错
- 自定义样式在瓦片源上无法显示,但该瓦片源配合提供方自己的样式可以正常工作
- 底图模糊,或其比例尺与上方绘制的所有内容不同
- 你添加的数据遮住了街道名称
- 图标缺失,但地图的其余部分显示正常
- 悬停或点击高亮完全不起作用,且没有引发任何报错
- 你有一个瓦片端点,但不确定它需要哪种源类型,或应该将
url指向其 TileJSON,而不是手动编写tiles
不属于本技能范围。 字体显示错误、脚本缺失或自行托管字形 PBF——请参阅 maplibre-fonts-glyphs。决定你正在设计的样式的颜色、类型和绘制顺序——请参阅 maplibre-cartography。决定数据集是否应该使用 GeoJSON 或矢量瓦片——请参阅 maplibre-tile-sources。
引用瓦片:url 与 tiles
瓦片通过缩放级别(Z)、列(X)和行(Y)寻址——这是栅格和矢量瓦片源通用的方案(更多信息请参阅 OpenStreetMap wiki)。在 MapLibre 源中,你可以通过 tiles URL 模板直接引用瓦片,也可以通过指向 TileJSON 端点的 url 来引用。
如果有 TileJSON 端点,优先使用 url。 MapLibre 会获取该文档,并自动读取瓦片 URL 模板、缩放范围、边界、署名信息,以及(对于矢量瓦片)可用的源图层。Martin 和 tileserver-gl 等瓦片服务会为其提供的每个瓦片集生成 TileJSON 端点,许多托管服务提供商也会这样做。
如果没有 TileJSON 端点——例如,原始栅格瓦片服务直接提供 URL 模板——请使用 tiles 数组,并在源定义中自行指定元数据(minzoom、maxzoom、attribution)。
指向 TileJSON 端点的 url:
{
"type": "vector",
"url": "https://example.com/tiles.json"
}
tiles 数组:
{
"type": "vector",
"tiles": ["https://example.com/tiles/{z}/{x}/{y}.pbf"],
"minzoom": 0,
"maxzoom": 14
}
{z}、{x} 和 {y} 是占位符,MapLibre 会在发出请求前将它们替换为每个瓦片的缩放级别、列和行。将仍含有花括号的模板粘贴到浏览器中,不会请求到任何真实瓦片,因此其状态码无法说明服务器的情况。要手动检查服务器,请请求位于源缩放范围内的真实瓦片,例如 /0/0/0.pbf。
手动配置 tiles 的代价是,除非你提供缩放范围,否则 MapLibre 不会知道该范围,并会默认使用 maxzoom: 22——这会请求瓦片集不包含的缩放级别,而这些请求返回的内容为空。因此,只要存在 TileJSON 端点,默认建议使用 url。(对于 PMTiles,同样适用这一规则,而且后果更严重——请参阅 maplibre-pmtiles-patterns。)
什么都没有渲染:source-layer 与瓦片架构
自定义样式在正常工作的瓦片源上什么都不渲染,最常见的原因是 source-layer 不匹配。
矢量瓦片源包含多个具名图层,例如 transportation、water、landuse 等;每个从中绘制内容的样式图层都必须准确指定其中一个名称:
{
"id": "roads",
"type": "line",
"source": "basemap",
"source-layer": "transportation"
}
矢量源必须设置 source-layer,并且其值必须与瓦片架构完全一致。拼写错误或使用了其他架构中的名称都不会产生错误或输出——该图层只是什么都不绘制。
在 TileJSON 中查找实际名称。 对于矢量源,TileJSON 的 vector_layers 字段会列出每个可用的 source-layer、其属性字段和缩放级别范围。如果存在该字段,它就是权威参考——TileJSON 3.0 要求矢量瓦片集提供该字段,但 2.x 文档早于这一要求,且不同提供商在是否提供该字段方面并不一致。没有该字段并不意味着没有架构:请根据名称(见下文)识别架构,并参照其已发布的图层列表。如果使用提供商预先构建的样式 URL,架构已经为你匹配好了;只有在自行编写图层时才会出现不匹配。
预定义瓦片架构
构建自定义样式时,你需要了解瓦片架构——也就是源图层名称及其属性。常见架构:
- OpenMapTiles — 应用最广泛的架构,基于 OpenStreetMap 数据。内容丰富、细节充足,包含
transportation、water、landuse、poi等源图层。社区样式生态最为庞大。 - Shortbread — 一种旨在精简且可互操作的开放标准,不依赖任何单一供应商。其结构比 OpenMapTiles 更简单;如果你要从头构建样式,它是一个简洁的基础。
- Protomaps — 专为 Protomaps PMTiles 底图生态打造。结构扁平、简单,包含
earth、water、roads、places等源图层;针对无服务器交付进行了优化。已发布的图层列表:docs.protomaps.com/basemaps/layers。
每种架构都会以文档形式发布其图层列表(参见参考资料)。它们都没有携带机器可读的版本标记,因此要检查架构就必须重新阅读页面;没有任何工具可以轮询其变更。
使用 Planetiler 或 tippecanoe 生成瓦片时,输出会将 TileJSON 元数据嵌入 MBTiles 或 PMTiles 文件。Martin 等瓦片服务会读取此元数据,并自动将其作为 TileJSON 端点公开。
图层顺序:绘制在标签下方
图层按照其在样式中出现的顺序从下到上绘制。调用 map.addLayer() 且不传入第二个参数时,会将图层追加到所有内容上方,包括街道名称——因此,以这种方式添加的数据叠加层会遮住底图标签。解决方法是将第一个 symbol 图层的 ID 作为 addLayer 的第二个参数传入,并通过程序查找,而不是硬编码(road-label 等名称因架构而异,更换底图时就会失效)。出于相同原因,在矢量图层之后添加栅格图层也会遮住它们。
如需查看完整的注入模式,以及从头构建样式时的标准图层顺序,请参阅 maplibre-cartography。
标签和图标缺失:glyphs 和 sprite
两个样式根属性提供符号图层绘制时使用的资源:
glyphs— 字体栈的 URL 模板:"glyphs": "https://example.com/fonts/{fontstack}/{range}.pbf"sprite— 精灵图和元数据的基础 URL,同时提供.json和.png:"sprite": "https://example.com/sprites/basic"
托管服务提供商提供的预构建样式 URL 已包含这些资源。构建自定义样式或自行托管时,必须提供它们。
两者缺失时的表现不同,而这种差异能让你从症状反向找出原因。 从 GL JS 5.11.0 开始,缺少 glyphs _不会_导致文字消失:MapLibre 会改用本地系统字体渲染文字,因此症状是字体不对,而不是文字缺失。如果标签仍在但样式不对,应检查 glyphs;如果标签完全不见,则不应优先检查它。缺少 sprite 时,每个缺失图标都会记录一条控制台警告(Image "<id>" could not be loaded),且不会渲染任何图标;每个图标对应的 map.hasImage(id) 都会返回 false。无法加载 sprite URL 也会记录错误。
有关回退机制及其局限(仅适用于 GL JS;Native 仍需要提供 PBF)、字体栈、自行托管与生成,以及脚本支持情况,请参阅 maplibre-fonts-glyphs。
栅格源:tileSize 和命名陷阱
两个栅格专属的常见问题:接入时不会报错,但渲染结果不正确,或与预期不符:
tileSize 默认为 512;经典 OSM 瓦片为 256。 tile.openstreetmap.org 和其他经典 Slippy 地图瓦片服务器提供 256px 瓦片,但 MapLibre 的栅格 tileSize 默认为 512px。对于 256px 的源,省略 tileSize: 256 不会报错——但会以错误的实际缩放级别渲染,导致影像和标签看起来偏差一个缩放级别,且没有任何错误或警告:
{
"type": "raster",
"tiles": ["https://tile.openstreetmap.org/{z}/{x}/{y}.png"],
"tileSize": 256
}
栅格端点的名称并不能说明其类型。 名为“hillshade”“terrain”或类似名称的端点不一定是 raster-dem 源。raster-dem 要求瓦片使用 PNG 或 WebP,通过像素打包的 RGB 编码高程(Mapbox 或 Terrarium 编码);如果 TileJSON 中存在 encoding 字段,该字段会指明所用编码。如果瓦片格式无法保存这种编码(例如 JPEG),且没有 encoding 字段,那么无论端点名称是什么,它都只是普通的 raster 影像——名为“hillshade”的端点通常提供的是预渲染的晕渲图像,而不是供 MapLibre 在客户端进行晕渲的原始高程数据。在将名称作为 raster-dem 的依据之前,请检查瓦片格式和任何 encoding 字段。
要素状态:数据源需要有 id,通常通过 promoteId 来设置
setFeatureState 和 feature-state 表达式键入的是要素的 id——它是要素对象的顶层成员,而不是 properties 内的值。如果数据源中的要素没有 id,setFeatureState 就无从关联,因此会静默失败:控制台没有错误,绘制表达式不会触发,getFeatureState 返回空值。
将 promoteId 设置为你已有的键值属性。 它会告知 MapLibre 使用该属性作为要素 id。只要标识符位于 properties 中,这就是正确的做法:
map.addSource('parcels', {
type: 'geojson',
data: parcels,
promoteId: 'parcel_id'
});
map.setFeatureState({ source: 'parcels', id: 'APN-1234' }, { hover: true });
对于矢量数据源,promoteId 可以是应用于所有 source-layer 的属性名称,也可以是 {<sourceLayer>: <propertyName>} 形式的对象。[9]
如果存在业务键,就不要使用 generateId 或手写 id。 这两种方法看似能解决问题,但都不适用于这种情况:
generateId: true会按features数组中的索引分配 id,并覆盖任何已有值。[9] 如果setData重新排序、筛选或追加数据,id 就不稳定,因此更新后状态会静默地关联到错误的要素。- 只有整数才适合写入顶层
id:如果没有promoteId,要素的 id“必须是整数,或可转换为整数的字符串”。[9] 像APN-1234这样的键值会被接受,但之后永远匹配不上。使用promoteId时,值可以是任意原始类型。
CORS
如果你的瓦片、字形或精灵图位于不同源,服务器必须发送 CORS 标头(Access-Control-Allow-Origin)。否则浏览器会阻止请求,地图将一片空白或缺少标签。
托管服务提供商会为你处理 CORS。对于可自托管的服务器或静态存储,请在服务器或 CDN 上配置 CORS。使用范围请求的数据源(PMTiles)还需要 Access-Control-Allow-Headers: Range。
诊断空白地图
按以下列表逐项排查——这些症状往往高度重叠:
| 症状 | 首先检查 |
|---|---|
| 完全没有内容,网络面板显示请求失败 | CORS 标头;瓦片 URL 本身(网络面板中的 404) |
| 完全没有内容,网络面板显示 200 | source-layer 名称是否与 TileJSON 的 vector_layers 一致 |
| 瓦片出现后,放大时消失 | 手动配置的 tiles 数据源缺少缩放级别范围;改用 url |
| 瓦片已渲染,文字字体错误 | 缺少 glyphs,或其 URL 加载失败 |
| 瓦片已渲染,完全没有文字 | 任何符号图层都没有 text-field;或 GL JS 版本早于 5.11 |
| 瓦片已渲染,没有图标 | 样式根节点缺少 sprite |
| 数据已绘制,但遮住了标签 | 图层顺序有误;插入到第一个 symbol 图层之前 |
| 矢量数据源没有绘制内容,栅格正常 | 完全缺少 source-layer——矢量数据源必须设置此项 |
| 栅格瓦片渲染的缩放级别偏差一级 | 经典 256px 数据源缺少 tileSize: 256(默认值为 512) |
setFeatureState 无效,且没有错误 | 要素没有 id;将 promoteId 设置为标识属性 |
相关技能
- maplibre-tile-sources — 为数据集选择 GeoJSON 还是瓦片。
- maplibre-pmtiles-patterns — 注册
pmtiles://协议并设置 PMTiles 专用数据源。 - maplibre-fonts-glyphs — 字体栈、字形端点和文字脚本覆盖范围。
- maplibre-sprites-icons —
spriteURL 格式、加载多个图集、自托管 sprite 资源以及路线盾牌。 - maplibre-cartography — 完整的图层注入模式,以及自定义样式的规范图层顺序。
参考资料
- MapLibre 样式规范 — maplibre.org/maplibre-style-spec/
- TileJSON 规范 — GitHub 上的规范仓库;3.0 版(2021 年 8 月)要求矢量瓦片集必须包含
vector_layers - Slippy 地图瓦片命名(Z/X/Y 方案) — OpenStreetMap wiki
- OpenMapTiles 架构 — openmaptiles.org/schema/
- Shortbread 瓦片架构 — shortbread-tiles.org
- Protomaps 底图图层 — docs.protomaps.com/basemaps/layers
- Martin 瓦片服务(TileJSON 端点)— maplibre.org/martin/
- MapLibre GL JS 文档 — maplibre.org/maplibre-gl-js/docs/
promoteId、generateId和feature-state— 样式规范:数据源 和 表达式
此技能内容为某一时点的快照。 如果主要来源与本文内容相矛盾,应以主要来源为准:上述参考资料、MapLibre 当前文档,或运行 MapLibre 时实际观察到的行为。请遵循主要来源,然后报告此处的不一致,并注明来源和你的 MapLibre 版本:编辑自己安装的副本对其他人毫无帮助,而且下次更新时会被覆盖。