1L.BingLayer = L.TileLayer.extend({ 2 options: { 3 // imagerySet: https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata#template-parameters 4 // supported: 5 // - Aerial, AerialWithLabels (Deprecated), AerialWithLabelsOnDemand 6 // - Road (Deprecated), RoadOnDemand 7 // - CanvasDark, CanvasLight, CanvasGray 8 // not supported: Birdseye*, Streetside 9 imagerySet: 'Aerial', // to be changed on next major version!! 10 11 // https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes 12 culture: '', 13 14 // https://docs.microsoft.com/en-us/bingmaps/articles/custom-map-styles-in-bing-maps#custom-map-styles-in-the-rest-and-tile-services 15 style: '', 16 17 // https://blogs.bing.com/maps/2015/02/12/high-ppi-maps-now-available-in-the-bing-maps-ajax-control 18 // not documented in REST API docs, but working 19 // warning: deprecated imagery sets may not support some values (depending also on zoom level) 20 retinaDpi: 'd2', 21 22 attribution: 'Bing', 23 minZoom: 1, 24 maxZoom: 21 25 // Actual `maxZoom` value may be less, depending on imagery set / coverage area 26 // - 19~20 for all 'Aerial*' 27 // - 20 for 'Road' (Deprecated) 28 }, 29 30 initialize: function (key, options) { 31 if (typeof key === 'object') { 32 options = key; 33 key = false; 34 } 35 L.TileLayer.prototype.initialize.call(this, null, options); 36 37 options = this.options; 38 options.key = options.key || options.bingMapsKey; 39 options.imagerySet = options.imagerySet || options.type; 40 if (key) { options.key = key; } 41 }, 42 43 tile2quad: function (x, y, z) { 44 var quad = ''; 45 for (var i = z; i > 0; i--) { 46 var digit = 0; 47 var mask = 1 << i - 1; 48 if ((x & mask) !== 0) { digit += 1; } 49 if ((y & mask) !== 0) { digit += 2; } 50 quad = quad + digit; 51 } 52 return quad; 53 }, 54 55 getTileUrl: function (coords) { 56 var data = { 57 subdomain: this._getSubdomain(coords), 58 quadkey: this.tile2quad(coords.x, coords.y, this._getZoomForUrl()), 59 culture: this.options.culture // compatibility for deprecated
59imagery sets ('Road' etc) 60 }; 61 return L.Util.template(this._url, data); 62 }, 63 64 callRestService: function (request, callback, context) { 65 context = context || this; 66 var uniqueName = '_bing_metadata_' + L.Util.stamp(this); 67 while (window[uniqueName]) { uniqueName += '_'; } 68 request += '&jsonp=' + uniqueName; 69 var script = document.createElement('script'); 70 script.setAttribute('type', 'text/javascript'); 71 script.setAttribute('src', request); 72 window[uniqueName] = function (response) { 73 delete window[uniqueName]; 74 script.remove(); 75 if (response.errorDetails) { 76 throw new Error(response.errorDetails); 77 } 78 callback.call(context, response); 79 }; 80 document.body.appendChild(script); 81 }, 82 83 _makeApiUrl: function (restApi, resourcePath, query) { 84 var baseAPIparams = { 85 version: 'v1', 86 restApi: restApi, 87 resourcePath: resourcePath 88 }; 89 query = L.extend({ 90 // errorDetail: true, // seems no effect 91 key: this.options.key 92 }, query); 93 94 // https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/base-url-structure 95 var template = 'https://dev.virtualearth.net/REST/{version}/{restApi}/{resourcePath}'; // ?queryParameters&key=BingMapsKey 96 return L.Util.template(template, baseAPIparams) + L.Util.getParamString(query); 97 }, 98 99 loadMetadata: function () { 100 if (this.metaRequested) { return; } 101 this.metaRequested = true; 102 var options = this.options; 103 // https://docs.microsoft.com/en-us/bingmaps/rest-services/imagery/get-imagery-metadata#complete-metadata-urls 104 var request = this._makeApiUrl('Imagery/Metadata', options.imagerySet, { 105 UriScheme: 'https', 106 include: 'ImageryProviders', 107 culture: options.culture, 108 style: options.style 109 }); 110 this.callRestService(request, function (meta) { 111 var r = meta.resourceSets[0].resources[0]; 112 if (!r.imageUrl) { throw new Error('imageUrl not found in response'); } 113 if (r.imageUrlSubdomains) { options.subdomains = r.imageUrlSubdomains; } 114 this._providers = r.imageryProviders ? this._prepAttrBounds(r.imageryProviders) : []; 115 this._attributions = []; 116 this._url = r.imageUrl; 117 if (options.retinaDpi && options.detectRetina && options.zoomOffset) { 118 this._url += '&dpi=' + options.retinaDpi; 119 } 120 this.fire('load', {meta: meta}); 121 if (this._map) { this._update(); } 122 }); 123 }, 124 125 _prepAttrBounds: function (providers) { 126 providers.forEach(function (provider) { 127 provider.coverageAreas.forEach(function (area) { 128 area.bounds = L.latLngBounds( 129 [area.bbox[0], area.bbox[1]], 130 [area.bbox[2], area.bbox[3]] 131 ); 132 }); 133 }); 134 return providers; 135 }, 136 137 _update: function (center) { 138 if (!this._url) { return; } 139 L.GridLayer.prototype._update.call(this, center); 140 this._update_attribution(); 141 }, 142 143 _update_attribution: function (remove) { 144 var attributionControl = this._map.attributionControl; 145 if (!attributionControl) { 146 this._attributions = {}; return; 147 } 148 var bounds = this._map.getBounds();
149 bounds = L.latLngBounds(bounds.getSouthWest().wrap(), bounds.getNorthEast().wrap()); 150 var zoom = this._getZoomForUrl(); 151 var attributions = this._providers.map(function (provider) { 152 return remove ? false : provider.coverageAreas.some(function (area) { 153 return zoom <= area.zoomMax && zoom >= area.zoomMin && 154 bounds.intersects(area.bounds); 155 }); 156 }); 157 attributions.forEach(function (a,i) { 158 if (a == this._attributions[i]) { // eslint-disable-line eqeqeq 159 return; 160 } else if (a) { 161 attributionControl.addAttribution(this._providers[i].attribution); 162 } else { 163 attributionControl.removeAttribution(this._providers[i].attribution); 164 } 165 }, this); 166 this._attributions = attributions; 167 }, 168 169 onAdd: function (map) { 170 // Note: Metadata could be loaded earlier, on layer initialize, 171 // but according to docs even such request is billable: 172 // https://docs.microsoft.com/en-us/bingmaps/getting-started/bing-maps-dev-center-help/understanding-bing-maps-transactions#rest-services 173 // That's why it's important to defer it till BingLayer is actually added to map 174 this.loadMetadata(); 175 L.GridLayer.prototype.onAdd.call(this, map); 176 }, 177 178 onRemove: function (map) { 179 if (this._providers) { this._update_attribution(true); } 180 L.GridLayer.prototype.onRemove.call(this, map); 181 } 182}); 183 184L.bingLayer = function (key, options) { 185 return new L.BingLayer(key, options); 186};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.