Skip to content

矢量金字塔技术研究

前言

在图像切片时代,多层次模型依靠的是影像金字塔。得益于影像栅格数据分辨率的特点,基于影像金字塔可以较好的实现多分辨率模型。但是在矢量切片时代中,就无法直接从影像金字塔技术获利了,因为矢量数据不具有分辨率这个特性,而是采用矢量金字塔技术来实现多层次、多尺度模型。

影像金字塔(分层)

影像金字塔技术通过影像重采样方法,建立一系列不同分辨率的影像图层,每个图层分割存储,并建立相应的空间索引机制,从而提高缩放浏览影像时的显示速度。如下图所示的影像金字塔,底部是影像的原始最高分辨率的表示,为512×512图像分辨率,越往上的影像的分辨率越小,分别为256×256,128×128,顶部是影像金字塔的最低分辨率的图像64×64,因此这个影像金字塔共有4层,即4个等级的分辨率。显然影像的图像分辨率越高,影像金字塔的等级越多。

HasPyramid

从给出的定义与图示来看,好像与我们目前使用的瓦片地图有一定的差别。是的,这是因为影像金字塔负责的内容仅仅是构建多分辨率层次,也就是每一层都是对应完整数据范围的一块数据,也就是我们常说的分层(栅格数据是均衡的,可以通过分辨率来作为尺度描述,所以多分辨率层级也就对应着多尺度层级)。

瓦片金字塔(分块)

节选自《高性能影像数据瓦片化关键技术研究-刘世永-2016》

要实现现如今使用的瓦片地图模型,还需要瓦片金字塔配合完成。

瓦片金字塔模型是当前应用最广的多层次地图数据组织模型,通过瓦片金字塔模型,前端在进行放大和缩小操作时,可以有效地减少数据读取的空间查询时间。通过只加载可视区域范围内的瓦片,可以减少数据加载量,降低网络传输压力,提高前端的数据可视化速度。

也就是说,瓦片金字塔实在影像金字塔的基础上,基于特定规格大小对每层影像进行切割。此操作也就是对应着我们常说的分块。

数据原始分辨率并不是标准化的,分层结果即不够标准化,也不够细致。所以在此基础上再次分片,既是减少了数据量使得传输与加载效率提升,同时也是给出了标准,兼容性更好。

瓦片金字塔的主要原理为:基于某个特定的地图投影坐标系(常规是Web墨卡托),将曲面的地球投影到二维平面,而后将该二维平面进行多尺度地划分,即相当于制作了多个不同分辨率层级的数字地图。各层级对应相应编码,层级越高地图所对应的分辨率越高;而后对每一层级的全球空间范围地图按照某种空间划分方法进行格网划分,划分成若干行和列的固定尺寸的正方形栅格图片,这些切分出来的规整的单个格网单元称为瓦片,各层级的划分方法都是相同的。

瓦片划分方法需满足以下条件:

  • 每个层级下的所有瓦片可以无缝拼合成一张全球空间范围的世界地图
  • 每个瓦片都有唯一编码,根据编码可以解算该瓦片对应的空间范围
  • 在某一层级下给定一坐标点可以根据其空间坐标解算其所在瓦片的编号

每一层级瓦片对应一层金字塔分层,所有层级的瓦片便构成了整个瓦片金字塔模型。每一层中的瓦片划分方法一般采用均匀四分的划分方法,即以赤道和中央经线的交点为初始中心,不断地对地图进行四分,直到每个格网的大小为tilesize * tilesize为止,其中tilesize表示单个瓦片的边长。基于此种划分方法,第0层金字塔(金字塔顶层)用一个瓦片就能表示整张世界地图,第1层要用4^z个瓦片来表示整个世界地图,z为当前瓦片的金字塔层级。

金字塔示意图1

瓦片投影坐标系

瓦片金字塔模型中的投影坐标系可以有多种,目前最广泛采用的是Web Mercator投影,它是Mercator投影的一种变体。

瓦片坐标系

