+
+
+ OpenLayers Google NG Layer Example
+
+
+
+
+
+
+
+
+
+
Google NG Layer Example
+
+ Google, api key, apikey
+
+
+ Demonstrate use of tiles from the Google Maps v3 API.
+
+
+
+
+ If you use OpenLayers.Layer.GoogleNG, the getTile method of the
+ GMaps v3 API's MapType is used to load tiles. This allows for
+ better integration than interacting with a whole map generated
+ by a google.maps.Map instance, as done with
+ OpenLayers.Layer.Google. See the
+ google-ng.js source
+ to see how this is done.
+
+
+
+
diff --git a/examples/google-ng.js b/examples/google-ng.js
new file mode 100644
index 0000000000..fa2ba35b25
--- /dev/null
+++ b/examples/google-ng.js
@@ -0,0 +1,28 @@
+var map;
+
+function init() {
+ map = new OpenLayers.Map('map');
+ map.addControl(new OpenLayers.Control.LayerSwitcher());
+
+ var gphy = new OpenLayers.Layer.GoogleNG(
+ {type: google.maps.MapTypeId.TERRAIN}
+ );
+ var gmap = new OpenLayers.Layer.GoogleNG(
+ // ROADMAP, the default
+ );
+ var ghyb = new OpenLayers.Layer.GoogleNG(
+ {type: google.maps.MapTypeId.HYBRID}
+ );
+ var gsat = new OpenLayers.Layer.GoogleNG(
+ {type: google.maps.MapTypeId.SATELLITE}
+ );
+
+ map.addLayers([gphy, gmap, ghyb, gsat]);
+
+ // GoogleNG 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/lib/OpenLayers.js b/lib/OpenLayers.js
index a9061c93b0..82249753f7 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -120,6 +120,7 @@
"OpenLayers/Marker/Box.js",
"OpenLayers/Popup.js",
"OpenLayers/Tile.js",
+ "OpenLayers/Tile/Google.js",
"OpenLayers/Tile/Image.js",
"OpenLayers/Tile/Image/IFrame.js",
"OpenLayers/Tile/WFS.js",
@@ -152,6 +153,7 @@
"OpenLayers/Layer/Boxes.js",
"OpenLayers/Layer/XYZ.js",
"OpenLayers/Layer/Bing.js",
+ "OpenLayers/Layer/GoogleNG.js",
"OpenLayers/Layer/TMS.js",
"OpenLayers/Layer/TileCache.js",
"OpenLayers/Layer/Zoomify.js",
diff --git a/lib/OpenLayers/Layer/GoogleNG.js b/lib/OpenLayers/Layer/GoogleNG.js
new file mode 100644
index 0000000000..7dc9e986f2
--- /dev/null
+++ b/lib/OpenLayers/Layer/GoogleNG.js
@@ -0,0 +1,239 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the Clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Layer/XYZ.js
+ */
+
+/**
+ * Class: OpenLayers.Layer.GoogleNG
+ * Google layer using tiles. Note: Terms of Service
+ * compliant use requires the map to be configured with an
+ * control and the attribution placed on or
+ * near the map.
+ *
+ * Inherits from:
+ * -
+ */
+OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+
+ /**
+ * Property: SUPPORTED_TRANSITIONS
+ * {Array} An immutable (that means don't change it!) list of supported
+ * transitionEffect values. This layer type supports none.
+ */
+ SUPPORTED_TRANSITIONS: [],
+
+ /**
+ * Property: attributionTemplate
+ * {String}
+ */
+ attributionTemplate: '' +
+ '
' +
+ '
' +
+ 'Map data - ' +
+ 'Terms of Use',
+
+ /**
+ * Property: mapTypes
+ * {Object} mapping of {google.maps.MapTypeId} to the t param of
+ * http://maps.google.com/maps? permalinks
+ */
+ mapTypes: {
+ "roadmap": "m",
+ "satellite": "k",
+ "hybrid": "h",
+ "terrain": "p"
+ },
+
+ /**
+ * Property: mapObject
+ * {google.maps.Map} Shared GMaps instance - will be set on the prototype
+ * upon instantiation of the 1st GoogleNG layer
+ */
+ mapObject: null,
+
+ /**
+ * APIProperty: type
+ * {google.maps.MapTypeId} See
+ * http://code.google.com/apis/maps/documentation/javascript/reference.html#MapTypeId
+ */
+ type: null,
+
+ /**
+ * Constructor: OpenLayers.Layer.GoogleNG
+ * Create a new GoogleNG layer. Requires the GMaps v3 JavaScript API script
+ * included in the html document.
+ *
+ * Example:
+ * (code)
+ * var terrain = new OpenLayers.Layer.GoogleNG({
+ * name: "Google Terrain",
+ * type: google.maps.MapTypeId.TERRAIN
+ * });
+ * (end)
+ *
+ * Parameters:
+ * options - {Object} Configuration properties for the layer.
+ *
+ * Required configuration properties:
+ * type - {google.maps.MapTypeId} The layer identifier. See
+ * http://code.google.com/apis/maps/documentation/javascript/reference.html#MapTypeId
+ * for valid types.
+ *
+ * Any other documented layer properties can be provided in the config object.
+ */
+ initialize: function(options) {
+ options = OpenLayers.Util.applyDefaults({
+ sphericalMercator: true
+ }, options);
+
+ if (!options.type) {
+ options.type = google.maps.MapTypeId.ROADMAP;
+ }
+ var newArgs = [options.name, null, options];
+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
+
+ this.options.numZoomLevels = options.numZoomLevels;
+ if (!this.mapObject) {
+ OpenLayers.Layer.GoogleNG.prototype.mapObject =
+ new google.maps.Map(document.createElement("div"));
+ }
+ if (this.mapObject.mapTypes[this.type]) {
+ this.initLayer();
+ } else {
+ google.maps.event.addListenerOnce(
+ this.mapObject,
+ "idle",
+ OpenLayers.Function.bind(this.initLayer, this)
+ );
+ }
+ },
+
+ /**
+ * Method: initLayer
+ *
+ * Sets layer properties according to the metadata provided by the API
+ */
+ initLayer: function() {
+ var mapType = this.mapObject.mapTypes[this.type];
+ if (!this.name) {
+ this.setName("Google " + mapType.name);
+ }
+
+ var numZoomLevels = mapType.maxZoom + 1;
+ if (this.options.numZoomLevels != null) {
+ numZoomLevels = Math.min(numZoomLevels, this.options.numZoomLevels);
+ }
+ var restrictedMinZoom;
+ if (this.restrictedMinZoom || mapType.minZoom) {
+ restrictedMinZoom = Math.max(
+ mapType.minZoom || 0, this.restrictedMinZoom || 0
+ );
+ }
+
+ this.addOptions({
+ restrictedMinZoom: restrictedMinZoom,
+ numZoomLevels: numZoomLevels,
+ tileSize: new OpenLayers.Size(
+ mapType.tileSize.width, mapType.tileSize.height
+ )
+ });
+ // redraw to populate tiles with content
+ this.redraw();
+ },
+
+ /**
+ * APIMethod: addTile
+ * Create a tile, initialize it, and add it to the layer div.
+ *
+ * Parameters
+ * bounds - {}
+ * position - {}
+ *
+ * Returns:
+ * {} The added OpenLayers.Tile.Google
+ */
+ addTile:function(bounds, position) {
+ return new OpenLayers.Tile.Google(
+ this, position, bounds, this.tileOptions
+ );
+ },
+
+ /**
+ * Method: updateAttribution
+ * Updates the attribution using the
+ */
+ updateAttribution: function() {
+ var center = this.map.getCenter();
+ center && center.transform(
+ this.map.getProjectionObject(),
+ new OpenLayers.Projection("EPSG:4326")
+ );
+ var size = this.map.getSize();
+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {
+ center: center ? center.lat + "," + center.lon : "",
+ zoom: this.map.getZoom(),
+ size: size.w + "x" + size.h,
+ t: this.mapTypes[this.type],
+ mapType: this.type
+ });
+ this.map && this.map.events.triggerEvent("changelayer", {layer: this});
+ },
+
+ /**
+ * Method: setMap
+ */
+ setMap: function() {
+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
+
+ this.updateAttribution();
+ this.map.events.register("moveend", this, this.updateAttribution);
+ },
+
+ /**
+ * Method: removeMap
+ */
+ removeMap: function() {
+ OpenLayers.Layer.XYZ.prototype.removeMap.apply(this, arguments);
+ this.map.events.unregister("moveend", this, this.updateAttribution);
+ },
+
+ /**
+ * APIMethod: clone
+ *
+ * Parameters:
+ * obj - {Object}
+ *
+ * Returns:
+ * {} An exact clone of this
+ *
+ */
+ clone: function(obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Layer.GoogleNG(this.options);
+ }
+ //get all additions from superclasses
+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+ // copy/set any non-init, non-simple values here
+ return obj;
+ },
+
+ /**
+ * Method: destroy
+ */
+ destroy: function() {
+ this.map &&
+ this.map.events.unregister("moveend", this, this.updateAttribution);
+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
+ },
+
+ CLASS_NAME: "OpenLayers.Layer.GoogleNG"
+});
\ No newline at end of file
diff --git a/lib/OpenLayers/Tile.js b/lib/OpenLayers/Tile.js
index 44d268f987..f71de074aa 100644
--- a/lib/OpenLayers/Tile.js
+++ b/lib/OpenLayers/Tile.js
@@ -103,7 +103,9 @@ OpenLayers.Tile = OpenLayers.Class({
this.position = position.clone();
this.bounds = bounds.clone();
this.url = url;
- this.size = size.clone();
+ if (size) {
+ this.size = size.clone();
+ }
//give the tile a unique id based on its BBOX.
this.id = OpenLayers.Util.createUniqueID("Tile_");
diff --git a/lib/OpenLayers/Tile/Google.js b/lib/OpenLayers/Tile/Google.js
new file mode 100644
index 0000000000..11686734a6
--- /dev/null
+++ b/lib/OpenLayers/Tile/Google.js
@@ -0,0 +1,151 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the Clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+
+/*
+ * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/Util.js
+ * @requires OpenLayers/Console.js
+ * @requires OpenLayers/Lang.js
+ */
+
+/*
+ * Class: OpenLayers.Tile.Google
+ * Instances of OpenLayers.Tile.Google are used to manage the tiles created
+ * by google.maps.MapType (see
+ * http://code.google.com/apis/maps/documentation/javascript/reference.html#MapType).
+ *
+ * Inherits from:
+ * -
+ */
+OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
+
+ /**
+ * Property: node
+ * {DOMElement} The tile node from the MapType's getTile method
+ */
+ node: null,
+
+ /**
+ * Constructor: OpenLayers.Tile.Google
+ * Constructor for a new instance.
+ *
+ * Parameters:
+ * layer - {} layer that the tile will go in.
+ * position - {}
+ * bounds - {}
+ * options - {Object}
+ */
+ initialize: function(layer, position, bounds, options) {
+ OpenLayers.Tile.prototype.initialize.apply(this, [
+ layer, position, bounds, null, null, options
+ ]);
+ },
+
+ /**
+ * APIMethod: destroy
+ * Nullify references to prevent circular references and memory leaks.
+ */
+ destroy:function() {
+ this.node && this.clear();
+ this.node = null;
+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * Method: clone
+ *
+ * Parameters:
+ * obj - {} The tile to be cloned
+ *
+ * Returns:
+ * {} An exact clone of this
+ */
+ clone: function (obj) {
+ if (obj == null) {
+ obj = new OpenLayers.Tile.Google(this.layer,
+ this.position,
+ this.bounds);
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(obj, this);
+
+ obj.node = null;
+
+ return obj;
+ },
+
+ /**
+ * Method: draw
+ * Check that a tile should be drawn, and draw it.
+ *
+ * Returns:
+ * {Boolean} Always returns true.
+ */
+ draw: function() {
+ var layerType = this.layer.mapObject.mapTypes[this.layer.type];
+ if (layerType && OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
+ var xyz = this.layer.getXYZ(this.bounds);
+ var point = new google.maps.Point(xyz.x, xyz.y);
+
+ // The hybrid tile consists of two images. For some reason, we have
+ // to make sure that the satellite image loads first, otherwise we
+ // occasionally get blank tiles for one of the two images. This is
+ // done by requesting the tile for just the satellite mapType
+ // first, before requesting the hybrid one.
+ //TODO revisit this - it may be a temporary issue with GMaps
+ var tempTile;
+ if (this.layer.type === google.maps.MapTypeId.HYBRID) {
+ tempTile = layerType.getTile(point, xyz.z, document);
+ }
+
+ this.node = layerType.getTile(point, xyz.z, document);
+
+ this.isLoading = true;
+ this.events.triggerEvent("loadstart");
+
+ this.layer.div.appendChild(this.node);
+
+ // We only modify what we need to - we expect the size to be set
+ // by getTile, and we have a test that will fail if this changes.
+ OpenLayers.Util.modifyDOMElement(
+ this.node, null, this.position, null, "absolute"
+ );
+
+ // The images inside the node returned from getTile seem to be
+ // preloaded already, so registering onload events on these images
+ // won't work. Instead, we trigger the loadend event immediately
+ // in the next cycle.
+ window.setTimeout(OpenLayers.Function.bind(function() {
+ this.isLoading = false;
+ // check for this.events - we may be destroyed already
+ this.events && this.events.triggerEvent("loadend");
+
+ // see hybrid tile issue above
+ //TODO revisit this - it may be a temporary issue with GMaps
+ if (tempTile) {
+ layerType.releaseTile(tempTile);
+ }
+ }, this), 0);
+ }
+ return true;
+ },
+
+ /**
+ * Method: clear
+ * Clear the tile of any bounds/position-related data so that it can
+ * be reused in a new location. To be implemented by subclasses.
+ */
+ clear: function() {
+ if (this.node) {
+ this.node.parentNode &&
+ this.node.parentNode.removeChild(this.node);
+ this.layer.mapObject.mapTypes[this.layer.type].releaseTile(this.node);
+ }
+ },
+
+ CLASS_NAME: "OpenLayers.Tile.Google"
+});
diff --git a/tests/Layer/GoogleNG.html b/tests/Layer/GoogleNG.html
new file mode 100644
index 0000000000..a302450440
--- /dev/null
+++ b/tests/Layer/GoogleNG.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Tile/Google.html b/tests/Tile/Google.html
new file mode 100644
index 0000000000..e1710847ba
--- /dev/null
+++ b/tests/Tile/Google.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/list-tests.html b/tests/list-tests.html
index 214c2ffb58..d5d9a8ae10 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -144,6 +144,7 @@