分享vue3+OpenTiny UI+cesium.js实现三维地球

效果图

3dfb2773104c4907b684f572bf8fbabd.png

使用vue3 + OpenTiny UI + cesium 实现三维地球

  1. node.js >= v16.0

  2. opentiny vue3 ui安装指南 https://opentiny.design/tiny-vue/zh-CN/os-theme/docs/installation yarn add @opentiny/vue@3

项目依赖

"dependencies": {

    "@opentiny/vue": "3",

    "core-js": "^3.8.3",

    "vue": "^3.2.13",

    "vue-router": "4",

    "cesium": "^1.99.0",

    "cesium-navigation-es6": "^3.0.8"

  }

模块化代码

main.js

import { createApp } from 'vue'
import App from './App.vue'
// 引入 @opentiny/vue 组件
import TinyVue from '@opentiny/vue'
import Cesium from 'cesium'

// 创建并挂载根实例
const app = createApp(App)
// 注册 @opentiny/vue 组件
app.use(TinyVue)
app.use(Cesium)
app.mount('#app')

App.vue

<template>
  <gis></gis>
</template>

<script>
import gis from './components/EarthGis.vue'

export default {
  name: 'App',
  components: {
    gis
  },
  data() {
    return {
    }
  },
  mounted(){
    // 在“about:blank”中阻止脚本执行,因为文档的框架已被沙盒化并且未设置“allow-scripts”权限。
    let iframe = document.getElementsByClassName('cesium-infoBox-iframe')[0];
    iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-popups allow-forms');
    iframe.setAttribute('src', ''); // 必须设置src为空 否则不会生效。
  }
}
</script>

<style>
body {
  margin: 0;
  padding: 0;
  background-color: #e9edfa;
}
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  background-color: transparent;
}
#loading {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%);
  z-index: 1;
}
.progressText {
  text-align: center;
}
</style>

EarthGis.vue

<template>
  <div class="content">
      <tiny-layout :cols="12">
        <tiny-row :gutter="10">
          <tiny-col :span="12">
            <div class="col" style="position: relative;">
                <div class="select-menu">
                  <tiny-dropdown title="影像来源"  size="medium" split-button @item-click="selectChange">
                    <template #dropdown>
                      <tiny-dropdown-menu popper-class="select-item">
                        <tiny-dropdown-item
                          v-for="(item, index) in imageryLayersOptions"
                          :key="index"
                          :label="item.label"
                          :item-data="item.value"
                        ></tiny-dropdown-item>
                      </tiny-dropdown-menu>
                    </template>
                  </tiny-dropdown>
                </div>
                <div id="cesium-container"></div>
            </div>
          </tiny-col>
        </tiny-row>
      </tiny-layout>
  </div>
</template>