所有瓦片的编码都是基于瓦片坐标系下进行的,瓦片坐标系的原点一般都在左上角或者左下角,TMS规范中是在左下角(GeoWebCache遵循该规范),但是现有的Google、Mapnik切片系统都是选用左上角作为原点,本文主要以原点在左上角的瓦片坐标系进行说明。 瓦片的编码方式如下图所示,层级用z表示,瓦片经线方向(指瓦片经度发生变化的方法,即东西向,东向为正)上编号为x,纬线方向(指瓦片维度发生变化的方向,即南北向,南向为正)上编号为y,因此每一个瓦片都可以通过一个三维元组(x,y,z)来唯一描述。

瓦片坐标系1瓦片坐标系2瓦片坐标系3

总的来说,如今我们所说的影像(栅格)金字塔大多指代的是影像金字塔与瓦片金字塔的结合体或是瓦片金字塔(默认含有分层),而不是单独指代分层或者单独指代分块。

矢量金字塔

影像金字塔是为栅格数据服务的,也是图像切片时代的核心产物。但是到了矢量切片时代后,由于矢量数据并不具备分辨率的特性,且矢量数据不同于栅格数据,它有着疏密不一致、分布不均与的特点,所以无法直接利用影像金字塔技术。不过瓦片金字塔是基于分层金字塔的基础上构建的,所以对数据类型并没有要求,在矢量切片中是可以直接复用的。总而言之,在矢量切片时代中,需要一个符合矢量数据特点的分层模型,作为矢量数据源与瓦片金字塔之间沟通的桥梁。同时考虑到应用上的兼容性,所以最终基于金字塔理论之上进行分层模型的定义,谓之:矢量金字塔。

对于矢量数据的分层,将使用比例尺作为尺度描述,建立一系列不同比例尺的分层。不同于栅格金字塔的多分辨率层级,矢量数据金字塔没有分辨率的概念,但是不同层级之间的数据详尽程度也是不同的。随着比例尺的由小到大,矢量要素也变得越来越详细;而随着比例尺由大到小,矢量要素也将变得精简与概化,以符合人们的使用要求。

总而言之,矢量金字塔的的目的就是解决在小比例尺下大数据量(或高密度区域)矢量数据聚集度高、要素重叠和显示速度慢的问题。(其实就是矢量数据的制图综合问题,矢量金字塔不过是其中的一个解而已)

注:我们此处所谈矢量金字塔,只是矢量数据分层金字塔,只是分层。

矢量分层

为了达成人们的使用要求,则需要对数据进行处理,以符合给定比例尺级别下保持相应的详尽程度。

与栅格数据不同,矢量数据通常都具有空间特征与属性信息。空间特征体现在数据的空间坐标系、空间分布以及几何特征;属性信息则是数据实体相关的一些信息。那么矢量数据的空间特征与属性信息则可作为分层的依据,首要对数据的属性信息进行处理,而后再基于空间特征进行处理。

属性分类分级

对于数据的属性特征处理主要是对属性信息进行分类和分级两种情况。分类是根据属性信息划分类别;分级即根据属性信息按照其重要程度划分不同的级别,并且赋予不同的权重值。

空间特征处理

基于空间特征的处理则就多种多样了,比如可以基于数据的分布(密度)抽稀、基于周长或面积进行选取、基于几何形状进行简化。在处理的同时,还需要是实际情况考虑是否维持数据的拓扑关系。

总而言之,可将上述的分层处理方法抽象为两种:

  • 选取(Filter):属性分类分级,基于密度、周长、面积的处理都属于选取
  • 简化(Simplify):基于空间几何形状的处理属于简化

当然,上述两种只是最基础的分层处理方法,后续还可以有更多处理方法,比如合并、融合、夸大等。但我想来,如果能够比较合理的完成上述两个操作,应该也是达到了基本可用层次。

不同行业的数据具有着不同的重点或侧重点,分层不仅需要结合矢量数据模型的特点,还需要结合行业背景与应用场景综合考虑。

此处再借用公谨(遥想公瑾当年)的一句描述进行佐证:

所谓的矢量金字塔模型,即基于制图综合的知识,分别设置海量数据在不同zoom下是否显示,是否简化,是否融合的一种策略,当动态提取切片时,根据这个策略选择数据,实际捞取的数据就非常少,有效解决了矢量切片不能解决数据太密集集中的问题。

矢量金字塔 → 矢量数据的多尺度表达 → 矢量数据的自动制图综合(保证综合前后要素内部及要素之间的拓扑关系是矢量地图正确显示的基本需求 😯)

