making attribution as Terms of Service compliant as we possibly can. p=bartvde (closes #3330)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@12070 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
right: inherit;
|
||||
bottom: 3px;
|
||||
line-height: 11px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
<script src="http://maps.google.com/maps/api/js?v=3.5&sensor=false"></script>
|
||||
|
||||
@@ -10,10 +10,7 @@
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Layer.GoogleNG
|
||||
* Google layer using <OpenLayers.Tile.Google> tiles. Note: Terms of Service
|
||||
* compliant use requires the map to be configured with an
|
||||
* <OpenLayers.Control.Attribution> control and the attribution placed on or
|
||||
* near the map.
|
||||
* Google layer using <OpenLayers.Tile.Google> tiles.
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Layer.XYZ>
|
||||
@@ -32,13 +29,11 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
* {String}
|
||||
*/
|
||||
attributionTemplate: '<span class="olGoogleAttribution ${mapType}">' +
|
||||
'<div><a target="_blank" href="http://maps.google.com/maps?' +
|
||||
'<div><a title="Click to see this area on Google Maps" ' +
|
||||
'target="_blank" href="http://maps.google.com/maps?' +
|
||||
'll=${center}&z=${zoom}&t=${t}"><img width="62" height="24" ' +
|
||||
'src="http://maps.gstatic.com/mapfiles/google_white.png"/></a>' +
|
||||
'</div><a style="white-space: nowrap" target="_blank" ' +
|
||||
'href="http://maps.google.com/maps/api/staticmap?sensor=true' +
|
||||
'¢er=${center}&zoom=${zoom}&size=${size}&maptype=${mapType}">' +
|
||||
'Map data</a> - <a style="white-space: nowrap" target="_blank" ' +
|
||||
'</div>${mapData}<a style="white-space: nowrap" target="_blank" ' +
|
||||
'href="http://www.google.com/help/terms_maps.html">' +
|
||||
'Terms of Use</a></span>',
|
||||
|
||||
@@ -54,13 +49,6 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
"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
|
||||
@@ -71,7 +59,10 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.GoogleNG
|
||||
* Create a new GoogleNG layer. Requires the GMaps v3 JavaScript API script
|
||||
* included in the html document.
|
||||
* (http://maps.google.com/maps/api/js?v=3.5&sensor=false) loaded in
|
||||
* the html document. Note: Terms of Service compliant use requires the map
|
||||
* to be configured with an <OpenLayers.Control.Attribution> control and
|
||||
* the attribution placed on the map.
|
||||
*
|
||||
* Example:
|
||||
* (code)
|
||||
@@ -103,15 +94,15 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
|
||||
|
||||
this.options.numZoomLevels = options.numZoomLevels;
|
||||
if (!this.mapObject) {
|
||||
OpenLayers.Layer.GoogleNG.prototype.mapObject =
|
||||
if (!OpenLayers.Layer.GoogleNG.mapObject) {
|
||||
OpenLayers.Layer.GoogleNG.mapObject =
|
||||
new google.maps.Map(document.createElement("div"));
|
||||
}
|
||||
if (this.mapObject.mapTypes[this.type]) {
|
||||
if (OpenLayers.Layer.GoogleNG.mapObject.mapTypes[this.type]) {
|
||||
this.initLayer();
|
||||
} else {
|
||||
google.maps.event.addListenerOnce(
|
||||
this.mapObject,
|
||||
OpenLayers.Layer.GoogleNG.mapObject,
|
||||
"idle",
|
||||
OpenLayers.Function.bind(this.initLayer, this)
|
||||
);
|
||||
@@ -124,7 +115,7 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
* Sets layer properties according to the metadata provided by the API
|
||||
*/
|
||||
initLayer: function() {
|
||||
var mapType = this.mapObject.mapTypes[this.type];
|
||||
var mapType = OpenLayers.Layer.GoogleNG.mapObject.mapTypes[this.type];
|
||||
if (!this.name) {
|
||||
this.setName("Google " + mapType.name);
|
||||
}
|
||||
@@ -171,8 +162,31 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
/**
|
||||
* Method: updateAttribution
|
||||
* Updates the attribution using the <attributionTemplate>
|
||||
*
|
||||
* Parameters:
|
||||
* copyrights - {Object} Object with "m", "k", "h" and "p" properties (see
|
||||
* <mapTypes>), each holding an array of copyrights.
|
||||
*/
|
||||
updateAttribution: function() {
|
||||
updateAttribution: function(copyrights) {
|
||||
var myCopyrights;
|
||||
if (this.type == google.maps.MapTypeId.HYBRID) {
|
||||
// the Copyright Service returns "k" and "m" copyrights for the
|
||||
// HYBRID layer type.
|
||||
var candidates = [].concat(
|
||||
copyrights["h"], copyrights["k"], copyrights["m"]
|
||||
);
|
||||
myCopyrights = [];
|
||||
for (var i=candidates.length-1; i>=0; --i) {
|
||||
if (OpenLayers.Util.indexOf(candidates, myCopyrights) == -1) {
|
||||
myCopyrights.push(candidates[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
myCopyrights = copyrights[this.mapTypes[this.type]];
|
||||
}
|
||||
var mapData = myCopyrights.length == 0 ? "" :
|
||||
"Map Data ©" + new Date().getFullYear() + " " +
|
||||
myCopyrights.join(", ") + " - ";
|
||||
var center = this.map.getCenter();
|
||||
center && center.transform(
|
||||
this.map.getProjectionObject(),
|
||||
@@ -180,23 +194,23 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
);
|
||||
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
|
||||
zoom: this.map.getZoom(),
|
||||
center: center.lat + "," + center.lon,
|
||||
mapType: this.type,
|
||||
mapData: mapData
|
||||
});
|
||||
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);
|
||||
this.events.register("moveend", this,
|
||||
OpenLayers.Layer.GoogleNG.loadCopyrights
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -204,7 +218,9 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
*/
|
||||
removeMap: function() {
|
||||
OpenLayers.Layer.XYZ.prototype.removeMap.apply(this, arguments);
|
||||
this.map.events.unregister("moveend", this, this.updateAttribution);
|
||||
this.events.unregister("moveend", this,
|
||||
OpenLayers.Layer.GoogleNG.loadCopyrights
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -227,14 +243,86 @@ OpenLayers.Layer.GoogleNG = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
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"
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Property: mapObject
|
||||
* {google.maps.Map} Shared GMaps instance - will be set upon instantiation of
|
||||
* the 1st GoogleNG layer
|
||||
*/
|
||||
OpenLayers.Layer.GoogleNG.mapObject = null;
|
||||
|
||||
/**
|
||||
* Function: loadCopyrights
|
||||
* Using the Google Maps Copyright Service mode (see
|
||||
* http://mapki.com/wiki/Google_Map_Parameters#Misc) to get the attribution for
|
||||
* the current map extent. Will be called by each GoogleNG layer instance on
|
||||
* moveend.
|
||||
*/
|
||||
OpenLayers.Layer.GoogleNG.loadCopyrights = function() {
|
||||
var me = OpenLayers.Layer.GoogleNG.loadCopyrights;
|
||||
if (me.numLoadingScripts == undefined) {
|
||||
me.loadingScripts = [];
|
||||
me.numLoadingScripts = 0;
|
||||
me.copyrights = {"m": [], "k": [], "h": [], "p": []};
|
||||
|
||||
// store window scope functions before overwriting them
|
||||
me.origGAddCopyright = window.GAddCopyright;
|
||||
me.origGVerify = window.GVerify;
|
||||
me.origGAppFeatures = window.GAppFeatures;
|
||||
|
||||
// defining window scope functions called by the script that the
|
||||
// Copyright Service returns
|
||||
window.GAddCopyright = function() {
|
||||
var copyright = arguments[7];
|
||||
var category = me.copyrights[arguments[0]];
|
||||
if (OpenLayers.Util.indexOf(category, copyright) == -1) {
|
||||
copyright && category.push(copyright);
|
||||
}
|
||||
};
|
||||
window.GVerify = OpenLayers.Function.True;
|
||||
window.GAppFeatures = OpenLayers.Function.bind(function() {
|
||||
me.numLoadingScripts--;
|
||||
if (me.numLoadingScripts == 0) {
|
||||
var script;
|
||||
for (var i=me.loadingScripts.length-1; i>=0; --i) {
|
||||
script = me.loadingScripts[i][0];
|
||||
me.loadingScripts[i][1].updateAttribution(me.copyrights);
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
// restore original functions
|
||||
window.GAddCopyright = me.origGAddCopyright;
|
||||
delete me.origGAddCopyright;
|
||||
window.GVerify = me.origGVerify;
|
||||
delete me.origGVerify;
|
||||
window.GAppFeatures = me.origGAppFeatures;
|
||||
delete me.origGAppFeatures;
|
||||
|
||||
delete me.loadingScripts;
|
||||
delete me.numLoadingScripts;
|
||||
delete me.copyrights;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
var mapProj = this.map.getProjectionObject();
|
||||
var llProj = new OpenLayers.Projection("EPSG:4326");
|
||||
var center = this.map.getCenter().transform(mapProj, llProj);
|
||||
var extent = this.map.getExtent().transform(mapProj, llProj);
|
||||
var params = {
|
||||
spn: extent.getHeight() + "," + extent.getWidth(),
|
||||
z: this.map.getZoom(),
|
||||
t: this.mapTypes[this.type],
|
||||
vp: center.lat + "," + center.lon
|
||||
};
|
||||
var url = "http://maps.google.com/maps?" +
|
||||
OpenLayers.Util.getParameterString(params);
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.src = url;
|
||||
me.numLoadingScripts++;
|
||||
me.loadingScripts.push([script, this]);
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
};
|
||||
|
||||
|
||||
@@ -86,7 +86,9 @@ OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
|
||||
* {Boolean} Always returns true.
|
||||
*/
|
||||
draw: function() {
|
||||
var layerType = this.layer.mapObject.mapTypes[this.layer.type];
|
||||
var layerType = OpenLayers.Layer.GoogleNG.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);
|
||||
@@ -143,7 +145,9 @@ OpenLayers.Tile.Google = OpenLayers.Class(OpenLayers.Tile, {
|
||||
if (this.node) {
|
||||
this.node.parentNode &&
|
||||
this.node.parentNode.removeChild(this.node);
|
||||
this.layer.mapObject.mapTypes[this.layer.type].releaseTile(this.node);
|
||||
OpenLayers.Layer.GoogleNG.mapObject.mapTypes[
|
||||
this.layer.type
|
||||
].releaseTile(this.node);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
numZoomLevels: 24,
|
||||
initLayer: function() {
|
||||
log[layer2.id] = true;
|
||||
var origMinZoom = layer2.mapObject.mapTypes[layer2.type].minZoom;
|
||||
var origMinZoom = OpenLayers.Layer.GoogleNG.mapObject.mapTypes[layer2.type].minZoom;
|
||||
// pretend the API reports a minZoom of 1
|
||||
layer2.mapObject.mapTypes[layer2.type].minZoom = 1;
|
||||
OpenLayers.Layer.GoogleNG.mapObject.mapTypes[layer2.type].minZoom = 1;
|
||||
OpenLayers.Layer.GoogleNG.prototype.initLayer.apply(this, arguments);
|
||||
layer2.mapObject.mapTypes[layer2.type].minZoom = origMinZoom;
|
||||
OpenLayers.Layer.GoogleNG.mapObject.mapTypes[layer2.type].minZoom = origMinZoom;
|
||||
}
|
||||
});
|
||||
map2.addLayer(layer2);
|
||||
@@ -49,7 +49,7 @@
|
||||
t.eq(log[layer2.id], true, "initLayer called for 2nd layer");
|
||||
|
||||
t.eq(layer.numZoomLevels, 10, "numZoomLevels from configuration takes precedence if lower");
|
||||
t.eq(layer2.numZoomLevels, layer2.mapObject.mapTypes[layer2.type].maxZoom+1, "numZoomLevels from API takes precedence if lower");
|
||||
t.eq(layer2.numZoomLevels, OpenLayers.Layer.GoogleNG.mapObject.mapTypes[layer2.type].maxZoom+1, "numZoomLevels from API takes precedence if lower");
|
||||
|
||||
t.eq(layer.restrictedMinZoom, 2, "restrictedMinZoom from configuration takes precedence if higher");
|
||||
t.eq(layer2.restrictedMinZoom, 1, "restrictedMinZoom from API takes precedence if higher");
|
||||
@@ -60,18 +60,25 @@
|
||||
}
|
||||
|
||||
function test_attribution(t) {
|
||||
t.plan(3);
|
||||
t.plan(4);
|
||||
|
||||
var log = [];
|
||||
map = new OpenLayers.Map("map");
|
||||
layer = new OpenLayers.Layer.GoogleNG({type: google.maps.MapTypeId.HYBRID});
|
||||
layer = new OpenLayers.Layer.GoogleNG({
|
||||
type: google.maps.MapTypeId.HYBRID,
|
||||
updateAttribution: function(copyrights) {
|
||||
log.push(copyrights);
|
||||
OpenLayers.Layer.GoogleNG.prototype.updateAttribution.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
map.addLayer(layer);
|
||||
map.zoomToMaxExtent();
|
||||
map.setCenter(new OpenLayers.LonLat(16, 48).transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject()), 5);
|
||||
|
||||
t.delay_call(1, function() {
|
||||
t.ok(layer.attribution.indexOf('olGoogleAttribution hybrid') !== -1, "Attribution has the correct css class");
|
||||
t.ok(layer.attribution.indexOf('?ll=0,0&z=0&t=h"') != -1, "maps.google.com link has correct parameters");
|
||||
t.ok(layer.attribution.indexOf('¢er=0,0&zoom=0&size=500x550&maptype=hybrid') != -1 , "Attribution has correct map data link");
|
||||
t.delay_call(3, function() {
|
||||
t.eq(log.length, 1, "updateAttribution was called once");
|
||||
t.ok(log[0]["m"].length && log[0]["k"].length, "'m' and 'k' copyrights populated for hybrid layer");
|
||||
t.ok(layer.attribution.indexOf('olGoogleAttribution hybrid') != -1, "Attribution has the correct css class");
|
||||
t.ok(layer.attribution.indexOf('?ll=48,16&z=5&t=h"') != -1, "maps.google.com link has correct parameters");
|
||||
map.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -356,7 +356,7 @@ div.olControlSaveFeaturesItemInactive {
|
||||
}
|
||||
|
||||
.olGoogleAttribution.hybrid, .olGoogleAttribution.satellite {
|
||||
color: #DDD;
|
||||
color: #EEE;
|
||||
}
|
||||
.olGoogleAttribution {
|
||||
color: #333;
|
||||
@@ -365,7 +365,7 @@ span.olGoogleAttribution a {
|
||||
color: #77C;
|
||||
}
|
||||
span.olGoogleAttribution.hybrid a, span.olGoogleAttribution.satellite a {
|
||||
color: white;
|
||||
color: #EEE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user