百度地图在线瓦片分两种,分别是
//maponline{s}.bdimg.com/starpic/u=x={x};y={y};z={z};v=009;type=sate&qt=satepc&fm=46&app=webearth2&v=009 /*影像底图地址*/ //maponline{s}.bdimg.com/tile/?x={x}&y={y}&z={z}&{p} /*其他电子地图、标注底图等的地址*/
不过,上述地址中的域名 maponline{s}.bdimg.com 可以替换成 online{s}.map.bdimg.com 或 shangetu{s}.map.bdimg.com,三个域名都可以访问。
百度地图采用 BD09 百度坐标系,叠加到 Leaflet 和 Cesium 默认的 WGS84 通用坐标系需要进行纠偏。
相关参数:
{s} 是可用的子域之一,用于克服浏览器对每个主机的并发请求数的限制,支持 0123。
{x} 是 TMS 切片方案中的图块 X 坐标,其中 0 是最西端的图块。
{y} 是 TMS 切片方案中的图块 Y 坐标,其中 0 是最南端的图块。
{z} 是 TMS 切片方案中切片的级别,零级是四叉树金字塔的根。
{p} 是以下加载代码中封装的自定义请求参数 param,具体参见下表和代码,下表内容在测试中得出,欢迎指正和补充。
{p}(代码中 param 参数)对应图层瓦片级别
img影像底图[1,19]
qt=vtile&styles=sl&showtext=0&scaler=1&v=083影像标注,路网[1,20]
qt=vtile&styles=sl&showtext=1&scaler=1&v=083影像标注,路网 + 注记[3,20]
qt=vtile&styles=sl&showtext=1&scaler=2&v=083影像标注,路网 + 高清注记[3,19]
qt=vtile&styles=pl&showtext=0&scaler=1&v=083电子地图,图形[3,20]
qt=vtile&styles=pl&showtext=1&scaler=1&v=083电子地图,图形 + 注记[3,20]
qt=vtile&styles=pl&showtext=1&scaler=2&v=083电子地图,图形 + 高清注记[3,19]
qt=vtile&styles=ph&showtext=0&scaler=1&v=083大字体电子地图,图形[3,20]
qt=vtile&styles=ph&showtext=1&scaler=1&v=083大字体电子地图,图形 + 注记[3,20]
qt=tile&styles=sl&showtext=1&scaler=1&v=083旧版影像标注,路网 + 注记[3,19]
qt=tile&styles=sl&showtext=1&scaler=2&v=083旧版影像标注,路网 + 高清注记[3,19]
qt=tile&styles=pl&showtext=1&scaler=1&v=083旧版电子地图,图形 + 注记[3,19]
qt=tile&styles=pl&showtext=1&scaler=2&v=083旧版电子地图,图形 + 高清注记[3,19]
qt=tile&styles=ph&showtext=1&scaler=1&v=083旧版大字体电子地图,图形 + 注记[3,19]
Leaflet 添加代码
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8"> <style type="text/css"> body { padding: 0; margin: 0; } html, body, #map { height: 100%; } #param-test { position: absolute; z-index: 999; left: 10px; top: 10px; background: white; padding: 0 3px; line-height: 28px; font-size: 14px } #url-input { width: 450px; height: 18px; margin-bottom: 3px; } </style> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.2/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.9.2/dist/leaflet.js"></script> <script src="https://unpkg.com/gcoord@0.3.2/dist/gcoord.js"></script> </head> <body> <div id="param-test"> 测试参数:<input id="url-input" type="text" placeholder="输入地址参数, 例:qt=vtile&styles=sl&showtext=0&scaler=1&v=083 或 img, 并回车" onkeydown="loadTile(this.value)" /> </div> <div id="map" /> </body> <script type="text/javascript"> L.Projection.BaiduMercator = L.Util.extend({}, L.Projection.Mercator, { R: 6378206, //百度椭球赤道半径 a=6378206,相当于在 WGS84 椭球赤道半径上加了 69 米 R_MINOR: 6356584.314245179, //百度椭球极半径 b=6356584.314245179,相当于在 WGS84 椭球极半径上减了 168 米 bounds: new L.Bounds([-20037725.11268234, -19994619.55417086], [20037725.11268234, 19994619.55417086]) //数据覆盖范围在经度[-180°,180°],纬度[-85.051129°, 85.051129°]之间 }) L.CRS.Baidu = L.Util.extend({}, L.CRS.Earth, { code: 'EPSG:Baidu', projection: L.Projection.BaiduMercator, transformation: new L.transformation(1, 0.5, -1, 0.5), scale: function (zoom) { return 1 / Math.pow(2, (18 - zoom)) }, zoom: function (scale) { return 18 - Math.log(1 / scale) / Math.LN2 }, wrapLng: undefined }) L.TileLayer.BaiDuTileLayer = L.TileLayer.extend({ initialize: function (param, options) { var templateImgUrl = "//maponline{s}.bdimg.com/starpic/u=x={x};y={y};z={z};v=009;type=sate&qt=satepc&fm=46&app=webearth2&v=009" var templateUrl = "//maponline{s}.bdimg.com/tile/?x={x}&y={y}&z={z}&{p}" var myUrl = (param === "img" ? templateImgUrl : templateUrl) options = L.extend({ getUrlArgs: (o) => { return { x: o.x, y: (-1 - o.y), z: o.z } }, p: param, subdomains: "0123", minZoom: 0, maxZoom: 23, minNativeZoom: 1, maxNativeZoom: 18 }, options) L.TileLayer.prototype.initialize.call(this, myUrl, options) }, getTileUrl: function (coords) { if (this.options.getUrlArgs) { return L.Util.template(this._url, L.extend({ s: this._getSubdomain(coords), r: L.Browser.retina ? '@2x' : '' }, this.options.getUrlArgs(coords), this.options)) } else { return L.TileLayer.prototype.getTileUrl.call(this, coords) } }, _setZoomTransform: function (level, center, zoom) { center = L.latLng(gcoord.transform([center.lng, center.lat], gcoord.WGS84, gcoord.BD09).reverse()) // 采用 gcoord 库进行纠偏 L.TileLayer.prototype._setZoomTransform.call(this, level, center, zoom) }, _getTiledPixelBounds: function (center) { center = L.latLng(gcoord.transform([center.lng, center.lat], gcoord.WGS84, gcoord.BD09).reverse()) // 采用 gcoord 库进行纠偏 return L.TileLayer.prototype._getTiledPixelBounds.call(this, center) } }) L.tileLayer.baiDuTileLayer = function (param, options) { return new L.TileLayer.BaiDuTileLayer(param, options) } var img_Layer = L.tileLayer.baiDuTileLayer("img"), // 影像底图 vsl01_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=sl&showtext=0&scaler=1&v=083") // 影像标注,路网 vsl11_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=sl&showtext=1&scaler=1&v=083") // 影像标注,路网 + 注记 vsl12_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=sl&showtext=1&scaler=2&v=083") // 影像标注,路网 + 高清注记 vpl01_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=pl&showtext=0&scaler=1&v=083") // 电子地图,图形 vpl11_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=pl&showtext=1&scaler=1&v=083") // 电子地图,图形 + 注记 vpl12_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=pl&showtext=1&scaler=2&v=083") // 电子地图,图形 + 高清注记 vph01_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=ph&showtext=0&scaler=1&v=083") // 大字体电子地图,图形 vph11_Layer = L.tileLayer.baiDuTileLayer("qt=vtile&styles=ph&showtext=1&scaler=1&v=083") // 大字体电子地图,图形 + 注记 _sl11_Layer = L.tileLayer.baiDuTileLayer("qt=tile&styles=sl&showtext=1&scaler=1&v=083") // 旧影像标注,路网 + 注记 _sl12_Layer = L.tileLayer.baiDuTileLayer("qt=tile&styles=sl&showtext=1&scaler=2&v=083") // 旧影像标注,路网 + 高清注记 _pl11_Layer = L.tileLayer.baiDuTileLayer("qt=tile&styles=pl&showtext=1&scaler=1&v=083") // 旧电子地图,图形 + 注记 _pl12_Layer = L.tileLayer.baiDuTileLayer("qt=tile&styles=pl&showtext=1&scaler=2&v=083") // 旧电子地图,图形 + 高清注记 _ph11_Layer = L.tileLayer.baiDuTileLayer("qt=tile&styles=ph&showtext=1&scaler=1&v=083") // 旧大字体电子地图,图形 + 注记 var map = L.map("map", { crs: L.CRS.Baidu, center: [29.708050, 118.321499], zoom: 15, zoomControl: false, attributionControl: false, doubleClickZoom: false }) var overlayLayers = { "影像底图": img_Layer, "影像标注,路网": vsl01_Layer, "影像标注,路网 + 注记": vsl11_Layer, "影像标注,路网 + 高清注记": vsl12_Layer, "电子地图,图形": vpl01_Layer, "电子地图,图形 + 注记": vpl11_Layer, "电子地图,图形 + 高清注记": vpl12_Layer, "大字体电子地图,图形": vph01_Layer, "大字体电子地图,图形 + 注记": vph11_Layer, "旧影像标注,路网 + 注记": _sl11_Layer, "旧影像标注,路网 + 高清注记": _sl12_Layer, "旧电子地图,图形 + 注记": _pl11_Layer, "旧电子地图,图形 + 高清注记": _pl12_Layer, "旧大字体电子地图,图形 + 注记": _ph11_Layer } L.control.layers([], overlayLayers, { autoZIndex: false }).addTo(map) L.marker([29.708050, 118.321499]).addTo(map) // 添加点用于纠偏测试 // map.on('dblclick', function (e) { console.dir(e.latlng.lng + "," + e.latlng.lat) }) var test_Layer = null function loadTile (param) { if (!window.event || window.event.keyCode === 13) { // keyCode ===13 表示按下回车 if (test_Layer !== null) { map.removeLayer(test_Layer) test_Layer = null } if (!param) return test_Layer = L.tileLayer.baiDuTileLayer(param) map.addLayer(test_Layer) } } var param = "img" document.getElementById("url-input").value = param loadTile(param) </script> </html>
Cesium 添加代码
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <style type="text/css"> body { padding: 0; margin: 0; } html, body, #map { height: 100%; } #param-test { position: absolute; z-index: 999; left: 10px; top: 10px; background: white; padding: 0 3px; line-height: 28px; font-size: 14px } #url-input { width: 450px; height: 18px; margin-bottom: 3px; } .cesium-viewer .cesium-viewer-bottom { display: none; } .cesium-viewer .cesium-baseLayerPicker-item { display: block; width: auto; margin: 3px 10px; } .cesium-viewer .cesium-baseLayerPicker-itemIcon, .cesium-viewer .cesium-baseLayerPicker-sectionTitle { display: none; } .cesium-viewer .cesium-baseLayerPicker-itemLabel { text-align: left; } .cesium-viewer .cesium-baseLayerPicker-dropDown { width: 250px; padding: 0; margin: 0; } .cesium-viewer .cesium-baseLayerPicker-choices { border: none; } .cesium-viewer .cesium-baseLayerPicker-selectedItem .cesium-baseLayerPicker-itemLabel { color: #00cbff; } </style> <link rel="stylesheet" href="https://unpkg.com/cesium@1.98.1/Build/Cesium/Widgets/widgets.css" /> <script src="https://unpkg.com/cesium@1.98.1/Build/Cesium/Cesium.js"></script> <script src="https://unpkg.com/gcoord@0.3.2/dist/gcoord.js"></script> </head> <body> <div id="param-test"> 测试参数:<input id="url-input" type="text" placeholder="输入地址参数, 例:qt=vtile&styles=sl&showtext=0&scaler=1&v=083 或 img, 并回车" onkeydown="loadTile(this.value)" /> </div> <div id="map" /> </body> <script type="text/javascript"> class BaiduMercatorProjection { // 创建椭球墨卡托投影,Cesium 源码中是以xyz三个轴的最长的轴作为球来计算 constructor (ellipsoid) { this._ellipsoid = Cesium.defaultValue(ellipsoid, Cesium.Ellipsoid.WGS84) this._semimajorAxis = this._ellipsoid.maximumRadius this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis } get ellipsoid () { return this._ellipsoid } get MaximumLatitude () { return this.mercatorAngleToGeodeticLatitude(Math.PI) } geodeticLatitudeToMercatorAngle (latitude) { if (latitude > this.MaximumLatitude) { latitude = this.MaximumLatitude } else if (latitude < -this.MaximumLatitude) { latitude = -this.MaximumLatitude } const tmp = this.ellipsoid.radii.z / this.ellipsoid.radii.x // 短半轴 / 长半轴 const e = Math.sqrt(1.0 - tmp * tmp) const con = e * Math.sin(latitude) const ts = Math.tan(Math.PI * 0.25 - latitude * 0.5) / Math.pow((1.0 - con) / (1.0 + con), e * 0.5) return - Math.log(Math.max(ts, 1E-10)) } project (cartographic, result) { const correct = gcoord.transform([Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)], gcoord.WGS84, gcoord.BD09) cartographic.longitude = Cesium.Math.toRadians(correct[0]), cartographic.latitude = Cesium.Math.toRadians(correct[1]) var x = cartographic.longitude * this.ellipsoid.radii.x var y = this.geodeticLatitudeToMercatorAngle(cartographic.latitude) * this.ellipsoid.radii.x var z = cartographic.height if (!Cesium.defined(result)) { return new Cesium.Cartesian3(x, y, z) } result.x = x, result.y = y, result.z = z return result } mercatorAngleToGeodeticLatitude (mercatorAngle) { const tmp = this.ellipsoid.radii.z / this.ellipsoid.radii.x // 短半轴 / 长半轴 const e = Math.sqrt(1.0 - tmp * tmp) const ts = Math.exp(-mercatorAngle) var phi = Math.PI / 2 - 2 * Math.atan(ts) for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) { con = e * Math.sin(phi) con = Math.pow((1 - con) / (1 + con), e / 2) dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi phi += dphi } return phi } unproject (cartesian, result) { var longitude = cartesian.x / this.ellipsoid.radii.x var latitude = this.mercatorAngleToGeodeticLatitude(cartesian.y / this.ellipsoid.radii.x) var height = cartesian.z const correct = gcoord.transform([Cesium.Math.toDegrees(longitude), Cesium.Math.toDegrees(latitude)], gcoord.BD09, gcoord.WGS84) longitude = Cesium.Math.toRadians(correct[0]), latitude = Cesium.Math.toRadians(correct[1]) if (!Cesium.defined(result)) { return new Cesium.Cartographic(longitude, latitude, height) } result.longitude = longitude, result.latitude = latitude, result.height = height return result } } Cesium.BaiduMercatorProjection = BaiduMercatorProjection class BaiduMercatorTilingScheme extends Cesium.WebMercatorTilingScheme { constructor (options) { super(options) this._projection = new Cesium.BaiduMercatorProjection(this._ellipsoid) } } Cesium.BaiduMercatorTilingScheme = BaiduMercatorTilingScheme class BaiduImageryProvider extends Cesium.UrlTemplateImageryProvider { constructor (param, options = {}) { var templateImgUrl = "//maponline{s}.bdimg.com/starpic/u=x={x};y={y};z={z};v=009;type=sate&fm=46&app=webearth2&v=009&qt=satepc" var templateUrl = "//maponline{s}.bdimg.com/tile/?x={x}&y={y}&z={z}&{p}" var myUrl = (param === "img" ? templateImgUrl : templateUrl).replace(/\{p\}/g, param) super(Object.assign({}, { url: myUrl, subdomains: "0123", minimumLevel: 1, maximumLevel: 18, tilingScheme: new Cesium.BaiduMercatorTilingScheme({ rectangleSouthwestInMeters: new Cesium.Cartesian2(-33554432, -33554432), rectangleNortheastInMeters: new Cesium.Cartesian2(33554432, 33554432), ellipsoid: new Cesium.Ellipsoid(6378206, 6378206, 6356584.314245179) }), }, options)) const that = this this._readyPromise.then(function (properties) { var customTags = { x: (l, x, y, z) => { return x - (1 << (z - 1)) }, y: (l, x, y, z) => { return (1 << (z - 1)) - y - 1 }, z: (l, x, y, z) => { return z } } that._tags = Cesium.combine(customTags, that._tags) that._pickFeaturesTags = Cesium.combine(customTags, that._pickFeaturesTags) that._rectangle = that._tilingScheme.rectangle }) } } Cesium.BaiduImageryProvider = BaiduImageryProvider Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwNGNmNWUwMi02NWEyLTQxNzItOTNhNC1mY2NiZTcxNDc2OWYiLCJpZCI6MTU5NDQsInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyJdLCJpYXQiOjE1NjkyMjk3MTN9.PYUfCHykW23NuwRzzz04yW7JyZ4vQlcb4kToZ44r42w" Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(118.282527, 29.692229, 118.360733, 29.724694) // 设置相机默认范围为自定义区域 Cesium.Camera.DEFAULT_VIEW_FACTOR = 0 // console.debug(viewer.camera.computeViewRectangle()) // 可以返回当前区域矩形,以弧度为单位 var viewer = new Cesium.Viewer("map", { animation: false, // 是否显示动画控件 baseLayerPicker: true, // 是否显示图层选择控件 vrButton: false, // 是否显示VR控件 fullscreenButton: false, // 是否显示全屏按钮 geocoder: false, // 是否显示地名查找控件 homeButton: false, // 是否显示返回主视角控件 sceneModePicker: false, // 是否显示投影方式控件 selectionIndicator: false, // 是否显示选中指示框 timeline: false, // 是否显示时间线控件 navigationHelpButton: false, // 是否显示帮助信息控件 infoBox: false, // 是否显示点击要素之后显示的信息 }) viewer.imageryLayers.removeAll(viewer.imageryLayers.get(0)) // 移除 Cesium 默认图层 viewer.scene.globe.baseColor = new Cesium.Color(0, 0, 0, 0) // 设置地球背景色黑色 var img_Layer = new Cesium.BaiduImageryProvider("img") // 影像底图 vsl01_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=sl&showtext=0&scaler=1&v=083") // 影像标注,路网 vsl11_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=sl&showtext=1&scaler=1&v=083") // 影像标注,路网 + 注记 vsl12_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=sl&showtext=1&scaler=2&v=083") // 影像标注,路网 + 高清注记 vpl01_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=pl&showtext=0&scaler=1&v=083") // 电子地图,图形 vpl11_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=pl&showtext=1&scaler=1&v=083") // 电子地图,图形 + 注记 vpl12_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=pl&showtext=1&scaler=2&v=083") // 电子地图,图形 + 高清注记 vph01_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=ph&showtext=0&scaler=1&v=083") // 大字体电子地图,图形 vph11_Layer = new Cesium.BaiduImageryProvider("qt=vtile&styles=ph&showtext=1&scaler=1&v=083") // 大字体电子地图,图形 + 注记 _sl11_Layer = new Cesium.BaiduImageryProvider("qt=tile&styles=sl&showtext=1&scaler=1&v=083") // 旧影像标注,路网 + 注记 _sl12_Layer = new Cesium.BaiduImageryProvider("qt=tile&styles=sl&showtext=1&scaler=2&v=083") // 旧影像标注,路网 + 高清注记 _pl11_Layer = new Cesium.BaiduImageryProvider("qt=tile&styles=pl&showtext=1&scaler=1&v=083") // 旧电子地图,图形 + 注记 _pl12_Layer = new Cesium.BaiduImageryProvider("qt=tile&styles=pl&showtext=1&scaler=2&v=083") // 旧电子地图,图形 + 高清注记 _ph11_Layer = new Cesium.BaiduImageryProvider("qt=tile&styles=ph&showtext=1&scaler=1&v=083") // 旧大字体电子地图,图形 + 注记 viewer.baseLayerPicker._dropPanel.children[0].innerHTML = "底图" viewer.baseLayerPicker.viewModel.imageryProviderViewModels = [ new Cesium.ProviderViewModel({ creationFunction: function () { return img_Layer }, name: "影像底图" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vsl01_Layer }, name: " 影像标注,路网" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vsl11_Layer }, name: "影像标注,路网 + 注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vsl12_Layer }, name: "影像标注,路网 + 高清注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vpl01_Layer }, name: "电子地图,图形" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vpl11_Layer }, name: "电子地图,图形 + 注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vpl12_Layer }, name: "电子地图,图形 + 高清注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vph01_Layer }, name: "大字体电子地图,图形" }), new Cesium.ProviderViewModel({ creationFunction: function () { return vph11_Layer }, name: "大字体电子地图,图形 + 注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return _sl11_Layer }, name: "旧影像标注,路网 + 注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return _sl12_Layer }, name: "旧影像标注,路网 + 高清注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return _pl11_Layer }, name: "旧电子地图,图形 + 注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return _pl12_Layer }, name: "旧电子地图,图形 + 高清注记" }), new Cesium.ProviderViewModel({ creationFunction: function () { return _ph11_Layer }, name: "旧大字体电子地图,图形 + 注记" }) ] // viewer.baseLayerPicker.viewModel.selectedImagery = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0] viewer.baseLayerPicker._dropPanel.children[2].innerHTML = "地形" viewer.baseLayerPicker.viewModel.terrainProviderViewModels = [] viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(118.321499, 29.708050), point: { pixelSize: 5, color: Cesium.Color.RED } }) //添加点用于纠偏测试 var test_Layer = null function loadTile (param) { if (!window.event || window.event.keyCode === 13) { // keyCode === 13 表示按下回车 if (test_Layer !== null) { viewer.imageryLayers.remove(test_Layer, true) test_Layer = null } if (!param) return test_Layer = viewer.imageryLayers.addImageryProvider(new Cesium.BaiduImageryProvider(param)) } } var param = "img" document.getElementById("url-input").value = param loadTile(param) </script> </html>