影像瓦片服务与发布管线

本文讲解遥感影像瓦片服务的完整发布管线,涵盖 XYZ 与 TMS 瓦片金字塔原理、Web Mercator 与自定义网格方案、GDAL2Tiles 与 rio-tiler 切片工具、COG 与 MBTiles 存储格式、PNG 与 WebP 与 AVIF 压缩取舍、对象存储与 CDN 静态发布、titiler 动态切片、MapLibre 前端加载优化,以及缓存命中率与容量规划。

引言

遥感影像动辄几十 GB,直接给浏览器一个 GeoTIFF 是不可行的:用户只看屏幕上的几百万像素,却要下载整幅影像。瓦片服务的核心思路就是把影像切成规则的小块并建立金字塔,前端按视野与缩放级别只取需要的瓦片,把传输量压到屏幕分辨率量级。

工程难点不在切片本身,而在整条管线的衔接。坐标系不统一会让瓦片错位,金字塔层级规划不当会让存储爆炸或缩放模糊,压缩格式选错会让透明区变黑或体积翻倍,缓存策略不当会让服务在流量高峰被打穿。这些问题在开发环境都看不出来,上线后才暴露。

另一个趋势是静态切片与动态切片的分工。静态切片把瓦片预生成好放对象存储,成本低、并发高、易上 CDN;动态切片由服务端按请求实时渲染,适合频繁更新的数据与按需波段合成。成熟的系统往往是两者结合:底图用静态,专题图层用动态。

本文按「金字塔原理 → 网格方案 → 切片工具 → 格式压缩 → 静态发布 → 动态服务 → 矢量瓦片 → 前端优化 → 运维容量」的顺序展开,给出可直接落地的命令、配置与参数。影像读取与重投影的基础见 栅格数据读写与 GDAL ,波段与色彩处理见 遥感影像基础与波段组合 。

需要说明的是,瓦片服务既是遥感问题也是 Web 工程问题。前者决定数据怎么切、切成什么样,后者决定怎么发、怎么缓存、怎么扛住流量。只懂一边都会踩坑:只懂遥感会切出体积巨大又无法缓存的瓦片,只懂 Web 会把坐标与投影处理得错漏百出。

目录

  1. 瓦片金字塔与切片原理
  2. 瓦片网格与坐标方案
  3. 影像切片工具链
  4. 瓦片格式与压缩
  5. 静态发布与对象存储
  6. 动态切片服务
  7. 矢量瓦片与 MVT
  8. 前端加载与性能优化
  9. 服务运维与容量规划
  10. 权衡取舍
  11. 常见坑清单
  12. 小结

1. 瓦片金字塔与切片原理

金字塔是同一影像的多分辨率表示。第 0 级是最高分辨率的原始切片,每往上一级分辨率减半,瓦片数量变为 4 倍,覆盖范围不变。用户缩小时加载高层级瓦片,放大时加载低层级,始终保持屏幕上的像素量大致恒定。

瓦片金字塔
z=0:  1 张瓦片覆盖全球
z=1:  4 张
z=2:  16 张
z=n:  4^n 张
分辨率(z) = 初始分辨率 / 2^z

瓦片编号用 x、y、z 三个整数唯一确定,z 是层级,x 是列号,y 是行号。瓦片尺寸通常取 256 或 512 像素。512 像素瓦片在同样的地图范围内瓦片数量减少到四分之一,HTTP 请求数更少,但单张体积更大,是现代框架的常见选择。

为什么切片有效

切片有效的根本原因是缓存。同一份底图瓦片被所有用户共享,第一次请求后即可被 CDN 与浏览器缓存,命中率可达 95% 以上。若直接传输整幅影像,每个用户每次都要下载完整数据,无法共享缓存。

切片的代价是存储冗余。金字塔的总瓦片数是最高层的约 1.33 倍(几何级数求和),加上多份格式与多个版本,存储会成倍增长。规划时必须预留冗余空间。

瓦片数量与存储估算

全球范围 z=0 到 z=n 的总瓦片数 = (4^(n+1) - 1) / 3
z=16 时约 5.7 亿张,z=18 时约 91 亿张
单张 30KB 计,z=18 全球约 2.7 PB,显然不可行
实际只切业务范围,范围占比 p 时瓦片数约为全球的 p 倍

估算时先按范围与最大层级算出瓦片数,再乘平均体积得到存储需求,最后按增长预期留 2 到 3 倍余量。很多项目在规划阶段低估存储,上线后被迫删减层级,造成返工。

