More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Layer / Google.js
blob:a/labs/openlayers/lib/OpenLayers/Layer/Google.js -> blob:b/labs/openlayers/lib/OpenLayers/Layer/Google.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/Layer/SphericalMercator.js
  * @requires OpenLayers/Layer/EventPane.js
  * @requires OpenLayers/Layer/FixedZoomLevels.js
  */
   
  /**
  * Class: OpenLayers.Layer.Google
  *
  * Inherits from:
  * - <OpenLayers.Layer.SphericalMercator>
  * - <OpenLayers.Layer.EventPane>
  * - <OpenLayers.Layer.FixedZoomLevels>
  */
  OpenLayers.Layer.Google = OpenLayers.Class(
  OpenLayers.Layer.EventPane,
  OpenLayers.Layer.FixedZoomLevels, {
   
  /**
  * Constant: MIN_ZOOM_LEVEL
  * {Integer} 0
  */
  MIN_ZOOM_LEVEL: 0,
   
  /**
  * Constant: MAX_ZOOM_LEVEL
  * {Integer} 21
  */
  MAX_ZOOM_LEVEL: 21,
   
  /**
  * Constant: RESOLUTIONS
  * {Array(Float)} Hardcode these resolutions so that they are more closely
  * tied with the standard wms projection
  */
  RESOLUTIONS: [
  1.40625,
  0.703125,
  0.3515625,
  0.17578125,
  0.087890625,
  0.0439453125,
  0.02197265625,
  0.010986328125,
  0.0054931640625,
  0.00274658203125,
  0.001373291015625,
  0.0006866455078125,
  0.00034332275390625,
  0.000171661376953125,
  0.0000858306884765625,
  0.00004291534423828125,
  0.00002145767211914062,
  0.00001072883605957031,
  0.00000536441802978515,
  0.00000268220901489257,
  0.0000013411045074462891,
  0.00000067055225372314453
  ],
   
  /**
  * APIProperty: type
  * {GMapType}
  */
  type: null,
   
  /**
  * APIProperty: wrapDateLine
  * {Boolean} Allow user to pan forever east/west. Default is true.
  * Setting this to false only restricts panning if
  * <sphericalMercator> is true.
  */
  wrapDateLine: true,
   
  /**
  * APIProperty: sphericalMercator
  * {Boolean} Should the map act as a mercator-projected map? This will
  * cause all interactions with the map to be in the actual map
  * projection, which allows support for vector drawing, overlaying
  * other maps, etc.
  */
  sphericalMercator: false,
   
  /**
  * Property: version
  * {Number} The version of the Google Maps API
  */
  version: null,
   
  /**
  * Constructor: OpenLayers.Layer.Google
  *
  * Parameters:
  * name - {String} A name for the layer.
  * options - {Object} An optional object whose properties will be set
  * on the layer.
  */
  initialize: function(name, options) {
  options = options || {};
  if(!options.version) {
  options.version = typeof GMap2 === "function" ? "2" : "3";
  }
  var mixin = OpenLayers.Layer.Google["v" +
  options.version.replace(/\./g, "_")];
  if (mixin) {
  OpenLayers.Util.applyDefaults(options, mixin);
  } else {
  throw "Unsupported Google Maps API version: " + options.version;
  }
   
  OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
  if (options.maxExtent) {
  options.maxExtent = options.maxExtent.clone();
  }
   
  OpenLayers.Layer.EventPane.prototype.initialize.apply(this,
  [name, options]);
  OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
  [name, options]);
   
  if (this.sphericalMercator) {
  OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
  this.initMercatorParameters();
  }
  },
   
  /**
  * Method: clone
  * Create a clone of this layer
  *
  * Returns:
  * {<OpenLayers.Layer.Google>} An exact clone of this layer
  */
  clone: function() {
  /**
  * This method isn't intended to be called by a subclass and it
  * doesn't call the same method on the superclass. We don't call
  * the super's clone because we don't want properties that are set
  * on this layer after initialize (i.e. this.mapObject etc.).
  */
  return new OpenLayers.Layer.Google(
  this.name, this.getOptions()
  );
  },
   
  /**
  * APIMethod: setVisibility
  * Set the visibility flag for the layer and hide/show & redraw
  * accordingly. Fire event unless otherwise specified
  *
  * Note that visibility is no longer simply whether or not the layer's
  * style.display is set to "block". Now we store a 'visibility' state
  * property on the layer class, this allows us to remember whether or
  * not we *desire* for a layer to be visible. In the case where the
  * map's resolution is out of the layer's range, this desire may be
  * subverted.
  *
  * Parameters:
  * visible - {Boolean} Display the layer (if in range)
  */
  setVisibility: function(visible) {
  // sharing a map container, opacity has to be set per layer
  var opacity = this.opacity == null ? 1 : this.opacity;
  OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
  this.setOpacity(opacity);
  },
   
  /**
  * APIMethod: display
  * Hide or show the Layer
  *
  * Parameters:
  * display - {Boolean}
  */
  display: function(visible) {
  if (!this._dragging) {
  this.setGMapVisibility(visible);
  }
  OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
  },
   
  /**
  * Method: moveTo
  *
  * Parameters:
  * bound - {<OpenLayers.Bounds>}
  * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
  * do some init work in that case.
  * dragging - {Boolean}
  */
  moveTo: function(bounds, zoomChanged, dragging) {
  this._dragging = dragging;
  OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
  delete this._dragging;
  },
   
  /**
  * APIMethod: setOpacity
  * Sets the opacity for the entire layer (all images)
  *
  * Parameter:
  * opacity - {Float}
  */
  setOpacity: function(opacity) {
  if (opacity !== this.opacity) {
  if (this.map != null) {
  this.map.events.triggerEvent("changelayer", {
  layer: this,
  property: "opacity"
  });
  }
  this.opacity = opacity;
  }
  // Though this layer's opacity may not change, we're sharing a container
  // and need to update the opacity for the entire container.
  if (this.getVisibility()) {
  var container = this.getMapContainer();
  OpenLayers.Util.modifyDOMElement(
  container, null, null, null, null, null, null, opacity
  );
  }
  },
   
  /**
  * APIMethod: destroy
  * Clean up this layer.
  */
  destroy: function() {
  /**
  * We have to override this method because the event pane destroy
  * deletes the mapObject reference before removing this layer from
  * the map.
  */
  if (this.map) {
  this.setGMapVisibility(false);
  var cache = OpenLayers.Layer.Google.cache[this.map.id];
  if (cache && cache.count <= 1) {
  this.removeGMapElements();
  }
  }
  OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
  },
   
  /**
  * Method: removeGMapElements
  * Remove all elements added to the dom. This should only be called if
  * this is the last of the Google layers for the given map.
  */
  removeGMapElements: function() {
  var cache = OpenLayers.Layer.Google.cache[this.map.id];
  if (cache) {
  // remove shared elements from dom
  var container = this.mapObject && this.getMapContainer();
  if (container && container.parentNode) {
  container.parentNode.removeChild(container);
  }
  var termsOfUse = cache.termsOfUse;
  if (termsOfUse && termsOfUse.parentNode) {
  termsOfUse.parentNode.removeChild(termsOfUse);
  }
  var poweredBy = cache.poweredBy;
  if (poweredBy && poweredBy.parentNode) {
  poweredBy.parentNode.removeChild(poweredBy);
  }
  }
  },
   
  /**
  * APIMethod: removeMap
  * On being removed from the map, also remove termsOfUse and poweredBy divs
  *
  * Parameters:
  * map - {<OpenLayers.Map>}
  */
  removeMap: function(map) {
  // hide layer before removing
  if (this.visibility && this.mapObject) {
  this.setGMapVisibility(false);
  }
  // check to see if last Google layer in this map
  var cache = OpenLayers.Layer.Google.cache[map.id];
  if (cache) {
  if (cache.count <= 1) {
  this.removeGMapElements();
  delete OpenLayers.Layer.Google.cache[map.id];
  } else {
  // decrement the layer count
  --cache.count;
  }
  }
  // remove references to gmap elements
  delete this.termsOfUse;
  delete this.poweredBy;
  delete this.mapObject;
  delete this.dragObject;
  OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
  },
   
  //
  // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
  //
   
  /**
  * APIMethod: getOLBoundsFromMapObjectBounds
  *
  * Parameters:
  * moBounds - {Object}
  *
  * Returns:
  * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the
  * passed-in MapObject Bounds.
  * Returns null if null value is passed in.
  */
  getOLBoundsFromMapObjectBounds: function(moBounds) {
  var olBounds = null;
  if (moBounds != null) {
  var sw = moBounds.getSouthWest();
  var ne = moBounds.getNorthEast();
  if (this.sphericalMercator) {
  sw = this.forwardMercator(sw.lng(), sw.lat());
  ne = this.forwardMercator(ne.lng(), ne.lat());
  } else {
  sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
  ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
  }
  olBounds = new OpenLayers.Bounds(sw.lon,
  sw.lat,
  ne.lon,
  ne.lat );
  }
  return olBounds;
  },
   
  /**
  * APIMethod: getWarningHTML
  *
  * Returns:
  * {String} String with information on why layer is broken, how to get
  * it working.
  */
  getWarningHTML:function() {
  return OpenLayers.i18n("googleWarning");
  },
   
   
  /************************************
  * *
  * MapObject Interface Controls *
  * *
  ************************************/
   
   
  // Get&Set Center, Zoom
   
  /**
  * APIMethod: getMapObjectCenter
  *
  * Returns:
  * {Object} The mapObject's current center in Map Object format
  */
  getMapObjectCenter: function() {
  return this.mapObject.getCenter();
  },
   
  /**
  * APIMethod: getMapObjectZoom
  *
  * Returns:
  * {Integer} The mapObject's current zoom, in Map Object format
  */
  getMapObjectZoom: function() {
  return this.mapObject.getZoom();
  },