From 64842fd711f61c22f52c649fc23b92b4ca5ca2ab Mon Sep 17 00:00:00 2001 From: ahocevar Date: Fri, 9 Jul 2010 12:01:11 +0000 Subject: [PATCH] add support for GMaps v3. Thanks tschaub for the big improvements to my original patch. p=tschaub,me, r=tschaub (closes #2493) git-svn-id: http://svn.openlayers.org/trunk/openlayers@10474 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/google-v3.html | 32 ++ examples/google-v3.js | 32 ++ examples/osm-google.html | 27 + examples/osm-google.js | 28 ++ lib/OpenLayers.js | 2 + lib/OpenLayers/Layer/Google.js | 387 ++------------- lib/OpenLayers/Layer/Google/v2.js | 692 ++++++++++++++++++++++++++ lib/OpenLayers/Layer/Google/v3.js | 786 ++++++++++++++++++++++++++++++ tests/Layer/Google/v3.html | 448 +++++++++++++++++ tests/list-tests.html | 1 + 10 files changed, 2078 insertions(+), 357 deletions(-) create mode 100644 examples/google-v3.html create mode 100644 examples/google-v3.js create mode 100644 examples/osm-google.html create mode 100644 examples/osm-google.js create mode 100644 lib/OpenLayers/Layer/Google/v2.js create mode 100644 lib/OpenLayers/Layer/Google/v3.js create mode 100644 tests/Layer/Google/v3.html diff --git a/examples/google-v3.html b/examples/google-v3.html new file mode 100644 index 0000000000..0b65073908 --- /dev/null +++ b/examples/google-v3.html @@ -0,0 +1,32 @@ + + + + OpenLayers Google (v3) Layer Example + + + + + + + + +

Google (v3) Layer Example

+

+ Demonstrate use the Google Maps v3 API. +

+
+
+

+ If you use the Google Maps v3 API with a Google layer, you don't + need to include an API key. This layer only works in the + spherical mercator projection. See the + google-v3.js source + to see how this is done. +

+ In order to position the Google attribution div in the default + location, you must include the extra theme/default/google.css + stylesheet. +

+
+ + diff --git a/examples/google-v3.js b/examples/google-v3.js new file mode 100644 index 0000000000..9c173e3128 --- /dev/null +++ b/examples/google-v3.js @@ -0,0 +1,32 @@ +var map; + +function init() { + map = new OpenLayers.Map('map'); + map.addControl(new OpenLayers.Control.LayerSwitcher()); + + var gphy = new OpenLayers.Layer.Google( + "Google Physical", + {type: google.maps.MapTypeId.TERRAIN} + ); + var gmap = new OpenLayers.Layer.Google( + "Google Streets", // the default + {numZoomLevels: 20} + ); + var ghyb = new OpenLayers.Layer.Google( + "Google Hybrid", + {type: google.maps.MapTypeId.HYBRID, numZoomLevels: 20} + ); + var gsat = new OpenLayers.Layer.Google( + "Google Satellite", + {type: google.maps.MapTypeId.SATELLITE, numZoomLevels: 22} + ); + + map.addLayers([gphy, gmap, ghyb, gsat]); + + // Google.v3 uses EPSG:900913 as projection, so we have to + // transform our coordinates + map.setCenter(new OpenLayers.LonLat(10.2, 48.9).transform( + new OpenLayers.Projection("EPSG:4326"), + map.getProjectionObject() + ), 5); +} diff --git a/examples/osm-google.html b/examples/osm-google.html new file mode 100644 index 0000000000..1973864261 --- /dev/null +++ b/examples/osm-google.html @@ -0,0 +1,27 @@ + + + + OpenLayers OSM and Google Example + + + + + + + + +

OSM and Google Together

+

+ Demonstrate use of an OSM layer and a Google layer as base layers. +

+
+
+

+ The Google(v3) layer and the OSM are both in the same projection + - spherical mercator - and can be used on a map together. + See the + osm-google.js source to see how this is done. +

+
+ + diff --git a/examples/osm-google.js b/examples/osm-google.js new file mode 100644 index 0000000000..77d96d82f4 --- /dev/null +++ b/examples/osm-google.js @@ -0,0 +1,28 @@ +var map; + +function init() { + map = new OpenLayers.Map({ + div: "map", + projection: new OpenLayers.Projection("EPSG:900913"), + units: "m", + maxResolution: 156543.0339, + maxExtent: new OpenLayers.Bounds( + -20037508, -20037508, 20037508, 20037508.34 + ) + }); + + var osm = new OpenLayers.Layer.OSM(); + var gmap = new OpenLayers.Layer.Google("Google Streets"); + + map.addLayers([osm, gmap]); + + map.addControl(new OpenLayers.Control.LayerSwitcher()); + + map.setCenter( + new OpenLayers.LonLat(10.2, 48.9).transform( + new OpenLayers.Projection("EPSG:4326"), + map.getProjectionObject() + ), + 5 + ); +} diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 9d53295c28..0023bf08de 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -107,6 +107,8 @@ "OpenLayers/Layer/EventPane.js", "OpenLayers/Layer/FixedZoomLevels.js", "OpenLayers/Layer/Google.js", + "OpenLayers/Layer/Google/v2.js", + "OpenLayers/Layer/Google/v3.js", "OpenLayers/Layer/VirtualEarth.js", "OpenLayers/Layer/Yahoo.js", "OpenLayers/Layer/HTTPRequest.js", diff --git a/lib/OpenLayers/Layer/Google.js b/lib/OpenLayers/Layer/Google.js index 71bcdcc2a7..f094c78cc4 100644 --- a/lib/OpenLayers/Layer/Google.js +++ b/lib/OpenLayers/Layer/Google.js @@ -7,7 +7,7 @@ * @requires OpenLayers/Layer/SphericalMercator.js * @requires OpenLayers/Layer/EventPane.js * @requires OpenLayers/Layer/FixedZoomLevels.js - * @requires OpenLayers/Console.js + * @requires OpenLayers/Layer/Google/v2.js */ /** @@ -80,23 +80,10 @@ OpenLayers.Layer.Google = OpenLayers.Class( sphericalMercator: false, /** - * Property: dragObject - * {GDraggableObject} Since 2.93, Google has exposed the ability to get - * the maps GDraggableObject. We can now use this for smooth panning + * Property: version + * {Number} The version of the Google Maps API */ - dragObject: null, - - /** - * Property: termsOfUse - * {DOMElement} Div for Google's copyright and terms of use link - */ - termsOfUse: null, - - /** - * Property: poweredBy - * {DOMElement} Div for Google's powered by logo and link - */ - poweredBy: null, + version: null, /** * Constructor: OpenLayers.Layer.Google @@ -107,16 +94,34 @@ OpenLayers.Layer.Google = OpenLayers.Class( * on the layer. */ initialize: function(name, options) { - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments); + 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, - arguments); - this.addContainerPxFunction(); + [name, options]); + if (this.sphericalMercator) { OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); this.initMercatorParameters(); } }, - + /** * Method: clone * Create a clone of this layer @@ -135,116 +140,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( this.name, this.getOptions() ); }, - - /** - * Method: loadMapObject - * Load the GMap and register appropriate event listeners. If we can't - * load GMap2, then display a warning message. - */ - loadMapObject:function() { - if (!this.type) { - this.type = G_NORMAL_MAP; - } - var mapObject, termsOfUse, poweredBy; - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - // there are already Google layers added to this map - mapObject = cache.mapObject; - termsOfUse = cache.termsOfUse; - poweredBy = cache.poweredBy; - // increment the layer count - ++cache.count; - } else { - // this is the first Google layer for this map - - var container = this.map.viewPortDiv; - var div = document.createElement("div"); - div.id = this.map.id + "_GMap2Container"; - div.style.position = "absolute"; - div.style.width = "100%"; - div.style.height = "100%"; - container.appendChild(div); - - // create GMap and shuffle elements - try { - mapObject = new GMap2(div); - - // move the ToS and branding stuff up to the container div - termsOfUse = div.lastChild; - container.appendChild(termsOfUse); - termsOfUse.style.zIndex = "1100"; - termsOfUse.style.right = ""; - termsOfUse.style.bottom = ""; - termsOfUse.className = "olLayerGoogleCopyright"; - - poweredBy = div.lastChild; - container.appendChild(poweredBy); - poweredBy.style.zIndex = "1100"; - poweredBy.style.right = ""; - poweredBy.style.bottom = ""; - poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; - - } catch (e) { - OpenLayers.Console.error(e); - return; - } - // cache elements for use by any other google layers added to - // this same map - OpenLayers.Layer.Google.cache[this.map.id] = { - mapObject: mapObject, - termsOfUse: termsOfUse, - poweredBy: poweredBy, - count: 1 - }; - } - - this.mapObject = mapObject; - this.termsOfUse = termsOfUse; - this.poweredBy = poweredBy; - - // ensure this layer type is one of the mapObject types - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), - this.type) === -1) { - this.mapObject.addMapType(this.type); - } - - //since v 2.93 getDragObject is now available. - if(typeof mapObject.getDragObject == "function") { - this.dragObject = mapObject.getDragObject(); - } else { - this.dragPanMapObject = null; - } - - if(this.isBaseLayer === false) { - this.setGMapVisibility(this.div.style.display !== "none"); - } - - }, - - /** - * APIMethod: onMapResize - */ - onMapResize: function() { - // workaround for resizing of invisible or not yet fully loaded layers - // where GMap2.checkResize() does not work. We need to load the GMap - // for the old div size, then checkResize(), and then call - // layer.moveTo() to trigger GMap.setCenter() (which will finish - // the GMap initialization). - if(this.visibility && this.mapObject.isLoaded()) { - this.mapObject.checkResize(); - } else { - if(!this._resized) { - var layer = this; - var handle = GEvent.addListener(this.mapObject, "load", function() { - GEvent.removeListener(handle); - delete layer._resized; - layer.mapObject.checkResize(); - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); - }); - } - this._resized = true; - } - }, /** * APIMethod: setVisibility @@ -269,43 +164,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( this.setOpacity(opacity); }, - /** - * Method: setGMapVisibility - * Display the GMap container and associated elements. - * - * Parameters: - * visible - {Boolean} Display the GMap elements. - */ - setGMapVisibility: function(visible) { - var cache = OpenLayers.Layer.Google.cache[this.map.id]; - if (cache) { - var container = this.mapObject.getContainer(); - if (visible === true) { - this.mapObject.setMapType(this.type); - container.style.display = ""; - this.termsOfUse.style.left = ""; - this.termsOfUse.style.display = ""; - this.poweredBy.style.display = ""; - cache.displayed = this.id; - } else { - if (cache.displayed === this.id) { - delete cache.displayed; - } - if (!cache.displayed) { - container.style.display = "none"; - this.termsOfUse.style.display = "none"; - // move ToU far to the left in addition to setting display - // to "none", because at the end of the GMap2 load - // sequence, display: none will be unset and ToU would be - // visible after loading a map with a google layer that is - // initially hidden. - this.termsOfUse.style.left = "-9999px"; - this.poweredBy.style.display = "none"; - } - } - } - }, - /** * APIMethod: setOpacity * Sets the opacity for the entire layer (all images) @@ -326,7 +184,7 @@ OpenLayers.Layer.Google = OpenLayers.Class( // 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.mapObject.getContainer(); + var container = this.getMapContainer(); OpenLayers.Util.modifyDOMElement( container, null, null, null, null, null, null, opacity ); @@ -347,7 +205,7 @@ OpenLayers.Layer.Google = OpenLayers.Class( this.setGMapVisibility(false); var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache && cache.count <= 1) { - this.removeGMapElements(false); + this.removeGMapElements(); } } OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); @@ -362,7 +220,7 @@ OpenLayers.Layer.Google = OpenLayers.Class( var cache = OpenLayers.Layer.Google.cache[this.map.id]; if (cache) { // remove shared elements from dom - var container = this.mapObject && this.mapObject.getContainer(); + var container = this.mapObject && this.getMapContainer(); if (container && container.parentNode) { container.parentNode.removeChild(container); } @@ -408,33 +266,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); }, - /** - * APIMethod: getZoomForExtent - * - * Parameters: - * bounds - {} - * - * Returns: - * {Integer} Corresponding zoom level for a specified Bounds. - * If mapObject is not loaded or not centered, returns null - * - getZoomForExtent: function (bounds) { - var zoom = null; - if (this.mapObject != null) { - var moBounds = this.getMapObjectBoundsFromOLBounds(bounds); - var moZoom = this.getMapObjectZoomFromMapObjectBounds(moBounds); - - //make sure zoom is within bounds - var moZoom = Math.min(Math.max(moZoom, this.minZoomLevel), - this.maxZoomLevel); - - zoom = this.getOLZoomFromMapObjectZoom(moZoom); - } - return zoom; - }, - - */ - // // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds // @@ -470,62 +301,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( return olBounds; }, - /** - * APIMethod: getMapObjectBoundsFromOLBounds - * - * Parameters: - * olBounds - {} - * - * Returns: - * {Object} A MapObject Bounds, translated from olBounds - * Returns null if null value is passed in - */ - getMapObjectBoundsFromOLBounds: function(olBounds) { - var moBounds = null; - if (olBounds != null) { - 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 - * Hack-on function because GMAPS does not give it to us - * - * Parameters: - * gLatLng - {GLatLng} - * - * Returns: - * {GPoint} A GPoint specifying gLatLng translated into "Container" coords - */ - addContainerPxFunction: function() { - if ( (typeof GMap2 != "undefined") && - !GMap2.prototype.fromLatLngToContainerPixel) { - - GMap2.prototype.fromLatLngToContainerPixel = function(gLatLng) { - - // first we translate into "DivPixel" - var gPoint = this.fromLatLngToDivPixel(gLatLng); - - // locate the sliding "Div" div - var div = this.getContainer().firstChild.firstChild; - - // adjust by the offset of "Div" and voila! - gPoint.x += div.offsetLeft; - gPoint.y += div.offsetTop; - - return gPoint; - }; - } - }, - /** * APIMethod: getWarningHTML * @@ -547,29 +322,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( // Get&Set Center, Zoom - /** - * APIMethod: setMapObjectCenter - * Set the mapObject to the specified center and zoom - * - * Parameters: - * center - {Object} MapObject LonLat format - * zoom - {int} MapObject zoom format - */ - setMapObjectCenter: function(center, zoom) { - this.mapObject.setCenter(center, zoom); - }, - - /** - * APIMethod: dragPanMapObject - * - * Parameters: - * dX - {Integer} - * dY - {Integer} - */ - dragPanMapObject: function(dX, dY) { - this.dragObject.moveBy(new GSize(-dX, dY)); - }, - /** * APIMethod: getMapObjectCenter * @@ -590,51 +342,7 @@ OpenLayers.Layer.Google = OpenLayers.Class( return this.mapObject.getZoom(); }, - - // LonLat - Pixel Translation - /** - * APIMethod: getMapObjectLonLatFromMapObjectPixel - * - * Parameters: - * moPixel - {Object} MapObject Pixel format - * - * Returns: - * {Object} MapObject LonLat translated from MapObject Pixel - */ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { - return this.mapObject.fromContainerPixelToLatLng(moPixel); - }, - - /** - * APIMethod: getMapObjectPixelFromMapObjectLonLat - * - * Parameters: - * moLonLat - {Object} MapObject LonLat format - * - * Returns: - * {Object} MapObject Pixel transtlated from MapObject LonLat - */ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { - return this.mapObject.fromLatLngToContainerPixel(moLonLat); - }, - - - // Bounds - - /** - * APIMethod: getMapObjectZoomFromMapObjectBounds - * - * Parameters: - * moBounds - {Object} MapObject Bounds format - * - * Returns: - * {Object} MapObject Zoom for specified MapObject Bounds - */ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { - return this.mapObject.getBoundsZoomLevel(moBounds); - }, - /************************************ * * * MapObject Primitives * @@ -675,27 +383,6 @@ OpenLayers.Layer.Google = OpenLayers.Class( return lat; }, - /** - * APIMethod: getMapObjectLonLatFromLonLat - * - * Parameters: - * lon - {Float} - * lat - {Float} - * - * Returns: - * {Object} MapObject LonLat built from lon and lat params - */ - getMapObjectLonLatFromLonLat: function(lon, lat) { - 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 /** @@ -723,21 +410,7 @@ OpenLayers.Layer.Google = OpenLayers.Class( getYFromMapObjectPixel: function(moPixel) { return moPixel.y; }, - - /** - * APIMethod: getMapObjectPixelFromXY - * - * Parameters: - * x - {Integer} - * y - {Integer} - * - * Returns: - * {Object} MapObject Pixel from x and y parameters - */ - getMapObjectPixelFromXY: function(x, y) { - return new GPoint(x, y); - }, - + CLASS_NAME: "OpenLayers.Layer.Google" }); diff --git a/lib/OpenLayers/Layer/Google/v2.js b/lib/OpenLayers/Layer/Google/v2.js new file mode 100644 index 0000000000..ffc68860fe --- /dev/null +++ b/lib/OpenLayers/Layer/Google/v2.js @@ -0,0 +1,692 @@ +/* Copyright (c) 2006-2008 MetaCarta, Inc., 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/Google.js + */ + +/** + * Constant: OpenLayers.Layer.Google.v2 + * + * Mixin providing functionality specific to the Google Maps API v2. + */ +OpenLayers.Layer.Google.v2 = { + + /** + * Property: termsOfUse + * {DOMElement} Div for Google's copyright and terms of use link + */ + termsOfUse: null, + + /** + * Property: poweredBy + * {DOMElement} Div for Google's powered by logo and link + */ + poweredBy: null, + + /** + * Property: dragObject + * {GDraggableObject} Since 2.93, Google has exposed the ability to get + * the maps GDraggableObject. We can now use this for smooth panning + */ + dragObject: null, + + /** + * Method: loadMapObject + * Load the GMap and register appropriate event listeners. If we can't + * load GMap2, then display a warning message. + */ + loadMapObject:function() { + if (!this.type) { + this.type = G_NORMAL_MAP; + } + var mapObject, termsOfUse, poweredBy; + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + // there are already Google layers added to this map + mapObject = cache.mapObject; + termsOfUse = cache.termsOfUse; + poweredBy = cache.poweredBy; + // increment the layer count + ++cache.count; + } else { + // this is the first Google layer for this map + + var container = this.map.viewPortDiv; + var div = document.createElement("div"); + div.id = this.map.id + "_GMap2Container"; + div.style.position = "absolute"; + div.style.width = "100%"; + div.style.height = "100%"; + container.appendChild(div); + + // create GMap and shuffle elements + try { + mapObject = new GMap2(div); + + // move the ToS and branding stuff up to the container div + termsOfUse = div.lastChild; + container.appendChild(termsOfUse); + termsOfUse.style.zIndex = "1100"; + termsOfUse.style.right = ""; + termsOfUse.style.bottom = ""; + termsOfUse.className = "olLayerGoogleCopyright"; + + poweredBy = div.lastChild; + container.appendChild(poweredBy); + poweredBy.style.zIndex = "1100"; + poweredBy.style.right = ""; + poweredBy.style.bottom = ""; + poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; + + } catch (e) { + throw(e); + return; + } + // cache elements for use by any other google layers added to + // this same map + OpenLayers.Layer.Google.cache[this.map.id] = { + mapObject: mapObject, + termsOfUse: termsOfUse, + poweredBy: poweredBy, + count: 1 + }; + } + + this.mapObject = mapObject; + this.termsOfUse = termsOfUse; + this.poweredBy = poweredBy; + + // ensure this layer type is one of the mapObject types + if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), + this.type) === -1) { + this.mapObject.addMapType(this.type); + } + + //since v 2.93 getDragObject is now available. + if(typeof mapObject.getDragObject == "function") { + this.dragObject = mapObject.getDragObject(); + } else { + this.dragPanMapObject = null; + } + + if(this.isBaseLayer === false) { + this.setGMapVisibility(this.div.style.display !== "none"); + } + + }, + + /** + * APIMethod: onMapResize + */ + onMapResize: function() { + // workaround for resizing of invisible or not yet fully loaded layers + // where GMap2.checkResize() does not work. We need to load the GMap + // for the old div size, then checkResize(), and then call + // layer.moveTo() to trigger GMap.setCenter() (which will finish + // the GMap initialization). + if(this.visibility && this.mapObject.isLoaded()) { + this.mapObject.checkResize(); + } else { + if(!this._resized) { + var layer = this; + var handle = GEvent.addListener(this.mapObject, "load", function() { + GEvent.removeListener(handle); + delete layer._resized; + layer.mapObject.checkResize(); + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); + }); + } + this._resized = true; + } + }, + + /** + * Method: setGMapVisibility + * Display the GMap container and associated elements. + * + * Parameters: + * visible - {Boolean} Display the GMap elements. + */ + setGMapVisibility: function(visible) { + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + var container = this.mapObject.getContainer(); + if (visible === true) { + this.mapObject.setMapType(this.type); + container.style.display = ""; + this.termsOfUse.style.left = ""; + this.termsOfUse.style.display = ""; + this.poweredBy.style.display = ""; + cache.displayed = this.id; + } else { + if (cache.displayed === this.id) { + delete cache.displayed; + } + if (!cache.displayed) { + container.style.display = "none"; + this.termsOfUse.style.display = "none"; + // move ToU far to the left in addition to setting display + // to "none", because at the end of the GMap2 load + // sequence, display: none will be unset and ToU would be + // visible after loading a map with a google layer that is + // initially hidden. + this.termsOfUse.style.left = "-9999px"; + this.poweredBy.style.display = "none"; + } + } + } + }, + + /** + * Method: getMapContainer + * + * Returns: + * {DOMElement} the GMap container's div + */ + getMapContainer: function() { + return this.mapObject.getContainer(); + }, + + // + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds + // + + /** + * APIMethod: getMapObjectBoundsFromOLBounds + * + * Parameters: + * olBounds - {} + * + * Returns: + * {Object} A MapObject Bounds, translated from olBounds + * Returns null if null value is passed in + */ + getMapObjectBoundsFromOLBounds: function(olBounds) { + var moBounds = null; + if (olBounds != null) { + 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; + }, + + + /************************************ + * * + * MapObject Interface Controls * + * * + ************************************/ + + + // Get&Set Center, Zoom + + /** + * APIMethod: setMapObjectCenter + * Set the mapObject to the specified center and zoom + * + * Parameters: + * center - {Object} MapObject LonLat format + * zoom - {int} MapObject zoom format + */ + setMapObjectCenter: function(center, zoom) { + this.mapObject.setCenter(center, zoom); + }, + + /** + * APIMethod: dragPanMapObject + * + * Parameters: + * dX - {Integer} + * dY - {Integer} + */ + dragPanMapObject: function(dX, dY) { + this.dragObject.moveBy(new GSize(-dX, dY)); + }, + + + // LonLat - Pixel Translation + + /** + * APIMethod: getMapObjectLonLatFromMapObjectPixel + * + * Parameters: + * moPixel - {Object} MapObject Pixel format + * + * Returns: + * {Object} MapObject LonLat translated from MapObject Pixel + */ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { + return this.mapObject.fromContainerPixelToLatLng(moPixel); + }, + + /** + * APIMethod: getMapObjectPixelFromMapObjectLonLat + * + * Parameters: + * moLonLat - {Object} MapObject LonLat format + * + * Returns: + * {Object} MapObject Pixel transtlated from MapObject LonLat + */ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { + return this.mapObject.fromLatLngToContainerPixel(moLonLat); + }, + + + // Bounds + + /** + * APIMethod: getMapObjectZoomFromMapObjectBounds + * + * Parameters: + * moBounds - {Object} MapObject Bounds format + * + * Returns: + * {Object} MapObject Zoom for specified MapObject Bounds + */ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { + return this.mapObject.getBoundsZoomLevel(moBounds); + }, + + /************************************ + * * + * MapObject Primitives * + * * + ************************************/ + + + // LonLat + + /** + * APIMethod: getMapObjectLonLatFromLonLat + * + * Parameters: + * lon - {Float} + * lat - {Float} + * + * Returns: + * {Object} MapObject LonLat built from lon and lat params + */ + getMapObjectLonLatFromLonLat: function(lon, lat) { + 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 + + /** + * APIMethod: getMapObjectPixelFromXY + * + * Parameters: + * x - {Integer} + * y - {Integer} + * + * Returns: + * {Object} MapObject Pixel from x and y parameters + */ + getMapObjectPixelFromXY: function(x, y) { + return new GPoint(x, y); + } + +}; +/* Copyright (c) 2006-2008 MetaCarta, Inc., 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/Google.js + */ + +/** + * Constant: OpenLayers.Layer.Google.v2 + * + * Mixin providing functionality specific to the Google Maps API v2. + */ +OpenLayers.Layer.Google.v2 = { + + /** + * Property: termsOfUse + * {DOMElement} Div for Google's copyright and terms of use link + */ + termsOfUse: null, + + /** + * Property: poweredBy + * {DOMElement} Div for Google's powered by logo and link + */ + poweredBy: null, + + /** + * Property: dragObject + * {GDraggableObject} Since 2.93, Google has exposed the ability to get + * the maps GDraggableObject. We can now use this for smooth panning + */ + dragObject: null, + + /** + * Method: loadMapObject + * Load the GMap and register appropriate event listeners. If we can't + * load GMap2, then display a warning message. + */ + loadMapObject:function() { + if (!this.type) { + this.type = G_NORMAL_MAP; + } + var mapObject, termsOfUse, poweredBy; + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + // there are already Google layers added to this map + mapObject = cache.mapObject; + termsOfUse = cache.termsOfUse; + poweredBy = cache.poweredBy; + // increment the layer count + ++cache.count; + } else { + // this is the first Google layer for this map + + var container = this.map.viewPortDiv; + var div = document.createElement("div"); + div.id = this.map.id + "_GMap2Container"; + div.style.position = "absolute"; + div.style.width = "100%"; + div.style.height = "100%"; + container.appendChild(div); + + // create GMap and shuffle elements + try { + mapObject = new GMap2(div); + + // move the ToS and branding stuff up to the container div + termsOfUse = div.lastChild; + container.appendChild(termsOfUse); + termsOfUse.style.zIndex = "1100"; + termsOfUse.style.right = ""; + termsOfUse.style.bottom = ""; + termsOfUse.className = "olLayerGoogleCopyright"; + + poweredBy = div.lastChild; + container.appendChild(poweredBy); + poweredBy.style.zIndex = "1100"; + poweredBy.style.right = ""; + poweredBy.style.bottom = ""; + poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; + + } catch (e) { + throw(e); + return; + } + // cache elements for use by any other google layers added to + // this same map + OpenLayers.Layer.Google.cache[this.map.id] = { + mapObject: mapObject, + termsOfUse: termsOfUse, + poweredBy: poweredBy, + count: 1 + }; + } + + this.mapObject = mapObject; + this.termsOfUse = termsOfUse; + this.poweredBy = poweredBy; + + // ensure this layer type is one of the mapObject types + if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), + this.type) === -1) { + this.mapObject.addMapType(this.type); + } + + //since v 2.93 getDragObject is now available. + if(typeof mapObject.getDragObject == "function") { + this.dragObject = mapObject.getDragObject(); + } else { + this.dragPanMapObject = null; + } + + if(this.isBaseLayer === false) { + this.setGMapVisibility(this.div.style.display !== "none"); + } + + }, + + /** + * APIMethod: onMapResize + */ + onMapResize: function() { + // workaround for resizing of invisible or not yet fully loaded layers + // where GMap2.checkResize() does not work. We need to load the GMap + // for the old div size, then checkResize(), and then call + // layer.moveTo() to trigger GMap.setCenter() (which will finish + // the GMap initialization). + if(this.visibility && this.mapObject.isLoaded()) { + this.mapObject.checkResize(); + } else { + if(!this._resized) { + var layer = this; + var handle = GEvent.addListener(this.mapObject, "load", function() { + GEvent.removeListener(handle); + delete layer._resized; + layer.mapObject.checkResize(); + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); + }); + } + this._resized = true; + } + }, + + /** + * Method: setGMapVisibility + * Display the GMap container and associated elements. + * + * Parameters: + * visible - {Boolean} Display the GMap elements. + */ + setGMapVisibility: function(visible) { + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + var container = this.mapObject.getContainer(); + if (visible === true) { + this.mapObject.setMapType(this.type); + container.style.display = ""; + this.termsOfUse.style.left = ""; + this.termsOfUse.style.display = ""; + this.poweredBy.style.display = ""; + cache.displayed = this.id; + } else { + if (cache.displayed === this.id) { + delete cache.displayed; + } + if (!cache.displayed) { + container.style.display = "none"; + this.termsOfUse.style.display = "none"; + // move ToU far to the left in addition to setting display + // to "none", because at the end of the GMap2 load + // sequence, display: none will be unset and ToU would be + // visible after loading a map with a google layer that is + // initially hidden. + this.termsOfUse.style.left = "-9999px"; + this.poweredBy.style.display = "none"; + } + } + } + }, + + /** + * Method: getMapContainer + * + * Returns: + * {DOMElement} the GMap container's div + */ + getMapContainer: function() { + return this.mapObject.getContainer(); + }, + + // + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds + // + + /** + * APIMethod: getMapObjectBoundsFromOLBounds + * + * Parameters: + * olBounds - {} + * + * Returns: + * {Object} A MapObject Bounds, translated from olBounds + * Returns null if null value is passed in + */ + getMapObjectBoundsFromOLBounds: function(olBounds) { + var moBounds = null; + if (olBounds != null) { + 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; + }, + + + /************************************ + * * + * MapObject Interface Controls * + * * + ************************************/ + + + // Get&Set Center, Zoom + + /** + * APIMethod: setMapObjectCenter + * Set the mapObject to the specified center and zoom + * + * Parameters: + * center - {Object} MapObject LonLat format + * zoom - {int} MapObject zoom format + */ + setMapObjectCenter: function(center, zoom) { + this.mapObject.setCenter(center, zoom); + }, + + /** + * APIMethod: dragPanMapObject + * + * Parameters: + * dX - {Integer} + * dY - {Integer} + */ + dragPanMapObject: function(dX, dY) { + this.dragObject.moveBy(new GSize(-dX, dY)); + }, + + + // LonLat - Pixel Translation + + /** + * APIMethod: getMapObjectLonLatFromMapObjectPixel + * + * Parameters: + * moPixel - {Object} MapObject Pixel format + * + * Returns: + * {Object} MapObject LonLat translated from MapObject Pixel + */ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { + return this.mapObject.fromContainerPixelToLatLng(moPixel); + }, + + /** + * APIMethod: getMapObjectPixelFromMapObjectLonLat + * + * Parameters: + * moLonLat - {Object} MapObject LonLat format + * + * Returns: + * {Object} MapObject Pixel transtlated from MapObject LonLat + */ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { + return this.mapObject.fromLatLngToContainerPixel(moLonLat); + }, + + + // Bounds + + /** + * APIMethod: getMapObjectZoomFromMapObjectBounds + * + * Parameters: + * moBounds - {Object} MapObject Bounds format + * + * Returns: + * {Object} MapObject Zoom for specified MapObject Bounds + */ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { + return this.mapObject.getBoundsZoomLevel(moBounds); + }, + + /************************************ + * * + * MapObject Primitives * + * * + ************************************/ + + + // LonLat + + /** + * APIMethod: getMapObjectLonLatFromLonLat + * + * Parameters: + * lon - {Float} + * lat - {Float} + * + * Returns: + * {Object} MapObject LonLat built from lon and lat params + */ + getMapObjectLonLatFromLonLat: function(lon, lat) { + 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 + + /** + * APIMethod: getMapObjectPixelFromXY + * + * Parameters: + * x - {Integer} + * y - {Integer} + * + * Returns: + * {Object} MapObject Pixel from x and y parameters + */ + getMapObjectPixelFromXY: function(x, y) { + return new GPoint(x, y); + } + +}; diff --git a/lib/OpenLayers/Layer/Google/v3.js b/lib/OpenLayers/Layer/Google/v3.js new file mode 100644 index 0000000000..590cb00210 --- /dev/null +++ b/lib/OpenLayers/Layer/Google/v3.js @@ -0,0 +1,786 @@ +/* Copyright (c) 2006-2008 MetaCarta, Inc., 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/Google.js + */ + +/** + * Constant: OpenLayers.Layer.Google.v3 + * + * Mixin providing functionality specific to the Google Maps API v3. Note that + */ +OpenLayers.Layer.Google.v3 = { + + /** + * Constant: DEFAULTS + * {Object} It is not recommended to change the properties set here. Note + * that Google.v3 layers only work when sphericalMercator is set to true. + * + * (code) + * { + * maxExtent: new OpenLayers.Bounds( + * -128 * 156543.0339, + * -128 * 156543.0339, + * 128 * 156543.0339, + * 128 * 156543.0339 + * ), + * sphericalMercator: true, + * maxResolution: 156543.0339, + * units: "m", + * projection: "EPSG:900913" + * } + * (end) + */ + DEFAULTS: { + maxExtent: new OpenLayers.Bounds( + -128 * 156543.0339, + -128 * 156543.0339, + 128 * 156543.0339, + 128 * 156543.0339 + ), + sphericalMercator: true, + maxResolution: 156543.0339, + units: "m", + projection: "EPSG:900913" + }, + + /** + * Method: loadMapObject + * Load the GMap and register appropriate event listeners. If we can't + * load GMap2, then display a warning message. + */ + loadMapObject:function() { + if (!this.type) { + this.type = google.maps.MapTypeId.ROADMAP; + } + var mapObject, termsOfUse, poweredBy; + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + // there are already Google layers added to this map + mapObject = cache.mapObject; + // increment the layer count + ++cache.count; + } else { + // this is the first Google layer for this map + + var container = this.map.viewPortDiv; + var div = document.createElement("div"); + div.id = this.map.id + "_GMapContainer"; + div.style.position = "absolute"; + div.style.width = "100%"; + div.style.height = "100%"; + container.appendChild(div); + + // create GMap and shuffle elements + var center = this.map.getCenter(); + mapObject = new google.maps.Map(div, { + center: center ? + new google.maps.LatLng(center.lat, center.lon) : + new google.maps.LatLng(0, 0), + zoom: this.map.getZoom() || 0, + mapTypeId: this.type, + disableDefaultUI: true, + keyboardShortcuts: false, + draggable: false, + disableDoubleClickZoom: true, + scrollwheel: false + }); + + // cache elements for use by any other google layers added to + // this same map + cache = { + mapObject: mapObject, + count: 1 + }; + OpenLayers.Layer.Google.cache[this.map.id] = cache; + this.repositionListener = google.maps.event.addListenerOnce( + mapObject, + "center_changed", + OpenLayers.Function.bind(this.repositionMapElements, this) + ); + } + + this.mapObject = mapObject; + }, + + /** + * Method: repositionMapElements + * + * Waits until powered by and terms of use elements are available and then + * moves them so they are clickable. + */ + repositionMapElements: function() { + + // This is the first time any Google layer in this mapObject has been + // made visible. The mapObject needs to know the container size. + google.maps.event.trigger(this.mapObject, "resize"); + + var div = this.mapObject.getDiv().firstChild; + if (div.childNodes.length < 3) { + this.repositionTimer = window.setTimeout( + OpenLayers.Function.bind(this.repositionMapElements, this), + 250 + ); + return false; + } + + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + var container = this.map.viewPortDiv + + // move the ToS and branding stuff up to the container div + var termsOfUse = div.lastChild; + container.appendChild(termsOfUse); + termsOfUse.style.zIndex = "1100"; + termsOfUse.style.bottom = ""; + termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3"; + termsOfUse.style.display = ""; + cache.termsOfUse = termsOfUse; + + var poweredBy = div.lastChild; + container.appendChild(poweredBy); + poweredBy.style.zIndex = "1100"; + poweredBy.style.bottom = ""; + poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint"; + poweredBy.style.display = ""; + cache.poweredBy = poweredBy; + + this.setGMapVisibility(true); + + }, + + /** + * APIMethod: onMapResize + */ + onMapResize: function() { + if (this.visibility) { + google.maps.event.trigger(this.mapObject, "resize"); + } else { + if (!this._resized) { + var layer = this; + google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() { + delete layer._resized; + google.maps.event.trigger(layer.mapObject, "resize"); + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); + }); + } + this._resized = true; + } + }, + + /** + * Method: setGMapVisibility + * Display the GMap container and associated elements. + * + * Parameters: + * visible - {Boolean} Display the GMap elements. + */ + setGMapVisibility: function(visible) { + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + var container = this.mapObject.getDiv(); + if (visible === true) { + this.mapObject.setMapTypeId(this.type); + container.style.display = ""; + if (cache.termsOfUse && cache.termsOfUse.style) { + cache.termsOfUse.style.left = ""; + cache.termsOfUse.style.display = ""; + cache.poweredBy.style.display = ""; + } + cache.displayed = this.id; + } else { + if (cache.displayed === this.id) { + delete cache.displayed; + } + if (!cache.displayed) { + container.style.display = "none"; + if (cache.termsOfUse && cache.termsOfUse.style) { + cache.termsOfUse.style.display = "none"; + // move ToU far to the left in addition to setting + // display to "none", because at the end of the GMap + // load sequence, display: none will be unset and ToU + // would be visible after loading a map with a google + // layer that is initially hidden. + cache.termsOfUse.style.left = "-9999px"; + cache.poweredBy.style.display = "none"; + } + } + } + } + }, + + /** + * Method: getMapContainer + * + * Returns: + * {DOMElement} the GMap container's div + */ + getMapContainer: function() { + return this.mapObject.getDiv(); + }, + + // + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds + // + + /** + * APIMethod: getMapObjectBoundsFromOLBounds + * + * Parameters: + * olBounds - {} + * + * Returns: + * {Object} A MapObject Bounds, translated from olBounds + * Returns null if null value is passed in + */ + getMapObjectBoundsFromOLBounds: function(olBounds) { + var moBounds = null; + if (olBounds != null) { + 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 google.maps.LatLngBounds( + new google.maps.LatLng(sw.lat, sw.lon), + new google.maps.LatLng(ne.lat, ne.lon) + ); + } + return moBounds; + }, + + + /************************************ + * * + * MapObject Interface Controls * + * * + ************************************/ + + + // LonLat - Pixel Translation + + /** + * APIMethod: getMapObjectLonLatFromMapObjectPixel + * + * Parameters: + * moPixel - {Object} MapObject Pixel format + * + * Returns: + * {Object} MapObject LonLat translated from MapObject Pixel + */ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { + var size = this.map.getSize(); + var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); + var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); + var res = this.map.getResolution(); + + var delta_x = moPixel.x - (size.w / 2); + var delta_y = moPixel.y - (size.h / 2); + + var lonlat = new OpenLayers.LonLat( + lon + delta_x * res, + lat - delta_y * res + ); + + if (this.wrapDateLine) { + lonlat = lonlat.wrapDateLine(this.maxExtent); + } + return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); + }, + + /** + * APIMethod: getMapObjectPixelFromMapObjectLonLat + * + * Parameters: + * moLonLat - {Object} MapObject LonLat format + * + * Returns: + * {Object} MapObject Pixel transtlated from MapObject LonLat + */ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); + var res = this.map.getResolution(); + var extent = this.map.getExtent(); + px = new OpenLayers.Pixel( + (1/res * (lon - extent.left)), + (1/res * (extent.top - lat)) + ); + return this.getMapObjectPixelFromXY(px.x, px.y); + }, + + + /** + * APIMethod: setMapObjectCenter + * Set the mapObject to the specified center and zoom + * + * Parameters: + * center - {Object} MapObject LonLat format + * zoom - {int} MapObject zoom format + */ + setMapObjectCenter: function(center, zoom) { + this.mapObject.setOptions({ + center: center, + zoom: zoom + }); + }, + + + // Bounds + + /** + * APIMethod: getMapObjectZoomFromMapObjectBounds + * + * Parameters: + * moBounds - {Object} MapObject Bounds format + * + * Returns: + * {Object} MapObject Zoom for specified MapObject Bounds + */ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { + return this.mapObject.getBoundsZoomLevel(moBounds); + }, + + /************************************ + * * + * MapObject Primitives * + * * + ************************************/ + + + // LonLat + + /** + * APIMethod: getMapObjectLonLatFromLonLat + * + * Parameters: + * lon - {Float} + * lat - {Float} + * + * Returns: + * {Object} MapObject LonLat built from lon and lat params + */ + getMapObjectLonLatFromLonLat: function(lon, lat) { + var gLatLng; + if(this.sphericalMercator) { + var lonlat = this.inverseMercator(lon, lat); + gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); + } else { + gLatLng = new google.maps.LatLng(lat, lon); + } + return gLatLng; + }, + + // Pixel + + /** + * APIMethod: getMapObjectPixelFromXY + * + * Parameters: + * x - {Integer} + * y - {Integer} + * + * Returns: + * {Object} MapObject Pixel from x and y parameters + */ + getMapObjectPixelFromXY: function(x, y) { + return new google.maps.Point(x, y); + }, + + /** + * APIMethod: destroy + * Clean up this layer. + */ + destroy: function() { + if (this.repositionListener) { + google.maps.event.removeListener(this.repositionListener); + } + if (this.repositionTimer) { + window.clearTimeout(this.repositionTimer); + } + OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments); + } + +}; +/* Copyright (c) 2006-2008 MetaCarta, Inc., 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/Google.js + */ + +/** + * Constant: OpenLayers.Layer.Google.v3 + * + * Mixin providing functionality specific to the Google Maps API v3. Note that + */ +OpenLayers.Layer.Google.v3 = { + + /** + * Constant: DEFAULTS + * {Object} It is not recommended to change the properties set here. Note + * that Google.v3 layers only work when sphericalMercator is set to true. + * + * (code) + * { + * maxExtent: new OpenLayers.Bounds( + * -128 * 156543.0339, + * -128 * 156543.0339, + * 128 * 156543.0339, + * 128 * 156543.0339 + * ), + * sphericalMercator: true, + * maxResolution: 156543.0339, + * units: "m", + * projection: "EPSG:900913" + * } + * (end) + */ + DEFAULTS: { + maxExtent: new OpenLayers.Bounds( + -128 * 156543.0339, + -128 * 156543.0339, + 128 * 156543.0339, + 128 * 156543.0339 + ), + sphericalMercator: true, + maxResolution: 156543.0339, + units: "m", + projection: "EPSG:900913" + }, + + /** + * Method: loadMapObject + * Load the GMap and register appropriate event listeners. If we can't + * load GMap2, then display a warning message. + */ + loadMapObject:function() { + if (!this.type) { + this.type = google.maps.MapTypeId.ROADMAP; + } + var mapObject, termsOfUse, poweredBy; + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + // there are already Google layers added to this map + mapObject = cache.mapObject; + // increment the layer count + ++cache.count; + } else { + // this is the first Google layer for this map + + var container = this.map.viewPortDiv; + var div = document.createElement("div"); + div.id = this.map.id + "_GMapContainer"; + div.style.position = "absolute"; + div.style.width = "100%"; + div.style.height = "100%"; + container.appendChild(div); + + // create GMap and shuffle elements + var center = this.map.getCenter(); + mapObject = new google.maps.Map(div, { + center: center ? + new google.maps.LatLng(center.lat, center.lon) : + new google.maps.LatLng(0, 0), + zoom: this.map.getZoom() || 0, + mapTypeId: this.type, + disableDefaultUI: true, + keyboardShortcuts: false, + draggable: false, + disableDoubleClickZoom: true, + scrollwheel: false + }); + + // cache elements for use by any other google layers added to + // this same map + cache = { + mapObject: mapObject, + count: 1 + }; + OpenLayers.Layer.Google.cache[this.map.id] = cache; + var layer = this; + this._googleOnIdle = google.maps.event.addListenerOnce(mapObject, "idle", function() { + var div = mapObject.getDiv().firstChild; + + // move the ToS and branding stuff up to the container div + var termsOfUse = div.lastChild; + container.appendChild(termsOfUse); + termsOfUse.style.zIndex = "1100"; + termsOfUse.style.bottom = ""; + termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3"; + cache.termsOfUse = termsOfUse; + + var poweredBy = div.lastChild; + container.appendChild(poweredBy); + poweredBy.style.zIndex = "1100"; + poweredBy.style.bottom = ""; + poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint"; + cache.poweredBy = poweredBy; + + layer.setGMapVisibility(layer.div.style.display !== "none"); + }); + } + + this.mapObject = mapObject; + }, + + /** + * APIMethod: onMapResize + */ + onMapResize: function() { + // workaround for resizing of invisible or not yet fully loaded layers + // where GMap2.checkResize() does not work. We need to load the GMap + // for the old div size, then checkResize(), and then call + // layer.moveTo() to trigger GMap.setCenter() (which will finish + // the GMap initialization). + if(this.visibility) { + google.maps.event.trigger(this.mapObject, 'resize'); + } else { + if(!this._resized) { + var layer = this; + google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() { + delete layer._resized; + google.maps.event.trigger(layer.mapObject, 'resize'); + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); + }); + } + this._resized = true; + } + }, + + /** + * Method: setGMapVisibility + * Display the GMap container and associated elements. + * + * Parameters: + * visible - {Boolean} Display the GMap elements. + */ + setGMapVisibility: function(visible) { + var cache = OpenLayers.Layer.Google.cache[this.map.id]; + if (cache) { + var container = this.mapObject.getDiv(); + if (visible === true) { + this.mapObject.setMapTypeId(this.type); + container.style.display = ""; + if(cache.termsOfUse) { + cache.termsOfUse.style.left = ""; + cache.termsOfUse.style.display = ""; + cache.poweredBy.style.display = ""; + } + cache.displayed = this.id; + } else { + if (cache.displayed === this.id) { + delete cache.displayed; + } + if (!cache.displayed) { + container.style.display = "none"; + if(cache.termsOfUse) { + cache.termsOfUse.style.display = "none"; + // move ToU far to the left in addition to setting + // display to "none", because at the end of the GMap + // load sequence, display: none will be unset and ToU + // would be visible after loading a map with a google + // layer that is initially hidden. + cache.termsOfUse.style.left = "-9999px"; + cache.poweredBy.style.display = "none"; + } + } + } + } + }, + + /** + * Method: getMapContainer + * + * Returns: + * {DOMElement} the GMap container's div + */ + getMapContainer: function() { + return this.mapObject.getDiv(); + }, + + // + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds + // + + /** + * APIMethod: getMapObjectBoundsFromOLBounds + * + * Parameters: + * olBounds - {} + * + * Returns: + * {Object} A MapObject Bounds, translated from olBounds + * Returns null if null value is passed in + */ + getMapObjectBoundsFromOLBounds: function(olBounds) { + var moBounds = null; + if (olBounds != null) { + 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 google.maps.LatLngBounds( + new google.maps.LatLng(sw.lat, sw.lon), + new google.maps.LatLng(ne.lat, ne.lon) + ); + } + return moBounds; + }, + + + /************************************ + * * + * MapObject Interface Controls * + * * + ************************************/ + + + // LonLat - Pixel Translation + + /** + * APIMethod: getMapObjectLonLatFromMapObjectPixel + * + * Parameters: + * moPixel - {Object} MapObject Pixel format + * + * Returns: + * {Object} MapObject LonLat translated from MapObject Pixel + */ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { + var size = this.map.getSize(); + var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); + var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); + var res = this.map.getResolution(); + + var delta_x = moPixel.x - (size.w / 2); + var delta_y = moPixel.y - (size.h / 2); + + var lonlat = new OpenLayers.LonLat( + lon + delta_x * res, + lat - delta_y * res + ); + + if (this.wrapDateLine) { + lonlat = lonlat.wrapDateLine(this.maxExtent); + } + return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); + }, + + /** + * APIMethod: getMapObjectPixelFromMapObjectLonLat + * + * Parameters: + * moLonLat - {Object} MapObject LonLat format + * + * Returns: + * {Object} MapObject Pixel transtlated from MapObject LonLat + */ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); + var res = this.map.getResolution(); + var extent = this.map.getExtent(); + px = new OpenLayers.Pixel( + (1/res * (lon - extent.left)), + (1/res * (extent.top - lat)) + ); + return this.getMapObjectPixelFromXY(px.x, px.y); + }, + + + /** + * APIMethod: setMapObjectCenter + * Set the mapObject to the specified center and zoom + * + * Parameters: + * center - {Object} MapObject LonLat format + * zoom - {int} MapObject zoom format + */ + setMapObjectCenter: function(center, zoom) { + this.mapObject.setOptions({ + center: center, + zoom: zoom + }); + }, + + + // Bounds + + /** + * APIMethod: getMapObjectZoomFromMapObjectBounds + * + * Parameters: + * moBounds - {Object} MapObject Bounds format + * + * Returns: + * {Object} MapObject Zoom for specified MapObject Bounds + */ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { + return this.mapObject.getBoundsZoomLevel(moBounds); + }, + + /************************************ + * * + * MapObject Primitives * + * * + ************************************/ + + + // LonLat + + /** + * APIMethod: getMapObjectLonLatFromLonLat + * + * Parameters: + * lon - {Float} + * lat - {Float} + * + * Returns: + * {Object} MapObject LonLat built from lon and lat params + */ + getMapObjectLonLatFromLonLat: function(lon, lat) { + var gLatLng; + if(this.sphericalMercator) { + var lonlat = this.inverseMercator(lon, lat); + gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); + } else { + gLatLng = new google.maps.LatLng(lat, lon); + } + return gLatLng; + }, + + // Pixel + + /** + * APIMethod: getMapObjectPixelFromXY + * + * Parameters: + * x - {Integer} + * y - {Integer} + * + * Returns: + * {Object} MapObject Pixel from x and y parameters + */ + getMapObjectPixelFromXY: function(x, y) { + return new google.maps.Point(x, y); + }, + + /** + * APIMethod: destroy + * Clean up this layer. + */ + destroy: function() { + this._googleOnIdle && + google.maps.event.removeListener(this._googleOnIdle); + OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments); + } + +}; diff --git a/tests/Layer/Google/v3.html b/tests/Layer/Google/v3.html new file mode 100644 index 0000000000..a9000558cb --- /dev/null +++ b/tests/Layer/Google/v3.html @@ -0,0 +1,448 @@ + + + + + + + +
+ + + + + + + + + +
+ + \ No newline at end of file diff --git a/tests/list-tests.html b/tests/list-tests.html index 1c3b49b3b0..1f1dfab57b 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -129,6 +129,7 @@
  • Layer/GeoRSS.html
  • Layer/GML.html
  • Layer/Google.html
  • +
  • Layer/Google/v3.html
  • Layer/Grid.html
  • Layer/HTTPRequest.html
  • Layer/Image.html