diff --git a/lib/OpenLayers/Layer/VirtualEarth.js b/lib/OpenLayers/Layer/VirtualEarth.js
index 5398b07b92..445b7d82b8 100644
--- a/lib/OpenLayers/Layer/VirtualEarth.js
+++ b/lib/OpenLayers/Layer/VirtualEarth.js
@@ -1,12 +1,55 @@
/* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
* See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
* text of the license. */
+
// @require: OpenLayers/Layer.js
-
-// load VE map control script
-document.write("");
-
-
+
+if (typeof VEMap != "undefined") {
+
+ /** Hack-on function because VE does not give it to us
+ *
+ * @param {VELatLong} veLatLng
+ *
+ * @returns A Pixel specifying veLatLong translated into "Container" coords
+ * @type Pixel
+ */
+ VEMap.prototype.fromLatLngToContainerPixel = function(veLatLong) {
+
+ // first we translate into "DivPixel"
+ var pixel = this.LatLongToPixel(veLatLong);
+
+ // locate the upper-left tile
+ var tile = this.mapelement.firstChild.childNodes[2];
+
+ // adjust by the offset of tile
+ pixel.x += tile.offsetLeft;
+ pixel.y += tile.offsetTop;
+
+ return pixel;
+ };
+
+ /** Hack-on function because VE does not give it to us
+ *
+ * @param {Pixel} pixel
+ *
+ * @returns A VELatLong specifying "Container" pixel translated to lonlat
+ * @type VELatLong
+ */
+ VEMap.prototype.fromContainerPixelToLatLng = function(pixel) {
+
+ // locate the upper-left tile
+ var tile = this.mapelement.firstChild.childNodes[2];
+
+ // adjust by the offset of tile
+ pixel.x -= tile.offsetLeft;
+ pixel.y -= tile.offsetTop;
+
+ // first we translate into "DivPixel"
+ return this.PixelToLatLong(pixel);
+ };
+
+}
+
/**
* @class
*/
@@ -74,20 +117,74 @@ OpenLayers.Layer.VirtualEarth.prototype =
veDiv.style.height = sz.h;
this.div.appendChild(veDiv);
- // create VEMap, hide nav controls
- this.vemap = new VEMap(this.name);
- this.vemap.LoadMap();
- this.vemap.HideDashboard();
-
- // catch pans and zooms from VE Map
- this.vemap.AttachEvent("onendcontinuouspan",
- this.catchPanZoom.bindAsEventListener(this));
- this.vemap.AttachEvent("onendzoom",
- this.catchPanZoom.bindAsEventListener(this));
-
-
+ try {
+
+ // create VEMap, hide nav controls
+ this.vemap = new VEMap(this.name);
+ this.vemap.LoadMap();
+ this.vemap.HideDashboard();
+
+ // catch pans and zooms from VE Map
+ this.vemap.AttachEvent("onendcontinuouspan",
+ this.catchPanZoom.bindAsEventListener(this));
+ this.vemap.AttachEvent("onendzoom",
+ this.catchPanZoom.bindAsEventListener(this));
+ } catch (e) {
+ this.loadWarningMessage();
+ }
+
},
+ /** If we can't load the vemap, then display an error message to the
+ * user and tell them where to go for help.
+ *
+ * @private
+ *
+ */
+ loadWarningMessage:function() {
+
+ this.div.style.backgroundColor = "red";
+
+ var html = "";
+ html += "The VE Layer was unable to load correctly.
";
+ html += "
";
+ html += "To get rid of this message, click on the VE Layer's "
+ html += "tab in the layer switcher in the upper-right corner.
";
+ html += "
";
+ html += "Most likely, this is because the VE library";
+ html += " script was either not correctly included.
";
+ html += "
";
+ html += "Developers: For help getting this working correctly, ";
+ html += "";
+ html += "click here";
+ html += "";
+
+ var viewSize = this.map.getSize();
+
+ msgW = Math.min(viewSize.w, 300);
+ msgH = Math.min(viewSize.h, 200);
+ var size = new OpenLayers.Size(msgW, msgH);
+
+ var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
+
+ var topLeft = centerPx.add(-size.w/2, -size.h/2);
+
+ var div = OpenLayers.Util.createDiv("veWarning",
+ topLeft,
+ size,
+ null,
+ null,
+ null,
+ "auto");
+
+ div.style.padding = "7px";
+ div.style.backgroundColor = "yellow";
+
+ div.innerHTML = html;
+ this.div.appendChild(div);
+ },
+
/**
* @param {event} e
*/
@@ -95,13 +192,256 @@ OpenLayers.Layer.VirtualEarth.prototype =
var veCenter = this.vemap.GetCenter();
var veZoom = this.vemap.GetZoomLevel();
- var olCenter = new OpenLayers.LonLat(veCenter.Longitude,
- veCenter.Latitude);
-
- this.map.setCenter(olCenter, veZoom - 1);
-
+ var olCenter = this.getOLLonLatFromVELatLong(veCenter);
+ var olZoom = this.getOLZoomFromVEZoom(veZoom);
+
+ this.map.setCenter(olCenter, olZoom);
},
-
+
+
+
+ /********************************************************/
+ /* */
+ /* Baselayer Functions */
+ /* */
+ /********************************************************/
+
+ /**
+ * @param {OpenLayers.Pixel} viewPortPx
+ *
+ * @returns An OpenLayers.LonLat which is the passed-in view port
+ * OpenLayers.Pixel, translated into lon/lat by VE
+ * If vemap is not loaded, returns null.
+ * @type OpenLayers.LonLat
+ */
+ getLonLatFromViewPortPx: function (viewPortPx) {
+ var lonlat = null;
+ if (this.vemap != null) {
+ var pixel = this.getPixelFromOLPixel(viewPortPx);
+
+ // note we use special hacked function here
+ var veLatLong = this.vemap.fromContainerPixelToLatLng(pixel)
+
+ lonlat = this.getOLLonLatFromVELatLong(veLatLong);
+ }
+ return lonlat;
+ },
+
+
+ /**
+ * @param {OpenLayers.LonLat} lonlat
+ *
+ * @returns An OpenLayers.Pixel which is the passed-in OpenLayers.LonLat,
+ * translated into view port pixels BY VE
+ * If vemap is not loaded, returns null.
+ * @type OpenLayers.Pixel
+ */
+ getViewPortPxFromLonLat: function (lonlat) {
+ var viewPortPx = null;
+ if (this.vemap != null) {
+ var veLatLong = this.getVELatLongFromOLLonLat(lonlat);
+
+ // note we use special hacked function here
+ var pixel = this.vemap.fromLatLngToContainerPixel(veLatLong);
+
+ viewPortPx = this.getOLPixelFromPixel(pixel);
+ }
+ return viewPortPx;
+ },
+
+
+ /**
+ * @param {OpenLayers.Bounds} bounds
+ *
+ * @returns Corresponding zoom level for a specified Bounds.
+ * If vemap is not loaded, returns null.
+ * @type int
+ */
+ getZoomForExtent: function (bounds) {
+ var zoom = null;
+ if (this.vemap != null) {
+ var gBounds = this.getVELatLongBoundsFromOLBounds(bounds);
+ var gZoom = this.vemap.getBoundsZoomLevel(gBounds);
+ zoom = this.getOLZoomFromGZoom(gZoom);
+ }
+ return zoom;
+ },
+
+ /**
+ * @returns A Bounds object which represents the lon/lat bounds of the
+ * current viewPort.
+ * If vemap is not loaded, returns null
+ * @type OpenLayers.Bounds
+ */
+ getExtent: function () {
+ var extent = null;
+ if (this.vemap != null) {
+ if (this.vemap.getCenter() == null) {
+ this.moveTo();
+ }
+ var veLatLongBounds = this.vemap.getBounds();
+ extent = this.getOLBoundsFromVELatLongBounds(veLatLongBounds);
+ }
+ return extent;
+ },
+
+
+ /********************************************************/
+ /* */
+ /* Translation Functions */
+ /* */
+ /* The following functions translate GMaps and OL */
+ /* formats for Pixel, LonLat, Bounds, and Zoom */
+ /* */
+ /********************************************************/
+
+ //
+ // TRANSLATION: GZoom <-> OpenLayers Zoom
+ //
+
+ /**
+ * @param {int} veZoom
+ *
+ * @returns An OpenLayers Zoom level, translated from the passed in veZoom
+ * Returns null if null value is passed in
+ * @type int
+ */
+ getOLZoomFromVEZoom: function(veZoom) {
+ var zoom = null;
+ if (veZoom != null) {
+ zoom = veZoom - 1;
+ }
+ return zoom;
+ },
+
+ /**
+ * @param {int} olZoom
+ *
+ * @returns A VEZoom level, translated from the passed in olZoom
+ * Returns null if null value is passed in
+ * @type int
+ */
+ getVEZoomFromOLZoom: function(olZoom) {
+ var zoom = null;
+ if (olZoom != null) {
+ zoom = olZoom + 1;
+ }
+ return zoom;
+ },
+
+ //
+ // TRANSLATION: VELatLong <-> LonLat
+ //
+
+ /**
+ * @param {VELatLong} veLatLong
+ *
+ * @returns An OpenLayers.LonLat, translated from the passed in VELatLong
+ * Returns null if null value is passed in
+ * @type OpenLayers.LonLat
+ */
+ getOLLonLatFromVELatLong: function(veLatLong) {
+ var olLonLat = null;
+ if (veLatLong != null) {
+ olLonLat = new OpenLayers.LonLat(veLatLong.Longitude,
+ veLatLong.Latitude);
+ }
+ return olLonLat;
+ },
+
+ /**
+ * @param {OpenLayers.LonLat} olLonLat
+ *
+ * @returns A VELatLong, translated from the passed in OpenLayers.LonLat
+ * Returns null if null value is passed in
+ * @type VELatLong
+ */
+ getVELatLongFromOLLonLat: function(olLonLat) {
+ var veLatLong = null;
+ if (olLonLat != null) {
+ veLatLong = new VELatLong(olLonLat.lat, olLonLat.lon);
+ }
+ return veLatLong;
+ },
+
+
+ //
+ // TRANSLATION: Pixel <-> OpenLayers.Pixel
+ //
+
+ /**
+ * @param {Pixel} pixel
+ *
+ * @returns An OpenLayers.Pixel, translated from the passed in Pixel
+ * Returns null if null value is passed in
+ * @type OpenLayers.Pixel
+ */
+ getOLPixelFromPixel: function(pixel) {
+ var olPixel = null;
+ if (pixel != null) {
+ olPixel = new OpenLayers.Pixel(pixel.x, pixel.y);
+ }
+ return olPixel;
+ },
+
+ /**
+ * @param {OpenLayers.Pixel} olPixel
+ *
+ * @returns A Pixel, translated from the passed in OpenLayers.Pixel
+ * Returns null if null value is passed in
+ *
+ * As it turns out, the only specifications we can see for the
+ * VE-compatible Pixel is an x & y property, which every
+ * OpenLayers.Pixel has by default. So just leave it as-is.
+ *
+ * @type GPoint
+ */
+ getGPointFromOLPixel: function(olPixel) {
+ return olPixel;
+ },
+
+ //
+ // TRANSLATION: VELatLongBounds <-> OpenLayers.Bounds
+ //
+
+ /**
+ * @param {VELatLongBounds} veLatLongBounds
+ *
+ * @returns An OpenLayers.Bounds, translated from veLatLongBounds
+ * Returns null if null value is passed in
+ * @type OpenLayers.Bounds
+ */
+ getOLBoundsFromVELatLongBounds: function(veLatLongBounds) {
+ var olBounds = null;
+ if (veLatLongBounds != null) {
+ var sw = veLatLongBounds.getSouthWest();
+ var ne = veLatLongBounds.getNorthEast();
+ olBounds = new OpenLayers.Bounds(sw.lng(),
+ sw.lat(),
+ ne.lng(),
+ ne.lat() );
+ }
+ return olBounds;
+ },
+
+ /**
+ * @param {OpenLayers.Bounds} olBounds
+ *
+ * @returns A VELatLongBounds, translated from olBounds
+ * Returns null if null value is passed in
+ * @type VELatLongBounds
+ */
+ getVELatLongBoundsFromOLBounds: function(olBounds) {
+ var veLatLongBounds = null;
+ if (olBounds != null) {
+ var sw = new VELatLong(olBounds.bottom, olBounds.left);
+ var ne = new VELatLong(olBounds.top, olBounds.right);
+ veLatLongBounds = new VELatLongBounds(sw, ne);
+ }
+ return veLatLongBounds;
+ },
+
+
/** @final @type String */
CLASS_NAME: "OpenLayers.Layer.VirtualEarth"