从零开始学GeoServer源码一(开篇)
从零开始学GeoServer源码二(搭建开发环境)
从零开始学GeoServer源码三(断点应该打在哪?)
从零开始学GeoServer源码四(自定义插件或拓展数据源)
从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms)
从零开始学GeoServer源码六(如何打包发布?)
从零开始学GeoServer源码七(如何注册服务并发布3dtiles和cesium的地形terrain?)
从零开始学GeoServer源码八(内存溢出?Out of Memory Error ?)
从零开始学GeoServer源码九(如何集成Cesium以实现预览3dtiles和terrain服务?)
从零开始学GeoServer源码十(如何修改菜单项以整合我们的功能?)
从零开始学GeoServer源码十一(如何解决No Multipart-config for Servlet错误)
从零开始学GeoServer源码十二(GeoServer中的切片规则)
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)
GeoServer
生成的矢量切片存在缺失,这个问题是在我将矢量切片存储到 MongoDB
的过程中发现并确认的,其实在写渲染矢量切片的那一篇博文openlayers
百万级和千万级数据量的矢量切片在渲染过程中的技术难点解析时,我就曾经产生过疑问,为什么已经提前切好片了,访问的时候还会去切片?今天终于可以解答这个问题了,且听我细细道来。
同一图层,坐标系是EPSG:4326
,当我对第1级进行 png
切片时,会产生8块切片,而当我将格式调整为 pbf
,即 vector-tile
时,只会产生一块切片。
上面我们介绍了现象,那会造成什么样的影响呢?对使用来说不会造成很大的影响,只不过就是访问切片很慢很慢,有种切片白切了的感觉。试想一下,明天要给客户演示,今天就开始切片了,结果到了明天一看,切片缺失了,给客户演示的时候,还是现切,这得益于 GeoServer
的自动切片机制,所以客户体验将会非常非常差。
那是什么原因造成的这种现象呢?那就需要调试代码了。于是我们发现了切片的流程,以第1级为例。
png
切片流程:
GetMap
请求)pbf
切片流程:
pbf
切片(只构造了一个GetMap
请求) 于是这里就存在问题了。因为 pbf
不能像 png
那样,可以直接裁切成几个小块的瓦片。因此正确的流程应该是构造出来8个 GetMap
请求,产生出来8个 pbf
瓦片才对。
于是就产生了一个问题:为什么切 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
找到 GeoServerTileLayer 中的 getMetaTilingFactors 修改即可。
解决了请求次数的问题之后,我们又发现了一个新的问题,即这8个请求每个请求的BoundingBox
都是【-180,-90,180,90】,显然是不对的,不过幸运的是,tile
对象的 xyz
值是对的。于是我们可以根据 xyz
的值来反算经纬度范围。
我们来看下切片调用的函数 GeoServerTileLayer.getMetatilingReponse
中的执行过程:
createMetaTile
//创建瓦片元数据对象dispatchGetMap
// 构造 GetMap 请求metaTile.setWebMap
// 将 GetMap 结果赋值给瓦片元数据对象setupCachingStrategy
//更新切片策略saveTiles
//保存瓦片 于是我们就需要在 dispatchGetMap
时根据行列号重新计算瓦片对应的经纬度范围,不会计算的同学可以去看我们之前讲过的从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms),我们这里直接上代码:
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:4326")){
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);
}
}
}
本文通过跟踪 GeoServer
中切片的生成过程,对比了普通瓦片和矢量瓦片,即 png
和 pbf
的生成过程,发现了 GeoServer
中生成矢量瓦片时缺失数据的 bug
,并通过分析切片过程,修改切片因子,将这一 bug
进行了修复,希望对后来者有所帮助,回见~