一、WMTS(Web Map Tile Service)
为了更快的将地图数据在前端显示,可以为一些数据不会变更或变动较小的服务创建地图缓存(Cache)。WMTS是一种采用图像金字塔的方式将地图服务按照预先设置的某种切图策略创建的地图缓存服务。
1、地图缓存:
地图缓存是一个包含了不同比例尺下整个地图范围的地图切片目录,是预先按照显示切图等级、比例尺、切图原点、DPI、图片大小等参数创建的静态图片,用于提高地图服务的响应速度。数据格式通常采用PNG或JPG。
前端向地图服务器请求时可以直接根据切图等级、行号、列号获取已缓存的图片,不用像动态地图服务那样根据地图当前范围动态地生成图片再响应到前端。
GeoServer创建缓存的方式有两种,一种是当用户查看地图时创建浏览的地图范围及相应等级下的部分缓存,主要优点是,它不需要预处理,并且仅缓存已请求的数据,因此也可节省磁盘空间。该方法的缺点是地图查看只会间歇性地加速,从而降低了用户体验的质量; 另一种方式是通过Seed创建,缺点是Seed可能是非常耗时和磁盘消耗的过程。
2、瓦片行列号的计算:
假设,地图的切图原点是(x0,y0),地图的瓦片大小是tileSize,地图屏幕上1像素代表的实际距是resolution。计算坐标点(x,y)所在的瓦片的行列号的公式是:
col = floor((x - x0 )/( tileSize *resolution))
row = floor((y0 - y )/( tileSize *resolution))
resolution = scale*0.0254/dpi
在OpenGIS的 WMTS标准实现中,确定比例尺分母时采用的是标准显示像素尺寸(1个像素大小)0.28mm×0.28mm。虽然实际的像素大小并不可知,但0.28mm对于当前的显示器是较为普遍的实际尺寸。所以在切图的块阵中有:
pixelSpan = scaleDenominator ×0.28 10-3 / metersPerUnit(crs);
tileSpanX = tileWidth ×pixelSpan;
tileSpanY = tileHeight ×pixelSpan;
tileMatrixMaxX = tileMatrixMinX + tileSpanX ×matrixWidth;
tileMatrixMinY = tileMatrixMaxY - tileSpanY ×matrixHeight;
备注:metersPerUnit(crs):坐标参照系下长度单位与米之间的转换因子,
地理坐标系时,单位为度则metersPerUnit:6378137*Math.PI*2/360=111319.49079327358;
投影坐标系时,单位为米即metersPerUnit:1
scaleDenominator :比例尺分母
matrixWidth:以图块为单位的宽
matrixHeight:以图块的单位的高
当知道请求的边界范围框bBox(bBoxMinX, bBoxMinY, bBoxMaxX, bBoxMaxY)时:
// 为补偿浮点计算的不准确性
epsilon = 1e-6
tileMinCol = floor((bBoxMinX - tileMatrixMinX) / tileSpanX + epsilon)
tileMaxCol = floor((bBoxMaxX - tileMatrixMinX) / tileSpanX - epsilon)
tileMinRow = floor((tileMatrixMaxY - bBoxMaxY) / tileSpanY + epsilon)
tileMaxRow = floor((tileMatrixMaxY - bBoxMinY) / tileSpanY - epsilon)
// 避免超出范围
if (tileMinCol < 0) tileMinCol = 0
if (tileMaxCol >= matrixWidth) tileMaxCol = matrixWidth-1
if (tileMinRow < 0) tileMinRow = 0
if (tileMaxRow >= matrixHeight) tileMaxRow = matrixHeight-1
3、地图比例尺换算原理(Scale 、Resolution、dpi):
在配置切片策略的时候,对金字塔的每个Level(切图的比例尺参数)都需要一个level 和 resolution
Scale:比例尺,即地图上的一厘米代表着实际上的多少厘米。原 scale 中表示的实际单位是厘米
Resolution:分辨率,实际含义代表当前地图范围内,1像素代表多少地图单位(地图单位/像素),地图单位取决于数据本身的空间参考。
dpi :代表每英寸的像素数
屏幕上1像素代表的实际距离计算
假设地图的坐标单位是米,dpi为96(ArcGIS中dpi默认是96, OGC标准输出的resolution is 90 DPI(25.4/0.28)) ;
1英寸=2.54厘米;
1英寸=96像素;
最终换算的单位是米;
如果当前地图比例尺为1:125000000,则代表图上1米等于实地125000000米;
米和像素间的换算公式:
1英寸=0.0254米=96像素
1像素=0.0254/96 米
则根据1:125000000比例尺,图上1像素代表实地距离是 125000000*0.0254/96 = 33072.9166666667米
即 resolution = scale*0.0254/dpi
4、GeoServer中 WMTS的切图策略(Gridsets)的确定
在新建切图策略时,需要先确定坐标系(即确定metersPerUnit)和边界bounds,图块大小默认为256*256的方形。计算思路是根据公式逆推出pixelSpan和scaleDenominator :
pixelSpan = scaleDenominator ×0.28 10-3 / metersPerUnit(crs);
tileSpanX = tileWidth ×pixelSpan;
tileSpanY = tileHeight ×pixelSpan;
tileMatrixMaxX = tileMatrixMinX + tileSpanX ×matrixWidth;
tileMatrixMinY = tileMatrixMaxY - tileSpanY ×matrixHeight;
当第一次点击"Add zoom level"时,设置第一级别,需要先确定matrixWidth=1还是matrixHeight =1,可以理解为第一个级别应在X或Y的单个方向上的图块数量为以1,后面的级别以四叉树金字塔结构往下分割。由
(tileMatrixMaxX -tileMatrixMinX) /tileWidth = pixelSpan×matrixWidth = resX;
(tileMatrixMaxY -tileMatrixMinY) /tileHeight = pixelSpan×matrixHeight = resY;
如果resY大于resX,则matrixWidth =1,以金字塔的分割方式可以看出一个单位内的X和Y方向上的图块数量的相同,所以,matrixHeight =Math.round(resY / resX),这个时候resX可以理解为在这个比例尺下X方向上的像素大小,而Y方向的像素大小为resY/matrixHeight ,取这两个方向上的像素最大值为当前比例尺下的像素尺寸,即
pixelSpan=Math.Max(resX,resY/matrixHeight )
在根据开始的边界bounds的左上角(切图原点),重新确定边界。
tileMatrixMaxX = tileMatrixMinX + tileWidth × pixelSpan×matrixWidth;
tileMatrixMinY = tileMatrixMaxY - tileHeight × pixelSpan×matrixHeight;
最后确定比例尺分母
scaleDenominator = pixelSpan * metersPerUnit(crs) /0.28 10-3
GeoServer
源码中GridSetFactory.java
中创建GridSet的方法:
public static GridSet createGridSet(String name, SRS srs, BoundingBox extent, boolean alignTopLeft, int levels, Double metersPerUnit, double pixelSize, int tileWidth, int tileHeight, boolean yCoordinateFirst) {
double extentWidth = extent.getWidth();
double extentHeight = extent.getHeight();
double resX = extentWidth / (double)tileWidth;//48.37030696171132
double resY = extentHeight / (double)tileHeight;//42.398664126933
int tilesWide;
int tilesHigh;
if (resX <= resY) {
tilesWide = 1;
tilesHigh = (int)Math.round(resY / resX);
resY /= (double)tilesHigh;
} else {
tilesHigh = 1;
tilesWide = (int)Math.round(resX / resY); //1
resX /= (double)tilesWide;//48.37030696171132
}
//取这两个方向上的像素最大值为当前比例尺下的像素尺寸
double res = Math.max(resX, resY);//48.37030696171132
//根据开始的边界bounds的左上角(切图原点),重新确定边界
double adjustedExtentWidth = (double)(tilesWide * tileWidth) * res;
double adjustedExtentHeight = (double)(tilesHigh * tileHeight) * res;
BoundingBox adjExtent = new BoundingBox(extent);
adjExtent.setMaxX(adjExtent.getMinX() + adjustedExtentWidth);
if (alignTopLeft) {
adjExtent.setMinY(adjExtent.getMaxY() - adjustedExtentHeight);
} else {
adjExtent.setMaxY(adjExtent.getMinY() + adjustedExtentHeight);
}
double[] resolutions = new double[levels];
resolutions[0] = res;
for(int i = 1; i < levels; ++i) {
resolutions[i] = resolutions[i - 1] / 2.0D;
}
return createGridSet(name, srs, adjExtent, alignTopLeft, resolutions, (double[])null, metersPerUnit, pixelSize, (String[])null, tileWidth, tileHeight, yCoordinateFirst);
}
二、WMTS支持的操作
- GetCapabilities:获取服务的元信息
- GetTile:获取缓存的瓦片
- GetFeatureInfo:获取点选的要素信息
GetCapabilities:
获取元数据信息,包含服务支持的操作、提供wmts服务的图层及其拥有的切图策略、输出格式等等信息。
操作示例:http://localhost:8080/geoserver/gwc/service/wmts?service=WMTS&version=1.0.0&request=GetCapabilities
响应示例:
GetTile:
获取缓存的瓦片
操作示例:
http://localhost:8080/geoserver/gwc/service/wmts?layer=topp%3Astates&style=&tilematrixset=EPSG%3A4326&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image%2Fpng&TileMatrix=EPSG%3A4326%3A5&TileCol=12&TileRow=8
响应示例:
GetFeatureInfo:
获取点选的要素信息,其中的参数J为所在图块中的行号,I为列号。
操作示例:
http://localhost:8080/geoserver/gwc/service/wmts?VERSION=1.0.0&LAYER=topp:states&STYLE=&TILEMATRIX=EPSG:4326:5&TILEMATRIXSET=EPSG:4326&SERVICE=WMTS&FORMAT=image/png&SERVICE=WMTS&REQUEST=GetFeatureInfo&INFOFORMAT=text/html&TileCol=12&TileRow=9&I=213&J=11
响应示例: