Skip to content

Instantly share code, notes, and snippets.

@pagameba
Created September 16, 2011 12:20
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save pagameba/1221998 to your computer and use it in GitHub Desktop.
Save pagameba/1221998 to your computer and use it in GitHub Desktop.
/*
* Portions of this code and logic copied from OpenLayers and
* redistributed under the original Clear BSD license terms:
*
* http://trac.osgeo.org/openlayers/browser/license.txt
*
* Copyright 2005-2010 OpenLayers Contributors, released under
* the Clear BSD license. See authors.txt for a list of contributors.
* All rights reserved.
*
* --
*
* Leaflet-specific modifications are released under the following
* terms:
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/
L.TileLayer.Bing = L.TileLayer.extend({
supportedTypes: ['Road', 'Aerial', 'AerialWithLabels'],
attributionTemplate: '<span style="display:inline-block">' +
'<a target="_blank" href="http://www.bing.com/maps/">' +
'<img src="{logo}" /></a><br><span>{copyrights}' +
'<a style="white-space: nowrap" target="_blank" '+
'href="http://www.microsoft.com/maps/product/terms.html">' +
'Terms of Use</a></span></span>',
initialize: function(/*String*/ apiKey, /*String*/ mapType, /*Object*/ options) {
this._apiKey = apiKey;
this._mapType = mapType;
this._loadMetadata();
L.Util.setOptions(this, options);
},
_loadMetadata: function() {
this._callbackId = "_l_tilelayer_bing_" + (L.TileLayer.Bing._callbackId++);
var that = this;
window[this._callbackId] = function() {
L.TileLayer.Bing.processMetadata.apply(that, arguments);
};
var params = {
key: this._apiKey,
jsonp: this._callbackId,
include: 'ImageryProviders'
},
url = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
this._mapType + L.Util.getParamString(params),
script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
script.id = this._callbackId;
document.getElementsByTagName("head")[0].appendChild(script);
},
_onMetadataLoaded: function() {},
onAdd: function(map, insertAtTheBottom) {
if (!this.metadata) {
this._onMetadataLoaded = L.Util.bind(function() {
L.TileLayer.prototype.onAdd.call(this, map, insertAtTheBottom);
map.on('moveend', this._updateAttribution, this);
this._updateAttribution();
}, this);
} else {
L.TileLayer.prototype.onAdd.call(this, map, insertAtTheBottom);
map.on('moveend', this._updateAttribution, this);
this._updateAttribution();
}
},
onRemove: function(map) {
if (this._map.attributionControl) {
this._map.attributionControl.removeAttribution(this.attribution);
}
this._map.off('moveend', this._updateAttribution, this);
L.TileLayer.prototype.onRemove.call(this, map);
},
getTileUrl: function(xy, z) {
var subdomains = this.options.subdomains,
quadDigits = [],
i = z,
digit,
mask,
quadKey;
// borrowed directly from OpenLayers
for (; i > 0; --i) {
digit = '0';
mask = 1 << (i - 1);
if ((xy.x & mask) != 0) {
digit++;
}
if ((xy.y & mask) != 0) {
digit++;
digit++;
}
quadDigits.push(digit);
}
return this._url
.replace('{subdomain}', subdomains[(xy.x + xy.y) % subdomains.length])
.replace('{quadkey}', quadDigits.join(""));
},
_updateAttribution: function() {
if (this._map.attributionControl) {
var metadata = this.metadata;
var res = metadata.resourceSets[0].resources[0];
var bounds = this._map.getBounds();
var providers = res.imageryProviders, zoom = this._map.getZoom() + 1,
copyrights = "", provider, i, ii, j, jj, bbox, coverage;
for (i=0,ii=providers.length; i<ii; ++i) {
provider = providers[i];
for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
coverage = provider.coverageAreas[j];
if (zoom <= coverage.zoomMax && zoom >= coverage.zoomMin && coverage.bbox.intersects(bounds)) {
copyrights += provider.attribution + " ";
j = jj;
}
}
}
this._map.attributionControl.removeAttribution(this.attribution);
this._map.attributionControl._attributions = {};
this._map.attributionControl._update();
this.attribution = this.attributionTemplate
.replace('{logo}', metadata.brandLogoUri)
.replace('{copyrights}', copyrights);
this._map.attributionControl.addAttribution(this.attribution);
}
}
});
L.TileLayer.Bing._callbackId = 0;
L.TileLayer.Bing.processMetadata = function(metadata) {
if (metadata.authenticationResultCode != 'ValidCredentials') {
throw "Invalid Bing Maps API Key"
}
if (!metadata.resourceSets.length || !metadata.resourceSets[0].resources.length) {
throw "No resources returned, perhaps " + this._mapType + " is an invalid map type?";
}
if (metadata.statusCode != 200) {
throw "Bing Maps API request failed with status code " + metadata.statusCode;
}
this.metadata = metadata;
var res = metadata.resourceSets[0].resources[0],
providers = res.imageryProviders,
i = 0,
j,
provider,
bbox,
script = document.getElementById(this._callbackId);
for (; i<providers.length; i++) {
provider = providers[i];
for (j=0; j<provider.coverageAreas.length; j++) {
bbox = provider.coverageAreas[j].bbox;
provider.coverageAreas[j].bbox = new L.LatLngBounds(new L.LatLng(bbox[0],bbox[1],true),new L.LatLng(bbox[2],bbox[3], true));
}
}
this._url = res.imageUrl.replace('{culture}','en-US');
this.options.subdomains = [].concat(res.imageUrlSubdomains);
script.parentNode.removeChild(script);
window[this._callbackId] = undefined; // cannot delete from window in IE
delete this._callbackId;
this._onMetadataLoaded();
}
@yuvipanda
Copy link

