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