PMTiles免服务切片?前端如何直接加载?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

引言

《PMTiles免服务切片?前端如何直接加载?》这个问题,本质上是在问:能不能不部署 GeoServer、TileServer GL、Martin 或专门的瓦片服务,只把一个 .pmtiles 文件放到对象存储、静态网站或普通 Web 服务器上,然后由前端地图直接读取并显示。

答案是可以。PMTiles 的核心价值就是把大量矢量切片或栅格切片封装到一个单文件中,前端通过 HTTP Range 请求按需读取指定层级、行列号对应的切片。对于中小型 WebGIS 项目、离线部署、静态站点地图和数据发布页面,PMTiles免服务切片可以显著降低部署复杂度。

背景:为什么 GIS 前端会需要 PMTiles免服务切片

传统 WebGIS 切片发布通常有两种方式:

  • 把切片拆成 /{z}/{x}/{y}.pbf/{z}/{x}/{y}.png 这样的海量小文件。
  • 部署一个切片服务,由服务端根据请求返回对应瓦片。

这两种方式都能工作,但在实际项目中经常遇到几个问题:

  • 小文件数量巨大,上传、迁移、备份都很慢。
  • 对象存储中海量小文件会增加管理成本。
  • 部署切片服务需要服务器、端口、反向代理和运维能力。
  • 内网或离线环境中,不希望引入复杂服务依赖。

PMTiles 的思路是把切片打包成一个归档文件,例如 china.pmtileslanduse.pmtilesbasemap.pmtiles。前端地图只需要访问这个单文件,就能根据视图范围读取需要的瓦片。

PMTiles免服务切片 前端直接加载PMTiles流程图
PMTiles 通过单文件归档和 HTTP Range 请求,让前端可以直接读取所需切片。

原理:PMTiles 前端直接加载为什么可行

PMTiles 是一种面向地图切片的单文件归档格式。它不是把所有瓦片一次性下载到浏览器,而是通过索引结构定位某个瓦片在文件中的位置,再用 HTTP Range 请求读取文件的一小段。

理解 PMTiles 前端加载,需要抓住三个关键词:

  • 单文件归档:所有切片数据和索引都在一个 .pmtiles 文件中。
  • HTTP Range 请求:浏览器可以请求文件的某个字节范围,而不是下载完整文件。
  • 前端协议适配:通过 pmtiles.jspmtiles:// 或指定 URL 转换为地图引擎能读取的切片请求。

也就是说,PMTiles免服务切片并不是“没有网络请求”,而是不需要专门的瓦片服务。它仍然需要一个能提供静态文件访问的 HTTP 环境,并且最好支持 Range 请求。

步骤:前端如何直接加载 PMTiles

步骤一:准备 PMTiles 文件

你需要先有一个 .pmtiles 文件。常见来源包括:

  • 使用 Tippecanoe 生成 MBTiles,再转换为 PMTiles。
  • 从已有矢量切片数据转换得到 PMTiles。
  • 使用 Protomaps 官方工具链生成 PMTiles。
  • 使用已发布的公开 PMTiles 数据进行测试。

如果你手里已经有 .mbtiles,可以使用 PMTiles CLI 进行转换。示例命令如下:

pmtiles convert input.mbtiles output.pmtiles

转换后建议先查看文件元信息,确认最大层级、最小层级、边界范围和切片类型是否符合预期:

pmtiles show output.pmtiles

步骤二:把 .pmtiles 放到可访问的静态路径

PMTiles免服务切片不需要瓦片服务,但仍需要把文件放到前端可以访问的位置,例如:

  • Nginx 静态目录
  • Apache 静态目录
  • 对象存储公开 Bucket
  • GitHub Pages、Cloudflare Pages、Netlify 等静态站点
  • 内网 Web 服务器

例如你的文件访问地址可以是:

https://example.com/data/basemap.pmtiles

在生产环境中,不建议把 PMTiles 放在需要复杂鉴权跳转的地址后面。前端地图加载瓦片时会频繁发送 Range 请求,如果鉴权或重定向配置不正确,容易出现加载失败或性能下降。

步骤三:确认服务器支持 HTTP Range

PMTiles 前端直接加载的关键是 Range 请求。可以使用浏览器开发者工具或命令行检查服务器响应头。

curl -I https://example.com/data/basemap.pmtiles

理想情况下,响应头中应该能看到类似内容:

Accept-Ranges: bytes

如果服务器不支持 Range,浏览器可能会尝试下载整个 PMTiles 文件,这会导致地图首屏极慢,甚至直接失败。

步骤四:使用 MapLibre GL JS 加载 PMTiles

目前在 WebGIS 前端中,MapLibre GL JS 搭配 pmtiles.js 是加载 PMTiles 的常见方式。下面是一个最小可用示例。

<div id="map" style="width:100%;height:500px;"></div>

<script type="module">
import maplibregl from "https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js";
import { Protocol } from "https://unpkg.com/pmtiles@latest/dist/pmtiles.js";

const protocol = new Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      mytiles: {
        type: "vector",
        url: "pmtiles://https://example.com/data/basemap.pmtiles"
      }
    },
    layers: [
      {
        id: "land",
        type: "fill",
        source: "mytiles",
        "source-layer": "land",
        paint: {
          "fill-color": "#d8ead3",
          "fill-outline-color": "#9bbf95"
        }
      },
      {
        id: "roads",
        type: "line",
        source: "mytiles",
        "source-layer": "roads",
        paint: {
          "line-color": "#ffffff",
          "line-width": 1.2
        }
      }
    ]
  },
  center: [116.39, 39.90],
  zoom: 10
});
</script>

这里最容易出错的是 source-layer。它必须和 PMTiles 中的矢量图层名称一致,不是随便填写的显示名称。

步骤五:查看 PMTiles 中有哪些 source-layer

如果你不知道 PMTiles 内部图层名称,可以用命令查看元数据:

pmtiles show basemap.pmtiles

也可以在生成矢量切片时就明确图层名。例如使用 Tippecanoe 时,常见参数包括:

tippecanoe -o roads.mbtiles -zg --layer=roads roads.geojson

这样前端样式中的 source-layer 就应该写成:

"source-layer": "roads"

步骤六:加载栅格 PMTiles

如果 PMTiles 中保存的是 PNG、JPG 或 WebP 栅格切片,MapLibre 的 source 配置需要使用 raster 类型。示例:

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      rasterTiles: {
        type: "raster",
        url: "pmtiles://https://example.com/data/imagery.pmtiles",
        tileSize: 256
      }
    },
    layers: [
      {
        id: "imagery",
        type: "raster",
        source: "rasterTiles"
      }
    ]
  },
  center: [116.39, 39.90],
  zoom: 8
});

矢量 PMTiles 和栅格 PMTiles 的样式写法不同。矢量切片需要写图层样式,栅格切片通常只作为影像底图或栅格底图显示。

常见坑:PMTiles 前端直接加载失败怎么排查

坑一:服务器不支持 Range 请求

这是 PMTiles免服务切片最常见的问题。表现为地图长时间空白、控制台报网络错误,或浏览器尝试下载完整文件。

  • 检查响应头是否有 Accept-Ranges: bytes
  • 检查对象存储或 CDN 是否转发 Range 请求。
  • 避免经过不支持 Range 的代理层。

坑二:跨域 CORS 配置不正确

如果 PMTiles 文件和前端页面不在同一个域名下,需要配置跨域访问。常见响应头包括:

Access-Control-Allow-Origin: *

如果有鉴权 Cookie 或 Authorization 头,还需要按实际安全策略配置,不能简单照搬通配符。

坑三:source-layer 写错

矢量 PMTiles 能成功请求,但地图没有任何要素,很多时候是 source-layer 不匹配。解决办法是查看 PMTiles 元数据,确认内部图层名。

坑四:坐标系不是 Web Mercator

WebGIS 瓦片通常使用 Web Mercator,也就是 EPSG:3857。如果你生成切片前的数据坐标系、切片网格或投影处理不一致,前端可能出现位置偏移、范围异常或完全看不到数据。

在制作 PMTiles 前,应确认源数据坐标系,并使用正确的切片工具流程进行重投影和切片。

坑五:文件太大但没有做层级和要素优化

PMTiles 是单文件,不代表可以无限塞入未经简化的原始数据。大范围、高精度、多属性矢量数据如果直接切片,可能造成文件过大、首屏慢和浏览器渲染压力高。

  • 控制最大缩放级别。
  • 按比例尺简化几何。
  • 删除前端不需要的属性字段。
  • 按主题拆分多个 PMTiles 文件。