Hello! I'm trying to get this to work but Bing is giving me '400 Bad Request' responses. Is there someplace with a working example of this I could look at? Thanks!

@pagameba
Copy link
Author

pagameba commented Mar 9, 2012

Hi, sorry I don't have a working example any more, I deleted it somewhere along the way. I will be coming back to this experiment in a few weeks as I will have need of it again, until then I don't have time to debug problems. I expect the issue is that Bing has changed something in their API and this code would need to be updated to accommodate the changes. This code was adapted from OpenLayers Bing support, I notice the OpenLayers Bing example is still working so perhaps you could start there if you need to get it working sooner .. http://openlayers.org/dev/examples/bing-tiles.html

Cheers

Paul

@rclark
Copy link

rclark commented Mar 11, 2012

I had some trouble with the processMetadata function. It looks to me like Leaflet commit e5f934e changed L.Utils.bind in such a way as to break your callback. A clumsy adjustment to your _loadMetadata routine fixed it for me:

//window[this._callbackId] = L.Util.bind(L.TileLayer.Bing.processMetadata, this);
that = this;
window[this._callbackId] = function() { L.TileLayer.Bing.processMetadata.apply(that, arguments); };

I forked your Gist with my changes

Thanks,
Ryan

@pagameba
Copy link
Author

pagameba commented Mar 11, 2012 via email

@yuvipanda
Copy link

/me pokes

@pagameba
Copy link
Author

:) sorry, its been a busy week. Does Ryan's fork linked above work for you? I haven't tried it yet.

@yuvipanda
Copy link

yuvipanda commented Mar 14, 2012 via email

@rclark
Copy link

rclark commented Mar 14, 2012

@yuvipanda
Copy link

Yes, Ryan's changes worked. Thanks! :D

It'll also be nice if you could explicitly state what license this code is under...

@pagameba
Copy link
Author

Great point. I've updated this Gist with Ryan's changes (untested) and appropriate license and copyright details.

Cheers

Paul

@rclark
Copy link

rclark commented Mar 14, 2012

There are still problems, I think, if you want to have more than one Bing layer in your app.

@ccgdesigns
Copy link

Issue with multiple Bing Layers and attributions being added multiple times:

Replace

`````` this._map.attributionControl.removeAttribution(this.attribution);```

with

    this._map.attributionControl._attributions = {};
    this._map.attributionControl._update();```

Forked here:
https://gist.github.com/2342958

@pagameba
Copy link
Author

updated gist with ccgdesigns contribution.

@Narrator
Copy link

When I try to overlay a custom tilelayer over the bing layer, the bing one always supersedes. As in, I want the custom layer to be on top. The custom layer falls under the aerial imagery, so i don't see it at all!

@pagameba
Copy link
Author

This code has been incorporated into https://github.com/shramov/leaflet-plugins, I suggest following up there.

@resideozsoy
Copy link

Hi,
We are using leaflet with bing map
I am wondering if it is possible to retrieve session id to decrease billable calls to bingmap
If we use session id instead of key after first call they are nonbillable.
For bingmap ajax there is getcredentials function but I could not find anything similar with leaflet.
callback function only includes traceid.
Thanks

@marie15
Copy link

marie15 commented May 31, 2016

Hi everybody, I'm trying to add tile bing to my map, espacially 'AerialWithLabels' type. So do you have any idea how to use that? I'm using leaflet with angularJS. Honestly, I'm beginner in leaflet.

Any help is appreciated. Thankx for advance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment