commit d115da4588ea508e428fde7e131be86038efc352
parent efc0fd78b3890549929699cc6eaf0f0b83ef3778
Author: Sergej Orlov <wladimirych@gmail.com>
Date: Mon, 21 Jul 2025 14:55:22 +0200
Bing layers: get URL template from WEB API without using MS JS API
Diffstat:
5 files changed, 60 insertions(+), 118 deletions(-)
diff --git a/eslint_rules/legacy_files_list.js b/eslint_rules/legacy_files_list.js
@@ -129,8 +129,6 @@ module.exports = [
'src/lib/leaflet.layer.rasterize/Bing.js',
'src/lib/leaflet.layer.rasterize/Yandex.js',
'src/lib/leaflet.layer.rasterize/RetinaTileLayer.js',
- 'src/lib/leaflet.layer.bing/dates.js',
- 'src/lib/leaflet.layer.bing/index.js',
'src/lib/logging/index.js',
'src/lib/iconFromBackgroundImage/index.js',
'src/lib/leaflet.controls.raise-on-focus/index.js',
diff --git a/src/layers.js b/src/layers.js
@@ -1,7 +1,7 @@
import L from "leaflet";
import '~/lib/leaflet.layer.yandex';
import '~/lib/leaflet.layer.google';
-import {BingLayer} from '~/lib/leaflet.layer.bing';
+import {BingSatLayer, BingOrdnanceSurveyLayer} from '~/lib/leaflet.layer.bing';
import config from './config';
import '~/lib/leaflet.layer.soviet-topomaps-grid';
import '~/lib/leaflet.layer.westraPasses';
@@ -171,14 +171,15 @@ class LayerGroupWithOptions extends L.LayerGroup {
{
title: 'Bing Satellite',
isDefault: true,
- layer: new BingLayer(config.bingKey,
+ layer: new BingSatLayer(
{
code: 'I',
isOverlay: false,
scaleDependent: false,
print: true,
jnx: true,
- shortName: 'bing_sat'
+ shortName: 'bing_sat',
+ attribution: '<a href="https://www.bing.com/maps?style=h">Microsoft</a>',
}
)
},
@@ -922,8 +923,7 @@ class LayerGroupWithOptions extends L.LayerGroup {
isDefault: false,
layer: new LayerGroupWithOptions(
[
- new BingLayer(config.bingKey, {
- type: 'OrdnanceSurvey',
+ new BingOrdnanceSurveyLayer({
minZoom: 12,
maxNativeZoom: 16,
bounds: [
diff --git a/src/lib/leaflet.layer.bing/index.js b/src/lib/leaflet.layer.bing/index.js
@@ -1,10 +1,12 @@
import L from 'leaflet';
+import {fetch} from '~/lib/xhr-promise';
+
function tile2quad(x, y, z) {
- var quad = '';
- for (var i = z; i > 0; i--) {
- var digit = 0;
- var mask = 1 << (i - 1);
+ let quad = '';
+ for (let i = z; i > 0; i--) {
+ let digit = 0;
+ const mask = 1 << (i - 1);
if ((x & mask) !== 0) {
digit += 1;
}
@@ -16,128 +18,71 @@ function tile2quad(x, y, z) {
return quad;
}
-const BingLayer = L.TileLayer.extend({
- options: {
- subdomains: [0, 1, 2, 3],
- type: 'Aerial',
- attribution: 'Bing',
- culture: ''
+const BingBaseLayer = L.TileLayer.extend({
+ getTileUrl: function (tilePoint) {
+ const data = {
+ quadkey: tile2quad(tilePoint.x, tilePoint.y, this._getZoomForUrl()),
+ };
+ return L.Util.template(this._url, L.extend(data, this.options));
},
+});
- initialize: function(key, options) {
- L.Util.setOptions(this, options);
-
- this._key = key;
- this._url = null;
- this._providers = [];
- this.metaRequested = false;
+const BingBaseLayerWithDynamicUrl = BingBaseLayer.extend({
+ initialize: function (options) {
+ BingBaseLayer.prototype.initialize.call(this, null, options);
+ this.layerInfoRequested = false;
},
- getTileUrl: function(tilePoint) {
- var zoom = this._getZoomForUrl();
- var subdomains = this.options.subdomains,
- s = this.options.subdomains[Math.abs((tilePoint.x + tilePoint.y) % subdomains.length)];
- return this._url.replace('{subdomain}', s)
- .replace('{quadkey}', tile2quad(tilePoint.x, tilePoint.y, zoom))
- .replace('{culture}', this.options.culture);
+ onAdd: function (map) {
+ this.loadLayerInfo();
+ L.TileLayer.prototype.onAdd.apply(this, [map]);
},
- loadMetadata: function() {
- if (this.metaRequested) {
+ _update: function () {
+ if (this._url === null || !this._map) {
return;
}
- this.metaRequested = true;
- var that = this;
- var cbid = '_bing_metadata_' + L.Util.stamp(this);
- window[cbid] = function(meta) {
- window[cbid] = undefined;
- var e = document.getElementById(cbid);
- e.parentNode.removeChild(e);
- if (meta.errorDetails) {
- throw new Error(meta.errorDetails);
- }
- that.initMetadata(meta);
- };
- var urlScheme = 'https';
- var url = urlScheme + '://dev.virtualearth.net/REST/v1/Imagery/Metadata/' +
- this.options.type + '?include=ImageryProviders&jsonp=' + cbid +
- '&key=' + this._key + '&UriScheme=' + urlScheme;
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = url;
- script.id = cbid;
- document.getElementsByTagName('head')[0].appendChild(script);
+ L.TileLayer.prototype._update.apply(this);
},
- initMetadata: function(meta) {
- var r = meta.resourceSets[0].resources[0];
- this.options.subdomains = r.imageUrlSubdomains;
- this._url = r.imageUrl;
- if (r.imageryProviders) {
- for (var i = 0; i < r.imageryProviders.length; i++) {
- var p = r.imageryProviders[i];
- for (var j = 0; j < p.coverageAreas.length; j++) {
- var c = p.coverageAreas[j];
- var coverage = {zoomMin: c.zoomMin, zoomMax: c.zoomMax, active: false};
- var bounds = new L.LatLngBounds(
- new L.LatLng(c.bbox[0] + 0.01, c.bbox[1] + 0.01),
- new L.LatLng(c.bbox[2] - 0.01, c.bbox[3] - 0.01)
- );
- coverage.bounds = bounds;
- coverage.attrib = p.attribution;
- this._providers.push(coverage);
- }
- }
- }
- this._update();
+ getLayerUrl: async function () {
+ throw new Error('Not implemented');
},
- _update: function() {
- if (this._url === null || !this._map) {
+ loadLayerInfo: async function () {
+ if (this.layerInfoRequested) {
return;
}
- this._update_attribution();
- L.TileLayer.prototype._update.apply(this, []);
+ this.layerInfoRequested = true;
+ this._url = await this.getLayerUrl();
+ this._update();
},
+});
- _update_attribution: function() {
- var bounds = L.latLngBounds(
- this._map.getBounds().getSouthWest().wrap(),
- this._map.getBounds().getNorthEast().wrap()
- );
- var zoom = this._map.getZoom();
- for (var i = 0; i < this._providers.length; i++) {
- var p = this._providers[i];
- if ((zoom <= p.zoomMax && zoom >= p.zoomMin) &&
- bounds.intersects(p.bounds)) {
- if (!p.active && this._map.attributionControl) {
- this._map.attributionControl.addAttribution(p.attrib);
- }
- p.active = true;
- } else {
- if (p.active && this._map.attributionControl) {
- this._map.attributionControl.removeAttribution(p.attrib);
- }
- p.active = false;
- }
- }
+const BingSatLayer = BingBaseLayerWithDynamicUrl.extend({
+ getLayerUrl: async function () {
+ const xhr = await fetch('https://www.bing.com/maps/style?styleid=aerial', {
+ responseType: 'json',
+ timeout: 5000,
+ });
+ return xhr.response['sources']['bing-aerial']['tiles'][0].replace(/^raster/u, 'https');
},
+});
- onAdd: function(map) {
- this.loadMetadata();
- L.TileLayer.prototype.onAdd.apply(this, [map]);
+const BingOrdnanceSurveyLayer = BingBaseLayerWithDynamicUrl.extend({
+ options: {
+ credentials: 'Auy875gcaw3RCFzVQSxi8Ytzw_K67r4Dw8DpGHavRZW_ciCBHLhQJAhCiXSdnzwH',
},
- onRemove: function(map) {
- for (var i = 0; i < this._providers.length; i++) {
- var p = this._providers[i];
- if (p.active && this._map.attributionControl) {
- this._map.attributionControl.removeAttribution(p.attrib);
- p.active = false;
- }
- }
- L.TileLayer.prototype.onRemove.apply(this, [map]);
- }
+ getLayerUrl: async function () {
+ const xhr = await fetch('https://www.bing.com/maps/style?styleid=ordnancesurvey', {
+ responseType: 'json',
+ headers: [['accept-language', 'en-GB']],
+ timeout: 5000,
+ });
+ return xhr.response['sources']['osMaps1']['tiles'][0].replace(/^raster/u, 'https');
+ },
});
-export {BingLayer};
+// eslint-disable-next-line import/no-unused-modules
+export {BingSatLayer, BingOrdnanceSurveyLayer, BingBaseLayerWithDynamicUrl};
diff --git a/src/lib/leaflet.layer.rasterize/Bing.js b/src/lib/leaflet.layer.rasterize/Bing.js
@@ -1,7 +1,7 @@
import L from 'leaflet';
-import {BingLayer} from '~/lib/leaflet.layer.bing';
+import {BingBaseLayerWithDynamicUrl} from '~/lib/leaflet.layer.bing';
-BingLayer.include({
+BingBaseLayerWithDynamicUrl.include({
waitTilesReadyToGrab: function() {
if (this._url) {
return Promise.resolve();
@@ -19,7 +19,7 @@ BingLayer.include({
},
cloneForPrint: function(options) {
- return new BingLayer(this._key, L.Util.extend({}, this.options, options));
+ return new this.constructor(L.Util.extend({}, this.options, options));
},
}
);
diff --git a/src/secrets.js.template b/src/secrets.js.template
@@ -1,5 +1,4 @@
const secrets = {
- bingKey: '0000000000000000000000000000000000000000000000000000000000000000',
sentryDSN: 'https://00000000000000000000000000000000@sentry.io/111111',
mapillary4: 'MLY|1111111111111111|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
google: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',