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