mcpField
M
maplibre-pmtiles-patterns

maplibre-pmtiles-patterns

github.com/maplibre云与 DevOps155772 次安装今天更新
代码仓库

关于 maplibre-pmtiles-patterns

这是一项面向使用 PMTiles 构建 MapLibre GL JS 地图的开发者技能。它说明归档文件的生成与托管、HTTP 范围请求交付及 MapLibre 集成细节,包括协议兼容性和缩放行为。

主要功能

  1. 01选择 PMTiles 或瓦片服务器
  2. 02生成和转换瓦片归档
  3. 03通过 HTTP 范围请求托管归档
  4. 04将 PMTiles 接入 MapLibre GL JS
  5. 05用 CLI 检查和验证归档

适用场景

  1. 01无需瓦片服务器发布静态地图
  2. 02通过 CDN 提供区域瓦片数据集
  3. 03集成矢量、栅格或高程瓦片
  4. 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 当前文档,或运行 MapLibre 时的实际行为——应以主要来源为准。遵循该来源,然后报告此差异,并注明来源和你的 MapLibre 版本:编辑已安装的副本对其他人没有帮助,而且会在下次更新时被覆盖。

评论

支持 Markdown