1. Import
This commit is contained in:
44
html/locating/leaflet-bing-layer-gh-pages/CHANGELOG.md
Normal file
44
html/locating/leaflet-bing-layer-gh-pages/CHANGELOG.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [3.3.0] - 2018-03-14
|
||||
|
||||
- ADDED: `options.style` to use [custom map styles](https://msdn.microsoft.com/en-us/library/mt823632.aspx)
|
||||
- ADDED: `AerialWithLabelsOnDemand` imagery set
|
||||
- ADDED: `minNativeZoom` and `maxNativeZoom` options
|
||||
- FIXED: imageryProviders error [#25](https://github.com/digidem/leaflet-bing-layer/pull/25)
|
||||
|
||||
## [v3.2.0] - 2017-08-09
|
||||
|
||||
- CHANGED: Use `https` requests
|
||||
- ADDED: Additional imagery sets (`RoadOnDemand`, `CanvasLight`, `CanvasDark`, `CanvasGray`, `OrdnanceSurvey`)
|
||||
|
||||
## [v3.1.0] - 2016-04-29
|
||||
|
||||
- ADDED: Use `https` for Bing API requests.
|
||||
|
||||
## [v3.0.1] - 2015-12-13
|
||||
|
||||
- FIXED: options.BingMapsKey backwards compatability
|
||||
- FIXED: options.bingMapsKey was not working for getMetaData
|
||||
- FIXED: catch errors (and log to console) for jsonp
|
||||
|
||||
## [v3.0.0] - 2015-12-08
|
||||
|
||||
- FIXED: **[BREAKING]** Export factory function on `L.tileLayer.bing` not `L.TileLayer.bing`
|
||||
- CHANGED: BingMapsKey is now passed on `options.bingMapsKey` (`options.BingMapsKey` will still work, but for convention this should start with a lowercase character)
|
||||
- IMPROVED: Package with browserify and require dependencies
|
||||
- IMPROVED: Throws error if invalid imagerySet is passed as option
|
||||
- ADDED: `getMetaData` method
|
||||
|
||||
## v2.0.2 - 2015-12-03
|
||||
|
||||
Initial release
|
||||
|
||||
[Unreleased]: https://github.com/digidem/leaflet-bing-layer/compare/v3.2.0...HEAD
|
||||
[v3.2.0]: https://github.com/digidem/leaflet-bing-layer/compare/v3.1.0...v3.2.0
|
||||
[v3.1.0]: https://github.com/digidem/leaflet-bing-layer/compare/v3.0.1...v3.1.0
|
||||
[v3.0.1]: https://github.com/digidem/leaflet-bing-layer/compare/v3.0.0...v3.0.1
|
||||
[v3.0.0]: https://github.com/digidem/leaflet-bing-layer/compare/v2.0.2...v3.0.0
|
||||
43
html/locating/leaflet-bing-layer-gh-pages/README.md
Normal file
43
html/locating/leaflet-bing-layer-gh-pages/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# leaflet-bing-layer
|
||||
|
||||
Bing Maps Layer for Leaflet v1.0.0
|
||||
|
||||
|
||||
### L.TileLayer.Bing(options|BingMapsKey)
|
||||
|
||||
Create a new Bing Maps Layer. Depends on [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) which needs a polyfill for [older browsers](http://caniuse.com/#feat=promises) by adding this script to your html `<head>`:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise"></script>
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| parameter | type | description |
|
||||
| ----------------------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `options` | string\|object | A valid [Bing Maps Key](https://msdn.microsoft.com/en-us/library/ff428642.aspx) or an `options` object. `options` inherits from [L.TileLayer options](http://mourner.github.io/Leaflet/reference.html#tilelayer-options) (e.g. you can use `minZoom` and `opacity` and etc) |
|
||||
| `options.bingMapsKey` | string | A valid Bing Maps Key [_required_] |
|
||||
| `[options.imagerySet]` | string | _optional:_ [Imagery Type](https://msdn.microsoft.com/en-us/library/ff701716.aspx) [_default=Aerial_]<br>- `Aerial` - Aerial imagery<br>- `AerialWithLabels` - Aerial imagery with a road overlay<br>- `AerialWithLabelsOnDemand` - Aerial imagery with on-demand road overlay.<br>- `CanvasDark` - A dark version of the road maps.<br>- `CanvasLight` - A lighter version of the road maps which also has some of the details such as hill shading disabled.<br>- `CanvasGray` - A grayscale version of the road maps.<br>- `Road` - Roads without additional imagery. Uses the legacy static tile service.<br>- `RoadOnDemand` - Roads without additional imagery. Uses the dynamic tile service.<br>- `OrdnanceSurvey` - Ordnance Survey imagery. This imagery is visible only for the London area.<br>**[Not supported](https://social.msdn.microsoft.com/Forums/en-US/3d80d4a6-f4c9-4926-a336-e0d545b1ef3c/is-it-possible-to-retrieve-birdseye-map-tiles-using-rest-services?forum=bingmapsservices)**: `Birdseye` and `BirdseyeWithLabels` |
|
||||
| `[options.culture]` | string | _optional:_ Language for labels, [see options](https://msdn.microsoft.com/en-us/library/hh441729.aspx) [_default=en_US_] |
|
||||
| `[options.style]` | string | _optional:_ Use a [custom map style](https://msdn.microsoft.com/en-us/library/mt823632.aspx) - only works with the `AerialWithLabelsOnDemand` and `RoadOnDemand` imagerySet options. |
|
||||
|
||||
Other options are passed through to a [Leaflet TileLayer](http://leafletjs.com/reference-1.3.0.html#tilelayer-l-tilelayer)
|
||||
|
||||
### Methods
|
||||
|
||||
| Method | Returns | Description |
|
||||
| ---------- | -------------- | ------------- |
|
||||
| `getMetaData(<LatLng> latlng, <Number> zoom)` | `Promise` | Get the [Bing Imagery metadata](https://msdn.microsoft.com/en-us/library/ff701712.aspx) for a specific [`LatLng`](http://leafletjs.com/reference.html#latlng) and zoom level. `latLng` or `zoom` are optional *if* the layer is attached to a map, they default to current map center and zoom. Returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves to the metadata JSON from Bing |
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
var map = L.map('map').setView([51.505, -0.09], 13)
|
||||
L.tileLayer.bing(MyBingMapsKey).addTo(map)
|
||||
```
|
||||
|
||||
[Live Example](http://digidem.github.io/leaflet-bing-layer/) see [index.html](index.html)
|
||||
|
||||
### License
|
||||
|
||||
MIT
|
||||
36
html/locating/leaflet-bing-layer-gh-pages/index.html
Normal file
36
html/locating/leaflet-bing-layer-gh-pages/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8 />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui' />
|
||||
<title>Leaflet Bing Maps Layer</title>
|
||||
<script src='http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.js'></script>
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Promise"></script>
|
||||
<script src="leaflet-bing-layer.js"></script>
|
||||
<link href='http://cdn.leafletjs.com/leaflet/v1.0.0-rc.1/leaflet.css' rel='stylesheet' />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#map {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='map'></div>
|
||||
|
||||
<script>
|
||||
var BING_KEY = 'AuhiCJHlGzhg93IqUH_oCpl_-ZUrIE6SPftlyGYUvr9Amx5nzA-WqGcPquyFZl4L'
|
||||
|
||||
var map = L.map('map').setView([51.505, -0.09], 13)
|
||||
|
||||
var bingLayer = L.tileLayer.bing(BING_KEY).addTo(map)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
276
html/locating/leaflet-bing-layer-gh-pages/index.js
Normal file
276
html/locating/leaflet-bing-layer-gh-pages/index.js
Normal file
@@ -0,0 +1,276 @@
|
||||
var L = require('leaflet')
|
||||
var fetchJsonp = require('fetch-jsonp')
|
||||
var bboxIntersect = require('bbox-intersect')
|
||||
|
||||
/**
|
||||
* Converts tile xyz coordinates to Quadkey
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} z
|
||||
* @return {Number} Quadkey
|
||||
*/
|
||||
function toQuadKey (x, y, z) {
|
||||
var index = ''
|
||||
for (var i = z; i > 0; i--) {
|
||||
var b = 0
|
||||
var mask = 1 << (i - 1)
|
||||
if ((x & mask) !== 0) b++
|
||||
if ((y & mask) !== 0) b += 2
|
||||
index += b.toString()
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Leaflet BBoxString to Bing BBox
|
||||
* @param {String} bboxString 'southwest_lng,southwest_lat,northeast_lng,northeast_lat'
|
||||
* @return {Array} [south_lat, west_lng, north_lat, east_lng]
|
||||
*/
|
||||
function toBingBBox (bboxString) {
|
||||
var bbox = bboxString.split(',')
|
||||
return [bbox[1], bbox[0], bbox[3], bbox[2]]
|
||||
}
|
||||
|
||||
var VALID_IMAGERY_SETS = [
|
||||
'Aerial',
|
||||
'AerialWithLabels',
|
||||
'AerialWithLabelsOnDemand',
|
||||
'Road',
|
||||
'RoadOnDemand',
|
||||
'CanvasLight',
|
||||
'CanvasDark',
|
||||
'CanvasGray',
|
||||
'OrdnanceSurvey'
|
||||
]
|
||||
|
||||
var DYNAMIC_IMAGERY_SETS = [
|
||||
'AerialWithLabelsOnDemand',
|
||||
'RoadOnDemand'
|
||||
]
|
||||
|
||||
/**
|
||||
* Create a new Bing Maps layer.
|
||||
* @param {string|object} options Either a [Bing Maps Key](https://msdn.microsoft.com/en-us/library/ff428642.aspx) or an options object
|
||||
* @param {string} options.BingMapsKey A valid Bing Maps Key (required)
|
||||
* @param {string} [options.imagerySet=Aerial] Type of imagery, see https://msdn.microsoft.com/en-us/library/ff701716.aspx
|
||||
* @param {string} [options.culture='en-US'] Language for labels, see https://msdn.microsoft.com/en-us/library/hh441729.aspx
|
||||
* @return {L.TileLayer} A Leaflet TileLayer to add to your map
|
||||
*
|
||||
* Create a basic map
|
||||
* @example
|
||||
* var map = L.map('map').setView([51.505, -0.09], 13)
|
||||
* L.TileLayer.Bing(MyBingMapsKey).addTo(map)
|
||||
*/
|
||||
L.TileLayer.Bing = L.TileLayer.extend({
|
||||
options: {
|
||||
bingMapsKey: null, // Required
|
||||
imagerySet: 'Aerial',
|
||||
culture: 'en-US',
|
||||
minZoom: 1,
|
||||
minNativeZoom: 1,
|
||||
maxNativeZoom: 19
|
||||
},
|
||||
|
||||
statics: {
|
||||
METADATA_URL: 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/{imagerySet}?key={bingMapsKey}&include=ImageryProviders&uriScheme=https&c={culture}',
|
||||
POINT_METADATA_URL: 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/{imagerySet}/{lat},{lng}?zl={z}&key={bingMapsKey}&uriScheme=https&c={culture}'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = { bingMapsKey: options }
|
||||
}
|
||||
if (options && options.BingMapsKey) {
|
||||
options.bingMapsKey = options.BingMapsKey
|
||||
console.warn('use options.bingMapsKey instead of options.BingMapsKey')
|
||||
}
|
||||
if (!options || !options.bingMapsKey) {
|
||||
throw new Error('Must supply options.BingMapsKey')
|
||||
}
|
||||
options = L.setOptions(this, options)
|
||||
if (VALID_IMAGERY_SETS.indexOf(options.imagerySet) < 0) {
|
||||
throw new Error("'" + options.imagerySet + "' is an invalid imagerySet, see https://github.com/digidem/leaflet-bing-layer#parameters")
|
||||
}
|
||||
if (options && options.style && DYNAMIC_IMAGERY_SETS.indexOf(options.imagerySet) < 0) {
|
||||
console.warn('Dynamic styles will only work with these imagerySet choices: ' + DYNAMIC_IMAGERY_SETS.join(', '))
|
||||
}
|
||||
|
||||
var metaDataUrl = L.Util.template(L.TileLayer.Bing.METADATA_URL, {
|
||||
bingMapsKey: this.options.bingMapsKey,
|
||||
imagerySet: this.options.imagerySet,
|
||||
culture: this.options.culture
|
||||
})
|
||||
|
||||
this._imageryProviders = []
|
||||
this._attributions = []
|
||||
|
||||
// Keep a reference to the promise so we can use it later
|
||||
this._fetch = fetchJsonp(metaDataUrl, {jsonpCallback: 'jsonp'})
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(this._metaDataOnLoad.bind(this))
|
||||
.catch(console.error.bind(console))
|
||||
|
||||
// for https://github.com/Leaflet/Leaflet/issues/137
|
||||
if (!L.Browser.android) {
|
||||
this.on('tileunload', this._onTileRemove)
|
||||
}
|
||||
},
|
||||
|
||||
createTile: function (coords, done) {
|
||||
var tile = document.createElement('img')
|
||||
|
||||
L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile))
|
||||
L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile))
|
||||
|
||||
if (this.options.crossOrigin) {
|
||||
tile.crossOrigin = ''
|
||||
}
|
||||
|
||||
/*
|
||||
Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
|
||||
http://www.w3.org/TR/WCAG20-TECHS/H67
|
||||
*/
|
||||
tile.alt = ''
|
||||
|
||||
// Don't create closure if we don't have to
|
||||
if (this._url) {
|
||||
tile.src = this.getTileUrl(coords)
|
||||
} else {
|
||||
this._fetch.then(function () {
|
||||
tile.src = this.getTileUrl(coords)
|
||||
}.bind(this)).catch(function (e) {
|
||||
console.error(e)
|
||||
done(e)
|
||||
})
|
||||
}
|
||||
|
||||
return tile
|
||||
},
|
||||
|
||||
getTileUrl: function (coords) {
|
||||
var quadkey = toQuadKey(coords.x, coords.y, coords.z)
|
||||
var url = L.Util.template(this._url, {
|
||||
quadkey: quadkey,
|
||||
subdomain: this._getSubdomain(coords),
|
||||
culture: this.options.culture
|
||||
})
|
||||
if (typeof this.options.style === 'string') {
|
||||
url += '&st=' + this.options.style
|
||||
}
|
||||
return url
|
||||
},
|
||||
|
||||
// Update the attribution control every time the map is moved
|
||||
onAdd: function (map) {
|
||||
map.on('moveend', this._updateAttribution, this)
|
||||
L.TileLayer.prototype.onAdd.call(this, map)
|
||||
this._attributions.forEach(function (attribution) {
|
||||
map.attributionControl.addAttribution(attribution)
|
||||
})
|
||||
},
|
||||
|
||||
// Clean up events and remove attributions from attribution control
|
||||
onRemove: function (map) {
|
||||
map.off('moveend', this._updateAttribution, this)
|
||||
this._attributions.forEach(function (attribution) {
|
||||
map.attributionControl.removeAttribution(attribution)
|
||||
})
|
||||
L.TileLayer.prototype.onRemove.call(this, map)
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the [Bing Imagery metadata](https://msdn.microsoft.com/en-us/library/ff701712.aspx)
|
||||
* for a specific [`LatLng`](http://leafletjs.com/reference.html#latlng)
|
||||
* and zoom level. If either `latlng` or `zoom` is omitted and the layer is attached
|
||||
* to a map, the map center and current map zoom are used.
|
||||
* @param {L.LatLng} latlng
|
||||
* @param {Number} zoom
|
||||
* @return {Promise} Resolves to the JSON metadata
|
||||
*/
|
||||
getMetaData: function (latlng, zoom) {
|
||||
if (!this._map && (!latlng || !zoom)) {
|
||||
return Promise.reject(new Error('If layer is not attached to map, you must provide LatLng and zoom'))
|
||||
}
|
||||
latlng = latlng || this._map.getCenter()
|
||||
zoom = zoom || this._map.getZoom()
|
||||
var PointMetaDataUrl = L.Util.template(L.TileLayer.Bing.POINT_METADATA_URL, {
|
||||
bingMapsKey: this.options.bingMapsKey,
|
||||
imagerySet: this.options.imagerySet,
|
||||
z: zoom,
|
||||
lat: latlng.lat,
|
||||
lng: latlng.lng
|
||||
})
|
||||
return fetchJsonp(PointMetaDataUrl, {jsonpCallback: 'jsonp'})
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.catch(console.error.bind(console))
|
||||
},
|
||||
|
||||
_metaDataOnLoad: function (metaData) {
|
||||
if (metaData.statusCode !== 200) {
|
||||
throw new Error('Bing Imagery Metadata error: \n' + JSON.stringify(metaData, null, ' '))
|
||||
}
|
||||
var resource = metaData.resourceSets[0].resources[0]
|
||||
this._url = resource.imageUrl
|
||||
this._imageryProviders = resource.imageryProviders || []
|
||||
this.options.subdomains = resource.imageUrlSubdomains
|
||||
this._updateAttribution()
|
||||
return Promise.resolve()
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the attribution control of the map with the provider attributions
|
||||
* within the current map bounds
|
||||
*/
|
||||
_updateAttribution: function () {
|
||||
var map = this._map
|
||||
if (!map || !map.attributionControl) return
|
||||
var zoom = map.getZoom()
|
||||
var bbox = toBingBBox(map.getBounds().toBBoxString())
|
||||
this._fetch.then(function () {
|
||||
var newAttributions = this._getAttributions(bbox, zoom)
|
||||
var prevAttributions = this._attributions
|
||||
// Add any new provider attributions in the current area to the attribution control
|
||||
newAttributions.forEach(function (attr) {
|
||||
if (prevAttributions.indexOf(attr) > -1) return
|
||||
map.attributionControl.addAttribution(attr)
|
||||
})
|
||||
// Remove any attributions that are no longer in the current area from the attribution control
|
||||
prevAttributions.filter(function (attr) {
|
||||
if (newAttributions.indexOf(attr) > -1) return
|
||||
map.attributionControl.removeAttribution(attr)
|
||||
})
|
||||
this._attributions = newAttributions
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of attributions for given bbox and zoom
|
||||
* @private
|
||||
* @param {Array} bbox [west, south, east, north]
|
||||
* @param {Number} zoom
|
||||
* @return {Array} Array of attribution strings for each provider
|
||||
*/
|
||||
_getAttributions: function (bbox, zoom) {
|
||||
return this._imageryProviders.reduce(function (attributions, provider) {
|
||||
for (var i = 0; i < provider.coverageAreas.length; i++) {
|
||||
if (bboxIntersect(bbox, provider.coverageAreas[i].bbox) &&
|
||||
zoom >= provider.coverageAreas[i].zoomMin &&
|
||||
zoom <= provider.coverageAreas[i].zoomMax) {
|
||||
attributions.push(provider.attribution)
|
||||
return attributions
|
||||
}
|
||||
}
|
||||
return attributions
|
||||
}, [])
|
||||
}
|
||||
})
|
||||
|
||||
L.tileLayer.bing = function (options) {
|
||||
return new L.TileLayer.Bing(options)
|
||||
}
|
||||
|
||||
module.exports = L.TileLayer.Bing
|
||||
399
html/locating/leaflet-bing-layer-gh-pages/leaflet-bing-layer.js
Normal file
399
html/locating/leaflet-bing-layer-gh-pages/leaflet-bing-layer.js
Normal file
@@ -0,0 +1,399 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
(function (global){
|
||||
var L = (typeof window !== "undefined" ? window['L'] : typeof global !== "undefined" ? global['L'] : null)
|
||||
var fetchJsonp = require('fetch-jsonp')
|
||||
var bboxIntersect = require('bbox-intersect')
|
||||
|
||||
/**
|
||||
* Converts tile xyz coordinates to Quadkey
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} z
|
||||
* @return {Number} Quadkey
|
||||
*/
|
||||
function toQuadKey (x, y, z) {
|
||||
var index = ''
|
||||
for (var i = z; i > 0; i--) {
|
||||
var b = 0
|
||||
var mask = 1 << (i - 1)
|
||||
if ((x & mask) !== 0) b++
|
||||
if ((y & mask) !== 0) b += 2
|
||||
index += b.toString()
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Leaflet BBoxString to Bing BBox
|
||||
* @param {String} bboxString 'southwest_lng,southwest_lat,northeast_lng,northeast_lat'
|
||||
* @return {Array} [south_lat, west_lng, north_lat, east_lng]
|
||||
*/
|
||||
function toBingBBox (bboxString) {
|
||||
var bbox = bboxString.split(',')
|
||||
return [bbox[1], bbox[0], bbox[3], bbox[2]]
|
||||
}
|
||||
|
||||
var VALID_IMAGERY_SETS = [
|
||||
'Aerial',
|
||||
'AerialWithLabels',
|
||||
'AerialWithLabelsOnDemand',
|
||||
'Road',
|
||||
'RoadOnDemand',
|
||||
'CanvasLight',
|
||||
'CanvasDark',
|
||||
'CanvasGray',
|
||||
'OrdnanceSurvey'
|
||||
]
|
||||
|
||||
var DYNAMIC_IMAGERY_SETS = [
|
||||
'AerialWithLabelsOnDemand',
|
||||
'RoadOnDemand'
|
||||
]
|
||||
|
||||
/**
|
||||
* Create a new Bing Maps layer.
|
||||
* @param {string|object} options Either a [Bing Maps Key](https://msdn.microsoft.com/en-us/library/ff428642.aspx) or an options object
|
||||
* @param {string} options.BingMapsKey A valid Bing Maps Key (required)
|
||||
* @param {string} [options.imagerySet=Aerial] Type of imagery, see https://msdn.microsoft.com/en-us/library/ff701716.aspx
|
||||
* @param {string} [options.culture='en-US'] Language for labels, see https://msdn.microsoft.com/en-us/library/hh441729.aspx
|
||||
* @return {L.TileLayer} A Leaflet TileLayer to add to your map
|
||||
*
|
||||
* Create a basic map
|
||||
* @example
|
||||
* var map = L.map('map').setView([51.505, -0.09], 13)
|
||||
* L.TileLayer.Bing(MyBingMapsKey).addTo(map)
|
||||
*/
|
||||
L.TileLayer.Bing = L.TileLayer.extend({
|
||||
options: {
|
||||
bingMapsKey: null, // Required
|
||||
imagerySet: 'Aerial',
|
||||
culture: 'en-US',
|
||||
minZoom: 1,
|
||||
minNativeZoom: 1,
|
||||
maxNativeZoom: 19
|
||||
},
|
||||
|
||||
statics: {
|
||||
METADATA_URL: 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/{imagerySet}?key={bingMapsKey}&include=ImageryProviders&uriScheme=https',
|
||||
POINT_METADATA_URL: 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/{imagerySet}/{lat},{lng}?zl={z}&key={bingMapsKey}&uriScheme=https'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = { bingMapsKey: options }
|
||||
}
|
||||
if (options && options.BingMapsKey) {
|
||||
options.bingMapsKey = options.BingMapsKey
|
||||
console.warn('use options.bingMapsKey instead of options.BingMapsKey')
|
||||
}
|
||||
if (!options || !options.bingMapsKey) {
|
||||
throw new Error('Must supply options.BingMapsKey')
|
||||
}
|
||||
options = L.setOptions(this, options)
|
||||
if (VALID_IMAGERY_SETS.indexOf(options.imagerySet) < 0) {
|
||||
throw new Error("'" + options.imagerySet + "' is an invalid imagerySet, see https://github.com/digidem/leaflet-bing-layer#parameters")
|
||||
}
|
||||
if (options && options.style && DYNAMIC_IMAGERY_SETS.indexOf(options.imagerySet) < 0) {
|
||||
console.warn('Dynamic styles will only work with these imagerySet choices: ' + DYNAMIC_IMAGERY_SETS.join(', '))
|
||||
}
|
||||
|
||||
var metaDataUrl = L.Util.template(L.TileLayer.Bing.METADATA_URL, {
|
||||
bingMapsKey: this.options.bingMapsKey,
|
||||
imagerySet: this.options.imagerySet
|
||||
})
|
||||
|
||||
this._imageryProviders = []
|
||||
this._attributions = []
|
||||
|
||||
// Keep a reference to the promise so we can use it later
|
||||
this._fetch = fetchJsonp(metaDataUrl, {jsonpCallback: 'jsonp'})
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.then(this._metaDataOnLoad.bind(this))
|
||||
.catch(console.error.bind(console))
|
||||
|
||||
// for https://github.com/Leaflet/Leaflet/issues/137
|
||||
if (!L.Browser.android) {
|
||||
this.on('tileunload', this._onTileRemove)
|
||||
}
|
||||
},
|
||||
|
||||
createTile: function (coords, done) {
|
||||
var tile = document.createElement('img')
|
||||
|
||||
L.DomEvent.on(tile, 'load', L.bind(this._tileOnLoad, this, done, tile))
|
||||
L.DomEvent.on(tile, 'error', L.bind(this._tileOnError, this, done, tile))
|
||||
|
||||
if (this.options.crossOrigin) {
|
||||
tile.crossOrigin = ''
|
||||
}
|
||||
|
||||
/*
|
||||
Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
|
||||
http://www.w3.org/TR/WCAG20-TECHS/H67
|
||||
*/
|
||||
tile.alt = ''
|
||||
|
||||
// Don't create closure if we don't have to
|
||||
if (this._url) {
|
||||
tile.src = this.getTileUrl(coords)
|
||||
} else {
|
||||
this._fetch.then(function () {
|
||||
tile.src = this.getTileUrl(coords)
|
||||
}.bind(this)).catch(function (e) {
|
||||
console.error(e)
|
||||
done(e)
|
||||
})
|
||||
}
|
||||
|
||||
return tile
|
||||
},
|
||||
|
||||
getTileUrl: function (coords) {
|
||||
var quadkey = toQuadKey(coords.x, coords.y, coords.z)
|
||||
var url = L.Util.template(this._url, {
|
||||
quadkey: quadkey,
|
||||
subdomain: this._getSubdomain(coords),
|
||||
culture: this.options.culture
|
||||
})
|
||||
if (typeof this.options.style === 'string') {
|
||||
url += '&st=' + this.options.style
|
||||
}
|
||||
return url
|
||||
},
|
||||
|
||||
// Update the attribution control every time the map is moved
|
||||
onAdd: function (map) {
|
||||
map.on('moveend', this._updateAttribution, this)
|
||||
L.TileLayer.prototype.onAdd.call(this, map)
|
||||
this._attributions.forEach(function (attribution) {
|
||||
map.attributionControl.addAttribution(attribution)
|
||||
})
|
||||
},
|
||||
|
||||
// Clean up events and remove attributions from attribution control
|
||||
onRemove: function (map) {
|
||||
map.off('moveend', this._updateAttribution, this)
|
||||
this._attributions.forEach(function (attribution) {
|
||||
map.attributionControl.removeAttribution(attribution)
|
||||
})
|
||||
L.TileLayer.prototype.onRemove.call(this, map)
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the [Bing Imagery metadata](https://msdn.microsoft.com/en-us/library/ff701712.aspx)
|
||||
* for a specific [`LatLng`](http://leafletjs.com/reference.html#latlng)
|
||||
* and zoom level. If either `latlng` or `zoom` is omitted and the layer is attached
|
||||
* to a map, the map center and current map zoom are used.
|
||||
* @param {L.LatLng} latlng
|
||||
* @param {Number} zoom
|
||||
* @return {Promise} Resolves to the JSON metadata
|
||||
*/
|
||||
getMetaData: function (latlng, zoom) {
|
||||
if (!this._map && (!latlng || !zoom)) {
|
||||
return Promise.reject(new Error('If layer is not attached to map, you must provide LatLng and zoom'))
|
||||
}
|
||||
latlng = latlng || this._map.getCenter()
|
||||
zoom = zoom || this._map.getZoom()
|
||||
var PointMetaDataUrl = L.Util.template(L.TileLayer.Bing.POINT_METADATA_URL, {
|
||||
bingMapsKey: this.options.bingMapsKey,
|
||||
imagerySet: this.options.imagerySet,
|
||||
z: zoom,
|
||||
lat: latlng.lat,
|
||||
lng: latlng.lng
|
||||
})
|
||||
return fetchJsonp(PointMetaDataUrl, {jsonpCallback: 'jsonp'})
|
||||
.then(function (response) {
|
||||
return response.json()
|
||||
})
|
||||
.catch(console.error.bind(console))
|
||||
},
|
||||
|
||||
_metaDataOnLoad: function (metaData) {
|
||||
if (metaData.statusCode !== 200) {
|
||||
throw new Error('Bing Imagery Metadata error: \n' + JSON.stringify(metaData, null, ' '))
|
||||
}
|
||||
var resource = metaData.resourceSets[0].resources[0]
|
||||
this._url = resource.imageUrl
|
||||
this._imageryProviders = resource.imageryProviders || []
|
||||
this.options.subdomains = resource.imageUrlSubdomains
|
||||
this._updateAttribution()
|
||||
return Promise.resolve()
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the attribution control of the map with the provider attributions
|
||||
* within the current map bounds
|
||||
*/
|
||||
_updateAttribution: function () {
|
||||
var map = this._map
|
||||
if (!map || !map.attributionControl) return
|
||||
var zoom = map.getZoom()
|
||||
var bbox = toBingBBox(map.getBounds().toBBoxString())
|
||||
this._fetch.then(function () {
|
||||
var newAttributions = this._getAttributions(bbox, zoom)
|
||||
var prevAttributions = this._attributions
|
||||
// Add any new provider attributions in the current area to the attribution control
|
||||
newAttributions.forEach(function (attr) {
|
||||
if (prevAttributions.indexOf(attr) > -1) return
|
||||
map.attributionControl.addAttribution(attr)
|
||||
})
|
||||
// Remove any attributions that are no longer in the current area from the attribution control
|
||||
prevAttributions.filter(function (attr) {
|
||||
if (newAttributions.indexOf(attr) > -1) return
|
||||
map.attributionControl.removeAttribution(attr)
|
||||
})
|
||||
this._attributions = newAttributions
|
||||
}.bind(this))
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of attributions for given bbox and zoom
|
||||
* @private
|
||||
* @param {Array} bbox [west, south, east, north]
|
||||
* @param {Number} zoom
|
||||
* @return {Array} Array of attribution strings for each provider
|
||||
*/
|
||||
_getAttributions: function (bbox, zoom) {
|
||||
return this._imageryProviders.reduce(function (attributions, provider) {
|
||||
for (var i = 0; i < provider.coverageAreas.length; i++) {
|
||||
if (bboxIntersect(bbox, provider.coverageAreas[i].bbox) &&
|
||||
zoom >= provider.coverageAreas[i].zoomMin &&
|
||||
zoom <= provider.coverageAreas[i].zoomMax) {
|
||||
attributions.push(provider.attribution)
|
||||
return attributions
|
||||
}
|
||||
}
|
||||
return attributions
|
||||
}, [])
|
||||
}
|
||||
})
|
||||
|
||||
L.tileLayer.bing = function (options) {
|
||||
return new L.TileLayer.Bing(options)
|
||||
}
|
||||
|
||||
module.exports = L.TileLayer.Bing
|
||||
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{"bbox-intersect":2,"fetch-jsonp":3}],2:[function(require,module,exports){
|
||||
module.exports = function(bbox1, bbox2){
|
||||
if(!(
|
||||
bbox1[0] > bbox2[2] ||
|
||||
bbox1[2] < bbox2[0] ||
|
||||
bbox1[3] < bbox2[1] ||
|
||||
bbox1[1] > bbox2[3]
|
||||
)){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},{}],3:[function(require,module,exports){
|
||||
(function (global, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['exports', 'module'], factory);
|
||||
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
|
||||
factory(exports, module);
|
||||
} else {
|
||||
var mod = {
|
||||
exports: {}
|
||||
};
|
||||
factory(mod.exports, mod);
|
||||
global.fetchJsonp = mod.exports;
|
||||
}
|
||||
})(this, function (exports, module) {
|
||||
'use strict';
|
||||
|
||||
var defaultOptions = {
|
||||
timeout: 5000,
|
||||
jsonpCallback: 'callback',
|
||||
jsonpCallbackFunction: null
|
||||
};
|
||||
|
||||
function generateCallbackFunction() {
|
||||
return 'jsonp_' + Date.now() + '_' + Math.ceil(Math.random() * 100000);
|
||||
}
|
||||
|
||||
// Known issue: Will throw 'Uncaught ReferenceError: callback_*** is not defined' error if request timeout
|
||||
function clearFunction(functionName) {
|
||||
// IE8 throws an exception when you try to delete a property on window
|
||||
// http://stackoverflow.com/a/1824228/751089
|
||||
try {
|
||||
delete window[functionName];
|
||||
} catch (e) {
|
||||
window[functionName] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function removeScript(scriptId) {
|
||||
var script = document.getElementById(scriptId);
|
||||
document.getElementsByTagName('head')[0].removeChild(script);
|
||||
}
|
||||
|
||||
var fetchJsonp = function fetchJsonp(url) {
|
||||
var options = arguments[1] === undefined ? {} : arguments[1];
|
||||
|
||||
var timeout = options.timeout != null ? options.timeout : defaultOptions.timeout;
|
||||
var jsonpCallback = options.jsonpCallback != null ? options.jsonpCallback : defaultOptions.jsonpCallback;
|
||||
|
||||
var timeoutId = undefined;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction();
|
||||
|
||||
window[callbackFunction] = function (response) {
|
||||
resolve({
|
||||
ok: true,
|
||||
// keep consistent with fetch API
|
||||
json: function json() {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
});
|
||||
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
|
||||
removeScript(jsonpCallback + '_' + callbackFunction);
|
||||
|
||||
clearFunction(callbackFunction);
|
||||
};
|
||||
|
||||
// Check if the user set their own params, and if not add a ? to start a list of params
|
||||
url += url.indexOf('?') === -1 ? '?' : '&';
|
||||
|
||||
var jsonpScript = document.createElement('script');
|
||||
jsonpScript.setAttribute('src', url + jsonpCallback + '=' + callbackFunction);
|
||||
jsonpScript.id = jsonpCallback + '_' + callbackFunction;
|
||||
document.getElementsByTagName('head')[0].appendChild(jsonpScript);
|
||||
|
||||
timeoutId = setTimeout(function () {
|
||||
reject(new Error('JSONP request to ' + url + ' timed out'));
|
||||
|
||||
clearFunction(callbackFunction);
|
||||
removeScript(jsonpCallback + '_' + callbackFunction);
|
||||
}, timeout);
|
||||
});
|
||||
};
|
||||
|
||||
// export as global function
|
||||
/*
|
||||
let local;
|
||||
if (typeof global !== 'undefined') {
|
||||
local = global;
|
||||
} else if (typeof self !== 'undefined') {
|
||||
local = self;
|
||||
} else {
|
||||
try {
|
||||
local = Function('return this')();
|
||||
} catch (e) {
|
||||
throw new Error('polyfill failed because global object is unavailable in this environment');
|
||||
}
|
||||
}
|
||||
|
||||
local.fetchJsonp = fetchJsonp;
|
||||
*/
|
||||
|
||||
module.exports = fetchJsonp;
|
||||
});
|
||||
},{}]},{},[1]);
|
||||
1
html/locating/leaflet-bing-layer-gh-pages/leaflet-bing-layer.min.js
vendored
Normal file
1
html/locating/leaflet-bing-layer-gh-pages/leaflet-bing-layer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
47
html/locating/leaflet-bing-layer-gh-pages/package.json
Normal file
47
html/locating/leaflet-bing-layer-gh-pages/package.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "leaflet-bing-layer",
|
||||
"version": "3.3.0",
|
||||
"description": "Bing Maps Layer for Leaflet v1.0.0",
|
||||
"main": "index.js",
|
||||
"browserify": {
|
||||
"transform": [
|
||||
"browserify-shim"
|
||||
]
|
||||
},
|
||||
"browserify-shim": {
|
||||
"leaflet": "global:L"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify index.js > leaflet-bing-layer.js",
|
||||
"postbuild": "uglifyjs leaflet-bing-layer.js -cm -o leaflet-bing-layer.min.js",
|
||||
"preversion": "npm test && npm run build",
|
||||
"lint": "standard index.js",
|
||||
"start": "budo index.js:leaflet-bing-layer.js --live",
|
||||
"test": "npm run lint"
|
||||
},
|
||||
"keywords": [
|
||||
"leaflet",
|
||||
"bing"
|
||||
],
|
||||
"author": "Gregor MacLennan",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/gmaclennan/leaflet-bing-layer.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/gmaclennan/leaflet-bing-layer/issues"
|
||||
},
|
||||
"homepage": "https://github.com/gmaclennan/leaflet-bing-layer",
|
||||
"dependencies": {
|
||||
"bbox-intersect": "^0.1.1",
|
||||
"browserify-shim": "^3.8.11",
|
||||
"fetch-jsonp": "^1.0.0",
|
||||
"leaflet": "^1.0.0-beta.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^12.0.1",
|
||||
"budo": "^7.0.2",
|
||||
"standard": "^5.4.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user