前提:需要对网格地图服务WMTS有一定的了解,了解其切片原理,明确坐标系、原点、级别与分辨率等概念。
说明:TileImage为TileArcGISRest、XYZ、WMTS等的父类,在实际使用中,不会用到TileImage,TileImage会在后续小节中介绍。
官网例子https://openlayers.org/en/v4.6.5/examples/arcgis-tiled.html?q=arcgis
var layer = new ol.layer.Tile({
extent: [-13884991, 2870341, -7455066, 6338219],
source: new ol.source.TileArcGISRest({
url: "https://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer"
})
})
官网例子https://openlayers.org/en/v4.6.5/examples/xyz-esri.html?q=arcgis
var layer = new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}'
})
})
官网例子https://openlayers.org/en/v4.6.5/examples/wmts.html?q=wmts
var projection = ol.proj.get('EPSG:3857');
var projectionExtent = projection.getExtent();
var size = ol.extent.getWidth(projectionExtent) / 256;
var resolutions = new Array(14);
var matrixIds = new Array(14);
for (var z = 0; z < 14; ++z) {
// generate resolutions and matrixIds arrays for this WMTS
resolutions[z] = size / Math.pow(2, z);
matrixIds[z] = z;
}
var layer = new ol.layer.Tile({
opacity: 0.7,
source: new ol.source.WMTS({
url: 'https://services.arcgisonline.com/arcgis/rest/services/Demographics/USA_Population_Density/MapServer/WMTS/',
layer: '0',
matrixSet: 'EPSG:3857',
format: 'image/png',
projection: projection,
tileGrid: new ol.tilegrid.WMTS({
origin: ol.extent.getTopLeft(projectionExtent),
resolutions: resolutions,
matrixIds: matrixIds
}),
style: 'default',
wrapX: true
})
})
在TileImage基础上,增加了params字段,同ImageArcGISRest中的params字段相同。
因此,在使用该类时,是动态获取瓦片,不会读取地图服务器的已缓存的瓦片。
在TileImage基础上,增加了maxZoom、minZoom字段,对图层进行级别限定
在TileImage基础上,增加了layer、style、version、format、matrixSet等OGC标准的wmts请求参数字段和dimensions扩展字段-用于添加额外的参数信息。
https://github.com/openlayers/openlayers/blob/v4.6.5/src/ol/source/tileimage.js
默认参数设置
ol.source.TileImage.prototype.getTileGridForProjection = function(projection) {}
ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) {}
ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) {
imageTile.getImage().src = src;
}
关键函数 createTile_ 、getTile 、getTileInternal_
流程:
// 根据z、x、y、Projection、pixelRatio 、key创建ol.Tile
ol.source.TileImage.prototype.createTile_ = function(z, x, y, pixelRatio, projection, key) {
var tileCoord = [z, x, y]; //默认的网格坐标
var urlTileCoord = this.getTileCoordForTileUrlFunction(
tileCoord, projection); // 转化为地图服务的网格坐标
var tileUrl = urlTileCoord ?
this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; // 计算瓦片的地址
var tile = new this.tileClass(
tileCoord,
tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY,
tileUrl !== undefined ? tileUrl : '',
this.crossOrigin,
this.tileLoadFunction,
this.tileOptions); // 生成对象ol.Tile
tile.key = key;
ol.events.listen(tile, ol.events.EventType.CHANGE,
this.handleTileChange, this);
return tile;
};
// 根据z、x、y、Projection、pixelRatio 获取ol.Tile
ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection) {
var sourceProjection = /** @type {!ol.proj.Projection} */ (this.getProjection()); // 图层坐标系
// 判断图层坐标系是否视图坐标系一致
if (!ol.ENABLE_RASTER_REPROJECTION ||
!sourceProjection || !projection ||
ol.proj.equivalent(sourceProjection, projection)) {
return this.getTileInternal(z, x, y, pixelRatio, sourceProjection || projection);
} else {
var cache = this.getTileCacheForProjection(projection);//通过坐标系获取缓存
var tileCoord = [z, x, y];
var tile;
var tileCoordKey = ol.tilecoord.getKey(tileCoord);
// 判断缓存是否存在
if (cache.containsKey(tileCoordKey)) {
tile = /** @type {!ol.Tile} */ (cache.get(tileCoordKey));
}
var key = this.getKey();
if (tile && tile.key == key) {
return tile;
} else {
var sourceTileGrid = this.getTileGridForProjection(sourceProjection); //图层格网
var targetTileGrid = this.getTileGridForProjection(projection); // 视图格网
var wrappedTileCoord =
this.getTileCoordForTileUrlFunction(tileCoord, projection);
// z、x、y转为对应图层的 z、x、y,并获取瓦片
var newTile = new ol.reproj.Tile(
sourceProjection, sourceTileGrid,
projection, targetTileGrid,
tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio),
this.getGutterInternal(),
function(z, x, y, pixelRatio) {
return this.getTileInternal(z, x, y, pixelRatio, sourceProjection);
}.bind(this), this.reprojectionErrorThreshold_,
this.renderReprojectionEdges_);
newTile.key = key;
if (tile) {
newTile.interimTile = tile;
newTile.refreshInterimChain();
cache.replace(tileCoordKey, newTile);
} else {
cache.set(tileCoordKey, newTile);
}
return newTile;
}
}
};
// 根据z、x、y、Projection、pixelRatio 获取ol.Tile
ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) {
var tile = null;
var tileCoordKey = ol.tilecoord.getKeyZXY(z, x, y); //根据z、x、y生成key值
var key = this.getKey();
// 根据key值,判断瓦片缓存中是否已存在。
if (!this.tileCache.containsKey(tileCoordKey)) { // 不存在
tile = this.createTile_(z, x, y, pixelRatio, projection, key); //创建瓦片
this.tileCache.set(tileCoordKey, tile); // 添加到缓冲中
} else {
tile = this.tileCache.get(tileCoordKey); // 存在,则获取瓦片
if (tile.key != key) { //更新瓦片
// The source's params changed. If the tile has an interim tile and if we
// can use it then we use it. Otherwise we create a new tile. In both
// cases we attempt to assign an interim tile to the new tile.
var interimTile = tile;
tile = this.createTile_(z, x, y, pixelRatio, projection, key);
//make the new tile the head of the list,
if (interimTile.getState() == ol.TileState.IDLE) {
//the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it
tile.interimTile = interimTile.interimTile;
} else {
tile.interimTile = interimTile;
}
tile.refreshInterimChain();
this.tileCache.replace(tileCoordKey, tile);
}
}
return tile;
};
https://github.com/openlayers/openlayers/blob/v4.6.5/src/ol/source/tilearcgisrest.js
关键函数 fixedTileUrlFunction、getRequestUrl_
流程:
// 图层的tileCoord(z、x、y)
ol.source.TileArcGISRest.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) {
// 获取格网
var tileGrid = this.getTileGrid();
if (!tileGrid) {
tileGrid = this.getTileGridForProjection(projection);
}
if (tileGrid.getResolutions().length <= tileCoord[0]) {
return undefined;
}
// 计算该瓦片的范围
var tileExtent = tileGrid.getTileCoordExtent(
tileCoord, this.tmpExtent_);
var tileSize = ol.size.toSize(
tileGrid.getTileSize(tileCoord[0]), this.tmpSize);
if (pixelRatio != 1) {
tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize);
}
// Apply default params and override with user specified values.
var baseParams = {
'F': 'image',
'FORMAT': 'PNG32',
'TRANSPARENT': true
};
ol.obj.assign(baseParams, this.params_);
return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
pixelRatio, projection, baseParams);
};
// arcgis url 请求
ol.source.TileArcGISRest.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent,
pixelRatio, projection, params) {
var urls = this.urls;
if (!urls) {
return undefined;
}
// ArcGIS Server only wants the numeric portion of the projection ID.
var srid = projection.getCode().split(':').pop();
params['SIZE'] = tileSize[0] + ',' + tileSize[1];
params['BBOX'] = tileExtent.join(',');
params['BBOXSR'] = srid;
params['IMAGESR'] = srid;
params['DPI'] = Math.round(
params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio
);
var url;
if (urls.length == 1) {
url = urls[0];
} else {
var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length);
url = urls[index];
}
var modifiedUrl = url
.replace(/MapServer\/?$/, 'MapServer/export')
.replace(/ImageServer\/?$/, 'ImageServer/exportImage');
return ol.uri.appendParams(modifiedUrl, params);
};
https://github.com/openlayers/openlayers/blob/v4.6.5/src/ol/source/xyz.js
继承TileImage
https://github.com/openlayers/openlayers/blob/v4.6.5/src/ol/source/wmts.js
var context = {
'layer': this.layer_,
'style': this.style_,
'tilematrixset': this.matrixSet_
};
if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) {
ol.obj.assign(context, {
'Service': 'WMTS',
'Request': 'GetTile',
'Version': this.version_,
'Format': this.format_
});
}
// 通过模版生成url
this.createFromWMTSTemplate_ = function(template) {
// TODO: we may want to create our own appendParams function so that params
// order conforms to wmts spec guidance, and so that we can avoid to escape
// special template params
template = (requestEncoding == ol.source.WMTSRequestEncoding.KVP) ?
ol.uri.appendParams(template, context) :
template.replace(/\{(\w+?)\}/g, function(m, p) {
return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m;
});
return (
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
* @return {string|undefined} Tile URL.
*/
function(tileCoord, pixelRatio, projection) {
if (!tileCoord) {
return undefined;
} else {
var localContext = {
'TileMatrix': tileGrid.getMatrixId(tileCoord[0]),
'TileCol': tileCoord[1],
'TileRow': -tileCoord[2] - 1
};
ol.obj.assign(localContext, dimensions);
var url = template;
if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) {
url = ol.uri.appendParams(url, localContext);
} else {
url = url.replace(/\{(\w+?)\}/g, function(m, p) {
return localContext[p];
});
}
return url;
}
});
};
// 通过Capabilities文档获取元信息
ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) {
……
return {
urls: urls,
layer: config['layer'],
matrixSet: matrixSet,
format: format,
projection: projection,
requestEncoding: requestEncoding,
tileGrid: tileGrid,
style: style,
dimensions: dimensions,
wrapX: wrapX,
crossOrigin: config['crossOrigin']
};
};
1、在加载arcgis server 地图服务时,根据实际需要,选择不同的调用方式
2、访问OGC的WMTS的服务时,以下两种方式可以任意选择