2. 瓦片网格与坐标方案

瓦片网格定义了经纬度到瓦片编号的映射,不同网格之间不兼容,选错会导致瓦片错位。

Web Mercator 与全球网格

Web Mercator(EPSG:3857)是全球 Web 地图的事实标准,把地球投影为正方形,全球范围约 ±20037508.34 米。它的优点是投影公式简单、切图方便;缺点是高纬度面积严重变形,两极无法表示。

XYZ 方案与 TMS 方案是同一网格的两种编号约定,唯一区别是 y 轴方向:XYZ 的 y 从北向南递增,TMS 的 y 从南向北递增。两者混用会让影像上下颠倒,这是最常见的集成错误。MapLibre 与 Leaflet 默认 XYZ,部分 WMS 与 GDAL 工具输出 TMS,务必在配置里显式声明。

方案投影y 轴方向典型使用者
XYZEPSG:3857北到南MapLibre、Leaflet
TMSEPSG:3857南到北GDAL、部分 WMS
WMTS任意由矩阵集定义行业标准服务
GCJ-02 网格加密坐标北到南国内部分底图
自定义网格本地投影自定义工程坐标系项目

国内坐标与自定义网格

国内地图服务存在坐标加密问题,GCJ-02 与 BD-09 是在 WGS84 基础上的非线性偏移,直接用 WGS84 影像叠加会错位几百米。涉及国内底图时必须做坐标转换,且转换不可逆(只能用近似反算)。

工程坐标系项目(如城建、水利)常用自定义投影与本地网格,此时不能套用 Web Mercator 网格,需要自定义 TileMatrixSet。自定义网格的关键是把原点、分辨率、瓦片尺寸定义清楚,并保证与业务数据的坐标一致。

3. 影像切片工具链

切片工具按输入输出分几类,选型取决于数据规模与更新频率。

GDAL 命令行

GDAL 提供最通用的切片能力。gdal2tiles 直接输出 XYZ 或 TMS 目录结构,支持多进程并行;gdal_translate 可把大影像转成 MBTiles。处理超大影像前,建议先转成 COG,让 GDAL 能按需读取而不必整幅载入。

gdal_translate -of COG -co COMPRESS=DEFLATE -co BLOCKSIZE=512 -co OVERVIEWS=IGNORE_EXISTING in.tif out_cog.tif  # 转 COG
gdal2tiles.py --profile=mercator --zoom=5-16 --processes=8 -w none out_cog.tif tiles/  # 并行切片
gdal_translate -of MBTILES out_cog.tif out.mbtiles  # 打包为 MBTiles

现代工具链

rio-tiler 与 titiler 是基于 rasterio 的 Python 方案,切片逻辑与动态渲染共用同一套代码,适合需要即时处理的场景。GeoServer 与 MapServer 是传统重量级服务,功能全面但资源占用高,适合已有 GIS 基础设施的团队。

工具类型适合场景资源占用
gdal2tiles命令行一次性批量切片低
rio-tilerPython 库嵌入自定义服务低
titiler服务动态切片与按需渲染中
GeoServer服务企业 GIS 集成高
MapServer服务高性能地图服务中

大影像的分块处理

单幅超过内存的影像不能直接切片。标准做法是先转 COG,让 GDAL 通过分块与概览按需读取,再对 COG 切片。若影像由多幅拼接而成,先做镶嵌(mosaic)并统一坐标系与色彩,再切片,否则接缝处会出现色差与错位。

镶嵌时要注意重叠区处理:取最新、取均值、取最大值各有用途。时序监测常用取最新,正射底图常用取均值。镶嵌后必须检查是否有未覆盖的缝隙,缝隙会在瓦片中表现为白线。

4. 瓦片格式与压缩

瓦片格式直接决定传输体积与视觉质量,是优化空间最大的一环。

格式对比

  • PNG:无损,支持透明,适合含透明区的专题图层与线划图,体积大。
  • JPEG:有损,体积小,适合照片类底图,但不支持透明,透明区会变黑。
  • WebP:同时支持有损与无损及透明,比 PNG 小 25% 以上,浏览器支持已很普及。
  • AVIF:压缩率最高,但编码慢、兼容性略逊,适合可离线预编码的场景。
