More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Tile / Image.js
blob:a/labs/openlayers/lib/OpenLayers/Tile/Image.js -> blob:b/labs/openlayers/lib/OpenLayers/Tile/Image.js
  /* 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,