OpenLayers源码阅读(五):ImageWMS与TileWMS

前提:需要对网络地图服务WMS有一定的了解,了解其方法、参数等。

1、我们如何使用

1.1 ImageWMS

官网例子https://openlayers.org/en/v4.6.5/examples/wms-image.html?q=wms

new ol.layer.Image({
    
    
   extent: [-13884991, 2870341, -7455066, 6338219],
   source: new ol.source.ImageWMS({
    
    
     url: 'https://ahocevar.com/geoserver/wms',
     params: {
    
    'LAYERS': 'topp:states'},
     ratio: 1,
     serverType: 'geoserver'
   })
})

1.2 TileWMS

官网例子https://openlayers.org/en/v4.6.5/examples/wms-tiled.html?q=wms

new ol.layer.Tile({
    
    
     extent: [-13884991, 2870341, -7455066, 6338219],
     source: new ol.source.TileWMS({
    
    
       url: 'https://ahocevar.com/geoserver/wms',
       params: {
    
    'LAYERS': 'topp:states', 'TILED': true},
       serverType: 'geoserver',
       // Countries have transparency, so do not fade tiles:
       transition: 0
     })
})

1.3 比较

一般情况下,ImageWMS、TileWMS在使用上基本差不多,但在网络请求上,ImageWMS为一次请求(一张图片),TileWMS多次请求(分为多个小图片请求)。

2、属性

2.1 ImageWMS

在这里插入图片描述重点关注一下几个属性:
serverType:服务类型,主要有mapserver、geoserver、qgis
params:标准的OGC WMS请求参数,其中,LAYERS 为必参
projection:坐标系,默认与view的坐标系一致
url:wms服务

2.2 TileWMS

在这里插入图片描述重点关注一下几个属性:
tileGrid:瓦片格网,
优先级:
  1、自定义的格网
  2、采用坐标系的格网
  3、无坐标系,采用基于全球范围,原点为[0,0]

2.3 比较

相对于ImageWMS,TileWMS具有TileImage的特有属性——tileGrid。

3、源码解析

3.1 ImageWMS

https://github.com/openlayers/openlayers/blob/v4.6.5/src/ol/source/imagewms.js
默认参数设置

// 定义版本 v1.3.0
this.v13_ = true; 
this.updateV13_(); 

/**
 * @private
 */
ol.source.ImageWMS.prototype.updateV13_ = function() {
    
    
  var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION;
  this.v13_ = ol.string.compareVersions(version, '1.3') >= 0;
};

// 定义getFeatureInfo时,鼠标所在的像素范围大小
/**
 * @const 
 * @type {ol.Size}
 * @private
 */
ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_ = [101, 101];

关键函数 getGetFeatureInfoUrl、getImageInternal、getRequestUrl_
流程:

调用
调用
getGetFeatureInfoUrl
getRequestUrl_
getImageInternal
/**
 * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
 * projection. Return `undefined` if the GetFeatureInfo URL cannot be
 * constructed.
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @param {ol.ProjectionLike} projection Projection.
 * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
 *     be provided. If `QUERY_LAYERS` is not provided then the layers specified
 *     in the `LAYERS` parameter will be used. `VERSION` should not be
 *     specified here.
 * @return {string|undefined} GetFeatureInfo URL.
 * @api
 */
ol.source.ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) {
    
    
  if (this.url_ === undefined) {
    
    
    return undefined;
  }
  var projectionObj = ol.proj.get(projection);
  var sourceProjectionObj = this.getProjection();
  // 根据坐标系重新计算coordinate和resolution
  if (sourceProjectionObj && sourceProjectionObj !== projectionObj) {
    
    
    resolution = ol.reproj.calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, resolution);
    coordinate = ol.proj.transform(coordinate, projectionObj, sourceProjectionObj);
  }
  
  // 计算中心点周边50.5像素的范围
  var extent = ol.extent.getForViewAndSize(
      coordinate, resolution, 0,
      ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_);

  var baseParams = {
    
    
    'SERVICE': 'WMS',
    'VERSION': ol.DEFAULT_WMS_VERSION,
    'REQUEST': 'GetFeatureInfo',
    'FORMAT': 'image/png',
    'TRANSPARENT': true,
    'QUERY_LAYERS': this.params_['LAYERS']
  };
  ol.obj.assign(baseParams, this.params_, params);
  
  // 根据版本确定参数
  var x = Math.floor((coordinate[0] - extent[0]) / resolution);
  var y = Math.floor((extent[3] - coordinate[1]) / resolution);
  baseParams[this.v13_ ? 'I' : 'X'] = x;
  baseParams[this.v13_ ? 'J' : 'Y'] = y;

  return this.getRequestUrl_(
      extent, ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_,
      1, sourceProjectionObj || projectionObj, baseParams);
};
/**
 * @inheritDoc
 */
