Bing.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
  2. * full list of contributors). Published under the 2-clause BSD license.
  3. * See license.txt in the OpenLayers distribution or repository for the
  4. * full text of the license. */
  5. /**
  6. * @requires OpenLayers/Layer/XYZ.js
  7. */
  8. /**
  9. * Class: OpenLayers.Layer.Bing
  10. * Bing layer using direct tile access as provided by Bing Maps REST Services.
  11. * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
  12. * information. Note: Terms of Service compliant use requires the map to be
  13. * configured with an <OpenLayers.Control.Attribution> control and the
  14. * attribution placed on or near the map.
  15. *
  16. * Inherits from:
  17. * - <OpenLayers.Layer.XYZ>
  18. */
  19. OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
  20. /**
  21. * Property: key
  22. * {String} API key for Bing maps, get your own key
  23. * at http://bingmapsportal.com/ .
  24. */
  25. key: null,
  26. /**
  27. * Property: serverResolutions
  28. * {Array} the resolutions provided by the Bing servers.
  29. */
  30. serverResolutions: [
  31. 156543.03390625, 78271.516953125, 39135.7584765625,
  32. 19567.87923828125, 9783.939619140625, 4891.9698095703125,
  33. 2445.9849047851562, 1222.9924523925781, 611.4962261962891,
  34. 305.74811309814453, 152.87405654907226, 76.43702827453613,
  35. 38.218514137268066, 19.109257068634033, 9.554628534317017,
  36. 4.777314267158508, 2.388657133579254, 1.194328566789627,
  37. 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,
  38. 0.07464553542435169
  39. ],
  40. /**
  41. * Property: attributionTemplate
  42. * {String}
  43. */
  44. attributionTemplate: '<span class="olBingAttribution ${type}">' +
  45. '<div><a target="_blank" href="http://www.bing.com/maps/">' +
  46. '<img src="${logo}" /></a></div>${copyrights}' +
  47. '<a style="white-space: nowrap" target="_blank" '+
  48. 'href="http://www.microsoft.com/maps/product/terms.html">' +
  49. 'Terms of Use</a></span>',
  50. /**
  51. * Property: metadata
  52. * {Object} Metadata for this layer, as returned by the callback script
  53. */
  54. metadata: null,
  55. /**
  56. * Property: protocolRegex
  57. * {RegExp} Regular expression to match and replace http: in bing urls
  58. */
  59. protocolRegex: /^http:/i,
  60. /**
  61. * APIProperty: type
  62. * {String} The layer identifier. Any non-birdseye imageryType
  63. * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
  64. * used. Default is "Road".
  65. */
  66. type: "Road",
  67. /**
  68. * APIProperty: culture
  69. * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx
  70. * for the definition and the possible values. Default is "en-US".
  71. */
  72. culture: "en-US",
  73. /**
  74. * APIProperty: metadataParams
  75. * {Object} Optional url parameters for the Get Imagery Metadata request
  76. * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx
  77. */
  78. metadataParams: null,
  79. /** APIProperty: tileOptions
  80. * {Object} optional configuration options for <OpenLayers.Tile> instances
  81. * created by this Layer. Default is
  82. *
  83. * (code)
  84. * {crossOriginKeyword: 'anonymous'}
  85. * (end)
  86. */
  87. tileOptions: null,
  88. /** APIProperty: protocol
  89. * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo
  90. * Can be 'http:' 'https:' or ''
  91. *
  92. * Warning: tiles may not be available under both HTTP and HTTPS protocols.
  93. * Microsoft approved use of both HTTP and HTTPS urls for tiles. However
  94. * this is undocumented and the Imagery Metadata API always returns HTTP
  95. * urls.
  96. *
  97. * Default is '', unless when executed from a file:/// uri, in which case
  98. * it is 'http:'.
  99. */
  100. protocol: ~window.location.href.indexOf('http') ? '' : 'http:',
  101. /**
  102. * Constructor: OpenLayers.Layer.Bing
  103. * Create a new Bing layer.
  104. *
  105. * Example:
  106. * (code)
  107. * var road = new OpenLayers.Layer.Bing({
  108. * name: "My Bing Aerial Layer",
  109. * type: "Aerial",
  110. * key: "my-api-key-here",
  111. * });
  112. * (end)
  113. *
  114. * Parameters:
  115. * options - {Object} Configuration properties for the layer.
  116. *
  117. * Required configuration properties:
  118. * key - {String} Bing Maps API key for your application. Get one at
  119. * http://bingmapsportal.com/.
  120. * type - {String} The layer identifier. Any non-birdseye imageryType
  121. * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
  122. * used.
  123. *
  124. * Any other documented layer properties can be provided in the config object.
  125. */
  126. initialize: function(options) {
  127. options = OpenLayers.Util.applyDefaults({
  128. sphericalMercator: true
  129. }, options);
  130. var name = options.name || "Bing " + (options.type || this.type);
  131. var newArgs = [name, null, options];
  132. OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
  133. this.tileOptions = OpenLayers.Util.extend({
  134. crossOriginKeyword: 'anonymous'
  135. }, this.options.tileOptions);
  136. this.loadMetadata();
  137. },
  138. /**
  139. * Method: loadMetadata
  140. */
  141. loadMetadata: function() {
  142. this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
  143. // link the processMetadata method to the global scope and bind it
  144. // to this instance
  145. window[this._callbackId] = OpenLayers.Function.bind(
  146. OpenLayers.Layer.Bing.processMetadata, this
  147. );
  148. var params = OpenLayers.Util.applyDefaults({
  149. key: this.key,
  150. jsonp: this._callbackId,
  151. include: "ImageryProviders"
  152. }, this.metadataParams);
  153. var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
  154. this.type + "?" + OpenLayers.Util.getParameterString(params);
  155. var script = document.createElement("script");
  156. script.type = "text/javascript";
  157. script.src = url;
  158. script.id = this._callbackId;
  159. document.getElementsByTagName("head")[0].appendChild(script);
  160. },
  161. /**
  162. * Method: initLayer
  163. *
  164. * Sets layer properties according to the metadata provided by the API
  165. */
  166. initLayer: function() {
  167. var res = this.metadata.resourceSets[0].resources[0];
  168. var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
  169. url = url.replace("{culture}", this.culture);
  170. url = url.replace(this.protocolRegex, this.protocol);
  171. this.url = [];
  172. for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
  173. this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
  174. }
  175. this.addOptions({
  176. maxResolution: Math.min(
  177. this.serverResolutions[res.zoomMin],
  178. this.maxResolution || Number.POSITIVE_INFINITY
  179. ),
  180. numZoomLevels: Math.min(
  181. res.zoomMax + 1 - res.zoomMin, this.numZoomLevels
  182. )
  183. }, true);
  184. if (!this.isBaseLayer) {
  185. this.redraw();
  186. }
  187. this.updateAttribution();
  188. },
  189. /**
  190. * Method: getURL
  191. *
  192. * Paramters:
  193. * bounds - {<OpenLayers.Bounds>}
  194. */
  195. getURL: function(bounds) {
  196. if (!this.url) {
  197. return;
  198. }
  199. var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
  200. var quadDigits = [];
  201. for (var i = z; i > 0; --i) {
  202. var digit = '0';
  203. var mask = 1 << (i - 1);
  204. if ((x & mask) != 0) {
  205. digit++;
  206. }
  207. if ((y & mask) != 0) {
  208. digit++;
  209. digit++;
  210. }
  211. quadDigits.push(digit);
  212. }
  213. var quadKey = quadDigits.join("");
  214. var url = this.selectUrl('' + x + y + z, this.url);
  215. return OpenLayers.String.format(url, {'quadkey': quadKey});
  216. },
  217. /**
  218. * Method: updateAttribution
  219. * Updates the attribution according to the requirements outlined in
  220. * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
  221. */
  222. updateAttribution: function() {
  223. var metadata = this.metadata;
  224. if (!metadata.resourceSets || !this.map || !this.map.center) {
  225. return;
  226. }
  227. var res = metadata.resourceSets[0].resources[0];
  228. var extent = this.map.getExtent().transform(
  229. this.map.getProjectionObject(),
  230. new OpenLayers.Projection("EPSG:4326")
  231. );
  232. var providers = res.imageryProviders || [],
  233. zoom = OpenLayers.Util.indexOf(this.serverResolutions,
  234. this.getServerResolution()),
  235. copyrights = "", provider, i, ii, j, jj, bbox, coverage;
  236. for (i=0,ii=providers.length; i<ii; ++i) {
  237. provider = providers[i];
  238. for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
  239. coverage = provider.coverageAreas[j];
  240. // axis order provided is Y,X
  241. bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);
  242. if (extent.intersectsBounds(bbox) &&
  243. zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
  244. copyrights += provider.attribution + " ";
  245. }
  246. }
  247. }
  248. var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);
  249. this.attribution = OpenLayers.String.format(this.attributionTemplate, {
  250. type: this.type.toLowerCase(),
  251. logo: logo,
  252. copyrights: copyrights
  253. });
  254. this.map && this.map.events.triggerEvent("changelayer", {
  255. layer: this,
  256. property: "attribution"
  257. });
  258. },
  259. /**
  260. * Method: setMap
  261. */
  262. setMap: function() {
  263. OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
  264. this.map.events.register("moveend", this, this.updateAttribution);
  265. },
  266. /**
  267. * APIMethod: clone
  268. *
  269. * Parameters:
  270. * obj - {Object}
  271. *
  272. * Returns:
  273. * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
  274. */
  275. clone: function(obj) {
  276. if (obj == null) {
  277. obj = new OpenLayers.Layer.Bing(this.options);
  278. }
  279. //get all additions from superclasses
  280. obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
  281. // copy/set any non-init, non-simple values here
  282. return obj;
  283. },
  284. /**
  285. * Method: destroy
  286. */
  287. destroy: function() {
  288. this.map &&
  289. this.map.events.unregister("moveend", this, this.updateAttribution);
  290. OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
  291. },
  292. CLASS_NAME: "OpenLayers.Layer.Bing"
  293. });
  294. /**
  295. * Function: OpenLayers.Layer.Bing.processMetadata
  296. * This function will be bound to an instance, linked to the global scope with
  297. * an id, and called by the JSONP script returned by the API.
  298. *
  299. * Parameters:
  300. * metadata - {Object} metadata as returned by the API
  301. */
  302. OpenLayers.Layer.Bing.processMetadata = function(metadata) {
  303. this.metadata = metadata;
  304. this.initLayer();
  305. var script = document.getElementById(this._callbackId);
  306. script.parentNode.removeChild(script);
  307. window[this._callbackId] = undefined; // cannot delete from window in IE
  308. delete this._callbackId;
  309. };