<script type="allow-scripts">
import 'cesium/Source/Widgets/widgets.css'
import {  Layout, Row, Col, Dropdown, DropdownMenu, DropdownItem } from '@opentiny/vue'
import { World } from './js/World/World.js'
export default {
  name: 'EarthGis',
  props: {
    msg: String
  },
  components: {
    TinyDropdown: Dropdown,
    TinyDropdownMenu: DropdownMenu,
    TinyDropdownItem: DropdownItem,
    TinyLayout: Layout,
    TinyRow: Row,
    TinyCol: Col
  },
  watch: {
    isLoading: function (val) {
      document.getElementById('loading').style.display = val ? 'black' : 'none'
    }
  },
  data() {
    return {
      isLoading: true,
      imageryLayersOptions: [{
        value: 'SingleTile',
        label: 'SingleTileImageryProvider'
      }],
      world: null
    }
  },
  methods: {
    selectChange(data) {
      let item = data.itemData
      switch (item) {
        case 'SingleTile':
          this.world.changeImagery()
          break
        default:
          break
      }
    }
  },
  mounted() {
    // 1. Create an instance of the World app
    this.world = new World('cesium-container');
    this.$nextTick(() => {
      this.selectChange({itemData: 'SingleTile'})
    })
  },
  destroy(){
    this.removeNavigation()
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#cesium-container{
  width: 100vw;
  height: 100vh;
}
.select-menu{
  position: absolute;
  top: 1rem;
  left: 1rem;
  background: #ffffff66;
  border-radius: 5px;
  padding: 10px;
}
.select-item{
  top: 1rem;
  left: 1rem;
  background: #ffffff66;
  border-radius: 5px;
  padding: 10px;
}
</style>

World.js

import { createViewer } from './components/viewer.js'
import { createImageryLayer } from './components/imageryLayer.js'
import { createTerrain } from './components/terrain.js'
import { createNavigation } from './components/navigation.js'
import { createCamera } from './components/camera.js';
import darkEarth from '@/assets/images/darkEarth.jpg'
import { FeatureDetection, 
    DirectionalLight, 
    Cartesian3, 
    JulianDate, 
    Math, 
    CameraEventType, 
    KeyboardEventModifier, 
    Cesium3DTileset,
    HeadingPitchRange,
    Matrix4,
    Cartographic,
    ScreenSpaceEventType,
    SingleTileImageryProvider,
    WebMercatorTilingScheme,
    ScreenSpaceEventHandler,
    WebMapTileServiceImageryProvider,
    GeographicTilingScheme,
    ImageryLayer, 
    UrlTemplateImageryProvider,
    OpenStreetMapImageryProvider,
    IonImageryProvider,
    createOsmBuildings,
    Rectangle  } from 'cesium'
// These variables are module-scoped: we cannot access them
// from outside the module
let viewer;
// 添加主题图层相关配置
let layerOption = {
  show: true, // 图像层是否可见
  alpha: 0.6, // 透明度
  nightAlpha: 1, // 地球夜晚一侧的透明度
  dayAlpha: 1, // 地球白天一侧的透明度
  brightness: 1, // 亮度
  contrast: 1, // 对比度
  hue: 0, // 色调
  saturation: 1, // 饱和度
  gamma: 1, // 伽马校正
}
class World {
    // 1. Create an instance of the World app
    constructor(id) {
        viewer = createViewer(id)
        viewer.imageryLayers.removeAll(true)
    }
    init(){
      // 修改场景环境,关闭相关特效
      viewer.scene.debugShowFramesPerSecond = true// 显示fps
      viewer.scene.moon.show = true// 月亮
      viewer.scene.fog.enabled = true// 雾
      viewer.scene.sun.show = true// 太阳
      viewer.scene.skyBox.show = true// 天空盒
      viewer.scene.globe.enableLighting = true // 激活基于太阳位置的光照(场景光照)
      viewer.resolutionScale = 1// 画面细度,默认值为1.0

      // 不显示cesium icon版权信息
      viewer._cesiumWidget._creditContainer.style.display="none"
      viewer.geocoder._form.children[0].placeholder = "请输入关键字"

      // DirectionalLight 表示 从无限远的地方向单一方向发射的光。解决模型光照问题
      viewer.scene.light = new DirectionalLight({
          direction: new Cartesian3(0.354925, -0.890918, -0.283358)
      })

      viewer.clock.currentTime = JulianDate.addHours(
          JulianDate.now(new Date()),
          12,
          new JulianDate()
      )
      // 启用深度测试,使地形后面的东西消失。
      viewer.scene.globe.depthTestAgainstTerrain = true
      viewer.scene.fxaa = false
      viewer.scene.postProcessStages.fxaa.enabled = true

      // 水雾特效
      viewer.scene.globe.showGroundAtmosphere = true

      // 设置最大俯仰角,[-90,0]区间内,默认为-30,单位弧度
      viewer.scene.screenSpaceCameraController.constrainedPitch = Math.toRadians(-20)
      viewer.scene.screenSpaceCameraController.autoResetHeadingPitch = false
      viewer.scene.screenSpaceCameraController.inertiaZoom = 0.5
      viewer.scene.screenSpaceCameraController.minimumZoomDistance = 50
      viewer.scene.screenSpaceCameraController.maximumZoomDistance = 20000000

      viewer.scene.screenSpaceCameraController.zoomEventTypes = [
          CameraEventType.RIGHT_DRAG,
          CameraEventType.WHEEL,
          CameraEventType.PINCH
      ]

      viewer.scene.screenSpaceCameraController.tiltEventTypes = [
          CameraEventType.MIDDLE_DRAG,
          CameraEventType.PINCH,
          {
              eventType: CameraEventType.LEFT_DRAG,
              modifier: KeyboardEventModifier.CTRL
          },
          {
              eventType: CameraEventType.RIGHT_DRAG,
              modifier: KeyboardEventModifier.CTRL
          }
      ]

      // 开启抗锯齿
      if (FeatureDetection.supportsImageRenderingPixelated()) {
          // 判断是否支持图像渲染像素化处理
          viewer.resolutionScale = window.devicePixelRatio
      }
      // 添加默认图层
      createImageryLayer()
      // 开启Navigation导航插件
      createNavigation(viewer)
      // 添加cesium自带的地形
      createTerrain(viewer)

      // 将三维球定位到中国
      viewer.camera.flyTo({
          destination: Cartesian3.fromDegrees(103.84, 31.15, 17860000),
          orientation: {
          heading: Math.toRadians(348.4202942851978),
          pitch: Math.toRadians(-89.74026687972041),
          roll: Math.toRadians(0)
          },
          complete: () => {
              // 定位完成之后的回调函数
              console.log('定位完成')
              document.getElementById('loading').style.display = 'none'
          }
      })

      // 设置默认的视角为中国
      createCamera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(
          // 西边经度
          89.5,
          // 南边维度
          20.4,
          // 东边经度
          110.4,
          // 北边维度
          61.2
      )

      //this.cion(layerOption)
      //this.osm(layerOption)
      this.hot(layerOption)
      //this.cartoVoyager(layerOption)
      //this.cartoDark(layerOption)
      //this.stamen(layerOption)
      //this.wmtsImages(layerOption)
      //this.osmBuildings()

      // const tilesetOption = {
      //     skipLevelOfDetail: true,
      //     baseScreenSpaceError: 1024,
      //     skipScreenSpaceErrorFactor: 16,
      //     skipLevels: 1,
      //     immediatelyLoadDesiredLevelOfDetail: false,
      //     loadSiblings: false,
      //     cullWithChildrenBounds: true
      // }

      // const modelPromise = this.addThreeDTiles(69380, tilesetOption);
      // modelPromise.then(model => {
      //     console.log('tileset: ', model)
      //     this.setPosition(model, 113.27, 23.13, 10) // 调整位置到,高度10米
      //     this.setPosition(model, undefined, undefined, 500) // 仅修改高度至500米
      //     this.serMatrix(model) // 使用默认变换矩阵(单位向量),实现回到默认位置的效果
      // })
    }
    // 切换图层
    changeImagery() {
      viewer.imageryLayers.removeAll(true)
      this.roaming()
    }
    async addThreeDTiles(url, option) {
      // 开启地形深度检测:
      // 控制在渲染场景时,相机是否进行深度测试以避免将被遮挡的物体绘制在前景
      // true: 相机会根据地形高度信息进行深度测试,避免将低于地面的物体绘制在地面之上
      viewer.scene.globe.depthTestAgainstTerrain = true

      let tileset = {}
      if (typeof url == 'number') {
        tileset = await Cesium3DTileset.fromIonAssetId(url, option);
      } else {
        tileset = await Cesium3DTileset.fromUrl(url, option);
      }

      viewer.scene.primitives.add(tileset);
      // 定位到模型
      viewer.zoomTo(
        tileset,
        new HeadingPitchRange(
          0.0,
          -0.5,
          tileset.boundingSphere.radius * 2.0 // 模型的包围球半径的2倍
        )
      )
      return tileset // 返回模型对象
    }
    setPosition(tileset, lng, lat, h) {
      // 计算出模型包围球的中心点(弧度制),从世界坐标转弧度制
      const cartographic = Cartographic.fromCartesian(
        tileset.boundingSphere.center
      )
      const { longitude, latitude, height } = cartographic

      // 模型包围球的中心点坐标,输出以笛卡尔坐标系表示的三维坐标点
      const current = Cartesian3.fromRadians(
        longitude,
        latitude,
        height
      )
      // 新的位置的中心点坐标,输出以笛卡尔坐标系表示的三维坐标点
      const offset = Cartesian3.fromDegrees(
        lng || Math.toDegrees(longitude),
        lat || Math.toDegrees(latitude),
        h || height
      );

      // 计算差向量:计算tileset的平移量,并将其应用到modelMatrix中
      const translation = Cartesian3.subtract(
        offset,
        current,
        new Cartesian3()
      )

      // 修改模型的变换矩阵,通过四维矩阵
      tileset.modelMatrix = Matrix4.fromTranslation(translation);
      viewer.zoomTo(tileset);
    }
    //Resets the position of a tileset to a specified model matrix or the identity matrix if none is provided.
    serMatrix(tileset, matrix) {
      tileset.modelMatrix = matrix || Matrix4.IDENTITY
      viewer.zoomTo(tileset);
    }
    showAllImagery(boolean = true) {
      // 获取图像图层集合
      const imageryLayers = viewer.imageryLayers;

      // 遍历图像图层并隐藏它们
      let numLayers = imageryLayers.length;
      for (let i = 0; i < numLayers; i++) {
        const layer = imageryLayers.get(i); // 获取图像图层对象
        layer.show = boolean; // 设置图像图层隐藏
      }
    }
    async roaming() {
      let isRoaming = true; // 漫游标志位
      const DEFAULT_LIGHTING = viewer.scene.globe.enableLighting; // 默认光照状态
      const DEFAULT_SKY_ATMOSPHERE = viewer.scene.skyAtmosphere.show; // 默认光照状态
      let bgImglayer; // 地球底图

      this.showAllImagery(false); // 隐藏所有图层
      viewer.clock.multiplier = -2000.0;  // 时间加速

      const provider = await SingleTileImageryProvider.fromUrl(darkEarth);
 
      provider._tilingScheme = new WebMercatorTilingScheme()
      bgImglayer = viewer.imageryLayers.addImageryProvider(provider); // 加载背景底图

      if (!DEFAULT_LIGHTING) {
        viewer.scene.globe.enableLighting = true; // 开启光照
      }
      if (!DEFAULT_SKY_ATMOSPHERE) {
        viewer.scene.skyAtmosphere.show = true; // 开启天空大气,能在一定程度上降低地球轮廓锯齿
      }

      // 添加鼠标事件,触发后停止漫游效果
      const handler = new ScreenSpaceEventHandler(viewer.scene.canvas); // 交互句柄
      handler.setInputAction(() => {
        isRoaming = false // 停止旋转
        this.showAllImagery(true) // 显示图层
        if (!DEFAULT_LIGHTING) {
          viewer.scene.globe.enableLighting = false; // 关闭光照
        }
        if (!DEFAULT_SKY_ATMOSPHERE) {
          viewer.scene.skyAtmosphere.show = false; // 关闭光照
        }
        viewer.imageryLayers.remove(bgImglayer, true); // 移除图层
        viewer.clock.multiplier = 1;  // 正常时间流速

        handler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK); // 移除鼠标事件监听
        this.init()
      }, ScreenSpaceEventType.LEFT_CLICK);

      (function roamingEvent() {
        if (isRoaming) {
          // 控制相机围绕 Z 轴旋转
          viewer.camera.rotate(Cartesian3.UNIT_Z, Math.toRadians(0.1));
          requestAnimationFrame(roamingEvent);
        }
      })()
    }
    // Cesium ION 服務
    cion (layerOption, id = 3812)  {
      const layer = new ImageryLayer(
        new IonImageryProvider({ assetId: id }),
        layerOption
      )
      viewer.imageryLayers.add(layer)
      return layer
    }
    // 加载osm地图
    osm (layerOption)  {
      const layer = new ImageryLayer(
        new OpenStreetMapImageryProvider({ url: 'https://a.tile.openstreetmap.org/' }),
        layerOption
      )
      viewer.imageryLayers.add(layer)
      return layer
    }
    // 加载Humanitarian OpenStreetMap Team style地图
    hot (layerOption)  {
      const layer = new ImageryLayer(
        new UrlTemplateImageryProvider({ url: 'https://tile-{s}.openstreetmap.fr/hot/{z}/{x}/{y}.png', subdomains: ['a', 'b', 'c'] }),
        layerOption
      )
      viewer.imageryLayers.add(layer)
      return layer
    }
    // 加载carto Basemaps 航海风格地图
    cartoVoyager (layerOption)  {
      const layer = new ImageryLayer(
        new UrlTemplateImageryProvider({ url: 'https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.png' }),
        layerOption
      )
      viewer.imageryLayers.add(layer)
      return layer
    }
    // 加载carto Basemaps 黑暗风格地图
    cartoDark (layerOption)  {
      const layer = new ImageryLayer(
        new UrlTemplateImageryProvider({ url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', subdomains: ['a', 'b', 'c', 'd'] }),
        layerOption
      )
      viewer.imageryLayers.add(layer) 
      return layer
    }
    // 加载Stamen地图
    stamen (layerOption) {
      const layer = new ImageryLayer(
        new UrlTemplateImageryProvider({ url: 'https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png' }),
        layerOption
      )
      viewer.imageryLayers.add(layer)
      return layer
    }
    // 加载WMTS瓦片地图服务
    wmtsImages () {
      // EPSG:900913(标准名为EPSG:3875)网格切分的瓦片。当将tileMatrixSetID设置为 'EPSG:4326',访问常用的EPSG:4326网络的瓦片
      // 访问GeoServer发布的地图瓦片服务 WebMapTileServiceImageryProvider的切片方案tilingScheme默认使用EPSG:3875投影,即伪墨卡托网格访问切片,与EPSG:4326网格的切片方案存在较大差异
      // Tiling是一种椭球体表面上的几何图形或图像的平铺方案。在详细级别为0,即最粗、最不详细的级别上,瓦片的数量是可配置的。在详细级别为1级以上,每个是一级级瓦片经纬两个方向上扩展为两个瓦片,共有四个子瓦片。如此扩展到最大的缩放级别,这也构成了一个图像瓦片的金字塔。TillingScheme有一个参数ellipsoid用来决定切处时使用的椭球,另外两个比较重要的参数numberOfLevelZeroTilesX和numberOfLevelZeroTilesY,用来决定0级瓦片的数量。
      // TilingSchemee有两个子类,为WebMercatorTilingScheme和GeographicTilingScheme。其中WebMercatorTilingScheme对应于EPSG:3857切片方案,常见于谷歌地图、微软必应地图以及大多数的ArcGIS在线地图,也是Cesium中默认的切片方案。
      // GeographicTilingScheme对应于EPSG:4326切片方案,是一个简单的地理投影方案,可直接将经纬度映射为X和Y,这种投影通常被称为地理投影、等矩形投影、等距圆柱形投影等。
      // 由于在X方向上,WebMercatorTilingScheme只有一个0级瓦片,而GeographicTilingScheme却有2个,这就导致了默认的EPSG:3857切片方案不能正确加载EPSG:4326切片方案的瓦片图像。

      let layer = new WebMapTileServiceImageryProvider({
          url : '/map/gwc/service/wmts/rest/xian:satellite/{style}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png',
          style : 'raster',
          tileMatrixSetID : 'EPSG:4326',
          tilingScheme: new GeographicTilingScheme(),
      });
      viewer.imageryLayers.addImageryProvider(layer);
      return layer
    }
    // 载入OSM建筑物
    osmBuildings = () => {
      // 突出显示所有的商业和住宅建筑,以查看整个城市不同社区的模式
      // Cesium OSM 建筑物通过3D Tiles,它可以在web上高效地流式传输和可视化。
      // 3D Tiles是一个开放的标准,所以Cesium OSM建筑可以在任何兼容它的查看器中使用,不仅仅是开源的Cesium。
      // Cesium全球3.5亿做建筑物,数据来源openStreetMap地图

      //OpenStreetMap(简称OSM,中文是公开地图)是一个网上地图协作计划,目标是创造一个内容自由且能让所有人编辑的世界地图。
      //其包含图层主要有高速公路、铁路、水系、水域、建筑、边界、建筑物等图层。我们不仅能够免费下载城市数据还可以下载全球数据。
      //网址为https://www.openstreetmap.org/
      //Cesium中支持使用OSM在线的建筑矢量三维模型,但目前OSM数据在国外较为细致,国内只支持几个大城市。
      //由于OSM建筑数据量大,加载较慢,用户在使用建筑白膜时,可根据需求,在OSM官网或百度、高德等地图服务商中下载建筑矢量数据,
      //使用ArcGIS等GIS软件和SketchUP等建模软件,生成建筑白膜,并使用建模软件对白膜进行贴图修改等操作,以实现城市建筑的美化,
      //使用CesiumLab等软件对建模的三维建筑数据 进行切片生成3Dtiles等Cesium支持的数据类型,对其进行加载使用。
      const addOSMAsync = () => {
        try {
          // 突出显示所有的商业和住宅建筑,以查看整个城市不同社区的模式
          viewer.scene.primitives.add(createOsmBuildings())
        } catch (error) {
          console.log(`Failed to add world imagery: ${error}`);
        }
      };
      addOSMAsync()
    }
}
    
export { World };

viewer.js

import { Viewer, Ion } from 'cesium'
function createViewer(id) {
    const cesiumToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmNDVhMjQ0Yi05MDg4LTRlNDYtYTIxMi00YmI4ZWYxOWMyNTgiLCJpZCI6MTQ5MzYwLCJpYXQiOjE2ODc3ODQ0OTh9.KlhT_LCnsEtYyawpEmJ_wx44_qTUbgze-SxGMRavbAM'
    Ion.defaultAccessToken = cesiumToken
    const viewerOption = {
        // 默认隐藏
        infoBox: true, //是否显示信息框
        animation:false,    //左下角的动画仪表盘
        baseLayerPicker:true,  //右上角的图层选择按钮
        geocoder:true,  //搜索框
        homeButton:true,  //home按钮
        sceneModePicker:true, //模式切换按钮
        timeline:true,    //底部的时间轴
        navigationHelpButton:true,  //右上角的帮助按钮
        fullscreenButton:true,   //右下角的全屏按钮
        contextOptions:{
          webgl:{
            alpha:true
          }
        }
    }
    const instance = new Viewer(id, viewerOption);
    return instance;
}

export { createViewer }

imageryLayer.js

import { ImageryLayer, UrlTemplateImageryProvider } from 'cesium'
// 添加主题图层相关配置
let layerOption = {
    show: true, // 图像层是否可见
    alpha: 0.6, // 透明度
    nightAlpha: 1, // 地球夜晚一侧的透明度
    dayAlpha: 1, // 地球白天一侧的透明度
    brightness: 1, // 亮度
    contrast: 1, // 对比度
    hue: 0, // 色调
    saturation: 1, // 饱和度
    gamma: 1, // 伽马校正
}
function createImageryLayer( option=layerOption ) {
    // 添加主题图层相关配置
    const instance = new ImageryLayer(
        new UrlTemplateImageryProvider({ url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', subdomains: ['a', 'b', 'c', 'd'] }),
        option
    )
    return instance;
}

export { createImageryLayer }

camera.js

import { Camera } from 'cesium'
function createCamera() {
    const instance = Camera;
    return instance;
}

export { createCamera }

terrain.js

import { createWorldTerrainAsync } from 'cesium'
function createTerrain(viewer) {
    // 添加地形数据
    const addWorldTerrainAsync = async () => {
        try {
          const terrainProvider = await createWorldTerrainAsync({
            requestWaterMask: false, // 水面特效
            requestVertexNormals: true // 地形照明
          });  
          viewer.terrainProvider = terrainProvider;
        } catch (error) {
          console.log(`Failed to add world imagery: ${error}`);
        }
    };
    addWorldTerrainAsync()
}

export { createTerrain }

navigation.js

import CesiumNavigation from 'cesium-navigation-es6'
import { Cartographic } from 'cesium'

let instance;
function createNavigation(viewer) {
    let navigationOptions = {};
    // 用于在使用重置导航重置地图视图时设置默认视图控制。接受的值是Cesium.Cartographic 和Cesium.Rectangle.
    navigationOptions.defaultResetView = Cartographic.fromDegrees(103.84, 31.15, 17860000);
    // 用于启用或禁用罗盘。true是启用罗盘,false是禁用罗盘。默认值为true。如果将选项设置为false,则罗盘将不会添加到地图中。
    navigationOptions.enableCompass= true;
    // 用于启用或禁用缩放控件。true是启用,false是禁用。默认值为true。如果将选项设置为false,则缩放控件 将不会添加到地图中。
    navigationOptions.enableZoomControls= true;
    // 用于启用或禁用距离图例。true是启用,false是禁用。默认值为true。如果将选项设置为false,距离图例将不会添加到地图中。
    navigationOptions.enableDistanceLegend= true;
    // 用于启用或禁用指南针外环。true是启用,false是禁用。默认值为true。如果将选项设置为false,则该环将可见但无效。
    navigationOptions.enableCompassOuterRing= true;
    navigationOptions.resetTooltip = "重置";
    navigationOptions.zoomInTooltip = "放大";
    navigationOptions.zoomOutTooltip = "缩小";
    // 开启Navigation 罗盘、图例、指南针等导航插件
    instance = new CesiumNavigation(viewer, navigationOptions);
    return instance;
}
function removeNavigation(){
    instance.destroy();
}

export { createNavigation, removeNavigation }

夜色中的地球图片

f2cf506772e849ac942ca35d489e1573.png

292c5d988ae84d24964fa483b5d70332.png

9f7a40342aee4771a15c49e75cce9216.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/499774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每日一练 两数相加问题(leetcode)

原题如下&#xff1a; 这道题目是一道链表题&#xff0c;我们对于这种链表类&#xff0c;很显然我们最后输出的是初始节点&#xff0c;所以我们要保留我们的初始头指针&#xff0c;那么我们的第一步一定是把头指针保留一份&#xff0c;然后再让头指针往后进行操作。那么我们进行…

EXCEL VBA根据表数据写入数据库中

EXCEL VBA根据表数据写入数据库中 Option Explicithttps://club.excelhome.net/thread-1687531-1-1.htmlSub UpdateAccess()Const adStateOpen 1Dim vData, i As Variant, j As LongDim AccessTable As String, ExcelTable As String, ExcelFile As String, AccessFile As Str…

SD-WAN安全策略,保护企业网络的新边界

随着企业的数字化转型和网络架构的演变&#xff0c;SD-WAN&#xff08;软件定义广域网&#xff09;作为一种新兴的网络技术&#xff0c;正在逐渐取代传统的基于硬件的广域网架构。然而&#xff0c;随之而来的安全威胁也在增加&#xff0c;因此在部署 SD-WAN时&#xff0c;制定合…

【python】深入探讨flask是如何预防CSRF攻击的

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

文本文件操作

大家好&#xff1a; 衷心希望各位点赞。 您的问题请留在评论区&#xff0c;我会及时回答。 文件操作 程序运行时&#xff0c;产生的数据都是临时数据&#xff0c;程序一旦运行结束都会被释放。通过文件可以将数据持久化。 C中对文件进行操作需要包含头文件<fstream> 文件…

spark-看看视频每章总结

一 spark基础 1.spark特点 2.spark架构 3.spark部署 4.代码执行流程 先由driver构建sparkcontext对象&#xff0c;再由executor分布式执行&#xff0c;结果汇集到driver上输出 二 sparkcore 1.RDD 2.RDD算子 3.宽窄依赖p100 4.spark内存计算 三.sparksql 1.spark SQL和spar…

机器学习中的XGBoost模型及其优缺点(包含Python代码样例)

目录 一、简介 二、优缺点 三、样例代码 四、总结 一、简介 XGBoost&#xff08;eXtreme Gradient Boosting&#xff09;是一种机器学习算法&#xff0c;用于解决分类和回归问题。它是基于梯度提升树&#xff08;Gradient Boosting Decision Trees&#xff09;方法的扩展&…

HarmonyOS像素转换-如何使用像素单位设置组件的尺寸。

1 卡片介绍 基于像素单位&#xff0c;展示了像素单位的基本知识与像素转换API的使用。 2 标题 像素转换&#xff08;ArkTS&#xff09; 3 介绍 本篇Codelab介绍像素单位的基本知识与像素单位转换API的使用。通过像素转换案例&#xff0c;向开发者讲解了如何使用像素单位设…

【AI】『Suno』哎呦不错呦,AI界的周董,快来创作你的歌曲吧!

前言 &#x1f34a;缘由 Suno AI的旋风终于还是吹到了音乐圈 &#x1f3c0;事情起因&#xff1a; 朋友说他练习时长两天半&#xff0c;用Suno发布了首张AI音乐专辑。震惊之余&#xff0c;第一反应是音乐圈门槛也这么低了&#xff0c;什么妖魔鬼怪都可以进军了嘛&#xff01;…

Java后端需要掌握的前端知识

第一章. HTML 与 CSS HTML 是什么&#xff1a;即 HyperText Markup language 超文本标记语言&#xff0c;咱们熟知的网页就是用它编写的&#xff0c;HTML 的作用是定义网页的内容和结构。 HyperText 是指用超链接的方式组织网页&#xff0c;把网页联系起来Markup 是指用 <…

Elasticsearch 开放 inference API 增加了对 Cohere Embeddings 的支持

作者&#xff1a;来自 Elastic Serena Chou, Jonathan Buttner, Dave Kyle 我们很高兴地宣布 Elasticsearch 现在支持 Cohere 嵌入&#xff01; 发布此功能是与 Cohere 团队合作的一次伟大旅程&#xff0c;未来还会有更多合作。 Cohere 是生成式 AI 领域令人兴奋的创新者&…

开始时间大于结束时间

1.dom中代码&#xff0c;监听所选日期值的变化&#xff0c;并把需要比较的时间字段作为参数传到监听方法中&#xff0c; <el-form-item label"起始日期" prop"startTime"><el-date-picker clearable size"small":disabled"isDisa…

从TCP/IP协议到socket编程详解

​ 我的所有学习笔记&#xff1a;https://github.com/Dusongg/StudyNotes⭐⭐⭐ ​ 文章目录 1 网络基础知识1.1 查看网络信息1.2 认识端口号1.3 UDP1.4 TCP1.4.1 确认应答机制1.4.2 TCP三次握手/四次挥手为什么是三次握手为什么是四次挥手listen 的第二个参数 backlog—— 全…

【项目技术介绍篇】若依开源项目RuoYi-Cloud后端技术介绍

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

基于微信小程序的日语词汇学习设计与实现(论文+源码)_kaic

日语词汇学习小程序 摘 要 日语词汇学习小程序是高校人才培养计划的重要组成部分&#xff0c;是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。本学生所在学院多采用半手工管理日语词汇学习小程序的方式&#x…

【训练时如何指定GPU或者CPU】

【训练时如何指定GPU或者CPU】 【先赞后看养成习惯】求关注点赞收藏&#xff01;&#xff01;&#xff01;&#x1f60a; 如果不知道自己电脑是否支持GPU或者CPU&#xff0c;我们可以使用下面这句命令&#xff0c;那么它会优先去调用GPU&#xff0c;如果没有GPU&#xff0c;就…

STM32CubeMX学习笔记28---FreeRTOS软件定时器

一、软件定时器简介 1 、基本概念 定时器&#xff0c;是指从指定的时刻开始&#xff0c;经过一个指定时间&#xff0c;然后触发一个超时事件&#xff0c;用户 可以自定义定时器的周期与频率。类似生活中的闹钟&#xff0c;我们可以设置闹钟每天什么时候响&#xff0c; 还能设置…

【linux深入剖析】文件描述符 | 对比 fd 和 FILE | 缓冲区

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.文件描述符fd2.文件描述…

【网络安全技术】——密码技术基础与身份认证技术(学习笔记)

&#x1f4d6; 前言&#xff1a;加密技术是目前网络安全的基础。数据加密技术是指对在网络中所发送的明文消息用加密密钥加密成密文进行传送&#xff0c;接收方用解密密钥进行解密再现明文消息&#xff0c;从而保证传输过程中密文信息即使被泄漏&#xff0c;在无密钥的情况下仍…

基于单片机三路信号故障诊断仿真设计

单片机设计介绍&#xff0c;基于单片机三路信号故障诊断仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机三路信号故障诊断仿真设计概要主要涵盖了系统设计的整体框架、关键模块功能、仿真方法以及预期实现的目…