No longer touching internal GMaps DOM elements.
Simple and effective: As soon as a map has a Google layer, the whole map viewport is added as control to the GMap. As soon as no Google layer is visible on the map any more, the map viewport is appended to the map container again. With this change, OpenLayers strictly limits its GMaps integration to the GMaps API. Also note that there are no css overrides for the attribution any more. Instead, controls can now be conditionally positioned differently for Google layer by using the .olForeignContainer selector.
This commit is contained in:
@@ -6,7 +6,6 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>OpenLayers Google (v3) Layer Example</title>
|
||||
<link rel="stylesheet" href="../theme/default/style.css" type="text/css">
|
||||
<link rel="stylesheet" href="../theme/default/google.css" type="text/css">
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<script src="http://maps.google.com/maps/api/js?v=3&sensor=false"></script>
|
||||
<script src="../lib/OpenLayers.js"></script>
|
||||
@@ -30,21 +29,6 @@
|
||||
spherical mercator projection. See the
|
||||
<a href="google-v3.js" target="_blank">google-v3.js source</a>
|
||||
to see how this is done.
|
||||
<p>
|
||||
In order to position the Google attribution div in the default
|
||||
location, you must include the extra theme/default/google.css
|
||||
stylesheet.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Note on Google Maps API versioning:</strong>
|
||||
This example uses the "nightly" version of Google Maps
|
||||
API. This is specified by using <code>v=3</code> in the
|
||||
the Google Maps API URL. Production applications should use the
|
||||
"release" or "frozen" versions of Google Maps
|
||||
API. See the <code>OpenLayers.Layer.Google.v3</code> API
|
||||
docs, and the
|
||||
<a href="https://developers.google.com/maps/documentation/javascript/basics#Versioning">Versioning Section</a>
|
||||
of the Google Maps API docs, for more details.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
var map;
|
||||
|
||||
function init() {
|
||||
map = new OpenLayers.Map('map');
|
||||
map = new OpenLayers.Map('map', {
|
||||
projection: 'EPSG:3857',
|
||||
layers: [
|
||||
new OpenLayers.Layer.Google(
|
||||
"Google Physical",
|
||||
{type: google.maps.MapTypeId.TERRAIN}
|
||||
),
|
||||
new OpenLayers.Layer.Google(
|
||||
"Google Streets", // the default
|
||||
{numZoomLevels: 20}
|
||||
),
|
||||
new OpenLayers.Layer.Google(
|
||||
"Google Hybrid",
|
||||
{type: google.maps.MapTypeId.HYBRID, numZoomLevels: 20}
|
||||
),
|
||||
new OpenLayers.Layer.Google(
|
||||
"Google Satellite",
|
||||
{type: google.maps.MapTypeId.SATELLITE, numZoomLevels: 22}
|
||||
)
|
||||
],
|
||||
center: new OpenLayers.LonLat(10.2, 48.9)
|
||||
// Google.v3 uses web mercator as projection, so we have to
|
||||
// transform our coordinates
|
||||
.transform('EPSG:4326', 'EPSG:3857'),
|
||||
zoom: 5
|
||||
});
|
||||
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);
|
||||
|
||||
// add behavior to html
|
||||
var animate = document.getElementById("animate");
|
||||
animate.onclick = function() {
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
right: inherit;
|
||||
width: 400px;
|
||||
}
|
||||
/* conditionally position control differently for Google Maps */
|
||||
.olForeignContainer div.olControlMousePosition {
|
||||
bottom: 28px;
|
||||
}
|
||||
#map {
|
||||
height: 512px;
|
||||
}
|
||||
@@ -38,7 +42,12 @@
|
||||
</p>
|
||||
<div id="map" class="smallmap"></div>
|
||||
|
||||
<div id="docs"></div>
|
||||
<div id="docs">
|
||||
<p>Note that maps with Google layers are a special case, because we
|
||||
cannot control the position of the attribution. To conditionally
|
||||
position controls differently for Google layers, prepend the
|
||||
css selector with <code>.olForeignContainer</code>.</p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
var map = new OpenLayers.Map({
|
||||
|
||||
@@ -15,35 +15,6 @@
|
||||
*
|
||||
* To use this layer, you must include the GMaps v3 API in your html.
|
||||
*
|
||||
* Because OpenLayers needs to control mouse events, it isolates the GMaps mapObject
|
||||
* (the DOM elements provided by Google) using the EventPane.
|
||||
* However, because the Terms of Use require some of those elements,
|
||||
* such as the links to Google's terms, to be clickable, these elements have
|
||||
* to be moved up to OpenLayers' container div. There is however no easy way
|
||||
* to identify these, and the logic (see the repositionMapElements function
|
||||
* in the source) may need to be changed if Google changes them.
|
||||
* These elements are not part of the published API and can be changed at any time,
|
||||
* so a given OpenLayers release can only guarantee support for the 'frozen'
|
||||
* Google release at the time of the OpenLayers release. See
|
||||
* https://developers.google.com/maps/documentation/javascript/basics#Versioning
|
||||
* for Google's current release cycle.
|
||||
*
|
||||
* For this reason, it's recommended that production code specifically loads
|
||||
* the current frozen version, for example:
|
||||
*
|
||||
* (code)
|
||||
* <script src="http://maps.google.com/maps/api/js?v=3.7&sensor=false"></script>
|
||||
* (end)
|
||||
*
|
||||
* but that development code should use the latest 'nightly' version, so that any
|
||||
* problems can be dealt with as soon as they arise, and before they affect the production, 'frozen', code.
|
||||
*
|
||||
* Note, however, that frozen versions are retired as part of Google's release
|
||||
* cycle, and once this happens, you will get the next version, in the example above, 3.8 once 3.7 is retired.
|
||||
*
|
||||
* This version supports 3.7.
|
||||
*
|
||||
*
|
||||
* Note that this layer configures the google.maps.map object with the
|
||||
* "disableDefaultUI" option set to true. Using UI controls that the Google
|
||||
* Maps API provides is not supported by the OpenLayers API.
|
||||
@@ -98,18 +69,13 @@ OpenLayers.Layer.Google.v3 = {
|
||||
++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
|
||||
// create GMap
|
||||
var center = this.map.getCenter();
|
||||
mapObject = new google.maps.Map(div, {
|
||||
var container = document.createElement('div');
|
||||
container.className = "olForeignContainer";
|
||||
container.style.width = '100%';
|
||||
container.style.height = '100%';
|
||||
mapObject = new google.maps.Map(container, {
|
||||
center: center ?
|
||||
new google.maps.LatLng(center.lat, center.lon) :
|
||||
new google.maps.LatLng(0, 0),
|
||||
@@ -130,87 +96,17 @@ OpenLayers.Layer.Google.v3 = {
|
||||
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;
|
||||
this.setGMapVisibility(this.visibility);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 || 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
|
||||
// depends on value of zIndex, which is not robust
|
||||
for (var i=div.children.length-1; i>=0; --i) {
|
||||
if (div.children[i].style.zIndex == 1000001) {
|
||||
var termsOfUse = div.children[i];
|
||||
container.appendChild(termsOfUse);
|
||||
termsOfUse.style.zIndex = "1100";
|
||||
termsOfUse.style.bottom = "";
|
||||
termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
|
||||
termsOfUse.style.display = "";
|
||||
cache.termsOfUse = termsOfUse;
|
||||
}
|
||||
if (div.children[i].style.zIndex == 1000000) {
|
||||
var poweredBy = div.children[i];
|
||||
container.appendChild(poweredBy);
|
||||
poweredBy.style.zIndex = "1100";
|
||||
poweredBy.style.bottom = "";
|
||||
poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
|
||||
poweredBy.style.display = "";
|
||||
cache.poweredBy = poweredBy;
|
||||
}
|
||||
if (div.children[i].style.zIndex == 10000002) {
|
||||
container.appendChild(div.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.setGMapVisibility(this.visibility);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: onMapResize
|
||||
*/
|
||||
onMapResize: function() {
|
||||
if (this.visibility) {
|
||||
google.maps.event.trigger(this.mapObject, "resize");
|
||||
} else {
|
||||
var cache = OpenLayers.Layer.Google.cache[this.map.id];
|
||||
if (!cache.resized) {
|
||||
var layer = this;
|
||||
google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
|
||||
google.maps.event.trigger(layer.mapObject, "resize");
|
||||
layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
|
||||
delete cache.resized;
|
||||
});
|
||||
}
|
||||
cache.resized = true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -238,27 +134,34 @@ OpenLayers.Layer.Google.v3 = {
|
||||
}
|
||||
var container = this.mapObject.getDiv();
|
||||
if (visible === true) {
|
||||
this.mapObject.setMapTypeId(type);
|
||||
container.style.left = "";
|
||||
if (cache.termsOfUse && cache.termsOfUse.style) {
|
||||
cache.termsOfUse.style.left = "";
|
||||
cache.termsOfUse.style.display = "";
|
||||
cache.poweredBy.style.display = "";
|
||||
if (!cache.gmapHasViewport) {
|
||||
this.map.div.removeChild(this.map.viewPortDiv);
|
||||
this.mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(this.map.viewPortDiv);
|
||||
cache.gmapHasViewport = true;
|
||||
this.map.div.appendChild(container);
|
||||
google.maps.event.trigger(this.mapObject, 'resize');
|
||||
}
|
||||
this.mapObject.setMapTypeId(type);
|
||||
cache.displayed = this.id;
|
||||
} else {
|
||||
delete cache.displayed;
|
||||
container.style.left = "-9999px";
|
||||
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";
|
||||
if (cache.gmapHasViewport) {
|
||||
this.map.div.removeChild(container);
|
||||
this.map.div.appendChild(
|
||||
this.mapObject.controls[google.maps.ControlPosition.TOP_LEFT].pop()
|
||||
);
|
||||
delete cache.gmapHasViewport;
|
||||
|
||||
// restore all styles of the viewPortDiv, in case GMaps
|
||||
// changes any. At the time of writing this, it definitely
|
||||
// changes the position.
|
||||
OpenLayers.Util.modifyDOMElement(
|
||||
this.map.viewPortDiv, null, null, null, 'relative',
|
||||
null, 'hidden'
|
||||
);
|
||||
this.map.viewPortDiv.style.width = "100%";
|
||||
this.map.viewPortDiv.style.height = "100%";
|
||||
}
|
||||
delete cache.displayed;
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -448,20 +351,6 @@ OpenLayers.Layer.Google.v3 = {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -772,8 +772,8 @@ OpenLayers.Map = OpenLayers.Class({
|
||||
}
|
||||
this.layers = null;
|
||||
}
|
||||
if (this.viewPortDiv) {
|
||||
this.div.removeChild(this.viewPortDiv);
|
||||
if (this.viewPortDiv && this.viewPortDiv.parentNode) {
|
||||
this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
|
||||
}
|
||||
this.viewPortDiv = null;
|
||||
|
||||
|
||||
@@ -170,68 +170,6 @@
|
||||
t.eq(satellite.div.style.display, "block", "Satellite layer is visible.");
|
||||
}
|
||||
|
||||
function test_Layer_Google_setGMapVisibility(t) {
|
||||
t.plan(3);
|
||||
|
||||
var map = new OpenLayers.Map('map');
|
||||
var gmap = new OpenLayers.Layer.Google("Google Streets");
|
||||
var dummy = new OpenLayers.Layer("Dummy", {isBaseLayer: true});
|
||||
map.addLayers([dummy, gmap]);
|
||||
map.zoomToMaxExtent();
|
||||
|
||||
// In v3, the terms of use and powered by elements are not available
|
||||
// until the layer loads. This can occur before the layer is visible,
|
||||
// but we don't try to access these elements until after the layer is
|
||||
// made visible for the first time.
|
||||
var cache = OpenLayers.Layer.Google.cache[map.id];
|
||||
t.ok(!cache.termsOfUse, "termsOfUse is not yet cached");
|
||||
t.ok(!cache.poweredBy, "poweredBy is not yet cached");
|
||||
|
||||
var called = 0;
|
||||
var original = gmap.repositionMapElements;
|
||||
gmap.repositionMapElements = function() {
|
||||
++called;
|
||||
original.apply(gmap, arguments);
|
||||
}
|
||||
|
||||
map.setBaseLayer(gmap);
|
||||
t.delay_call(4, function() {
|
||||
t.ok(called > 0, "repositionMapElements called");
|
||||
map.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
function test_Layer_Google_setGMapVisibility_allOverlays(t) {
|
||||
t.plan(3);
|
||||
|
||||
var map = new OpenLayers.Map('map', {allOverlays: true});
|
||||
var gmap = new OpenLayers.Layer.Google("Google Streets", {visibility: false});
|
||||
var dummy = new OpenLayers.Layer("Dummy");
|
||||
map.addLayers([gmap, dummy]);
|
||||
map.zoomToMaxExtent();
|
||||
|
||||
// In v3, the terms of use and powered by elements are not available
|
||||
// until the layer loads. This can occur before the layer is visible,
|
||||
// but we don't try to access these elements until after the layer is
|
||||
// made visible for the first time.
|
||||
var cache = OpenLayers.Layer.Google.cache[map.id];
|
||||
t.ok(!cache.termsOfUse, "termsOfUse is not yet cached");
|
||||
t.ok(!cache.poweredBy, "poweredBy is not yet cached");
|
||||
|
||||
var called = 0;
|
||||
var original = gmap.repositionMapElements;
|
||||
gmap.repositionMapElements = function() {
|
||||
++called;
|
||||
original.apply(gmap, arguments);
|
||||
}
|
||||
|
||||
gmap.setVisibility(true);
|
||||
t.delay_call(2, function() {
|
||||
t.ok(called > 0, "repositionMapElements called");
|
||||
map.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
function test_allOverlays_invisible(t) {
|
||||
|
||||
t.plan(1);
|
||||
@@ -319,10 +257,9 @@
|
||||
t.plan(2);
|
||||
|
||||
var origPrecision = OpenLayers.Util.DEFAULT_PRECISION;
|
||||
// GMaps v3 seems to use a default precision of 13, which is lower
|
||||
// than what we use in OpenLayers.
|
||||
// Our default precision is very high - millimeters should be enough.
|
||||
// See http://trac.osgeo.org/openlayers/ticket/3059
|
||||
OpenLayers.Util.DEFAULT_PRECISION = 13;
|
||||
OpenLayers.Util.DEFAULT_PRECISION = 12;
|
||||
|
||||
var map = new OpenLayers.Map("map");
|
||||
|
||||
@@ -370,6 +307,28 @@
|
||||
|
||||
}
|
||||
|
||||
function test_moveViewportDiv(t) {
|
||||
t.plan(2);
|
||||
|
||||
var map = new OpenLayers.Map('map', {
|
||||
projection: 'EPSG:3857',
|
||||
center: [0, 0],
|
||||
zoom: 1
|
||||
});
|
||||
var gmap = new OpenLayers.Layer.Google();
|
||||
map.addLayer(gmap);
|
||||
|
||||
t.delay_call(1, function() {
|
||||
t.ok(map.viewPortDiv.parentNode !== map.div, 'viewport moved inside GMaps');
|
||||
|
||||
var osm = new OpenLayers.Layer.OSM();
|
||||
map.addLayer(osm);
|
||||
map.setBaseLayer(osm);
|
||||
|
||||
t.ok(map.viewPortDiv.parentNode === map.div, 'viewport moved back');
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -3,15 +3,7 @@
|
||||
bottom: 2px;
|
||||
left: auto;
|
||||
}
|
||||
.olLayerGoogleV3.olLayerGoogleCopyright {
|
||||
bottom: 0px;
|
||||
right: 0px !important;
|
||||
}
|
||||
.olLayerGooglePoweredBy {
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
.olLayerGoogleV3.olLayerGooglePoweredBy {
|
||||
bottom: 0px !important;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user