cesium加载高德地图并纠偏

高德地图采用中国国家测绘局制订的地理信息系统的坐标系GCJ02,即火星坐标系,他是在wgs84坐标系的基础上进行一次加密。因此cesium在加载高德地图服务底图时,会存在偏差和纠偏的问题。是否纠偏,根据具体的业务需求。本文通过引用第三方纠偏库,同时支持cesium加载高德底图GCJ02坐标系及纠偏后的WGS84坐标系。

1. 引入第三方纠偏库

gcoord(geographic coordinates)是一个处理地理坐标转换的JS库,用来修正百度地图、高德地图及其它互联网地图坐标系不统一的问题。

1.1 安装

npm i gcoord

1.2 引入

import gcoord from 'gcoord';

2. 自定义高德纠偏切片方案

2.1 WebMercatorTilingScheme类

该类是cesium自带的EPSG:3857切片方案,因为高德的加密偏移问题,需要基于该类进行扩展,自定义高德纠偏切片方案。其中的原理,这里不去解释,后续有时间,另起专栏讲讲webgis的底层原理。

AmapMercatorTilingScheme.js

/*
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-03 17:36:41
 * @LastEditTime: 2023-04-03 21:02:36
 * @LastEditors: maizi
 */
import gcoord from 'gcoord';

class AmapMercatorTilingScheme extends Cesium.WebMercatorTilingScheme {
  constructor(options) {
    super(options)
    let projection = new Cesium.WebMercatorProjection()
    this._projection.project = function(cartographic, result) {
      //WGS84转GCJ02坐标
      result = gcoord.transform([
        Cesium.Math.toDegrees(cartographic.longitude), 
        Cesium.Math.toDegrees(cartographic.latitude)
      ], gcoord.WGS84, gcoord.GCJ02)
      result = projection.project(
        new Cesium.Cartographic(
          Cesium.Math.toRadians(result[0]),
          Cesium.Math.toRadians(result[1])
        )
      )
      return new Cesium.Cartesian2(result.x, result.y)
    }
    this._projection.unproject = function(cartesian, result) {
      let cartographic = projection.unproject(cartesian)
       //GCJ02转WGS84坐标
      result = gcoord.transform([
        Cesium.Math.toDegrees(cartographic.longitude), 
        Cesium.Math.toDegrees(cartographic.latitude)
      ], gcoord.GCJ02, gcoord.WGS84)

      return new Cesium.Cartographic(
        Cesium.Math.toRadians(result[0]),
        Cesium.Math.toRadians(result[1])
      )
    }
  }
}

export default AmapMercatorTilingScheme

2.2 UrlTemplateImageryProvider类

该类允许用户通过url模板实现瓦片底图的加载,基于该类进行扩展,使用上述的切片方案自定义高德底图提供器。

AmapImageryProvider .js


/*
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-03 17:36:41
 * @LastEditTime: 2023-04-03 21:02:36
 * @LastEditors: maizi
 */
import AmapMercatorTilingScheme from './AmapMercatorTilingScheme'
//影像(无标注)
const IMG_URL = 'https://webst0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=6&x={x}&y={y}&z={z}'
//电子(无标注)
const VEC_URL = 'https://wprd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&ltype=3&x={x}&y={y}&z={z}'
//道路(无标注)
const ROAD_URL = 'https://webst0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&ltype=11&x={x}&y={y}&z={z}'
 //电子(有标注)
const VEC_LABEL_URL = 'https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
//道路(有标注)
const ROAD_LABEL_URL = 'http://webst0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'

class AmapImageryProvider extends Cesium.UrlTemplateImageryProvider {
  constructor(options = {}) {
    switch(options.type) {
      case 'img_w':
        options['url'] = IMG_URL
        break
      case 'vec_w':
        options['url'] = VEC_URL
        break
      case 'road_w':
        options['url'] = ROAD_URL
        break
      case 'vec_label_w':
        options['url'] = VEC_LABEL_URL
        break
      case 'road_label_w':
        options['url'] = ROAD_LABEL_URL
        break
    }
    if (!options.subdomains || !options.subdomains.length) {
      options['subdomains'] = ['1', '2', '3', '4']
    }
    if (options.crs === 'WGS84') {
      options['tilingScheme'] = new AmapMercatorTilingScheme()
    }
    super(options)
  }
}
export default AmapImageryProvider

3. 完整示例代码

GdLayer.vue

<!--
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-03 11:34:28
 * @LastEditTime: 2023-04-03 20:23:51
 * @LastEditors: maizi
-->

<template>
  <div id="container">
    <div class="pane_container">
      <el-checkbox v-model="checked" class="my-check">纠偏</el-checkbox>
      <el-button size="small" @click="changeMap(0)">影像地图</el-button>
      <el-button size="small" @click="changeMap(1)">电子地图(无标注)</el-button>
      <el-button size="small" @click="changeMap(2)">街道图(无标注)</el-button>
      <el-button size="small" @click="changeMap(3)">电子地图(有标注)</el-button>
      <el-button size="small" @click="changeMap(4)">街道图(有标注)</el-button>
    </div>
  </div>
</template>

<script>
import * as MapWorks from './js/MapWorks'
export default {
  name: 'GdLayer',
  data(){
    return {
      checked: false
    }
  },
  mounted() {
    this.init();
  },
  beforeDestroy(){
    //实例被销毁前调用,页面关闭、路由跳转、v-if和改变key值
    MapWorks.destroy();
  },

  methods:{
    init(){
      let container = document.getElementById("container");
      MapWorks.initMap(container)
      MapWorks.changeBaseMap(0)
      let coords = [104.0634012222290039, 30.6598806381225586, 500]
      let hpr = {
        heading: 0,
        pitch:-90.0,
        roll: 0
      }
      MapWorks.setView(coords, hpr)
    },

    changeMap(index){
      MapWorks.changeBaseMap(index, this.checked)
    }
  }

}
</script>

<style lang="scss" scoped>
#container{
  width: 100%;
  height: 100%;
  background: rgba(7, 12, 19, 1);
  overflow: hidden;
  background-size: 40px 40px, 40px 40px;
  background-image: linear-gradient(hsla(0, 0%, 100%, 0.05) 1px, transparent 0), linear-gradient(90deg, hsla(0, 0%, 100%, 0.05) 1px, transparent 0);
  .pane_container{
    margin-top: 12px;
    margin-left: 12px;
    position: absolute;
    padding: 10px 15px;
    border-radius: 4px;
    border: 1px solid rgba(128, 128, 128, 0.5);
    color: #ffffff;
    background: rgba(0, 0, 0, 0.4);
    box-shadow: 0 3px 14px rgb(128 128 128 / 50%);
    z-index: 2;
    .my-check{
      margin-right: 12px;
    }
  }
}


</style>

MapWorks.js 

/*
 * @Description: 
 * @Author: maizi
 * @Date: 2023-04-03 17:34:21
 * @LastEditTime: 2023-04-03 20:24:37
 * @LastEditors: maizi
 */
import AmapImageryProvider from './AmapImageryProvider'
// 初始视图定位在中国
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(90, -20, 110, 90);

let viewer = null;
function initMap(container) {
  viewer = new Cesium.Viewer(container, {
    animation: false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    infoBox: false,
    sceneModePicker: false,
    selectionIndicator: false,
    timeline: false,
    navigationHelpButton: false, 
    scene3DOnly: true,
    orderIndependentTranslucency: false,
    contextOptions: {
      webgl: {
        alpha: true
      }
    }
  })
  viewer._cesiumWidget._creditContainer.style.display = 'none'
  viewer.scene.fxaa = true
  viewer.scene.postProcessStages.fxaa.enabled = true
  if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
    // 判断是否支持图像渲染像素化处理
    viewer.resolutionScale = window.devicePixelRatio
  }
  // 移除默认影像
  removeAll()
  // 地形深度测试
  viewer.scene.globe.depthTestAgainstTerrain = true
  // 背景色
  viewer.scene.globe.baseColor = new Cesium.Color(0.0, 0.0, 0.0, 0)
}

function addGdLayer(options) {
  const layerProvider = new AmapImageryProvider(options);
  viewer.imageryLayers.addImageryProvider(layerProvider);
}
function changeBaseMap(type,enabled) {
  removeAll()
  switch(type) {
    case 0: //影像(无标注)
      addGdLayer({
        type: 'img_w',
        crs: enabled ? 'WGS84' : null
      })
      break
    case 1: //电子(无标注)
      addGdLayer({
        type: 'vec_w',
        crs: enabled ? 'WGS84' : null
      })
      break
    case 2: //道路(无标注)
      addGdLayer({
        type: 'road_w',
        crs: enabled ? 'WGS84' : null
      })
      break
    case 3:  //电子(有标注)
      addGdLayer({
        type: 'vec_label_w',
        crs: enabled ? 'WGS84' : null
      })
      break
    case 4: //道路(有标注)
      addGdLayer({
        type: 'road_label_w',
        crs: enabled ? 'WGS84' : null
      })
      break
  }
}

function removeAll() {
  viewer.imageryLayers.removeAll();
}

function destroy() {
  viewer.entities.removeAll();
  viewer.imageryLayers.removeAll();
  viewer.destroy();
}

function setView(coords,hpr) {
  viewer.scene.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(coords[0], coords[1], coords[2]),
    orientation: {
      heading: Cesium.Math.toRadians(hpr.heading),
      pitch: Cesium.Math.toRadians(hpr.pitch),
      roll: Cesium.Math.toRadians(hpr.roll),
    }
  });
}


export {
  initMap,
  addGdLayer,
  changeBaseMap,
  setView,
  removeAll,
  destroy
}

4 运行结果 

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值