格式透明压缩率编码速度建议
PNG支持低快专题图层、线划
JPEG不支持中快卫星底图
WebP支持高中通用首选
AVIF支持最高慢离线预编码
gdal_translate -of MBTILES -co TILE_FORMAT=WEBP -co QUALITY=85 out_cog.tif out_webp.mbtiles  # WebP 瓦片
gdaladdo -r average --config COMPRESS_OVERVIEW DEFLATE out_cog.tif 2 4 8 16 32  # 生成概览层

COG 与 MBTiles

COG(Cloud Optimized GeoTIFF)是带内部概览与分块的 GeoTIFF,支持 HTTP 范围请求,是动态切片与对象存储发布的基础。MBTiles 是 SQLite 单文件格式,便于分发与备份,但不支持并发写,适合静态成品而非频繁更新。

概览层(overview)是 COG 的关键。没有概览,缩小时的读取会退化为全分辨率扫描,性能差几个数量级。生成概览时用 average 重采样保色彩,用 nearest 保分类值。

色彩与波段处理

遥感影像常是多波段的,发布前要转成三波段 RGB 或做拉伸。常用手段有线性拉伸、百分比截断拉伸(如 2% 到 98%)、以及针对单波段的直方图均衡。拉伸参数应在整幅影像上统一计算,若逐瓦片拉伸会导致相邻瓦片亮度不一致,拼接后出现方块感。

多光谱与假彩色合成也是瓦片服务的常见需求。真彩色用红绿蓝波段,假彩色常用近红外替代红波段突出植被。这些组合最好在切片前固化成独立图层,而不是依赖前端实时合成,以降低前端复杂度。

5. 静态发布与对象存储

静态切片是最简单可靠的发布方式:把瓦片目录同步到对象存储,前面挂 CDN 即可。

目录结构与同步

瓦片目录按 z/x/y 组织,与前端请求路径一一对应,对象存储的 key 直接就是路径,无需任何服务端逻辑。

aws s3 sync ./tiles s3://my-bucket/tiles --cache-control "public, max-age=604800"  # 同步并设缓存
aws s3 cp ./tiles s3://my-bucket/tiles --recursive --content-encoding gzip  # 全量上传

缓存与跨域

缓存头是静态发布的灵魂。瓦片内容几乎不变,可设长缓存(如 7 天到 1 年),但更新时要用新路径或版本号绕过缓存。ETag 与 Last-Modified 帮助浏览器做条件请求,减少重复下载。

跨域(CORS)必须正确配置,否则浏览器会拦截瓦片请求。需要允许的响应头至少包含 Content-Type 与 ETag,允许的来源按需限定,不要图省事用通配符配合凭据。若用 CDN,CORS 头要在回源响应中带上并被 CDN 缓存。

6. 动态切片服务

动态切片在请求时按需渲染,无需预生成全部瓦片,特别适合数据频繁更新、波段组合多变或按权限过滤的场景。

titiler 部署

titiler 是基于 FastAPI 与 rio-tiler 的现代动态切片服务,支持直接从 COG 切片、波段表达式、重采样与色彩映射。它无状态,可水平扩展,配合对象存储即可服务海量影像。

from fastapi import FastAPI  # Web 框架
from titiler.core.factory import TilerFactory  # 切片工厂
app = FastAPI()
cog = TilerFactory()  # 默认提供 XYZ 与 WMTS 端点
app.include_router(cog.router, prefix="/cog", tags=["COG"])  # 挂载路由
uvicorn app:app --host 0.0.0.0 --port 8000 --workers 4  # 启动 4 进程

动态与静态的取舍

动态切片省去预生成,但每个请求都要读数据、解码、重采样、编码,CPU 开销大,且难以被 CDN 缓存(除非 URL 唯一确定)。实践中常用混合策略:热点区域预生成静态瓦片,长尾区域用动态服务兜底,两者通过同一 URL 前缀对外,前端无感知。

动态服务的性能关键在 COG 的分块与概览质量。分块过大导致单次读取浪费,过小导致请求数增多;概览缺失或层级不足会导致缩小时读取全分辨率数据。一般建议分块 512×512,概览层级覆盖到瓦片最小层级。

动态服务的缓存策略

动态切片默认难以缓存,因为 URL 往往只含影像 ID 与瓦片编号,若数据更新则缓存失效。解决方法是把版本号或数据指纹写进 URL,让 URL 唯一对应一份数据,之后即可放心使用长缓存。这既保留了动态的灵活性,又拿到了静态的缓存收益。

