关于 maplibre-pmtiles-patterns
这是一项面向使用 PMTiles 构建 MapLibre GL JS 地图的开发者技能。它说明归档文件的生成与托管、HTTP 范围请求交付及 MapLibre 集成细节,包括协议兼容性和缩放行为。
主要功能
- 01选择 PMTiles 或瓦片服务器
- 02生成和转换瓦片归档
- 03通过 HTTP 范围请求托管归档
- 04将 PMTiles 接入 MapLibre GL JS
- 05用 CLI 检查和验证归档
适用场景
- 01无需瓦片服务器发布静态地图
- 02通过 CDN 提供区域瓦片数据集
- 03集成矢量、栅格或高程瓦片
- 04排查 MapLibre 协议和缩放问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ maplibre/ maplibre-agent-skills --skill maplibre-pmtiles-patterns安装后在 Claude Code 中输入/maplibre-pmtiles-patterns确认已加载。
详细介绍
MapLibre PMTiles 模式
PMTiles 是一种单文件格式,用于存储矢量或栅格地图瓦片。你可以将一个(或几个)文件托管在任意静态主机上;MapLibre 通过 HTTP 请求字节范围。不需要瓦片服务器,也不需要动态后端。本指南介绍何时使用 PMTiles、如何生成和托管它们,以及如何将它们连接到 MapLibre GL JS。
何时使用本指南
- 无需运行瓦片服务器即可托管地图瓦片(S3、Cloudflare R2、GitHub Pages 等)
- 构建完全静态或无服务器的地图技术栈
- 使用范围请求通过 CDN 提供大型瓦片集
- 从 OSM 或其他来源生成 PMTiles(Planetiler、tippecanoe)
- 使用 Overture Maps 或其他单文件瓦片数据集搭配 MapLibre
什么是 PMTiles,以及它为何重要
- 矢量和栅格 — PMTiles 同时支持这两种类型。一个文件可以包含矢量图层(例如水域、道路、POI)、栅格影像(PNG/JPEG)或 raster-dem(高程数据,例如用于地形的 Terrarium 格式)。在样式中,相应地使用
type: 'vector'、type: 'raster'或type: 'raster-dem'。 - 每张地图一个文件 — 一个
.pmtiles文件通常会在同一个归档中包含完整的瓦片金字塔(所有缩放级别)和所有图层(矢量或栅格)。该格式以紧凑的布局(例如 Hilbert 曲线)存储瓦片,使客户端只需请求所需的字节范围。对于覆盖范围很大的数据,你可以按区域拆分为多个文件。 - HTTP 范围请求 — 客户端只请求所需的字节范围(例如一个瓦片),因此服务器无需理解 x/y/z。任何支持
Range标头的主机都可使用。 - 托管 — 你可以直接通过静态存储(S3、R2、GitHub Pages、Netlify)提供服务:客户端使用范围请求,因此不需要瓦片服务器。或者,tileserver-gl 或 Martin 也可以提供 PMTiles(通过本地路径、HTTP URL 或 S3),适用于希望由一台服务器同时提供样式、字形或其他数据源的情况。
- 创建 — 你可以通过将 MBTiles 转换为 PMTiles(PMTiles CLI),或从源数据生成(Planetiler、tippecanoe、GDAL 等)来获取 PMTiles。你也可以使用 Protomaps,这是一个可供下载预构建 PMTiles(例如全球或区域底图)并自行托管的服务提供商,也可以通过 PMTiles CLI 创建自定义提取数据——无需自行从 OSM 生成。Protomaps 底图基于 OpenStreetMap 数据构建;任何使用这些底图的地图都必须注明 OSM 来源。请参阅下文的 PMTiles CLI 和 生成 PMTiles。
- 适合 CDN — 范围请求很适合缓存;将文件放在 CDN 后面即可实现快速的全球访问。
优先选择 PMTiles 而非传统瓦片服务器的情况:
- 你希望完全无需服务器逻辑(仅静态托管)。
- 你有范围明确的数据集(国家、区域、主题),可装入一个或几个文件。
- 你希望部署简单、运维开销低(上传文件、设置缓存标头即可)。
优先选择瓦片服务器(例如 tileserver-gl、Martin)的情况:
- 你需要从数据库(PostGIS)生成动态瓦片,或需要频繁更新数据。
- 你有非常庞大的全球数据集,希望按需生成瓦片或仅按区域生成。
MapLibre 集成:PMTiles 协议
MapLibre 原生不支持 PMTiles。你需要使用 PMTiles 库添加协议处理程序,才能使用 pmtiles://(或指向 .pmtiles 文件的 https://)数据源。
安装:
npm install pmtiles
注册协议并在样式中使用:
import * as pmtiles from 'pmtiles';
import * as maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
// Add PMTiles protocol so sources can reference .pmtiles URLs
const protocol = new pmtiles.Protocol();
maplibregl.addProtocol('pmtiles', protocol.tile);
const map = new maplibregl.Map({
container: 'map',
style: {
version: 8,
sources: {
tiles: {
type: 'vector',
url: 'pmtiles://https://example.com/data.pmtiles'
}
},
layers: [
{
id: 'background',
type: 'background',
paint: { 'background-color': '#f8f4f0' }
},
{
id: 'water',
type: 'fill',
source: 'tiles',
'source-layer': 'water',
paint: { 'fill-color': '#a0c8f0' }
}
// add more layers as needed — each uses the same source, different 'source-layer'
]
},
center: [0, 0],
zoom: 2
});
// Optional: remove protocol on map teardown
// map.on('remove', () => maplibregl.removeProtocol('pmtiles'));
MapLibre GL JS 4.0.0 中的 addProtocol 已更改。 v3 的 (params, callback) 签名已废弃——4.0.0 的变更日志写道:“将 addProtocol 改为基于 Promise,不再使用回调,并且可取消。”现在,处理函数应为 async (params, abortController),且必须解析为带有 data 属性的对象:{data, cacheControl?, expires?}。解析为裸 ArrayBuffer、Blob、Response 或 Uint8Array 不符合约定——无论处理函数解析出什么,MapLibre 都会读取其 .data;因此,裸缓冲区会被视为完全没有数据,而 arrayBuffer 请求会得到一个空的 ArrayBuffer(0):瓦片会变成空白,而不是报错。在构造第一个地图之前,只需在模块上注册一次处理函数——使用 maplibregl.addProtocol(...) 或命名导入的 addProtocol:协议注册表是全局的,因此注册一次即可供页面上的所有地图使用。
// ❌ v3 — on v4+ the second argument is an AbortController, so callback(...) throws,
// and the returned {cancel} has no data: the tile comes back blank
maplibregl.addProtocol('custom', (params, callback) => {
fetch(params.url)
.then((r) => r.arrayBuffer())
.then((buffer) => callback(null, buffer))
.catch((error) => callback(error));
return { cancel: () => {} };
});
// ✅ v4+ — async (params, abortController) resolving to {data}
maplibregl.addProtocol('custom', async (params, abortController) => {
const response = await fetch(params.url, { signal: abortController.signal });
if (!response.ok) throw new Error(`Tile fetch error: ${response.statusText}`);
const buffer = await response.arrayBuffer();
return { data: buffer }; // the {data} wrapper is required; a bare buffer is not
});
PMTiles 用户无需更改代码。 new pmtiles.Protocol().tile 是库中的 v4 处理函数,并由 v3compat 兼容层封装(pmtiles ≥ 3.0.0):它会检查第二个参数是否为 AbortController,并进行适配。因此,以下单行代码在 GL JS v3、v4、v5 和 v6 中都正确:maplibregl.addProtocol('pmtiles', protocol.tile);而且它解析出的对象已经符合 {data: Uint8Array, cacheControl, expires} 的格式。如果项目仍在使用 pmtiles 2.x,那么它的 tile 仅支持回调,必须同时升级该库和 MapLibre。编写自己的处理函数,或者手写的回调处理函数在升级到 v4 后出问题时,需要遵循上述约定。
引用图层: 样式包含一个指向 .pmtiles URL 的源(例如 sources.tiles)。从该文件绘制内容的 layers 数组中的每个图层都使用 source: 'tiles' 和 "source-layer": "layerName",其中 layerName 是文件内矢量图层的名称(取决于瓦片使用的架构)。添加多个具有不同 source-layer 值的样式图层,即可从同一个文件中显示道路、标签等内容。
重要: url 可以是 pmtiles://https://...(协议 + 指向 .pmtiles 文件的 HTTPS URL)。该库会通过范围请求获取文件。如果使用标签或图标,样式仍必须定义 glyphs 和 sprite(参见 maplibre-source-wiring)。
缩放范围来自文件头——请使用 url:,而不是 tiles:。 PMTiles 归档会在文件头中存储自己的最小和最大缩放级别。使用 url: 'pmtiles://https://...' 引用时,协议会读取文件头,并向 MapLibre 提供包含正确 minzoom/maxzoom 的 TileJSON,因此超出归档最大缩放级别时会自动过度缩放,无需手动设置 maxzoom。如果改为手动配置 tiles: ['pmtiles://.../{z}/{x}/{y}'] 模板,则会绕过文件头查询。协议仍会提供归档最大缩放级别以内的单个瓦片请求——这不是处理函数缺失或出现 404 的问题——但由于没有缩放范围,MapLibre 会假定 maxzoom: 22,并持续请求归档中不存在的缩放级别;这些请求会返回空内容(矢量瓦片为空白,栅格瓦片则什么也不显示),而不是进行过度缩放。请始终使用 url:。
❌ 数据源只有两个瓦片位置属性:url 和 tiles。 对于 vector、raster 或 raster-dem 数据源,MapLibre 样式规范中都没有 tileset 属性。如果缩放范围不正确,请调整这两个属性中的用法;不要尝试使用第三种属性。
Raster 和 raster-dem: 同一协议也适用于栅格 PMTiles。影像请使用 type: 'raster' 数据源。对于地形/高程,请使用带有 "encoding": "terrarium"(或 "mapbox")的 type: 'raster-dem' 数据源,以便 MapLibre 应用山体阴影或 3D 地形;然后在样式的 terrain 属性中引用该数据源。数据源示例:
"elevation": {
"type": "raster-dem",
"url": "pmtiles://https://example.com/elevation.pmtiles",
"encoding": "terrarium"
}
在 React 中使用 PMTiles: 在应用启动时注册一次协议,而不是在每个组件中注册,这样 MapLibre 在任何地图挂载前就能获得处理程序。例如,在根级 effect 中调用 maplibregl.addProtocol('pmtiles', protocol.tile),或在地图提供程序初始化时调用。最后一个地图卸载时(或应用关闭时),调用 maplibregl.removeProtocol('pmtiles') 以避免资源泄漏。React 风格的设置示例,请参阅 PMTiles for MapLibre GL(Protomaps)。
托管 PMTiles
任何能够提供文件并支持 HTTP Range 请求的主机都适用。
- AWS S3 — 启用公开读取(或使用签名 URL);S3 支持 Range。设置
Cache-Control,还可选择使用 CloudFront。 - Cloudflare R2 — 兼容 S3;启用公开访问或使用签名 URL。将其置于 Cloudflare 后面以进行缓存。
- GitHub Pages — 只要文件大小低于 100 MB,MapLibre GL JS 就能从同一仓库中的 .pmtiles 文件加载瓦片。
- Netlify / Vercel — 上传 .pmtiles 文件;静态托管通常支持 Range。请查看各提供商的文件大小限制。
- 任何静态主机 — 确保服务器返回
Accept-Ranges: bytes,并能正确响应Range标头。
CORS: 浏览器会向 PMTiles URL 发送跨域请求。主机必须发送 Access-Control-Allow-Origin: *(或你的域名)和 Access-Control-Allow-Headers: Range(或允许所有标头)。否则,MapLibre 将无法加载瓦片。
缓存标头: 为提升性能,请为 .pmtiles 文件设置较长的缓存时间(例如,如果文件不可变,可设置 Cache-Control: public, max-age=31536000)。CDN 会缓存 Range 响应。
PMTiles CLI
pmtiles CLI 是用于处理 PMTiles(以及用于转换的 MBTiles)的官方命令行工具。它是一个无需运行时依赖的单一二进制文件,只需下载并运行即可。
安装和使用的原因:
- 将 MBTiles 转换为 PMTiles — 许多工具(tippecanoe、GDAL、martin-cp)会输出 MBTiles。只需一条命令,就能将任何 .mbtiles 文件转换为 .pmtiles 文件:
pmtiles convert in.mbtiles out.pmtiles。如果你的工作流已经会生成 MBTiles,这通常是获取 PMTiles 最简单的方式。 - 检查和验证归档 —
pmtiles show <file>会打印标头和元数据(边界、缩放级别范围、瓦片数量)。pmtiles verify <file>会检查归档完整性。可用于调试或在上传前确认文件。 - 提取子集 —
pmtiles extract会从现有文件创建较小的 .pmtiles 文件(例如按边界框或缩放级别范围提取),这样你就能发布某个区域或有限的缩放级别范围,而无需从源数据重新生成。
❌ 子命令列表是固定的 — 不要尝试使用听起来合理的动词。 go-pmtiles 仅提供 show、tile、verify、extract、merge、serve、convert、cluster、upload、edit 和 version。没有 info 或 inspect 动词,除 edit 外,也没有任何命令行方式可以设置元数据。读取归档的标头、边界和缩放级别范围,应使用命令 pmtiles show;检查完整性,应使用 pmtiles verify。
安装: 从 GitHub Releases (go-pmtiles) 下载适用于你的操作系统和架构的二进制文件,或使用 Docker:protomaps/go-pmtiles。
它无法处理的内容: CLI 只能处理瓦片归档(MBTiles 和 PMTiles)。它无法读取 GeoJSON、Shapefile、OSM 或其他源格式。要从这些格式创建 PMTiles,请使用能生成瓦片的工具(见下文的 生成 PMTiles);如果该工具输出 MBTiles,再运行 pmtiles convert 将其转换为 PMTiles。
生成 PMTiles
两种路径: (1)转换 — PMTiles CLI 仅在 MBTiles ↔ PMTiles 之间转换;无法读取 GeoJSON、Shapefile、OSM 或其他源格式。(2)从源数据生成 — tippecanoe、Planetiler 和通过 GDAL 使用的 ogr2ogr 等工具可读取多种文件类型或数据库,并生成矢量瓦片(PMTiles 或 MBTiles)。如果输出的是 MBTiles,请使用 pmtiles convert 转换为 PMTiles。
PMTiles CLI(仅转换:MBTiles ↔ PMTiles)
关于安装原因和其他命令(show、verify、extract),请参阅上文的 PMTiles CLI。若要将 MBTiles 转换为 PMTiles:
pmtiles convert input.mbtiles output.pmtiles
以下工具可从源数据生成瓦片(GeoJSON、OSM、Shapefile、PostGIS 等)。它们输出 PMTiles 或 MBTiles;如果输出的是 MBTiles,请运行 pmtiles convert 转换为 PMTiles。
Planetiler(OSM / OpenMapTiles 架构)
Planetiler 可读取 OpenStreetMap(或其他来源),并按照 OpenMapTiles 架构输出 PMTiles 或 MBTiles。
# Example: download a region's OSM extract and build a PMTiles file from it
java -jar planetiler.jar --download --area=monaco --output=monaco.pmtiles
有关区域名称、自定义数据源和架构选项,请参阅 Planetiler 文档。输出是单个 .pmtiles 文件,可上传到 S3/R2/静态主机。
tippecanoe
tippecanoe 可从源格式生成矢量瓦片:GeoJSON、FlatGeobuf、CSV。从 v2.17 起,它可以直接输出 PMTiles(-o output.pmtiles)。你也可以输出 MBTiles,再使用 pmtiles convert 转换。
# Direct PMTiles output (v2.17+)
tippecanoe -zg -o output.pmtiles input.geojson
# Or MBTiles then convert: tippecanoe -o output.mbtiles -z 14 input.geojson && pmtiles convert output.mbtiles output.pmtiles
ogr2ogr(GDAL)
GDAL 的 ogr2ogr 可从多种地理空间格式(Shapefile、PostGIS、GeoJSON 等)生成瓦片,并可写入 MBTiles 或 PMTiles(GDAL 3.8+)。它最适合较小的数据集;对于大型矢量瓦片集,tippecanoe 的效率更高。
Raster 和 raster-dem PMTiles
PMTiles 支持 raster 瓦片(PNG/JPEG,例如卫星影像或预渲染影像)和 raster-dem(高程/地形,例如 Terrarium 或 Mapbox 编码)。请使用可生成 raster 或 raster-dem PMTiles 的工具;两者使用相同的协议和托管方式。在样式中,影像使用 type: 'raster',地形则使用 type: 'raster-dem' 和 "encoding": "terrarium"(或 "mapbox")——示例请参阅上文的 MapLibre Integration。
Overture Maps
Overture Maps 发布全球开放地图数据。一些服务商以 PMTiles 形式分发基于 Overture 的数据(例如建筑物、地点、交通数据)。你也可以使用 Planetiler 或其他流水线,根据 Overture 数据构建 PMTiles。像上文所述,在 MapLibre 样式中使用 PMTiles URL。
性能提示
- CDN — 将 .pmtiles 文件托管在 CDN(CloudFront、Cloudflare)上,以便在全球范围内快速处理范围请求。
- 压缩 — PMTiles 会压缩存储瓦片;库会负责解压缩。确保服务器不会以破坏范围请求的方式,对整个文件进行二次压缩(例如 gzip)。
- 多个文件 — 对于覆盖范围很大的数据,可按区域拆分为多个 .pmtiles 文件,并切换源 URL,或按边界使用多个数据源。
- 缓存 — 为文件设置强缓存标头;浏览器和 CDN 会缓存范围响应。
相关技能
- maplibre-tile-sources — 为数据集选择 GeoJSON 或瓦片。
- maplibre-source-wiring — TileJSON、
source-layer、字形和精灵图、CORS。 - maplibre-style-patterns — 矢量数据源的图层和绘制配置(包括由 PMTiles 提供支持的数据源)。(尚未纳入仓库。)
参考资料
- MapLibre GL JS:PMTiles 源和协议 — 官方示例:添加协议、矢量源和栅格源。
- MapLibre GL 的 PMTiles(Protomaps)— 设置、矢量/栅格/栅格 DEM(Terrarium)源、React 用法。
addProtocol(MapLibre GL JS API) — 自定义协议处理程序签名和{data}返回结构。- PMTiles — 格式和协议
- pmtiles CLI — 创建 PMTiles 的最简单方式(
convert、show、verify、extract) - Planetiler — OSM → PMTiles/MBTiles
- MapLibre GL JS — 样式规范和 API
此技能内容为快照。 如果主要来源与此内容相矛盾——包括上述参考资料、MapLibre 当前文档,或运行 MapLibre 时的实际行为——应以主要来源为准。遵循该来源,然后报告此差异,并注明来源和你的 MapLibre 版本:编辑已安装的副本对其他人没有帮助,而且会在下次更新时被覆盖。