我们在用OpenLayers客户端来实施的天地图项目、山西平台项目等都遇到一个浏览效果不佳的问题,即已经给图层赋了transitionEffect属性为resize,仍然只有在地图的前面几级有过度效果(1-10级),这样在11级以后的浏览过程中,如果网速不够快,有瓦片不能及时显示出来,就会出现地图显示区域有很多空白,这样浏览的效果就很差。
现在我们来解决这个问题:
首先我们把这个过度效果相关的代码找出来,红色字是OpenLayers原先的代码,蓝色字是我修改后的代码,黄底字是关键位置。
OpenLayers/lib/OpenLayers/Tile/Image.js
/**
* Method: startTransition 开始显示过度效果
* This method is invoked on tiles that are backBuffers for tiles in the
* grid. The grid tile is about to be cleared and a new tile source
* loaded. This is where the transition effect needs to be started
* to provide visual continuity.
*/
* Method: startTransition 开始显示过度效果
* This method is invoked on tiles that are backBuffers for tiles in the
* grid. The grid tile is about to be cleared and a new tile source
* loaded. This is where the transition effect needs to be started
* to provide visual continuity.
*/
startTransition: function() {
// backBufferTile has to be valid and ready to use
if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
return;
}
// backBufferTile has to be valid and ready to use
if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
return;
}
// calculate the ratio of change between the current resolution of the
// backBufferTile and the layer. If several animations happen in a
// row, then the backBufferTile will scale itself appropriately for
// each request.
var ratio = 1;
if (this.backBufferTile.resolution) {
ratio = this.backBufferTile.resolution / this.layer.getResolution();
}
// if the ratio is not the same as it was last time (i.e. we are
// zooming), then we need to adjust the backBuffer tile
// backBufferTile and the layer. If several animations happen in a
// row, then the backBufferTile will scale itself appropriately for
// each request.
var ratio = 1;
if (this.backBufferTile.resolution) {
ratio = this.backBufferTile.resolution / this.layer.getResolution();
}
// if the ratio is not the same as it was last time (i.e. we are
// zooming), then we need to adjust the backBuffer tile
//A
//OpenLayers
//if (ratio != this.lastRatio) {
//if (ratio != this.lastRatio) {
//update by huanggp
if (this.backBufferTile.resolution != this.layer.getResolution()) {
if (this.layer.transitionEffect == 'resize') {
// In this case, we can just immediately resize the
// backBufferTile.
var upperLeft = new OpenLayers.LonLat(
this.backBufferTile.bounds.left,
this.backBufferTile.bounds.top
);
var size = new OpenLayers.Size(
this.backBufferTile.size.w * ratio,
this.backBufferTile.size.h * ratio
);
if (this.layer.transitionEffect == 'resize') {
// In this case, we can just immediately resize the
// backBufferTile.
var upperLeft = new OpenLayers.LonLat(
this.backBufferTile.bounds.left,
this.backBufferTile.bounds.top
);
var size = new OpenLayers.Size(
this.backBufferTile.size.w * ratio,
this.backBufferTile.size.h * ratio
);
var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,
null, px, size);
var imageSize = this.backBufferTile.imageSize;
imageSize = new OpenLayers.Size(imageSize.w * ratio,
imageSize.h * ratio);
var imageOffset = this.backBufferTile.imageOffset;
if(imageOffset) {
imageOffset = new OpenLayers.Pixel(
imageOffset.x * ratio, imageOffset.y * ratio
);
}
OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,
null, px, size);
var imageSize = this.backBufferTile.imageSize;
imageSize = new OpenLayers.Size(imageSize.w * ratio,
imageSize.h * ratio);
var imageOffset = this.backBufferTile.imageOffset;
if(imageOffset) {
imageOffset = new OpenLayers.Pixel(
imageOffset.x * ratio, imageOffset.y * ratio
);
}
OpenLayers.Util.modifyDOMElement(
this.backBufferTile.imgDiv, null, imageOffset, imageSize
) ;
this.backBufferTile.imgDiv, null, imageOffset, imageSize
) ;
this.backBufferTile.show();
}
} else {
// default effect is just to leave the existing tile
// until the new one loads if this is a singleTile and
// there was no change in resolution. Otherwise we
// don't bother to show the backBufferTile at all
if (this.layer.singleTile) {
this.backBufferTile.show();
} else {
this.backBufferTile.hide();
}
}
this.lastRatio = ratio;
}
} else {
// default effect is just to leave the existing tile
// until the new one loads if this is a singleTile and
// there was no change in resolution. Otherwise we
// don't bother to show the backBufferTile at all
if (this.layer.singleTile) {
this.backBufferTile.show();
} else {
this.backBufferTile.hide();
}
}
this.lastRatio = ratio;
},
OpenLayers/lib/OpenLayers/Layer.js
/**
* APIMethod: getResolutionForZoom 根据地图显示级别获取图层当前分辨率
*
* Parameter:
* zoom - {Float}
*
* Returns:
* {Float} A suitable resolution for the specified zoom.
*/
getResolutionForZoom: function(zoom) {
//B
* APIMethod: getResolutionForZoom 根据地图显示级别获取图层当前分辨率
*
* Parameter:
* zoom - {Float}
*
* Returns:
* {Float} A suitable resolution for the specified zoom.
*/
getResolutionForZoom: function(zoom) {
//B
//OpenLayers
//zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
//update by huanggp
if(this.options.topLevel){
zoom = Math.max(0, Math.min(zoom - this.options.topLevel, this.resolutions.length - 1));
}else{
zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
}
var resolution;
if(this.map.fractionalZoom) {
var low = Math.floor(zoom);
var high = Math.ceil(zoom);
resolution = this.resolutions[low] -
((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
} else {
resolution = this.resolutions[Math.round(zoom)];
}
return resolution;
},
if(this.options.topLevel){
zoom = Math.max(0, Math.min(zoom - this.options.topLevel, this.resolutions.length - 1));
}else{
zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
}
var resolution;
if(this.map.fractionalZoom) {
var low = Math.floor(zoom);
var high = Math.ceil(zoom);
resolution = this.resolutions[low] -
((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
} else {
resolution = this.resolutions[Math.round(zoom)];
}
return resolution;
},
从代码上面我们可以看出这个过度效果的机制就是判断当前图层瓦片分辨率与上次浏览的分辨率有没有变化来进行的,OpenLayers在A和B两处有些不合理导致前面我描述的问题。
A:开始过度效果是用if (ratio != this.lastRatio) 做比较,ratio是上次浏览的瓦片的分辨率与当前图层瓦片分辨率变化的比例,这样我们在放大缩小的过程中就会出现连续比例尺变化的比例相等的情况,就不会进行过度。
B:获得图层当前分辨率的方法错误,输入参数zoom是当前地图(map)显示的级别,假设地图的级别范围是(1-20),图层(layer)级别是(7-17),那么根据OpenLayers的代码zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1)); 得到的数组的zoom(index)就是错误<当zoom为7时,得到的index是6,而正确的index结果应该是0>。