服务端还可加一层内存或 Redis 缓存,缓存最近访问的热点瓦片。热点通常集中在城市中心与常用缩放级别,缓存命中率往往能达到很高,能显著削峰。

7. 矢量瓦片与 MVT

矢量瓦片把矢量数据按瓦片组织,前端渲染,相比栅格瓦片可动态改样式、无极缩放、体积更小。

生成方式

tippecanoe 是最常用的命令行工具,能从 GeoJSON 生成 MVT 并自动做简化与抽稀;PostGIS 的 ST_AsMVT 可直接在数据库里聚合生成,适合动态查询场景。

SELECT ST_AsMVT(t, 'roads', 4096, 'geom')  -- 生成名为 roads 的图层
FROM (
  SELECT ST_AsMVTGeom(ST_Transform(geom, 3857), ST_TileEnvelope(12, 3400, 1650)) AS geom, name, type
  FROM roads WHERE geom && ST_TileEnvelope(12, 3400, 1650)  -- 空间过滤
) AS t;

图层与属性设计

MVT 支持多图层与任意属性,但属性越多体积越大。只保留渲染需要的字段,长字段名换成短码,字符串枚举转整数,能显著减小体积。几何要按缩放级别做简化,低层级用抽稀后的概览几何,高层级用完整几何。

矢量瓦片的一个常见问题是坐标系必须是 EPSG:3857,且坐标要按瓦片范围做局部归一化(MVT 规范用 4096 或 8192 的整数网格)。坐标系错了会让矢量错位,归一化错了会让精度丢失。

8. 前端加载与性能优化

前端是瓦片服务的最终消费者,加载策略直接决定体验。

MapLibre 配置要点

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      sat: { type: "raster", tiles: ["/tiles/{z}/{x}/{y}.webp"], tileSize: 512, maxzoom: 18 }
    },
    layers: [{ id: "sat", type: "raster", source: "sat" }]
  }
});

优化手段

  • tileSize 与真实瓦片尺寸一致,设置错误会导致瓦片被错误缩放。
  • 限制最大缩放级别,超过数据分辨率的缩放只会拉伸模糊,浪费带宽。
  • 合理预取:当前视野外一圈的瓦片提前加载,减少拖动的空白等待。
  • 浏览器缓存与服务端缓存配合,二次访问应几乎零请求。
  • 高 DPI 屏幕若需要 512 像素瓦片,应使用 512 尺寸的瓦片而非 256 放大。

前端还要处理瓦片缺失的优雅降级。空白瓦片不要返回 404 让控制台刷屏,返回一张 1×1 的透明 PNG 或 204 状态更合适,但要与真正的错误区分开。

移动端与离线加载

移动端网络波动大,瓦片加载要更保守。可降低最大缩放级别、用更小体积的 WebP、并对已访问区域做持久缓存。若需要离线使用,通常预下载指定范围与层级的瓦片包,用 MBTiles 存到本地,由应用从本地读取。

离线包的体积要提前估算并让用户可见。一个城市 z=10 到 z=16 的底图包可能达到数百 MB 到数 GB,必须在下载前提示体积与用途,避免用户流量被意外消耗。

9. 服务运维与容量规划

瓦片服务上线后的核心指标是缓存命中率与响应延迟。

关键指标

指标含义目标
缓存命中率CDN 命中请求占比大于 90%
P95 延迟95 分位响应时间小于 200ms
回源率回源请求占比小于 10%
瓦片缺失率404 或空白占比小于 1%
单瓦片体积平均传输大小小于 50KB

容量规划

容量估算的起点是每日瓦片请求数与平均瓦片体积。假设日请求 1000 万次、平均 30KB,则日流量约 300GB,全部回源的带宽需求远超单机能力,必须依赖 CDN。回源带宽按回源率折算,10% 回源即 30GB/日,普通实例足够。

金字塔级别要按需裁剪。若业务最大只到 z=16,就不要切到 z=20,否则存储与切片时间成倍增加却无人访问。同理,只切业务覆盖范围,不要对全球空白区切片。用 gdal2tiles 的 zoom 参数与范围裁剪控制输出。

监控要覆盖服务与数据两侧。服务侧看 QPS、延迟、错误率、回源率;数据侧看瓦片缺失、体积异常、更新滞后。数据侧问题往往更隐蔽,一次失败的更新可能让某个区域长期返回旧瓦片。

灰度发布与数据更新

