从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)

目录

  • 前言
  • 1.现象
  • 2.切片流程
  • 3.产生问题的原因
  • 4.修改默认采样因子
  • 5.修改BoundingBox
  • 6.总结

前言

  GeoServer 生成的矢量切片存在缺失,这个问题是在我将矢量切片存储到 MongoDB 的过程中发现并确认的,其实在写渲染矢量切片的那一篇博文openlayers百万级和千万级数据量的矢量切片在渲染过程中的技术难点解析时,我就曾经产生过疑问,为什么已经提前切好片了,访问的时候还会去切片?今天终于可以解答这个问题了,且听我细细道来。

1.现象

  同一图层,坐标系是EPSG:4326,当我对第1级进行 png 切片时,会产生8块切片,而当我将格式调整为 pbf,即 vector-tile 时,只会产生一块切片。
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第1张图片
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第2张图片
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第3张图片
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第4张图片
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第5张图片

2.切片流程

  上面我们介绍了现象,那会造成什么样的影响呢?对使用来说不会造成很大的影响,只不过就是访问切片很慢很慢,有种切片白切了的感觉。试想一下,明天要给客户演示,今天就开始切片了,结果到了明天一看,切片缺失了,给客户演示的时候,还是现切,这得益于 GeoServer 的自动切片机制,所以客户体验将会非常非常差。
  那是什么原因造成的这种现象呢?那就需要调试代码了。于是我们发现了切片的流程,以第1级为例。
png切片流程

  1. 先采样出来一个大图(只构造了一个GetMap请求)
  2. 然后将该大图裁成8块小图
  3. 依次存储后结束

pbf切片流程

  1. 先采样第1块pbf切片(只构造了一个GetMap请求)
  2. 直接存储
  3. 结束切片

  于是这里就存在问题了。因为 pbf 不能像 png 那样,可以直接裁切成几个小块的瓦片。因此正确的流程应该是构造出来8个 GetMap 请求,产生出来8个 pbf 瓦片才对。

3.产生问题的原因

  于是就产生了一个问题:为什么切 pbf 时只构造了一个 GetMap 请求?
  通过调试代码发现,GeoServer 在切片时有个参数叫 meta-tiling factor ,翻译过来叫切片因子,默认是4✖4,当我们把4✖4改成1✖1时,就会产生8个 GetMap 请求了。
  现在我们就可以回答上面的那个问题了,因为第1级是2行4列,即8块切片,而默认采样时是4✖4,即16块采一次,完全满足8块的要求,因此,采一次就可以了。但是这是 png 采样用的因子,我们现在是 pbf ,因此不能再这样采样了,所以强制改成1✖1即可.

这一块的代码在gwc-core1.19.jar中,类名为org.geowebcache.storage.TileRangeIterator,方法名为nextMetaGridLocation

从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第6张图片

4.修改默认采样因子

  找到 GeoServerTileLayer 中的 getMetaTilingFactors 修改即可。
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第7张图片

5.修改BoundingBox

  解决了请求次数的问题之后,我们又发现了一个新的问题,即这8个请求每个请求的BoundingBox 都是【-180,-90,180,90】,显然是不对的,不过幸运的是,tile 对象的 xyz 值是对的。于是我们可以根据 xyz 的值来反算经纬度范围。
  我们来看下切片调用的函数 GeoServerTileLayer.getMetatilingReponse 中的执行过程:

  1. createMetaTile //创建瓦片元数据对象
  2. dispatchGetMap // 构造 GetMap 请求
  3. metaTile.setWebMap // 将 GetMap 结果赋值给瓦片元数据对象
  4. setupCachingStrategy //更新切片策略
  5. saveTiles //保存瓦片

  于是我们就需要在 dispatchGetMap 时根据行列号重新计算瓦片对应的经纬度范围,不会计算的同学可以去看我们之前讲过的从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms),我们这里直接上代码:
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)_第8张图片

private void modifyBboxOfVectorTileLayer(ConveyorTile tile, String format, BoundingBox bbox) {
    if(format.contains("vector-tile")){
        long[] xyz=tile.getStorageObject().getXYZ();
        long x=xyz[0];
        long y=xyz[1];
        long z=xyz[2];
        if(tile.getGridSetId().equals("EPSG:4306")){
            int row_numberOfTiles=(int)Math.pow(2,z);
            int col_numberOfTiles=2*row_numberOfTiles;
            //每一列或每一行的跨度
            double resX = MBTilesFileUserDefine.WORLD_ENVELOPE.getSpan(0) / (double) col_numberOfTiles;
            double resY =MBTilesFileUserDefine.WORLD_ENVELOPE.getSpan(1) / (double) row_numberOfTiles;
            // 该坐标系经度和纬度的最小值
            double offsetX = MBTilesFileUserDefine.WORLD_ENVELOPE.getMinimum(0);
            double offsetY = MBTilesFileUserDefine.WORLD_ENVELOPE.getMinimum(1);
            double minLon=offsetX+(double)x*resX;
            double maxLon=offsetX+(double)(x+1)*resX;
            double minLat=offsetY+(double)y*resY;
            double maxLat=offsetY+(double)(y+1)*resY;
            bbox.setMinX(minLon);
            bbox.setMaxX(maxLon);
            bbox.setMinY(minLat);
            bbox.setMaxY(maxLat);
        }
    }
}

6.总结

  本文通过跟踪 GeoServer 中切片的生成过程,对比了普通瓦片和矢量瓦片,即 pngpbf 的生成过程,发现了 GeoServer 中生成矢量瓦片时缺失数据的 bug ,并通过分析切片过程,修改切片因子,将这一 bug 进行了修复,希望对后来者有所帮助,回见~

你可能感兴趣的:(GeoServer,GeoServer,矢量切片,缺失,不完整,pbf)