openlayer根据服务器返回状态码,替换使用本地图片

一、问题产生

使用openlayer加载自己公司的wmts服务,要求根据用户权限、瓦片存在与否等进行直观的瓦片替换。例如某张瓦片不存在,则替换该瓦片为404的一张图片,某个区域用户无权限查看,则替换为无权限(401)图片(如下图);

在这里插入图片描述

二、问题分析&解决

首先贴上加载服务的代码。关于tileGridprojection等属性与本人影像服务器计算有关,也与本博客无关,故不细说。

在这里,我们使用的是ol.layer.tile类来加载瓦片,瓦片行列号计算方式满足wmts标准。故采用ol.source.WMTS来进行加载。

let wmtsLayer = new ol.layer.Tile({
    source: new ol.source.WMTS({
        url: url,
        projection: projection, //数据的投影坐标系
        tileGrid: tileGrid,
        format: "image/png",
        crossOrigin: 'anonymous'
    })
});

map.addLayer(wmtsLayer)

在加载服务之后,根据控制台的ajax请求,定位到瓦片请求的具体代码,找到源文件TileImage.js中的tileLoadFunction。查阅api也可以得到。

该类如下定义:

super({
//--其余代码
   tileLoadFunction ?
        options.tileLoadFunction : defaultTileLoadFunction,
//--其余代码
 });

找到defaultTileLoadFunction定义:
在这里插入图片描述
这里我们发现,最终生成的wmts格式的url,被赋值给了一个Image对象。我们的思路就有了。于是我们跟踪这个tileLoadFunction,发现其可以在new ol.source.WMTS()中进行配置。于是我们进行配置:

let wmtsLayer = new ol.layer.Tile({
    source: new ol.source.WMTS({
        url: url,
        projection: projection, //数据的投影坐标系
        tileGrid: tileGrid,
        format: "image/png",
        crossOrigin: 'anonymous',
        tileLoadFunction: function (imageTile, src) {
				//--调用xmlhttprequst,请求src获取状态码。
				//--如果状态码为401替换imageTile的image为本地链接的某幅图片
				//--如果状态码200,则直接替换为将src赋值给imageTile
		}
    })
});

map.addLayer(wmtsLayer)

然后这样做。满心欢喜去测试,发现代码替换后全部黑屏。瓦片无法正常加载。直觉就是异步的问题。在defaultTileLoadFunction中,使用的是同步的方式,将src赋值给image。由image去完成图片的请求。但是上述示例中采用的是先请求在异步回调中给src赋值.于是做如下测试:

	//--其余代码
   tileLoadFunction: function (imageTile, src) {
				setTimeout(function(){
					(imageTile.getImage()).src = src
				},200)
		}
	//--其余代码

果然一片黑屏。即,该函数无法支持异步请求。必然是上层逻辑在调用该函数后立马有对imageTile的处理过程。于是跟踪源码。发现调用的地方是在ol/ImageTile.js中的load函数:
在这里插入图片描述
果然在下面有额外的逻辑。到这里,我们不能修改源码。该怎么办呢。
查阅api+跟踪源码,发现对于创建Tile的类也可以自己定义(佩服ol的灵活设计):

在这里插入图片描述
即,我可以自定义一个类,让瓦片按照我自己定义的类的加载规则来加载。

于是我们实现一个自己的类,继承默认的ImageTile.js,修改load函数,实现异步加载瓦片。

扫描二维码关注公众号,回复: 11688432 查看本文章
/**
 * @module ol/ImageTile
 */
import ImageTile from '../ol/ImageTile.js';
import TileState from '../ol/TileState.js';
import {
  listenImage
} from '../ol/Image.js';


class ImageTileAnsyc extends ImageTile {

  /**
   * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
   * @param {TileState} state State.
   * @param {string} src Image source URI.
   * @param {?string} crossOrigin Cross origin.
   * @param {import("./Tile.js").LoadFunction} tileLoadFunction Tile load function.
   * @param {import("./Tile.js").Options=} opt_options Tile options.
   */
  constructor(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options) {
    super(tileCoord, state, src, crossOrigin, tileLoadFunction, opt_options);
  }
  load() {
    if (this.state == TileState.ERROR) {
      this.state = TileState.IDLE;
      this.image_ = new Image();
      if (this.crossOrigin_ !== null) {
        this.image_.crossOrigin = this.crossOrigin_;
      }
    }
    if (this.state == TileState.IDLE) {
      this.state = TileState.LOADING;
      this.changed();
      this.tileLoadFunction_(this, this.src_).then(() => {
        this.unlisten_ = listenImage(
          this.image_,
          this.handleImageLoad_.bind(this),
          this.handleImageError_.bind(this)
        );
      });

    }
  }

}

export default ImageTileAnsyc;

什么都没变,只是修改了load函数,将tileLoadFunction配置为异步加载。在tileLoadFunction内的内容执行完毕时,再执行接下来的逻辑。
然后修改wmts的定义。完整代码如下:

let wmtsLayer = new ol.layer.Tile({
    source: new ol.source.WMTS({
        url: url,
        projection: projection, //数据的投影坐标系
        tileGrid: tileGrid,
        format: "image/png",
        crossOrigin: 'anonymous',
        tileClass: ImageTileAnsyc,
        tileLoadFunction: function (imageTile, src) {
				return new Promise((resolve) => {
			    let xhr = new XMLHttpRequest();
			    xhr.open('GET', url, true);
			    xhr.onreadystatechange = function () {
			        if (this.readyState == 4) {
			            if (this.status == 200 || this.status == 304) {
			                imageTile.getImage().src = src
			            }
			            if (this.status == 401) {
			                imageTile.getImage().src = tile_401
			            }
			            if (this.status == 402) {
			                imageTile.getImage().src = tile_402
			            }
			            if (this.status == 404) {
			                // imageTile.getImage().src = tile_404
			            }
			            if (this.status == 423) {
			                imageTile.getImage().src = tile_423
			            }
			            resolve()
			        }
			    }
			    xhr.send();
			})
		}
    })
});

map.addLayer(wmtsLayer)·

测试,通过。

当然,看到这里,应该是可以解决问题了。但是补充一点,大家一定会看出来,这里一张瓦片请求了2次。一次是xhr请求,另一次是对image的src赋值。能否进行优化?

当然可以。下面是我使用blob类型请求二进制流图片,然后直接将二进制流图片赋值给image的过程:

tileLoadFunction: function (imageTile, src) {
	  return new Promise((resolve) => {
        var xmlhttp;
        xmlhttp = new XMLHttpRequest();
        xmlhttp.open("GET", src, true);
        xmlhttp.responseType = "blob";
        xmlhttp.onload = function () {
            if (this.status == 200) {
                var blob = this.response;
                (imageTile.getImage()).src = window.URL.createObjectURL(blob);
                imageTile.setState(2);
            }else if (this.status == 404) {
                (imageTile.getImage()).src = './static/images/401.png'
            }
            resolve();
        }
        xmlhttp.send();
    })
}

这里是最终代码。测试,一切正常。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_29722281/article/details/100716935