注:此处所述分层,并不是比例尺等级。而是在不同比例尺层级下,矢量数据的详尽程度。而分层处理,即为通过一定的手段来控制数据的详尽程度。

技术实现

在技术实现上,目前我看到的都是以瓦片金字塔结构为基础,叠加分层处理手段的方式实现的。因为瓦片金字塔是在分层金字塔的基础之上,而分层可分为两个部分:

  1. 尺度分级定义 → 一系列的比例尺等级
  2. 分层分级处理(综合算子) → 一系列的处理算子(如:Filter、Simplify)

OGC TileMatrixSet 定义

而尺度分级定义不论是在分层金字塔还是瓦片金字塔中都是一致的,也就是将分层分级处理(综合算子)剥离出来单独实现,在最终的瓦片生产流程中,接入瓦片金字塔即可。

ogc-tilematrixset-def

ogc-tilematrixset-uml

GeoWebCache 实现

  • 瓦片金字塔结构
java
public class GridSet {
	
    private String name;

    // 投影坐标系
    private SRS srs;

    // 瓦片宽, such as 256
    private int tileWidth;

    // 瓦片高, such as 256
    private int tileHeight;

    /**
     * Whether the y-coordinate of {@link #tileOrigin()} is at the top (true) or at the bottom
     * (false)
     */
    protected boolean yBaseToggle = false;

    /**
     * By default the coordinates are {x,y}, this flag reverses the output for WMTS getcapabilities
     */
    private boolean yCoordinateFirst = false;

    private boolean scaleWarning = false;

    // 将坐标参考系统 (CRS) 单位转换为米的系数
    // 也就是说,这个参数表示的是给定的CRS中一个单元转换为米的系数。换句话说,也就是在指定的CRS中,一个单元表示多少米。
    // 目前常用的就两种投影,一是以米为单位的(即metersPerUnit为1);其次是以度为单位的经纬度投影(metersPerUnit表示为1度代表多少米,即:360/赤道周长,不同CRS使用不同的椭球体,所以其赤道周长也会存在一定差异。)
    private double metersPerUnit;

    // 像素大小, 通常给定0.28mm
    private double pixelSize;

    // 范围, 通常是投影坐标系的最大范围
    private BoundingBox originalExtent;
 
    // 所有的金字塔层次集合
    private Grid[] gridLevels;

    private String description;

    /**
     * {@code true} if the resolutions are preserved and the scaleDenominators calculated, {@code
     * false} if the resolutions are calculated based on the sacale denominators.
     */
    private boolean resolutionsPreserved;
}
  • 单层金字塔模型实现
java
public class Grid {

    // 当前层横向上瓦片数量
    private long numTilesWide;

    // 当前层纵向上瓦片数量
    private long numTilesHigh;

    // 当前层的分辨率
    private double resolution;
    // 当前层的比例尺分母
    private double scaleDenom;

    private String name;
}

现如今,不论是矢量还是栅格,在基于已有标准(比如电子地图数据规范中的地图分级)的情况下,已经给出了一个瓦片金字塔的框架结构,缺少的是金字塔每层的数据,也就是需要自行进行数据分层并放入给定的层级结构中。就拿GeoServer来说,默认情况下,他就直接套用了给定的瓦片金字塔层级结构,且仅做了几何形状的Simplify(无法处理密度问题),那么就会出现大数据量或高密度区域在小比例尺下瓦片尺寸很大,爆炸的大的情况 ☹️

对于分层操作,一般情况下可以先对数据进行属性分类(如:河流&境区)和分级(如:一级河流、二级河流),再结合比例尺分层处理(综合算子集中实现)。 注:行业不同数据也不同,数据不同处理方式也不同,所以就没有标准的处理规则,但理论终归是相通的。

感想

感觉GIS在应用上的知识还是比较封闭的,不像计算机相关的应用知识一般,很容易就能获取到,或者是获取的渠道很清晰。从我目前走过的道路来看,我获取这些知识的途径大概分为四种,收益从上到下:

  • 研究开源GIS应用软件源码
  • 实际项目应用中的探索
  • 理论书籍、论文
  • 各位大佬的文章

不知道是不是我还没有获取到更正确的途径,总感觉比较封闭,新来的人不容易进入(像我已经工作接近6年了)。可能还是从业的人太少了 🙄

参考

基于 MIT 许可发布