ol.source.ImageWMS.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) {
    
    

  if (this.url_ === undefined) {
    
    
    return null;
  }
  
  // 找到最接近的分辨率
  resolution = this.findNearestResolution(resolution);

  if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) {
    
    
    pixelRatio = 1;
  }

  var imageResolution = resolution / pixelRatio;

  var center = ol.extent.getCenter(extent);
  var viewWidth = Math.ceil(ol.extent.getWidth(extent) / imageResolution);
  var viewHeight = Math.ceil(ol.extent.getHeight(extent) / imageResolution);
  var viewExtent = ol.extent.getForViewAndSize(center, imageResolution, 0,
      [viewWidth, viewHeight]);
  var requestWidth = Math.ceil(this.ratio_ * ol.extent.getWidth(extent) / imageResolution);
  var requestHeight = Math.ceil(this.ratio_ * ol.extent.getHeight(extent) / imageResolution);
  var requestExtent = ol.extent.getForViewAndSize(center, imageResolution, 0,
      [requestWidth, requestHeight]);
  
  // 判断图片是否存在,根据分辨率、像素比率、范围,存在则返回图片
  var image = this.image_;
  if (image &&
      this.renderedRevision_ == this.getRevision() &&
      image.getResolution() == resolution &&
      image.getPixelRatio() == pixelRatio &&
      ol.extent.containsExtent(image.getExtent(), viewExtent)) {
    
    
    return image;
  }
  // 不存在,则发送请求
  var params = {
    
    
    'SERVICE': 'WMS',
    'VERSION': ol.DEFAULT_WMS_VERSION,
    'REQUEST': 'GetMap',
    'FORMAT': 'image/png',
    'TRANSPARENT': true
  };
  ol.obj.assign(params, this.params_);

  this.imageSize_[0] = Math.round(ol.extent.getWidth(requestExtent) / imageResolution);
  this.imageSize_[1] = Math.round(ol.extent.getHeight(requestExtent) / imageResolution);

  var url = this.getRequestUrl_(requestExtent, this.imageSize_, pixelRatio,
      projection, params);

  this.image_ = new ol.Image(requestExtent, resolution, pixelRatio,
      url, this.crossOrigin_, this.imageLoadFunction_);

  this.renderedRevision_ = this.getRevision();

  ol.events.listen(this.image_, ol.events.EventType.CHANGE,
      this.handleImageChange, this);

  return this.image_;

};
/**
 * @param {ol.Extent} extent Extent.
 * @param {ol.Size} size Size.
 * @param {number} pixelRatio Pixel ratio.
 * @param {ol.proj.Projection} projection Projection.
 * @param {Object} params Params.
 * @return {string} Request URL.
 * @private
 */
ol.source.ImageWMS.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) {
    
    
  
  ol.asserts.assert(this.url_ !== undefined, 9); // `url` must be configured or set using `#setUrl()`
  // 根据坐标系编码,确定wms版本是否为v1.3.0
  params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode();

  if (!('STYLES' in this.params_)) {
    
    
    params['STYLES'] = '';
  }
   
  // 根据serverType确定dpi
  if (pixelRatio != 1) {
    
    
    switch (this.serverType_) {
    
    
      case ol.source.WMSServerType.GEOSERVER:
        var dpi = (90 * pixelRatio + 0.5) | 0; // 取整
        if ('FORMAT_OPTIONS' in params) {
    
    
          params['FORMAT_OPTIONS'] += ';dpi:' + dpi;
        } else {
    
    
          params['FORMAT_OPTIONS'] = 'dpi:' + dpi;
        }
        break;
      case ol.source.WMSServerType.MAPSERVER:
        params['MAP_RESOLUTION'] = 90 * pixelRatio;
        break;
      case ol.source.WMSServerType.CARMENTA_SERVER:
      case ol.source.WMSServerType.QGIS:
        params['DPI'] = 90 * pixelRatio;
        break;
      default:
        ol.asserts.assert(false, 8); // Unknown `serverType` configured
        break;
    }
  }

  params['WIDTH'] = size[0];
  params['HEIGHT'] = size[1];

  var axisOrientation = projection.getAxisOrientation();
  var bbox;
  // 根据版本,确定bbox传值方式
  if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') {
    
    
    bbox = [extent[1], extent[0], extent[3], extent[2]];
  } else {
    
    
    bbox = extent;
  }
  params['BBOX'] = bbox.join(',');

  return ol.uri.appendParams(/** @type {string} */ (this.url_), params);
};

3.2 TileWMS

关键函数 getGetFeatureInfoUrl、fixedTileUrlFunction、getRequestUrl_(基本同上)
流程:

调用
调用
getGetFeatureInfoUrl
getRequestUrl_
fixedTileUrlFunction

/**
 * Return the GetFeatureInfo URL for the passed coordinate, resolution, and
 * projection. Return `undefined` if the GetFeatureInfo URL cannot be
 * constructed.
 * @param {ol.Coordinate} coordinate Coordinate.
 * @param {number} resolution Resolution.
 * @param {ol.ProjectionLike} projection Projection.
 * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should
 *     be provided. If `QUERY_LAYERS` is not provided then the layers specified
 *     in the `LAYERS` parameter will be used. `VERSION` should not be
 *     specified here.
 * @return {string|undefined} GetFeatureInfo URL.
 * @api
 */
ol.source.TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) {
    
    
  var projectionObj = ol.proj.get(projection);
  var sourceProjectionObj = this.getProjection();

  var tileGrid = this.getTileGrid();
  if (!tileGrid) {
    
    
    tileGrid = this.getTileGridForProjection(projectionObj);
  }

  var tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, resolution);

  if (tileGrid.getResolutions().length <= tileCoord[0]) {
    
    
    return undefined;
  }
  // 根据tileCoord确定tileResolution 、tileExtent 、tileSize 
  var tileResolution = tileGrid.getResolution(tileCoord[0]);
  var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
  var tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0]), this.tmpSize);


  var gutter = this.gutter_;
  if (gutter !== 0) {
    
    
    tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize);
    tileExtent = ol.extent.buffer(tileExtent, tileResolution * gutter, tileExtent);
  }

  if (sourceProjectionObj && sourceProjectionObj !== projectionObj) {
    
    
    tileResolution = ol.reproj.calculateSourceResolution(sourceProjectionObj, projectionObj, coordinate, tileResolution);
    tileExtent = ol.proj.transformExtent(tileExtent, projectionObj, sourceProjectionObj);
    coordinate = ol.proj.transform(coordinate, projectionObj, sourceProjectionObj);
  }

  var baseParams = {
    
    
    'SERVICE': 'WMS',
    'VERSION': ol.DEFAULT_WMS_VERSION,
    'REQUEST': 'GetFeatureInfo',
    'FORMAT': 'image/png',
    'TRANSPARENT': true,
    'QUERY_LAYERS': this.params_['LAYERS']
  };
  ol.obj.assign(baseParams, this.params_, params);

  var x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution);
  var y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution);

  baseParams[this.v13_ ? 'I' : 'X'] = x;
  baseParams[this.v13_ ? 'J' : 'Y'] = y;

  return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
      1, sourceProjectionObj || projectionObj, baseParams);
};
/**
 * @inheritDoc
 */
ol.source.TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) {
    
    

  var tileGrid = this.getTileGrid();
  if (!tileGrid) {
    
    
    tileGrid = this.getTileGridForProjection(projection);
  }

  if (tileGrid.getResolutions().length <= tileCoord[0]) {
    
    
    return undefined;
  }

  if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) {
    
    
    pixelRatio = 1;
  }
  // 根据tileCoord确定tileResolution 、tileExtent 、tileSize 
  var tileResolution = tileGrid.getResolution(tileCoord[0]);
  var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_);
  var tileSize = ol.size.toSize(
      tileGrid.getTileSize(tileCoord[0]), this.tmpSize);

  var gutter = this.gutter_;
  if (gutter !== 0) {
    
    
    tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize);
    tileExtent = ol.extent.buffer(tileExtent,
        tileResolution * gutter, tileExtent);
  }

  if (pixelRatio != 1) {
    
    
    tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize);
  }

  var baseParams = {
    
    
    'SERVICE': 'WMS',
    'VERSION': ol.DEFAULT_WMS_VERSION,
    'REQUEST': 'GetMap',
    'FORMAT': 'image/png',
    'TRANSPARENT': true
  };
  ol.obj.assign(baseParams, this.params_);

  return this.getRequestUrl_(tileCoord, tileSize, tileExtent,
      pixelRatio, projection, baseParams);
};

3.3 比较

相同点:两者在实现逻辑上,基本一样,根据范围计算出分辨率、图片大小,然后再拼接参数发送请求。
不同点:ImageWMS根据view的范围求出,而TileWMS根据tileCoord计算出瓦片范围再求出。

4、总结

1、在实际对WMS服务调用过程中,一般ImageWMS和TileWMS都可以,并无很大的区别。而两者的区别,主要是在大数据量的请求上。如果一次请求数据过大并感觉渲染明显出现停顿的现象,则推荐使用TileWMS调用。
2、ImageArcGISRest和TileArcGISRest的关系,和上述的两者的关系类似。

猜你喜欢

转载自blog.csdn.net/u013240519/article/details/103531133