底图更新不能一次性全量替换,否则新数据的错误会立刻影响所有用户。稳妥做法是灰度:先切一个小范围或低层级,验证无误后再逐步扩大。配合版本化路径,新旧瓦片可以共存,出问题随时回滚。

更新策略还要考虑缓存穿透。当瓦片路径变化时,所有缓存失效,回源量会在短时间内激增。可以分批切换路径,或在 CDN 上做预热,把新瓦片提前推到边缘节点,避免切流瞬间打爆回源。

权衡取舍

决策点选项 A选项 B建议
发布方式静态预切片动态切片底图静态,专题动态
瓦片尺寸256512现代框架用 512
格式PNG(无损透明)WebP(体积小)通用首选 WebP
存储目录加对象存储MBTiles 单文件在线用目录,分发用 MBTiles
切片范围全层级全范围按需裁剪一律按需裁剪

核心权衡是成本与灵活性。静态切片成本低但更新慢,动态切片灵活但算力贵。多数系统的最优解是混合,且用 CDN 把静态部分的成本压到最低。

成本对照

方案一次性成本运行成本更新成本
静态预切片切片算力与存储极低(CDN 为主)高(需重切)
动态切片低高(每请求算力)极低(数据即改即用)
混合中中中

上表的成本是相对量级,实际差异可达一个数量级以上。静态切片的一次性切片时间随层级指数增长,但此后几乎只有存储与 CDN 费用;动态切片的单次请求成本虽小,乘以请求量后往往远超静态方案的存储开销。

判断依据是更新频率。数据一年更新一次,静态切片几乎必然更划算;数据每天更新,动态切片或增量切片才是正解。增量切片只重切变化区域,是静态方案应对高频更新的折中手段。

常见坑清单

  • 瓦片上下颠倒:XYZ 与 TMS 的 y 轴约定混用,配置里显式声明编号方案。
  • 影像整体错位几百米:用了 GCJ-02 底图配 WGS84 数据,做坐标转换或统一到同一基准。
  • 透明区变黑:用了 JPEG 存带透明的瓦片,改用 PNG 或 WebP。
  • 缩小时加载极慢:COG 缺概览层,用 gdaladdo 生成并覆盖到最小层级。
  • 瓦片请求被浏览器拦截:CORS 头缺失或未在 CDN 回源响应中带上。
  • 更新后前端仍是旧瓦片:缓存头过长且路径未变,用版本化路径或缩短缓存。
  • 拖动时大片空白:预取不足或并发限制太低,调整预取范围与并发数。
  • 存储暴涨:切了无人访问的高层级或全球范围,按业务范围与最大级别裁剪。
  • 动态服务高峰被打满:无缓存且未水平扩展,加 CDN 或改静态预切片。
  • 矢量瓦片属性体积过大:保留了全部字段,只留渲染字段并做枚举压缩。
  • 瓦片边缘出现细白线:切图时未做边界缓冲或镶嵌有缝,开启重采样溢出并检查覆盖。
  • 相邻瓦片亮度不一致:逐瓦片拉伸而非全图统一,改为全图统计后统一拉伸参数。

小结

影像瓦片服务的本质是「用空间换时间、用预计算换带宽」。理解金字塔与网格是前提,选对格式与存储是基础,缓存策略与容量规划决定上线后的稳定性。把这几件事做扎实,一个静态目录加 CDN 就能支撑起很高的并发。

对工程团队而言,建议先把底图做成静态 COG 加瓦片目录,用对象存储与 CDN 发布,把成本压到最低;只有当数据频繁更新或需要按需渲染时,才引入 titiler 这类动态服务。无论哪种方式,都要把坐标方案、瓦片编号约定、格式与缓存策略在配置里写死,避免跨团队集成时的隐性错误。

还有一点容易被忽略:瓦片服务是长期运行的基础设施,数据格式与目录结构一旦对外发布就很难更改。因此在设计初期就要把版本化、多格式并存、按需裁剪这些能力留好接口,宁可多花一点设计成本,也不要等到需要兼容时再推倒重来。

下一步可把切片管线接入数据平台做自动化,相关思路见 数据湖技术选型 ,影像的预处理与栅格读写优化可参考 栅格数据读写与 GDAL ,整体遥感数据链路见 遥感与空间数据概览 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「遥感与空间数据」更多文章

  1. 云原生遥感处理
  2. 卫星平台与任务规划
  3. 高光谱遥感处理