请问如何结合bingMap使用?
limp0509 opened this issue · comments
vur3,做的国际项目,客户指定要使用微软的bing地图,谢谢大佬了
hi @limp0509, welcome!
用 bing 地图还是bing 的底图瓦片服务?目前不支持 bing 地图结合使用
用 bing 地图还是bing 的底图瓦片服务?目前不支持 bing 地图结合使用
得用bing 的底图瓦片服务- -
vue3,做的国际项目,客户指定要使用微软的bing地图,谢谢大佬了
bing 地图的瓦片投影的实现需要把 xy 坐标转换为四叉树的字符串 QuadKeys,xy 轴转换成 quadKeys 传入 {quadKeys} 中
http://r1.tiles.ditu.live.com/tiles/r{quadKeys}.png?g=100&mkt=zh-cn
RasterLayer 可以通过 urlTemplate 支持用 function 来实现你的需求
const url1 =
'http://r1.tiles.ditu.live.com/tiles/';
const layer1 = new RasterLayer({
zIndex: 1,
}).source(url1, {
parser: {
type: 'rasterTile',
tileSize: 256,
getURLFromTemplate: (template: string, properties: { x: number; y: number; z: number }) => {
// 通过 x,y,z 计算 quadkey 和 url
const quadKey = ?;
return `${template}/r${quadKeys}.png?g=100&mkt=zh-cn`;
}
},
});
Compute quad key string for given x/y tile coordinates and zoom level
https://github.com/meteotest/quadkeys/blob/master/index.js#L33-L60
Compute quad key string for given x/y tile coordinates and zoom level
https://github.com/meteotest/quadkeys/blob/master/index.js#L33-L60
谢谢,一开始我只有一个微软地图申请的key,不清楚怎么用avtvl7去输入微软地图的key生成地图,后面参考一个叫leaflet-bing-layer的库,生成出来了,https://github.com/digidem/leaflet-bing-layer
这是我现在的做法
vue3代码:
<template>
<div>
<div style="width: 100%;height: 400px" id="map"></div>
</div>
</template>
<script setup lang="ts">
import * as L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { PointLayer,LineLayer } from '@antv/l7';
import { L7Layer } from '@antv/l7-leaflet';
import bingMap from './leaflet-bing.ts';
onMounted(()=>{
bingMap.init()
const map = L.map('map', {
minZoom: 1,
}).setView([30, 112], 3);
var opt = {
bingMapsKey: '此处填微软地图申请的key',
imagerySet: "CanvasLight",
dragging: false,
culture: 'zh-Hans',
maxZoom: 7,
minZoom: 2,
zoomIn: 0.5,
zoomOut: 0.5,
};
L.tileLayer.bing(opt).addTo(map);
const l7layer = new L7Layer().addTo(map);
const scene = l7layer.getScene();
scene.addImage(
'plane',
'https://gw.alipayobjects.com/zos/bmw-prod/0ca1668e-38c2-4010-8568-b57cb33839b9.svg'
);
scene.on('loaded', () => {
Promise.all([
fetch(
'https://gw.alipayobjects.com/os/bmw-prod/2960e1fc-b543-480f-a65e-d14c229dd777.json'
).then(d => d.json()),
fetch(
'https://gw.alipayobjects.com/os/basement_prod/4472780b-fea1-4fc2-9e4b-3ca716933dc7.json'
).then(d => d.text()),
fetch(
'https://gw.alipayobjects.com/os/basement_prod/a5ac7bce-181b-40d1-8a16-271356264ad8.json'
).then(d => d.text())
]).then(function onLoad([ world, dot, flyline ]) {
const dotData = eval(dot);
// @ts-ignore
const flydata = eval(flyline).map(item => {
// @ts-ignore
const latlng1 = item.from.split(',').map(e => {
return e * 1;
});
// @ts-ignore
const latlng2 = item.to.split(',').map(e => {
return e * 1;
});
return { coord: [ latlng1, latlng2 ] };
});
const worldLine = new LineLayer()
.source(world)
.color('#41fc9d')
.size(0.5)
.style({
opacity: 0.4
});
const dotPoint = new PointLayer()
.source(dotData, {
parser: {
type: 'json',
x: 'lng',
y: 'lat'
}
})
.shape('circle')
.color('#ffed11')
.animate(true)
.size(40);
const flyLine = new LineLayer({ blend: 'normal' })
.source(flydata, {
parser: {
type: 'json',
coordinates: 'coord'
}
})
.color('#ff6b34')
.texture('plane')
.shape('arc')
.size(15)
.animate({
duration: 1,
interval: 0.2,
trailLength: 0.05
})
.style({
textureBlend: 'replace',
lineTexture: true, // 开启线的贴图功能
iconStep: 10, // 设置贴图纹理的间距
});
const flyLine2 = new LineLayer()
.source(flydata, {
parser: {
type: 'json',
coordinates: 'coord'
}
})
.color('#ff6b34')
.shape('arc')
.size(1)
.style({
lineType: 'dash',
dashArray: [ 5, 5 ],
opacity: 0.5
});
scene.addLayer(worldLine);
scene.addLayer(dotPoint);
scene.addLayer(flyLine2);
scene.addLayer(flyLine);
});
});
})
</script>
<style scoped>
</style>
leaflet-bing.ts代码
// bing map init devTools
export default {
init: function (){
return (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}&culture={culture}&include=ImageryProviders&uriScheme=https&style=',
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,
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
}).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]);
}
}