diff options
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Tile/Image.js')
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Tile/Image.js | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Tile/Image.js b/misc/openlayers/lib/OpenLayers/Tile/Image.js new file mode 100644 index 0000000..2fdffb3 --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Tile/Image.js @@ -0,0 +1,510 @@ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + + +/** + * @requires OpenLayers/Tile.js + * @requires OpenLayers/Animation.js + * @requires OpenLayers/Util.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, { + + /** + * APIProperty: events + * {<OpenLayers.Events>} An events object that handles all + * events on the tile. + * + * Register a listener for a particular event with the following syntax: + * (code) + * tile.events.register(type, obj, listener); + * (end) + * + * Supported event types (in addition to the <OpenLayers.Tile> events): + * beforeload - Triggered before an image is prepared for loading, when the + * url for the image is known already. Listeners may call <setImage> on + * the tile instance. If they do so, that image will be used and no new + * one will be created. + */ + + /** + * APIProperty: url + * {String} The URL of the image being requested. No default. Filled in by + * layer.getURL() function. May be modified by loadstart listeners. + */ + url: null, + + /** + * Property: imgDiv + * {HTMLImageElement} The image for this tile. + */ + imgDiv: null, + + /** + * Property: frame + * {DOMElement} The image element is appended to the frame. Any gutter on + * the image will be hidden behind the frame. If no gutter is set, + * this will be null. + */ + frame: null, + + /** + * Property: imageReloadAttempts + * {Integer} Attempts to load the image. + */ + imageReloadAttempts: null, + + /** + * Property: layerAlphaHack + * {Boolean} True if the png alpha hack needs to be applied on the layer's div. + */ + layerAlphaHack: null, + + /** + * Property: asyncRequestId + * {Integer} ID of an request to see if request is still valid. This is a + * number which increments by 1 for each asynchronous request. + */ + asyncRequestId: null, + + /** + * APIProperty: maxGetUrlLength + * {Number} If set, requests that would result in GET urls with more + * characters than the number provided will be made using form-encoded + * HTTP POST. It is good practice to avoid urls that are longer than 2048 + * characters. + * + * Caution: + * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most + * Opera versions do not fully support this option. On all browsers, + * transition effects are not supported if POST requests are used. + */ + maxGetUrlLength: null, + + /** + * Property: canvasContext + * {CanvasRenderingContext2D} A canvas context associated with + * the tile image. + */ + canvasContext: null, + + /** + * APIProperty: crossOriginKeyword + * The value of the crossorigin keyword to use when loading images. This is + * only relevant when using <getCanvasContext> for tiles from remote + * origins and should be set to either 'anonymous' or 'use-credentials' + * for servers that send Access-Control-Allow-Origin headers with their + * tiles. + */ + crossOriginKeyword: 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>} + * options - {Object} + */ + initialize: function(layer, position, bounds, url, size, options) { + OpenLayers.Tile.prototype.initialize.apply(this, arguments); + + this.url = url; //deprecated remove me + + this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); + + if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { + // only create frame if it's needed + this.frame = document.createElement("div"); + this.frame.style.position = "absolute"; + this.frame.style.overflow = "hidden"; + } + if (this.maxGetUrlLength != null) { + OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); + } + }, + + /** + * APIMethod: destroy + * nullify references to prevent circular references and memory leaks + */ + destroy: function() { + if (this.imgDiv) { + this.clear(); + this.imgDiv = null; + this.frame = null; + } + // don't handle async requests any more + this.asyncRequestId = null; + OpenLayers.Tile.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: draw + * Check that a tile should be drawn, and draw it. + * + * Returns: + * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned + * false. + */ + draw: function() { + var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); + if (shouldDraw) { + // The layer's reproject option is deprecated. + if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { + // getBoundsFromBaseLayer is defined in deprecated.js. + this.bounds = this.getBoundsFromBaseLayer(this.position); + } + if (this.isLoading) { + //if we're already loading, send 'reload' instead of 'loadstart'. + this._loadEvent = "reload"; + } else { + this.isLoading = true; + this._loadEvent = "loadstart"; + } + this.renderTile(); + this.positionTile(); + } else if (shouldDraw === false) { + this.unload(); + } + return shouldDraw; + }, + + /** + * Method: renderTile + * Internal function to actually initialize the image tile, + * position it correctly, and set its url. + */ + renderTile: function() { + if (this.layer.async) { + // Asynchronous image requests call the asynchronous getURL method + // on the layer to fetch an image that covers 'this.bounds'. + var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1; + this.layer.getURLasync(this.bounds, function(url) { + if (id == this.asyncRequestId) { + this.url = url; + this.initImage(); + } + }, this); + } else { + // synchronous image requests get the url immediately. + this.url = this.layer.getURL(this.bounds); + this.initImage(); + } + }, + + /** + * Method: positionTile + * 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. + */ + positionTile: function() { + var style = this.getTile().style, + size = this.frame ? this.size : + this.layer.getImageSize(this.bounds), + ratio = 1; + if (this.layer instanceof OpenLayers.Layer.Grid) { + ratio = this.layer.getServerResolution() / this.layer.map.getResolution(); + } + style.left = this.position.x + "px"; + style.top = this.position.y + "px"; + style.width = Math.round(ratio * size.w) + "px"; + style.height = Math.round(ratio * size.h) + "px"; + }, + + /** + * Method: clear + * Remove the tile from the DOM, clear it of any image related data so that + * it can be reused in a new location. + */ + clear: function() { + OpenLayers.Tile.prototype.clear.apply(this, arguments); + var img = this.imgDiv; + if (img) { + var tile = this.getTile(); + if (tile.parentNode === this.layer.div) { + this.layer.div.removeChild(tile); + } + this.setImgSrc(); + if (this.layerAlphaHack === true) { + img.style.filter = ""; + } + OpenLayers.Element.removeClass(img, "olImageLoadError"); + } + this.canvasContext = null; + }, + + /** + * Method: getImage + * Returns or creates and returns the tile image. + */ + getImage: function() { + if (!this.imgDiv) { + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); + + var style = this.imgDiv.style; + if (this.frame) { + var left = 0, top = 0; + if (this.layer.gutter) { + left = this.layer.gutter / this.layer.tileSize.w * 100; + top = this.layer.gutter / this.layer.tileSize.h * 100; + } + style.left = -left + "%"; + style.top = -top + "%"; + style.width = (2 * left + 100) + "%"; + style.height = (2 * top + 100) + "%"; + } + style.visibility = "hidden"; + style.opacity = 0; + if (this.layer.opacity < 1) { + style.filter = 'alpha(opacity=' + + (this.layer.opacity * 100) + + ')'; + } + style.position = "absolute"; + if (this.layerAlphaHack) { + // move the image out of sight + style.paddingTop = style.height; + style.height = "0"; + style.width = "100%"; + } + if (this.frame) { + this.frame.appendChild(this.imgDiv); + } + } + + return this.imgDiv; + }, + + /** + * APIMethod: setImage + * Sets the image element for this tile. This method should only be called + * from beforeload listeners. + * + * Parameters + * img - {HTMLImageElement} The image to use for this tile. + */ + setImage: function(img) { + this.imgDiv = img; + }, + + /** + * Method: initImage + * Creates the content for the frame on the tile. + */ + initImage: function() { + if (!this.url && !this.imgDiv) { + // fast path out - if there is no tile url and no previous image + this.isLoading = false; + return; + } + this.events.triggerEvent('beforeload'); + this.layer.div.appendChild(this.getTile()); + this.events.triggerEvent(this._loadEvent); + var img = this.getImage(); + var src = img.getAttribute('src') || ''; + if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) { + this._loadTimeout = window.setTimeout( + OpenLayers.Function.bind(this.onImageLoad, this), 0 + ); + } else { + this.stopLoading(); + if (this.crossOriginKeyword) { + img.removeAttribute("crossorigin"); + } + OpenLayers.Event.observe(img, "load", + OpenLayers.Function.bind(this.onImageLoad, this) + ); + OpenLayers.Event.observe(img, "error", + OpenLayers.Function.bind(this.onImageError, this) + ); + this.imageReloadAttempts = 0; + this.setImgSrc(this.url); + } + }, + + /** + * Method: setImgSrc + * Sets the source for the tile image + * + * Parameters: + * url - {String} or undefined to hide the image + */ + setImgSrc: function(url) { + var img = this.imgDiv; + if (url) { + img.style.visibility = 'hidden'; + img.style.opacity = 0; + // don't set crossOrigin if the url is a data URL + if (this.crossOriginKeyword) { + if (url.substr(0, 5) !== 'data:') { + img.setAttribute("crossorigin", this.crossOriginKeyword); + } else { + img.removeAttribute("crossorigin"); + } + } + img.src = url; + } else { + // Remove reference to the image, and leave it to the browser's + // caching and garbage collection. + this.stopLoading(); + this.imgDiv = null; + if (img.parentNode) { + img.parentNode.removeChild(img); + } + } + }, + + /** + * Method: getTile + * Get the tile's markup. + * + * Returns: + * {DOMElement} The tile's markup + */ + getTile: function() { + return this.frame ? this.frame : this.getImage(); + }, + + /** + * Method: createBackBuffer + * Create a backbuffer for this tile. A backbuffer isn't exactly a clone + * of the tile's markup, because we want to avoid the reloading of the + * image. So we clone the frame, and steal the image from the tile. + * + * Returns: + * {DOMElement} The markup, or undefined if the tile has no image + * or if it's currently loading. + */ + createBackBuffer: function() { + if (!this.imgDiv || this.isLoading) { + return; + } + var backBuffer; + if (this.frame) { + backBuffer = this.frame.cloneNode(false); + backBuffer.appendChild(this.imgDiv); + } else { + backBuffer = this.imgDiv; + } + this.imgDiv = null; + return backBuffer; + }, + + /** + * Method: onImageLoad + * Handler for the image onload event + */ + onImageLoad: function() { + var img = this.imgDiv; + this.stopLoading(); + img.style.visibility = 'inherit'; + img.style.opacity = this.layer.opacity; + this.isLoading = false; + this.canvasContext = null; + this.events.triggerEvent("loadend"); + + if (this.layerAlphaHack === true) { + img.style.filter = + "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + + img.src + "', sizingMethod='scale')"; + } + }, + + /** + * Method: onImageError + * Handler for the image onerror event + */ + onImageError: function() { + var img = this.imgDiv; + if (img.src != null) { + this.imageReloadAttempts++; + if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) { + this.setImgSrc(this.layer.getURL(this.bounds)); + } else { + OpenLayers.Element.addClass(img, "olImageLoadError"); + this.events.triggerEvent("loaderror"); + this.onImageLoad(); + } + } + }, + + /** + * Method: stopLoading + * Stops a loading sequence so <onImageLoad> won't be executed. + */ + stopLoading: function() { + OpenLayers.Event.stopObservingElement(this.imgDiv); + window.clearTimeout(this._loadTimeout); + delete this._loadTimeout; + }, + + /** + * APIMethod: getCanvasContext + * Returns a canvas context associated with the tile image (with + * the image drawn on it). + * Returns undefined if the browser does not support canvas, if + * the tile has no image or if it's currently loading. + * + * The function returns a canvas context instance but the + * underlying canvas is still available in the 'canvas' property: + * (code) + * var context = tile.getCanvasContext(); + * if (context) { + * var data = context.canvas.toDataURL('image/jpeg'); + * } + * (end) + * + * Returns: + * {Boolean} + */ + getCanvasContext: function() { + if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) { + if (!this.canvasContext) { + var canvas = document.createElement("canvas"); + canvas.width = this.size.w; + canvas.height = this.size.h; + this.canvasContext = canvas.getContext("2d"); + this.canvasContext.drawImage(this.imgDiv, 0, 0); + } + return this.canvasContext; + } + }, + + CLASS_NAME: "OpenLayers.Tile.Image" + +}); + +/** + * Constant: OpenLayers.Tile.Image.IMAGE + * {HTMLImageElement} The image for a tile. + */ +OpenLayers.Tile.Image.IMAGE = (function() { + var img = new Image(); + img.className = "olTileImage"; + // avoid image gallery menu in IE6 + img.galleryImg = "no"; + return img; +}()); + |