|
/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
|
* full list of contributors). Published under the Clear BSD license. |
|
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
|
* full text of the license. */ |
|
|
|
|
|
/** |
|
* @requires OpenLayers/Tile.js |
|
*/ |
|
|
|
/** |
|
* Class: OpenLayers.Tile.Image |
|
* Instances of OpenLayers.Tile.Image are used to manage the image tiles |
|
* used by various layers. Create a new image tile with the |
|
* <OpenLayers.Tile.Image> constructor. |
|
* |
|
* Inherits from: |
|
* - <OpenLayers.Tile> |
|
*/ |
|
OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { |
|
|
|
/** |
|
* Property: url |
|
* {String} The URL of the image being requested. No default. Filled in by |
|
* layer.getURL() function. |
|
*/ |
|
url: null, |
|
|
|
/** |
|
* Property: imgDiv |
|
* {DOMElement} The div element which wraps the image. |
|
*/ |
|
imgDiv: null, |
|
|
|
/** |
|
* Property: frame |
|
* {DOMElement} The image element is appended to the frame. Any gutter on |
|
* the image will be hidden behind the frame. |
|
*/ |
|
frame: null, |
|
|
|
/** |
|
* Property: layerAlphaHack |
|
* {Boolean} True if the png alpha hack needs to be applied on the layer's div. |
|
*/ |
|
layerAlphaHack: null, |
|
|
|
/** |
|
* Property: isBackBuffer |
|
* {Boolean} Is this tile a back buffer tile? |
|
*/ |
|
isBackBuffer: false, |
|
|
|
/** |
|
* Property: lastRatio |
|
* {Float} Used in transition code only. This is the previous ratio |
|
* of the back buffer tile resolution to the map resolution. Compared |
|
* with the current ratio to determine if zooming occurred. |
|
*/ |
|
lastRatio: 1, |
|
|
|
/** |
|
* Property: isFirstDraw |
|
* {Boolean} Is this the first time the tile is being drawn? |
|
* This is used to force resetBackBuffer to synchronize |
|
* the backBufferTile with the foreground tile the first time |
|
* the foreground tile loads so that if the user zooms |
|
* before the layer has fully loaded, the backBufferTile for |
|
* tiles that have been loaded can be used. |
|
*/ |
|
isFirstDraw: true, |
|
|
|
/** |
|
* Property: backBufferTile |
|
* {<OpenLayers.Tile>} A clone of the tile used to create transition |
|
* effects when the tile is moved or changes resolution. |
|
*/ |
|
backBufferTile: null, |
|
|
|
/** TBD 3.0 - reorder the parameters to the init function to remove |
|
* URL. the getUrl() function on the layer gets called on |
|
* each draw(), so no need to specify it here. |
|
* |
|
* Constructor: OpenLayers.Tile.Image |
|
* Constructor for a new <OpenLayers.Tile.Image> instance. |
|
* |
|
* Parameters: |
|
* layer - {<OpenLayers.Layer>} layer that the tile will go in. |
|
* position - {<OpenLayers.Pixel>} |
|
* bounds - {<OpenLayers.Bounds>} |
|
* url - {<String>} Deprecated. Remove me in 3.0. |
|
* size - {<OpenLayers.Size>} |
|
*/ |
|
initialize: function(layer, position, bounds, url, size) { |
|
OpenLayers.Tile.prototype.initialize.apply(this, arguments); |
|
|
|
this.url = url; //deprecated remove me |
|
|
|
this.frame = document.createElement('div'); |
|
this.frame.style.overflow = 'hidden'; |
|
this.frame.style.position = 'absolute'; |
|
|
|
this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); |
|
}, |
|
|
|
/** |
|
* APIMethod: destroy |
|
* nullify references to prevent circular references and memory leaks |
|
*/ |
|
destroy: function() { |
|
if (this.imgDiv != null) { |
|
if (this.layerAlphaHack) { |
|
// unregister the "load" handler |
|
OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0]); |
|
} |
|
|
|
// unregister the "load" and "error" handlers. Only the "error" handler if |
|
// this.layerAlphaHack is true. |
|
OpenLayers.Event.stopObservingElement(this.imgDiv); |
|
|
|
if (this.imgDiv.parentNode == this.frame) { |
|
this.frame.removeChild(this.imgDiv); |
|
this.imgDiv.map = null; |
|
} |
|
this.imgDiv.urls = null; |
|
// abort any currently loading image |
|
this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; |
|
} |
|
this.imgDiv = null; |
|
if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) { |
|
this.layer.div.removeChild(this.frame); |
|
} |
|
this.frame = null; |
|
|
|
/* clean up the backBufferTile if it exists */ |
|
if (this.backBufferTile) { |
|
this.backBufferTile.destroy(); |
|
this.backBufferTile = null; |
|
} |
|
|
|
this.layer.events.unregister("loadend", this, this.resetBackBuffer); |
|
|
|
OpenLayers.Tile.prototype.destroy.apply(this, arguments); |
|
}, |
|
|
|
/** |
|
* Method: clone |
|
* |
|
* Parameters: |
|
* obj - {<OpenLayers.Tile.Image>} The tile to be cloned |
|
* |
|
* Returns: |
|
* {<OpenLayers.Tile.Image>} An exact clone of this <OpenLayers.Tile.Image> |
|
*/ |
|
clone: function (obj) { |
|
if (obj == null) { |
|
obj = new OpenLayers.Tile.Image(this.layer, |
|
this.position, |
|
this.bounds, |
|
this.url, |
|
this.size); |
|
} |
|
|
|
//pick up properties from superclass |
|
obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]); |
|
|
|
//dont want to directly copy the image div |
|
obj.imgDiv = null; |
|
|
|
|
|
return obj; |
|
}, |
|
|
|
/** |
|
* Method: draw |
|
* Check that a tile should be drawn, and draw it. |
|
* |
|
* Returns: |
|
* {Boolean} Always returns true. |
|
*/ |
|
draw: function() { |
|
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { |
|
this.bounds = this.getBoundsFromBaseLayer(this.position); |
|
} |
|
var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments); |
|
|
|
if ((OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) || |
|
this.layer.singleTile) { |
|
if (drawTile) { |
|
//we use a clone of this tile to create a double buffer for visual |
|
//continuity. The backBufferTile is used to create transition |
|
//effects while the tile in the grid is repositioned and redrawn |
|
if (!this.backBufferTile) { |
|
this.backBufferTile = this.clone(); |
|
this.backBufferTile.hide(); |
|
// this is important. It allows the backBuffer to place itself |
|
// appropriately in the DOM. The Image subclass needs to put |
|
// the backBufferTile behind the main tile so the tiles can |
|
// load over top and display as soon as they are loaded. |
|
this.backBufferTile.isBackBuffer = true; |
|
|
|
// potentially end any transition effects when the tile loads |
|
this.events.register('loadend', this, this.resetBackBuffer); |
|
|
|
// clear transition back buffer tile only after all tiles in |
|
// this layer have loaded to avoid visual glitches |
|
this.layer.events.register("loadend", this, this.resetBackBuffer); |
|
} |
|
// run any transition effects |
|
this.startTransition(); |
|
} else { |
|
// if we aren't going to draw the tile, then the backBuffer should |
|
// be hidden too! |
|
if (this.backBufferTile) { |
|
this.backBufferTile.clear(); |
|
} |
|
} |
|
} else { |
|
if (drawTile && this.isFirstDraw) { |
|
this.events.register('loadend', this, this.showTile); |
|
this.isFirstDraw = false; |
|
} |
|
} |
|
|
|
if (!drawTile) { |
|
return false; |
|
} |
|
|
|
if (this.isLoading) { |
|
//if we're already loading, send 'reload' instead of 'loadstart'. |
|
this.events.triggerEvent("reload"); |
|
} else { |
|
this.isLoading = true; |
|
this.events.triggerEvent("loadstart"); |
|
} |
|
|
|
return this.renderTile(); |
|
}, |
|
|
|
/** |
|
* Method: resetBackBuffer |
|
* Triggered by two different events, layer loadend, and tile loadend. |
|
* In any of these cases, we check to see if we can hide the |
|
* backBufferTile yet and update its parameters to match the |
|
* foreground tile. |
|
* |
|
* Basic logic: |
|
* - If the backBufferTile hasn't been drawn yet, reset it |
|
* - If layer is still loading, show foreground tile but don't hide |
|
* the backBufferTile yet |
|
* - If layer is done loading, reset backBuffer tile and show |
|
* foreground tile |
|
*/ |
|
resetBackBuffer: function() { |
|
this.showTile(); |
|
if (this.backBufferTile && |
|
(this.isFirstDraw || !this.layer.numLoadingTiles)) { |
|
this.isFirstDraw = false; |
|
// check to see if the backBufferTile is within the max extents |
|
// before rendering it |
|
var maxExtent = this.layer.maxExtent; |
|
var withinMaxExtent = (maxExtent && |
|
this.bounds.intersectsBounds(maxExtent, false)); |
|
if (withinMaxExtent) { |
|
this.backBufferTile.position = this.position; |
|
this.backBufferTile.bounds = this.bounds; |
|
this.backBufferTile.size = this.size; |
|
this.backBufferTile.imageSize = this.layer.getImageSize(this.bounds) || this.size; |
|
this.backBufferTile.imageOffset = this.layer.imageOffset; |
|
this.backBufferTile.resolution = this.layer.getResolution(); |
|
this.backBufferTile.renderTile(); |
|
} |
|
|
|
this.backBufferTile.hide(); |
|
} |
|
}, |
|
|
|
/** |
|
* Method: renderTile |
|
* Internal function to actually initialize the image tile, |
|
* position it correctly, and set its url. |
|
*/ |
|
renderTile: function() { |
|
if (this.imgDiv == null) { |
|
this.initImgDiv(); |
|
} |
|
|
|
this.imgDiv.viewRequestID = this.layer.map.viewRequestID; |
|
|
|
if (this.layer.async) { |
|
// Asyncronous image requests call the asynchronous getURL method |
|
// on the layer to fetch an image that covers 'this.bounds', in the scope of |
|
// 'this', setting the 'url' property of the layer itself, and running |
|
// the callback 'positionFrame' when the image request returns. |
|
this.layer.getURLasync(this.bounds, this, "url", this.positionImage); |
|
} else { |
|
// syncronous image requests get the url and position the frame immediately, |
|
// and don't wait for an image request to come back. |
|
|
|
// needed for changing to a different server for onload error |
|
if (this.layer.url instanceof Array) { |
|
this.imgDiv.urls = this.layer.url.slice(); |
|
} |
|
|
|
this.url = this.layer.getURL(this.bounds); |
|
|
|
// position the frame immediately |
|
this.positionImage(); |
|
} |
|
return true; |
|
}, |
|
|
|
/** |
|
* Method: positionImage |
|
* Using the properties currenty set on the layer, position the tile correctly. |
|
* This method is used both by the async and non-async versions of the Tile.Image |
|
* code. |
|
*/ |
|
positionImage: function() { |
|
// if the this layer doesn't exist at the point the image is |
|
// returned, do not attempt to use it for size computation |
|
if (this.layer === null) { |
|
return; |
|
} |
|
// position the frame |
|
OpenLayers.Util.modifyDOMElement(this.frame, |
|
null, this.position, this.size); |
|
|
|
var imageSize = this.layer.getImageSize(this.bounds); |
|
if (this.layerAlphaHack) { |
|
OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, |
|
null, null, imageSize, this.url); |
|
} else { |
|
OpenLayers.Util.modifyDOMElement(this.imgDiv, |
|
null, null, imageSize) ; |
|
this.imgDiv.src = this.url; |
|
} |
|
}, |
|
|
|
/** |
|
* Method: clear |
|
* Clear the tile of any bounds/position-related data so that it can |
|
* be reused in a new location. |
|
*/ |
|
clear: function() { |
|
if(this.imgDiv) { |
|
this.hide(); |
|
if (OpenLayers.Tile.Image.useBlankTile) { |
|
this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Method: initImgDiv |
|
* Creates the imgDiv property on the tile. |
|
*/ |
|
initImgDiv: function() { |
|
|
|
var offset = this.layer.imageOffset; |
|
var size = this.layer.getImageSize(this.bounds); |
|
|
|
if (this.layerAlphaHack) { |
|
this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null, |
|
offset, |
|
size, |
|
null, |
|