方法比较:PMTiles、XYZ 切片和传统切片服务怎么选

方案 适合场景 优点 限制
PMTiles免服务切片 静态发布、离线部署、中小型专题地图、对象存储分发 单文件、易迁移、不需要切片服务、适合 CDN 依赖 Range 请求,动态查询能力有限
XYZ 小文件切片 传统静态瓦片发布、简单底图服务 结构直观,兼容性强 文件数量巨大,上传和维护成本高
传统切片服务 多用户平台、动态样式、权限控制、实时数据 服务端能力强,便于统一管理 需要部署和运维服务,系统复杂度更高
PostGIS 动态服务 需要空间查询、筛选、高频更新的数据平台 支持动态条件、空间分析和权限控制 对数据库、接口和缓存设计要求更高

如果你的目标只是把一批已经处理好的地图切片发布到网页上,PMTiles 前端直接加载通常比部署完整切片服务更轻量。如果你需要按用户权限过滤数据、实时编辑、空间查询或复杂统计,仍然应该考虑 PostGIS 加后端 API 或专业地图服务。

检查清单:上线前确认这些配置

  • PMTiles 文件可以通过 HTTPS 地址直接访问。
  • 服务器或对象存储支持 Accept-Ranges: bytes
  • 跨域 CORS 配置允许前端页面读取 PMTiles。
  • MapLibre GL JS 已正确注册 pmtiles 协议。
  • 矢量切片的 source-layer 与 PMTiles 内部图层名一致。
  • 栅格切片使用 type: "raster",矢量切片使用 type: "vector"
  • 地图中心点和缩放级别落在 PMTiles 覆盖范围内。
  • PMTiles 的最小层级和最大层级满足业务需求。
  • 大数据量矢量图层已经做几何简化和字段精简。
  • 浏览器控制台没有 CORS、404、Range 或样式图层错误。

FAQ

PMTiles免服务切片是不是完全不需要服务器?

不是。它不需要专门的瓦片服务,但仍需要一个能提供静态文件访问的 HTTP 服务器、对象存储或静态站点环境。前端必须能通过 URL 访问 .pmtiles 文件。

PMTiles 前端直接加载会不会一次性下载整个文件?

正常情况下不会。PMTiles 会通过 HTTP Range 请求读取需要的字节片段。但如果服务器不支持 Range,或者中间代理破坏了 Range 请求,就可能导致异常下载或加载失败。

PMTiles 可以加载到 Leaflet 吗?

可以,但具体实现要看你加载的是栅格切片还是矢量切片。MapLibre GL JS 对矢量 PMTiles 的支持路径更常见,Leaflet 更适合栅格瓦片或配合额外插件使用。如果是复杂矢量样式,优先建议使用 MapLibre GL JS。

PMTiles 和 MBTiles 有什么区别?

MBTiles 通常是 SQLite 数据库文件,更适合服务端读取和管理。PMTiles 是为云端静态分发和 HTTP Range 访问设计的单文件格式,更适合浏览器前端直接加载。

PMTiles 适合发布全国级数据吗?

可以,但要看数据复杂度、缩放级别和样式需求。全国级行政区、道路、水系等数据如果没有简化和分层,文件会很大,前端渲染也会变慢。建议按比例尺综合、按主题拆分,并控制最大缩放级别。

为什么 PMTiles 请求成功但地图是空白?

优先检查四点:地图视图是否在数据范围内,source-layer 是否正确,样式图层类型是否匹配,PMTiles 的切片类型是矢量还是栅格。很多空白问题不是文件加载失败,而是样式配置没有命中数据。

结论

PMTiles免服务切片适合把已经处理好的 GIS 切片数据以单文件方式发布到前端。它的关键不是“没有服务”,而是“不需要专门的瓦片服务”:只要静态文件环境支持 HTTP Range、CORS 配置正确,前端就可以通过 MapLibre GL JS 和 pmtiles.js 直接加载。

实际项目中,建议先用一个小范围 PMTiles 文件验证加载流程,再处理正式数据。重点检查 Range 请求、source-layer、坐标系、图层样式和数据范围。只要这些环节配置正确,PMTiles 前端直接加载会是一个非常轻量、稳定、便于部署的 WebGIS 切片发布方案。