diff --git a/examples/google-factbook-tc.html b/examples/google-factbook-tc.html
new file mode 100644
index 0000000000..c3e50832fe
--- /dev/null
+++ b/examples/google-factbook-tc.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OpenLayers With Google Layer Example
+
+
+
diff --git a/examples/google-factbook.html b/examples/google-factbook.html
new file mode 100644
index 0000000000..7bb1709048
--- /dev/null
+++ b/examples/google-factbook.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OpenLayers With Google Layer Example
+
+
+
diff --git a/examples/multimap-mercator.html b/examples/multimap-mercator.html
new file mode 100644
index 0000000000..535a88c6c8
--- /dev/null
+++ b/examples/multimap-mercator.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ OpenLayers VE Mercator Example
+
+
+
diff --git a/examples/spherical-mercator.html b/examples/spherical-mercator.html
new file mode 100644
index 0000000000..692bf4da08
--- /dev/null
+++ b/examples/spherical-mercator.html
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ OpenLayers Spherical Mercator Example
+
+
+
diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js
index 344f5f95fe..59da069e42 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -88,6 +88,7 @@
"OpenLayers/Tile/Image.js",
"OpenLayers/Tile/WFS.js",
"OpenLayers/Layer/Image.js",
+ "OpenLayers/Layer/SphericalMercator.js",
"OpenLayers/Layer/EventPane.js",
"OpenLayers/Layer/FixedZoomLevels.js",
"OpenLayers/Layer/Google.js",
diff --git a/lib/OpenLayers/Layer/Google.js b/lib/OpenLayers/Layer/Google.js
index 87857128a8..771bce825f 100644
--- a/lib/OpenLayers/Layer/Google.js
+++ b/lib/OpenLayers/Layer/Google.js
@@ -4,17 +4,20 @@
/**
+ * @requires OpenLayers/Layer/SphericalMercator.js
* @requires OpenLayers/Layer/EventPane.js
* @requires OpenLayers/Layer/FixedZoomLevels.js
*
* Class: OpenLayers.Layer.Google
*
* Inherits:
+ * -
* -
* -
*/
-OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
- OpenLayers.Layer.FixedZoomLevels, {
+OpenLayers.Layer.Google = OpenLayers.Class(
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
/**
* Constant: MIN_ZOOM_LEVEL
@@ -62,6 +65,14 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
*/
type: null,
+ /**
+ * 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,
+
/**
* Constructor: OpenLayers.Layer.Google
*
@@ -74,6 +85,10 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
this.addContainerPxFunction();
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
},
/**
@@ -151,7 +166,6 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
this.mapObject.checkResize();
},
-
/**
* APIMethod: getZoomForExtent
*
@@ -199,10 +213,17 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
if (moBounds != null) {
var sw = moBounds.getSouthWest();
var ne = moBounds.getNorthEast();
- olBounds = new OpenLayers.Bounds(sw.lng(),
- sw.lat(),
- ne.lng(),
- ne.lat() );
+ 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;
},
@@ -220,16 +241,17 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
getMapObjectBoundsFromOLBounds: function(olBounds) {
var moBounds = null;
if (olBounds != null) {
- var sw = new GLatLng(olBounds.bottom, olBounds.left);
- var ne = new GLatLng(olBounds.top, olBounds.right);
- moBounds = new GLatLngBounds(sw, ne);
+ var sw = this.sphericalMercator ?
+ this.inverseMercator(olBounds.bottom, olBounds.left) :
+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
+ var ne = this.sphericalMercator ?
+ this.inverseMercator(olBounds.top, olBounds.right) :
+ new OpenLayers.LonLat(olBounds.top, olBounds.right);
+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
+ new GLatLng(ne.lat, ne.lon));
}
return moBounds;
},
-
-
-
-
/**
* Method: addContainerPxFunction
@@ -396,7 +418,9 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.lng();
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
+ moLonLat.lng();
},
/**
@@ -409,7 +433,10 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.lat();
+ var lat = this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
+ moLonLat.lat();
+ return lat;
},
/**
@@ -423,7 +450,14 @@ OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane,
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
- return new GLatLng(lat, lon);
+ var gLatLng;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
+ } else {
+ gLatLng = new GLatLng(lat, lon);
+ }
+ return gLatLng;
},
// Pixel
diff --git a/lib/OpenLayers/Layer/MultiMap.js b/lib/OpenLayers/Layer/MultiMap.js
index 7465f9a049..06d4ab8b9c 100644
--- a/lib/OpenLayers/Layer/MultiMap.js
+++ b/lib/OpenLayers/Layer/MultiMap.js
@@ -7,6 +7,8 @@
* @requires OpenLayers/Layer/FixedZoomLevels.js
*
* Class: OpenLayers.Layer.MultiMap
+ * Note that MultiMap does not fully support the sphericalMercator
+ * option. See Ticket #953 for more details.
*
* Inherits:
* -
@@ -69,6 +71,11 @@ OpenLayers.Layer.MultiMap = OpenLayers.Class(
OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
+ if (this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ this.RESOLUTIONS.unshift(10);
+ }
},
/**
@@ -201,7 +208,9 @@ OpenLayers.Layer.MultiMap = OpenLayers.Class(
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.lon;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lon :
+ moLonLat.lon;
},
/**
@@ -214,7 +223,9 @@ OpenLayers.Layer.MultiMap = OpenLayers.Class(
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.lat;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.lon, moLonLat.lat).lat :
+ moLonLat.lat;
},
/**
@@ -228,7 +239,14 @@ OpenLayers.Layer.MultiMap = OpenLayers.Class(
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
- return new MMLatLon(lat, lon);
+ var mmLatLon;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ mmLatLon = new MMLatLon(lonlat.lat, lonlat.lon);
+ } else {
+ mmLatLon = new MMLatLon(lat, lon);
+ }
+ return mmLatLon;
},
// Pixel
diff --git a/lib/OpenLayers/Layer/SphericalMercator.js b/lib/OpenLayers/Layer/SphericalMercator.js
new file mode 100644
index 0000000000..a5c40f58c3
--- /dev/null
+++ b/lib/OpenLayers/Layer/SphericalMercator.js
@@ -0,0 +1,107 @@
+/**
+ * @requires OpenLayers/Layer.js
+ *
+ * Class: OpenLayers.Layer.SphericalMercator
+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate
+ * conversion for working with commercial APIs which use a spherical
+ * mercator projection. Using this layer as a base layer, additional
+ * layers can be used as overlays if they are in the same projection.
+ *
+ * A layer is given properties of this object by setting the sphericalMercator
+ * property to true.
+ *
+ * More projection information:
+ * - http://spatialreference.org/ref/user/google-projection/
+ *
+ * Proj4 Text:
+ * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
+ * +k=1.0 +units=m +nadgrids=@null +no_defs
+ *
+ * WKT:
+ * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
+ * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]],
+ * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295],
+ * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
+ * PROJECTION["Mercator_1SP_Google"],
+ * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0],
+ * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0],
+ * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
+ * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
+ */
+OpenLayers.Layer.SphericalMercator = {
+
+ /**
+ * Method: getExtent
+ * Get the map's extent.
+ *
+ * Returns:
+ * {} The map extent.
+ */
+ getExtent: function() {
+ var extent = null;
+ if (this.sphericalMercator) {
+ extent = this.map.calculateBounds();
+ } else {
+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
+ }
+ return extent;
+ },
+
+ /**
+ * Method: initMercatorParameters
+ * Set up the mercator parameters on the layer: resolutions,
+ * projection, units.
+ */
+ initMercatorParameters: function() {
+ // set up properties for Mercator - assume EPSG:900913
+ this.RESOLUTIONS = [];
+ var maxResolution = 156543.0339;
+ for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
+ }
+ this.units = "m";
+ this.projection = "EPSG:900913";
+ },
+
+ /**
+ * Method: forwardMercator
+ * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
+ *
+ * Parameters:
+ * lon - {float}
+ * lat - {float}
+ *
+ * Returns:
+ * {} The coordinates transformed to Mercator.
+ */
+ forwardMercator: function(lon, lat) {
+ var x = lon * 20037508.34 / 180;
+ var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
+
+ y = y * 20037508.34 / 180;
+
+ return new OpenLayers.LonLat(x, y);
+ },
+
+ /**
+ * Method: inverseMercator
+ * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
+ *
+ * Parameters:
+ * x - {float} A map x in Spherical Mercator.
+ * y - {float} A map y in Spherical Mercator.
+ *
+ * Returns:
+ * {} The coordinates transformed to EPSG:4326.
+ */
+ inverseMercator: function(x, y) {
+
+ var lon = (x / 20037508.34) * 180;
+ var lat = (y / 20037508.34) * 180;
+
+ lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
+
+ return new OpenLayers.LonLat(lon, lat);
+ }
+
+};
diff --git a/lib/OpenLayers/Layer/VirtualEarth.js b/lib/OpenLayers/Layer/VirtualEarth.js
index 0f4634f857..401259e364 100644
--- a/lib/OpenLayers/Layer/VirtualEarth.js
+++ b/lib/OpenLayers/Layer/VirtualEarth.js
@@ -14,7 +14,8 @@
* -
*/
OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
- OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
+ OpenLayers.Layer.EventPane,
+ OpenLayers.Layer.FixedZoomLevels, {
/**
* Constant: MIN_ZOOM_LEVEL
@@ -58,6 +59,15 @@ OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
*/
type: null,
+ /**
+ * 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,
+
/**
* Constructor: OpenLayers.Layer.VirtualEarth
*
@@ -69,6 +79,10 @@ OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
},
/**
@@ -214,7 +228,9 @@ OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.Longitude;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
+ moLonLat.Longitude;
},
/**
@@ -227,7 +243,9 @@ OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.Latitude;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
+ moLonLat.Latitude;
},
/**
@@ -241,7 +259,14 @@ OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
- return new VELatLong(lat, lon);
+ var veLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
+ } else {
+ veLatLong = new VELatLong(lat, lon);
+ }
+ return veLatLong;
},
// Pixel
diff --git a/lib/OpenLayers/Layer/Yahoo.js b/lib/OpenLayers/Layer/Yahoo.js
index dfe308bfec..c2953875c5 100644
--- a/lib/OpenLayers/Layer/Yahoo.js
+++ b/lib/OpenLayers/Layer/Yahoo.js
@@ -57,6 +57,14 @@ OpenLayers.Layer.Yahoo = OpenLayers.Class(
* {YahooMapType}
*/
type: null,
+
+ /**
+ * 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,
/**
* Constructor: OpenLayers.Layer.Yahoo
@@ -69,6 +77,10 @@ OpenLayers.Layer.Yahoo = OpenLayers.Class(
OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
arguments);
+ if(this.sphericalMercator) {
+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
+ this.initMercatorParameters();
+ }
},
/**
@@ -294,7 +306,9 @@ OpenLayers.Layer.Yahoo = OpenLayers.Class(
* {Float} Longitude of the given MapObject LonLat
*/
getLongitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.Lon;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
+ moLonLat.Lon;
},
/**
@@ -307,7 +321,9 @@ OpenLayers.Layer.Yahoo = OpenLayers.Class(
* {Float} Latitude of the given MapObject LonLat
*/
getLatitudeFromMapObjectLonLat: function(moLonLat) {
- return moLonLat.Lat;
+ return this.sphericalMercator ?
+ this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
+ moLonLat.Lat;
},
/**
@@ -321,7 +337,14 @@ OpenLayers.Layer.Yahoo = OpenLayers.Class(
* {Object} MapObject LonLat built from lon and lat params
*/
getMapObjectLonLatFromLonLat: function(lon, lat) {
- return new YGeoPoint(lat, lon);
+ var yLatLong;
+ if(this.sphericalMercator) {
+ var lonlat = this.inverseMercator(lon, lat);
+ yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
+ } else {
+ yLatLong = new YGeoPoint(lat, lon);
+ }
+ return yLatLong;
},
// Pixel
diff --git a/tests/Layer/test_Google.html b/tests/Layer/test_Google.html
index 9dc89666e3..2402c5e24e 100644
--- a/tests/Layer/test_Google.html
+++ b/tests/Layer/test_Google.html
@@ -128,6 +128,23 @@
window.location.host);
}
}
+
+ function test_Layer_Goole_forwardMercator(t){
+ t.plan(2);
+ //Just test that the fowardMercator function still exists.
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ layer.forwardMercator = function(evt) {
+ t.ok( true, "GoogleMercator.forwardMercator was called and executed." );
+ return;
+ }
+ layer.forwardMercator();
+ //Now test the fowardMercator returns the expected LonLat object
+ var layer = new OpenLayers.Layer.Google('Test Layer', {'sphericalMercator': true});
+ var lonlat2 = new OpenLayers.LonLat(Math.random(),Math.random());
+ var result = layer.forwardMercator(lonlat2.lon, lonlat2.lat);
+ t.ok( result instanceof OpenLayers.LonLat, "OpenLayers.Google.fowardMercator returns LonLat object" );
+
+ }
function test_Layer_Google_overlay(t) {
// Test for #849.
diff --git a/tests/Layer/test_SphericalMercator.html b/tests/Layer/test_SphericalMercator.html
new file mode 100644
index 0000000000..bd77422ca9
--- /dev/null
+++ b/tests/Layer/test_SphericalMercator.html
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
diff --git a/tests/list-tests.html b/tests/list-tests.html
index f1dcc0de89..0cf3d878b0 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -40,6 +40,7 @@
Layer/test_EventPane.html
Layer/test_FixedZoomLevels.html
Layer/test_GeoRSS.html
+ Layer/test_SphericalMercator.html
Layer/test_Google.html
Layer/test_Grid.html
Layer/test_HTTPRequest.html