Leaflet 和 Cesium 加载纠偏后腾讯地图在线瓦片,腾讯地图最新最全在线瓦片地址

腾讯地图地址

腾讯地图在线瓦片分三种,分别是

//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg /*影像底图地址*/
//p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg /*地形底图地址*/
//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={ry}&{p} /*其他电子地图、标注底图等的地址*/

腾讯地图采用 GCJ02 火星坐标系,叠加到 Leaflet 和 Cesium 默认的 WGS84 通用坐标系需要进行纠偏。

相关参数:

  • {s} 是可用的子域之一,用于克服浏览器对每个主机的并发请求数的限制,支持 0123。
  • {x} 是 TMS 切片方案中的图块 X 坐标,其中 0 是最西端的图块。
  • {ry} 是 TMS 切片方案中的图块 Y 坐标,其中 0 是最南端的图块。与 WMTS 中 {y} 的关系是 {ry} + {y} = Math.pow(2,{z}) - 1。
  • {z} 是 TMS 切片方案中切片的级别,零级是四叉树金字塔的根。
  • {sx} 是腾讯 url 地址中的图块 X 坐标,因为腾讯影像底图瓦片的最小缩放级别为4级,计算方法是对 TMS 中 {x} / 16。
  • {sy} 是腾讯 url 地址中的图块 Y 坐标,因为腾讯影像底图瓦片的最小缩放级别为4级,计算方法是对 TMS 中 {ry} / 16
  • {p} 是以下加载代码中封装的自定义请求参数 param,具体参见下表和代码,下表内容在测试中得出,欢迎指正和补充。
{p}(代码中 param 参数) 对应图层 瓦片级别
img 影像底图 [1,18]
dem 地形底图 [1,15]
type=vector&styleid=1 电子地图 [1,18]
type=vector&styleid=2 影像标注,路网 + 注记 [1,15]
type=vector&styleid=3 电子地图,路网 + 注记 + 楼块+水系 [1,18]
type=vector&styleid=4 电子地图,暗色地图 [1,18]
type=vector&styleid=8 电子地图,暖色地图 [1,18]
type=vector&styleid=9 电子地图,暖色地图,突出学校、医院等公共设施 [1,18]

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/[email protected]/dist/leaflet.css" />
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js">script>
    <script src="https://unpkg.com/[email protected]/dist/gcoord.js">script>
  head>
  <body>
    <div id="param-test">
      测试参数:<input id="url-input" type="text" placeholder="输入地址参数, 例:type=vector&styleid=1、img 或 dem, 并回车" onkeydown="loadTile(this.value)" />
    div>
    <div id="map" />
  body>
  <script type="text/javascript">
    L.TileLayer.TencentTileLayer = L.TileLayer.extend({
      initialize: function (param, options) {
        var templateImgUrl = "//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg"
        var templateDemUrl = "//p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg"
        var templateUrl = "//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={ry}&{p}"
        var myUrl = (param === "img" ? templateImgUrl : (param === "dem" ? templateDemUrl : templateUrl))
        options = L.extend({// 位运算符:a >> b 等价于 Math.floor(a / Math.pow(2, b)),a << b 等价于 Math.floor(a * Math.pow(2, b))
          getUrlArgs: (o) => { return { x: o.x, ry: (1 << o.z) - o.y - 1, z: o.z, sx: o.x >> 4, sy: ((1 << o.z) - o.y - 1) >> 4 } },
          p: param, subdomains: "0123", minZoom: 0, maxZoom: 23, minNativeZoom: 1, maxNativeZoom: param === "dem" ? 15 : 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.GCJ02).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.GCJ02).reverse()) // 采用 gcoord 库进行纠偏
        return L.TileLayer.prototype._getTiledPixelBounds.call(this, center)
      },
    })
    L.tileLayer.tencentTileLayer = function (param, options) { return new L.TileLayer.TencentTileLayer(param, options) }
    var img_Layer = L.tileLayer.tencentTileLayer("img"), // 影像底图
      dem_Layer = L.tileLayer.tencentTileLayer("dem"), // 地形底图
      tvs1_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=1"), // 电子地图,区域面 + 路网 + 注记 + 楼块
      tvs2_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=2"), // 影像标注,路网 + 注记
      tvs3_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=3"), // 简化地图,区域面 + 路网 + 注记 + 楼块
      tvs4_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=4"), // 电子地图,暗色地图
      tvs8_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=8"), // 电子地图,淡色地图
      tvs9_Layer = L.tileLayer.tencentTileLayer("type=vector&styleid=9") // 电子地图,淡色地图,突出学校、医院等公共设施

    var map = L.map("map", { center: [29.708050, 118.321499], zoom: 15, zoomControl: false, attributionControl: false, doubleClickZoom: false })
    var overlayLayers = {
      "影像底图": img_Layer, "地形底图": dem_Layer, "电子地图,区域面 + 路网 + 注记 + 楼块": tvs1_Layer, "影像标注,路网 + 注记": tvs2_Layer, "简化地图,区域面 + 路网 + 注记 + 楼块": tvs3_Layer, 
      "电子地图,暗色地图": tvs4_Layer, "电子地图,淡色地图": tvs8_Layer, "电子地图,淡色地图,突出学校、医院等公共设施": tvs9_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.tencentTileLayer(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/[email protected]/Build/Cesium/Widgets/widgets.css" />
    <script src="https://unpkg.com/[email protected]/Build/Cesium/Cesium.js">script>
    <script src="https://unpkg.com/[email protected]/dist/gcoord.js">script>
  head>
  <body>
    <div id="param-test">
      测试参数:<input id="url-input" type="text" placeholder="输入地址参数, 例:type=vector&styleid=1、img 或 dem, 并回车" onkeydown="loadTile(this.value)" />
    div>
    <div id="map" />
  body>
  <script type="text/javascript">
    class TencentMercatorProjection extends Cesium.WebMercatorProjection {
      project (cartographic, result) {
        const correct = gcoord.transform([Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)], gcoord.WGS84, gcoord.GCJ02)
        cartographic.longitude = Cesium.Math.toRadians(correct[0])
        cartographic.latitude = Cesium.Math.toRadians(correct[1])
        return super.project(cartographic, result)
      }
      unproject (cartesian, result) {
        result = super.unproject(cartesian, result)
        const correct = gcoord.transform([Cesium.Math.toDegrees(result.longitude), Cesium.Math.toDegrees(result.latitude)], gcoord.GCJ02, gcoord.WGS84)
        result.longitude = Cesium.Math.toRadians(correct[0])
        result.latitude = Cesium.Math.toRadians(correct[1])
        return result
      }
    }  
    Cesium.TencentMercatorProjection = TencentMercatorProjection
    class TencentMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
      constructor (options) {
        super(options)
        this._projection = new Cesium.TencentMercatorProjection(this._ellipsoid);
      }
    }
    Cesium.TencentMercatorTilingScheme = TencentMercatorTilingScheme
    class TencentImageryProvider extends Cesium.UrlTemplateImageryProvider {
      constructor (param, options = {}) {
        var templateImgUrl = "//p{s}.map.gtimg.com/sateTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg"
        var templateDemUrl = "//p{s}.map.gtimg.com/demTiles/{z}/{sx}/{sy}/{x}_{ry}.jpg"
        var templateUrl = "//rt{s}.map.gtimg.com/tile?z={z}&x={x}&y={ry}&{p}"
        var myUrl = (param === "img" ? templateImgUrl : (param === "dem" ? templateDemUrl : templateUrl)).replace(/\{p\}/g, param)
        super(Object.assign({}, {
          url: myUrl, subdomains: "0123", minimumLevel: 1, maximumLevel:  param === "dem" ? 15 : 18, tilingScheme: new Cesium.TencentMercatorTilingScheme(),
          customTags: {
            x: (l, x, y, z) => { return x },
            ry: (l, x, y, z) => { return (1 << z) - y - 1 }, 
            z: (l, x, y, z) => { return z },
            sx: (l, x, y, z) => { return x >> 4 },
            sy: (l, x, y, z) => { return ((1 << z) - y - 1) >> 4 }
          }
        }, options))
      }
    }
    Cesium.TencentImageryProvider = TencentImageryProvider

    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.TencentImageryProvider("img"), // 影像底图
      dem_Layer = new Cesium.TencentImageryProvider("dem"), // 地形底图
      tvs1_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=1"), // 电子地图,区域面 + 路网 + 注记 + 楼块
      tvs2_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=2"), // 影像标注,路网 + 注记
      tvs3_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=3"), // 简化地图,区域面 + 路网 + 注记 + 楼块
      tvs4_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=4"), // 电子地图,暗色地图
      tvs8_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=8"), // 电子地图,淡色地图
      tvs9_Layer = new Cesium.TencentImageryProvider("type=vector&styleid=9") // 电子地图,淡色地图,突出学校、医院等公共设施

    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 dem_Layer }, name: "地形底图" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs1_Layer }, name: "电子地图,区域面 + 路网 + 注记 + 楼块" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs2_Layer }, name: "影像标注,路网 + 注记" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs3_Layer }, name: "简化地图,区域面 + 路网 + 注记 + 楼块" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs4_Layer }, name: "电子地图,暗色地图" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs8_Layer }, name: "电子地图,淡色地图" }),
      new Cesium.ProviderViewModel({ creationFunction: function () { return tvs9_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.TencentImageryProvider(param))
      }
    }
    var param = "type=vector&styleid=1"
    document.getElementById("url-input").value = param
    loadTile(param)
  script>
html>

参考链接

其他文章:https://blog.csdn.net/shaxiaozilove/article/details/118685919
腾讯地图控制台(2022年11月11日停服):https://map.qq.com/

你可能感兴趣的:(WebGIS,web)