diff --git a/examples/bing-tiles.html b/examples/bing-tiles.html new file mode 100644 index 0000000000..018b9773ae --- /dev/null +++ b/examples/bing-tiles.html @@ -0,0 +1,35 @@ + + + + OpenLayers Bing Tiles Example + + + + + +

Basic Bing Tiles Example

+ +
+ bing tiles +
+ +
Use Bing with direct tile access
+ +
+ +
+

This example shows a very simple map with Bing layers that use + direct tile access through Bing Maps REST Services.

See + bing-tiles.js for the + source code.

+
+ + + + diff --git a/examples/bing-tiles.js b/examples/bing-tiles.js new file mode 100644 index 0000000000..7ccbb8375b --- /dev/null +++ b/examples/bing-tiles.js @@ -0,0 +1,26 @@ +// API key for http://openlayers.org. Please get your own at +// http://bingmapsportal.com/ and use that instead. +var apiKey = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf"; + +var map = new OpenLayers.Map( 'map'); + +var road = new OpenLayers.Layer.Bing({ + key: apiKey, + type: "Road" +}); +var aerial = new OpenLayers.Layer.Bing({ + key: apiKey, + type: "Aerial" +}); +var hybrid = new OpenLayers.Layer.Bing({ + key: apiKey, + type: "AerialWithLabels", + name: "Bing Aerial With Labels" +}); + +map.addLayers([road, aerial, hybrid]); +map.addControl(new OpenLayers.Control.LayerSwitcher()); +map.setCenter(new OpenLayers.LonLat(-71.147, 42.472).transform( + new OpenLayers.Projection("EPSG:4326"), + map.getProjectionObject() +), 12); diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index 5a0a6cd634..f926090836 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -127,6 +127,7 @@ "OpenLayers/Layer/GeoRSS.js", "OpenLayers/Layer/Boxes.js", "OpenLayers/Layer/XYZ.js", + "OpenLayers/Layer/Bing.js", "OpenLayers/Layer/TMS.js", "OpenLayers/Layer/TileCache.js", "OpenLayers/Layer/Zoomify.js", diff --git a/lib/OpenLayers/Layer/Bing.js b/lib/OpenLayers/Layer/Bing.js new file mode 100644 index 0000000000..d99b20e5ac --- /dev/null +++ b/lib/OpenLayers/Layer/Bing.js @@ -0,0 +1,312 @@ +/* Copyright (c) 2006-2010 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.Bing + * Bing layer using direct tile access as provided by Bing Maps REST Services. + * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more + * information. + * + * Inherits from: + * - + */ +OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { + + /** + * Constant: RESOLUTIONS + */ + RESOLUTIONS: [ + 78271.517, + 39135.7585, + 19567.87925, + 9783.939625, + 4891.9698125, + 2445.98490625, + 1222.992453125, + 611.4962265625, + 305.74811328125, + 152.874056640625, + 76.4370283203125, + 38.21851416015625, + 19.109257080078127, + 9.554628540039063, + 4.777314270019532, + 2.388657135009766, + 1.194328567504883, + 0.5971642837524415, + 0.29858214187622073, + 0.14929107093811037, + 0.07464553546905518, + 0.03732276773452759, + 0.018661383867263796 + ], + + /** + * Property: attributionTemplate + * {String} + */ + attributionTemplate: '' + + '
' + + '
${copyrights}' + + '' + + 'Terms of Use
', + + /** + * Property: sphericalMercator + * {Boolean} always true for this layer type + */ + sphericalMercator: true, + + /** + * Property: metadata + * {Object} Metadata for this layer, as returned by the callback script + */ + metadata: null, + + /** + * APIProperty: type + * {String} The layer identifier. Any non-birdseye imageryType + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be + * used. Default is "Road". + */ + type: "Road", + + /** + * Constant: EVENT_TYPES + * {Array(String)} Supported application event types. Register a listener + * for a particular event with the following syntax: + * (code) + * layer.events.register(type, obj, listener); + * (end) + * + * Listeners will be called with a reference to an event object. The + * properties of this event depends on exactly what happened. + * + * All event objects have at least the following properties: + * object - {Object} A reference to layer.events.object. + * element - {DOMElement} A reference to layer.events.element. + * + * Supported map event types (in addition to those from ): + * added - Triggered after the layer is added to a map. Listeners + * will receive an object with a *map* property referencing the + * map and a *layer* property referencing the layer. + */ + EVENT_TYPES: ["added"], + + /** + * Constructor: OpenLayers.Layer.Bing + * Create a new Bing layer. + * + * Example: + * (code) + * var road = new OpenLayers.Layer.Bing({ + * name: "My Bing Aerial Layer", + * type: "Aerial", + * key: "my-api-key-here", + * }); + * (end) + * + * Parameters: + * config - {Object} Configuration properties for the layer. + * + * Required configuration properties: + * key - {String} Bing Maps API key for your application. Get one at + * http://bingmapsportal.com/. + * type - {String} The layer identifier. Any non-birdseye imageryType + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be + * used. + * + * Any other documented layer properties can be provided in the config object. + */ + initialize: function(options) { + // concatenate events specific to vector with those from the base + this.EVENT_TYPES = + OpenLayers.Layer.Bing.prototype.EVENT_TYPES.concat( + OpenLayers.Layer.prototype.EVENT_TYPES + ); + var name = options.name || "Bing " + (options.type || this.type); + var newArgs = [name, null, options]; + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); + this.loadMetadata(this.type); + }, + + /** + * Method: loadMetadata + * + * Parameters: + * imageryType - {String} + */ + loadMetadata: function(imageryType) { + this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); + // link the processMetadata method to the global scope and bind it + // to this instance + window[this._callbackId] = OpenLayers.Function.bind( + OpenLayers.Layer.Bing.processMetadata, this + ); + var url = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" + + imageryType + "?key=" + this.key + "&jsonp=" + this._callbackId + + "&include=ImageryProviders"; + var script = document.createElement("script"); + script.type = "text/javascript"; + script.src = url; + script.id = this._callbackId; + document.getElementsByTagName("head")[0].appendChild(script); + }, + + /** + * Method: initLayer + * + * Sets layer properties according to the metadata provided by the API + */ + initLayer: function() { + var res = this.metadata.resourceSets[0].resources[0]; + var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); + this.url = []; + for (var i=0; i} + */ + getURL: function(bounds) { + if (!this.url) { + return OpenLayers.Util.getImagesLocation() + "blank.gif"; + } + var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z; + var quadDigits = []; + for (var i = z; i > 0; --i) { + var digit = '0'; + var mask = 1 << (i - 1); + if ((x & mask) != 0) { + digit++; + } + if ((y & mask) != 0) { + digit++; + digit++; + } + quadDigits.push(digit); + } + var quadKey = quadDigits.join(""); + var url = this.selectUrl('' + x + y + z, this.url); + + return OpenLayers.String.format(url, {'quadkey': quadKey}); + }, + + /** + * Method: updateAttribution + * Updates the attribution according to the requirements outlined in + * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html + */ + updateAttribution: function() { + var metadata = this.metadata; + if (!metadata || !this.map) { + return; + } + var res = metadata.resourceSets[0].resources[0]; + var extent = this.map.getExtent().transform( + this.map.getProjectionObject(), + new OpenLayers.Projection("EPSG:4326") + ); + var providers = res.imageryProviders, zoom = this.map.getZoom() + 1, + copyrights = "", provider, i, ii, j, jj, bbox, coverage; + for (i=0,ii=providers.length; i= coverage.zoomMin) { + copyrights += provider.attribution + " "; + } + } + } + this.attribution = OpenLayers.String.format(this.attributionTemplate, { + type: this.type.toLowerCase(), + logo: metadata.brandLogoUri, + copyrights: copyrights + }); + this.map && this.map.events.triggerEvent("changelayer", {layer: this}); + }, + + /** + * Method: setMap + */ + setMap: function() { + OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); + if (this.map.getCenter()) { + this.updateAttribution(); + } + this.map.events.register("moveend", this, this.updateAttribution); + // TODO: move this event to Layer + // http://trac.osgeo.org/openlayers/ticket/2983 + this.events.triggerEvent("added", {map: this.map, layer: this}); + }, + + /** + * APIMethod: clone + * + * Parameters: + * obj - {Object} + * + * Returns: + * {} An exact clone of this + */ + clone: function(obj) { + if (obj == null) { + obj = new OpenLayers.Layer.Bing(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.Bing" +}); + +/** + * Function: OpenLayers.Layer.Bing.processMetadata + * This function will be bound to an instance, linked to the global scope with + * an id, and called by the JSONP script returned by the API. + * + * Parameters: + * metadata - {Object} metadata as returned by the API + */ +OpenLayers.Layer.Bing.processMetadata = function(metadata) { + this.metadata = metadata; + this.initLayer(); + var script = document.getElementById(this._callbackId); + script.parentNode.removeChild(script); + window[this._callbackId] = undefined; // cannot delete from window in IE + delete this._callbackId; +}; diff --git a/lib/OpenLayers/Layer/XYZ.js b/lib/OpenLayers/Layer/XYZ.js index ef421b52de..a8572a4050 100644 --- a/lib/OpenLayers/Layer/XYZ.js +++ b/lib/OpenLayers/Layer/XYZ.js @@ -107,7 +107,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { }, /** - * Method: getUrl + * Method: getURL * * Parameters: * bounds - {} @@ -118,6 +118,27 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { * parameters */ getURL: function (bounds) { + var xyz = this.getXYZ(bounds); + var url = this.url; + if (url instanceof Array) { + var s = '' + xyz.x + xyz.y + xyz.z; + url = this.selectUrl(s, url); + } + + return OpenLayers.String.format(url, xyz); + }, + + /** + * Method: getXYZ + * Calculates x, y and z for the given bounds. + * + * Parameters: + * bounds - {} + * + * Returns: + * {Object} - an object with x, y and z properties. + */ + getXYZ: function(bounds) { var res = this.map.getResolution(); var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); @@ -127,16 +148,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom() + this.zoomOffset; - var url = this.url; - var s = '' + x + y + z; - if (url instanceof Array) - { - url = this.selectUrl(s, url); - } - - var path = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z}); - - return path; + return {'x': x, 'y': y, 'z': z}; }, /** diff --git a/tests/Layer/Bing.html b/tests/Layer/Bing.html new file mode 100644 index 0000000000..10ebaa9f75 --- /dev/null +++ b/tests/Layer/Bing.html @@ -0,0 +1,65 @@ + + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 8a8bc7933c..a52a98a949 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -125,6 +125,7 @@
  • Layer.html
  • Layer/ArcIMS.html
  • Layer/ArcGIS93Rest.html
  • +
  • Layer/Bing.html
  • Layer/EventPane.html
  • Layer/FixedZoomLevels.html
  • Layer/GeoRSS.html
  • diff --git a/theme/default/style.css b/theme/default/style.css index c161418352..6d5fd24ac1 100644 --- a/theme/default/style.css +++ b/theme/default/style.css @@ -397,3 +397,10 @@ div.olControlSaveFeaturesItemInactive { top: 5px; right: 0px; } + +.olBingAttribution { + color: #DDD; +} +.olBingAttribution.road { + color: #333; +}