cesium学习笔记

一、下载源码,源码介绍

学习视频参考:1、Cesium.JS从入门到精通
2、Cesium示例程序学习和讲解

官网下载:cesium.js官方网站源码包下载
如果下载较慢,可使用已经下好的:cesium1.89官网源码

下面所有案例的每一课的代码:cesium学习笔记代码

学习cesium的参考路线:CesiumGIS李胜

二、html案例体验

1、新建一个工程,下面包含libs和src两个目录,libs下面放cesium的源码build的整个cesium文件夹,如下图

在这里插入图片描述
2、新建html,编写以下内容,用Live server 打开即可看到效果

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=s, initial-scale=1.0">
  <title>cesium第一课</title>
  //cesium.js引入
  <script src="../libs/Cesium/Cesium.js"></script>
  //cesium样式引入
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  //导入cesium的类型声明,有这个才会有快捷智能提示
  <script src="../libs/Cesium/Cesium.d.ts"></script>
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg'
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer');

  </script>
</body>

</html>

3、运行后如下图:
在这里插入图片描述

三、cesium中的类介绍

1.它们分别是:

1、Viewer查看器类
(1)、它是cesium展示三维要素内容的主要窗口,它不仅仅是包含三维地球的视窗,还包含了一些基础控件,在定义Viewer对象的同时需要设定基础部件、图层等的初始化状态
(2)、Viewer创建代码:new Cesium.Viewer(cesiumContainer,options);
参数说明:第一个参数cesiumContainer我们用于指定地图主窗口diy的id,第二个参数options是Viewer的可选设置参数,我们可以对基础地理环境进行设置。

 /*
        options:{Object) 可选参数,包含以下属性:
        animation:[Boolean} 是否显示动画控制器,默认为true。
        baseLayerPicker:{Boolean) 是否显示底图切换控件,默认为true.
        fullscreenButton:[Boolean) 是否显示全屏控制器,默认为true。
        geocoder:{Boolean} 是否显示地理编码控件,默认为true。
        homeButton:{Boolean} 是否显示回到初始位置控制器,默认为true。
        infoBox:[Boolean] 是否显示信息框,默认为true。
        sceneModePicker:Boolean} 是否显示场景模式切换控制器,默认为true.
        selectionIndicator:Boolean} 是否显示选择指示器,默认为true。
        timeline:Boolean} 是否显示时间轴,默认为true。
        navigationHelpButton:Boolean) 是否显示导航帮助控制器,默认为true。
        navigationInstructionslnitiallyVisible: Boolean) 是否显示导航提示,默认为true.
        scene3DOnly:{Boolean} 是否只能以3D场景模式运行,默认为false,即支持2D和3D两种场景模式。
        sceneMode:{SceneMode) 指定Cesium初始场景模式,可选值为SceneMode.SCENE2D、SceneMode.SCENE3D.
        SceneMode.COLUMBUS VIEW,默认为SceneMode.SCENE3D.
        mapProjection:{MapProjection} 指定Cesium的地图投影模式,默认为MapProjection.WebMercator。
        globe:{Globe} 指定Cesium的地球模型实例,默认为Globe。
        imageryProvider:{imageryProvider} 指定Cesium的底图图层实例,默认为lmageryProvider.
        terrainProvider: {TerrainProvider} 指定Cesium的地形图层实例,默认为TerrainProvider.
      */
 const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {//选项参考http://mars3d.cn/api/cesium/Viewer.html#.ConstructorOptions
      animation: false,//动画小组件:左下角仪表
      timeline: false,//下方的时间线
      // sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,//地球模式,3d、2d、哥伦布2.5d,具体选项可参考http://mars3d.cn/api/cesium/global.html#SceneMode
      baseLayerPicker: false,//右上角的地图底图选择器
      geocoder: false,//地名地址搜索框
      fullscreenButton: false,//全屏按钮
      homeButton: false,//回到球体最初视角
      sceneModePicker: false,//二三维切换
      navigationHelpButton: false,//帮助按钮
      creditContainer: document.createElement("div"),//最下方的版权信息,用一个空div代替
      vrButton: false,//分屏模式按钮(双屏对比)
      scene3DOnly: false,// 在Cesium的Viewer接口中,scene3DOnly属性是一个布尔值,用于设置是否只显示3D场景。
      // 如果将该属性设置为true,则Cesium会仅显示3D场景,而不会显示2D平面投影的地安
      // 具体来说,如果将scene3DOnly属性设置为true,则Cesium只会显示三维地球场景,
      // 不会显示类似于Google地图的2D平面投影。这样可以节省计算资源,提高地图渲染的性能。
      // 同时,由于不显示2D平面投影,因此在3D场景中显示的地球数据和模型会更加真实和准确。
      // 需要注意的是,如果将scene3DOnly属性设置为true,则在Viewer中使用一些与2D平面投影相关的功能将无法使用,
      // 例如在2D平面上添加标注或者显示像素坐标。因此,在需要使用这些功能的场景下,
      // 需要将该属性设置为false,以便同时显示2D和3D场景。
      infoBox:false,//infoBox属性是用于控制信息框的显示和隐藏的属性,就是双击某个地区后显示其信息
      skyBox: new Cesium.SkyBox({//天空盒
        sources: {
          positiveX: './data/tycho2t3_80_px.jpg',
          negativeX: './data/tycho2t3_80_mx.jpg',
          positiveY: './data/tycho2t3_80_py.jpg',
          negativeY: './data/tycho2t3_80_my.jpg',
          positiveZ: './data/tycho2t3_80_pz.jpg',
          negativeZ: './data/tycho2t3_80_mz.jpg',
        }
      }),
      /*
      Cesium中的SkyBox (天空盒)是用于星现场景背景的3D立方体贴图。它通常用于模拟天空,
      包括天空的颜色、云朵、星空等。SkyBox是由6个面组成的立方体贴图,每个面都是个矩形纹理映射。
      在Cesium中,SkyBox可以通过设置场景的skyBox属性来使用。具体的参数如下:
      sources: 指定天空盒的六个面的纹理图像的URL数组,通常包括前、后、左、右、上下六gativeZ :图像。例如:
      **/
    });

2、Scene场景类
(1)、它是Cesium中Scene是非常重要的类,是所有3D图形对象的容器,是在viewer内部隐式创建的,也可以对基础地理环境进行设置,包括地图、地形等。
(2)、需要注意的是在viewer中设置图层等价于在scene中设置图层,代码验证:console.log(viewer.imgerLaters == viewer.scene.imgerLaters);//输出true
(3)、还可以对场景数据进行设置,cesium底层空间数据绘制方法是依赖Primitive API,可以根据图形学原理绘制灵活的高级图形
(4)、还可以对场景进行交互,比如鼠标事件对场景的控制、相机事件,可以修改场景环境,比如修改地图显示隐藏、光照强度、图层样式、地形数据、在图层上绘制点、线、面、体

viewer.scene.camera.setView({//通过scene控制相机对视口进行切换
  destination: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 1000)//设置经纬度和高度
})

3、Entity实体类
(1)、Entity是由Primitive封装而来的,Entity不是属于Scene,它封装程度高、构造简单、使用便捷使得开发者专注于数据的呈现,不必关心底层的可视化机制,还提供了用于构建复杂的、时间动态可视化的结构,与静态数据自然的结合在一起;EntityAPI 能够提供灵活的、高性能的可视化,同时提供一致性的、易于学习、易于使用的接口。
(2)、代码示例了解Entity

const entity = viewer.entities.add({//创建一个Entity点对象
position: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 400),//设置经纬度和高度
point: {
  pixelSize: 100,//设置点大小
  color: new Cesium.Color(0, 1, 0, 1)//设定点颜色      
}
})
viewer.trackedEntity = entity;//将摄像头设置在圆点处(track跟踪)

4、DataSourceCollection数据源集合类
(1)、它是cesium中加载矢量数据的最主要方式之一,最大的特点是支持矢量数据集和外部文件的调用,主要有三种调用方法,分别为CzmlDataSource加载Czml、KmlDataSource加载Kml、GeoJsonDataSource加载GeoJson格式数据,在gis开发中加载矢量数据是必不可少的功能。将矢量数据转化为以上任何一种方式,便可以在cesium中实现矢量数据的加载和存取。
(2)、示例演示:geojson数据获取:geojson数据获取

//DataSource用于将任意数据转换为EntityCollection,这里调用的是geojson数据
viewer.dataSources.add(
  Cesium.GeoJsonDataSource.load(
    "./data/hubei.json"
  )
)

2.四大类的完整演示代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第一课</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表
    });
    viewer.scene.globe.show = true//地图显示,默认是true
    console.log(viewer.imgerLaters == viewer.scene.imgerLaters);//输出true

    viewer.scene.camera.setView({//通过scene控制相机对视口进行切换
      destination: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000000)//设置经纬度和高度
    })

    const entity = viewer.entities.add({//创建一个Entity点对象
      position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000000),//设置经纬度和高度
      point: {
        pixelSize: 100,//设置点大小
        color: new Cesium.Color(0, 1, 0, 1)//设定点颜色      
      }
    })
    viewer.trackedEntity = entity;//将摄像头设置在圆点处(track跟踪)

    //DataSource用于将任意数据转换为EntityCollection,这里调用的是geojson数据
    viewer.dataSources.add(
      Cesium.GeoJsonDataSource.load(
        "./data/hubei.json"
      )
    )
  </script>
</body>

</html>

代码运行效果如下:
在这里插入图片描述

四、cesium的坐标与转换

1、cesium具用真实地理坐标的三维球体,用户通过二维屏幕与cesium进行操作,我们要把三维模型绘制到三维球体上,就需要在地理坐标和屏幕坐标之间做转换,下面介绍下cesium的主要坐标系:
(1)、WGS84经纬度坐标系 (没有实际的对象)。
(2)、WGS84弧度坐标系 (Cartographic)
(3)、笛卡尔空间直角坐标系 (Cartesian3)
(4)、平面坐标系,也叫屏幕坐标系 (Cartesian2)
(5)、4D笛卡尔坐标系 (Cartesian4)

2、坐标系详解:
(1)、首先前两个都是WGS84坐标系,WGS84:World Geodetic System 1984,是为GPS全球定位系统使用而建立的坐标系统。坐标原点为地球质心
在这里插入图片描述
在cesium中没有实际的对象来描述WGS84经纬度坐标系的,都是以弧度的方式进行运用的,也就是当前的这个对象类new Cesium.Cartographic(longitude, latitude, height),这个类有三个构造参数,经度、纬度、高度,这个函数内部的弧度计算采用:弧度=Π/180*经纬度
(2)、笛卡尔空间直角坐标系,以空间中O点为原点,建立三条两两垂直的数轴X、Y、Z
在这里插入图片描述
笛卡尔直角坐标系的原点就是椭球的中心,我们通过new Cesium.Cartesian3(x,y,z)来构建Cartesian3的类对象,三个参数xyz分别代表三根数轴上的值
(3)、平面坐标系,也叫屏幕坐标系。它是一个二维的笛卡尔坐标系。屏幕左上角为原点水平方向为x,垂直方向为y,向下为正。
在这里插入图片描述
构造对象new Cesium.Cartesian2(x,y)

3、坐标转换
(1)、经纬度和WGS84弧度的相互转换:弧度= Π/180*经纬度角度经纬度角度=180/Π*弧度。在cesium中可以用两种方式实现转换:

  • 构造函数法生成WGS84弧度坐标对象new Cesium.Cartographic(longitude弧度,latitude弧度,height米)
  • 静态函数法构建WGS84弧度坐标对象var cartographic= Cesium.Cartographic.fromDegrees(longitude经度,latitude纬度,height米)var cartographic= Cesium.Cartographic.fromRadians(longitude弧度, latitude弧度,height米)

(2)、笛卡尔空间直角坐标系转换为WGS84坐标系:在cesium中可以使用var cartographic= Cesium.Cartographic.fromCartesian(cartesian3)var cartographic= Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian3)、或者批量传入数组转 var cartographics=Cesium.Elipsoid.WGS84.cartesianArrayToCartographicArray([cartesian1,cartesian2,cartesian3])
(3)、平面坐标系转笛卡尔空间直角坐标系、屏幕坐标转WGS84坐标,这里的场景坐标是包含了地形、倾斜、模型的坐标var cartesian3= viewer.scene.pickPosition(Cartesian2)
(4)、屏幕坐标转地表坐标var cartesian3=viewer.scene.globe.pick(viewer.camera.getPickRay(Cartesian2),viewer.scene);这里是地球表面的WGS84坐标,包含地形,不包括模型、倾斜摄影表面
(5)、平面坐标转椭球面坐标var cartesian3=viewer.scene.camera.pickEllipsoid(Cartesian2),这里的椭球面坐标是参考椭球的WGS84坐标,不包含地形、模型、倾斜摄影表面
(6)、笛卡尔空间直角坐标系转平面坐标系var cartesian2=Cesium.SceneTransforms.wgs84ToWindowCoordinates(viewer.scene,cartesian3),这个静态函数需要传入场景与笛卡尔空间坐标2个参数,执行成功后返回平面坐标的数值

五、相机系统介绍

cesium中相机坐标系是右手坐标系!
在这里插入图片描述

官方文档api参考:Cesium.Camera
在二维地图中,如果我们要查看某个地方,只需要定位经纬度即可,但是三维地图不仅仅需要经纬度确定视点位置,还需要视线方向,例如观察某物体找到了物体位置,但是视线方向反了,那么在视域中是看不到物体的,所以相机的存在就是为了控制场景中的视域。
1、相机参数设置
(1)、设置地球初始化后相机视角远近:Cesium.Camera.DEFAULT_VIEW_FACTOR = 10,效果如下:
在这里插入图片描述
(2)、设置相机视角默认偏移:Cesium.Camera.DEFAULT_OFFSET = new Cesium.HeadingPitchRange(-0.2, -0.5, 100)
(3)、设置相机默认视角经纬度:Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(117.940573, 36.808406, 118.313421, 36.468825);
注意,上面三个参数一定要在初始化视图之前进行定义
(4)、限制地球围着x轴旋转:viewer.camera.constrainedAxis = Cesium.Cartesian3.UNIT_X;

2、相机系统方法介绍
(1)、setView():setView通过定义相机飞行目的地的三维坐标和视线方向,将视角直接切换到所设定的视域范围内,设置一个常量,用于存储飞行的目的地的坐标
(2)、flyTo():视角跳转,过渡跳转,有过程
(3)、lookAt():lookAt将视角设置到跳转的设置的目的地上,但是用鼠标任意旋转视角方向,是不会改变其位置的,一般用于锁定某个场景的视角
(4)、viewBoundingSphere() 视角切换效果和setView一样,没有飞行过渡,直接切换,但是它可以给一个指定的目标点,从多个角度更好的观测物体样式和状态,也就是说相机围绕目标点旋转而不是地球,比如加载glb

3、相机系统示例代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第一课,基本使用</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {//选项参考http://mars3d.cn/api/cesium/Viewer.html#.ConstructorOptions
      animation: false,//动画小组件:左下角仪表
      timeline: false,//下方的时间线
      // sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,//地球模式,3d、2d、哥伦布2.5d,具体选项可参考http://mars3d.cn/api/cesium/global.html#SceneMode
      baseLayerPicker: false,//右上角的地图底图选择器
      geocoder: false,//地名地址搜索框
      fullscreenButton: false,//全屏按钮
      homeButton: false,//回到球体最初视角
      sceneModePicker: false,//二三维切换
      navigationHelpButton: false,//帮助按钮
      creditContainer: document.createElement("div"),//最下方的版权信息,用一个空div代替
      vrButton: false,//分屏模式按钮(双屏对比)
      /*
        options:{Object) 可选参数,包含以下属性:
        animation:[Boolean} 是否显示动画控制器,默认为true。
        baseLayerPicker:{Boolean) 是否显示底图切换控件,默认为true.
        fullscreenButton:[Boolean) 是否显示全屏控制器,默认为true。
        geocoder:{Boolean} 是否显示地理编码控件,默认为true。
        homeButton:{Boolean} 是否显示回到初始位置控制器,默认为true。
        infoBox:[Boolean] 是否显示信息框,默认为true。
        sceneModePicker:Boolean} 是否显示场景模式切换控制器,默认为true.
        selectionIndicator:Boolean} 是否显示选择指示器,默认为true。
        timeline:Boolean} 是否显示时间轴,默认为true。
        navigationHelpButton:Boolean) 是否显示导航帮助控制器,默认为true。
        navigationInstructionslnitiallyVisible: Boolean) 是否显示导航提示,默认为true.
        scene3DOnly:{Boolean} 是否只能以3D场景模式运行,默认为false,即支持2D和3D两种场景模式。
        sceneMode:{SceneMode) 指定Cesium初始场景模式,可选值为SceneMode.SCENE2D、SceneMode.SCENE3D.
        SceneMode.COLUMBUS VIEW,默认为SceneMode.SCENE3D.
        mapProjection:{MapProjection} 指定Cesium的地图投影模式,默认为MapProjection.WebMercator。
        globe:{Globe} 指定Cesium的地球模型实例,默认为Globe。
        imageryProvider:{imageryProvider} 指定Cesium的底图图层实例,默认为lmageryProvider.
        terrainProvider: {TerrainProvider} 指定Cesium的地形图层实例,默认为TerrainProvider.
      */
      scene3DOnly: false,// 在Cesium的Viewer接口中,scene3DOnly属性是一个布尔值,用于设置是否只显示3D场景。
      // 如果将该属性设置为true,则Cesium会仅显示3D场景,而不会显示2D平面投影的地安
      // 具体来说,如果将scene3DOnly属性设置为true,则Cesium只会显示三维地球场景,
      // 不会显示类似于Google地图的2D平面投影。这样可以节省计算资源,提高地图渲染的性能。
      // 同时,由于不显示2D平面投影,因此在3D场景中显示的地球数据和模型会更加真实和准确。
      // 需要注意的是,如果将scene3DOnly属性设置为true,则在Viewer中使用一些与2D平面投影相关的功能将无法使用,
      // 例如在2D平面上添加标注或者显示像素坐标。因此,在需要使用这些功能的场景下,
      // 需要将该属性设置为false,以便同时显示2D和3D场景。
      infoBox:false,//infoBox属性是用于控制信息框的显示和隐藏的属性,就是双击某个地区后显示其信息
      skyBox: new Cesium.SkyBox({//天空盒
        sources: {
          positiveX: './data/tycho2t3_80_px.jpg',
          negativeX: './data/tycho2t3_80_mx.jpg',
          positiveY: './data/tycho2t3_80_py.jpg',
          negativeY: './data/tycho2t3_80_my.jpg',
          positiveZ: './data/tycho2t3_80_pz.jpg',
          negativeZ: './data/tycho2t3_80_mz.jpg',
        }
      }),
      /*
      Cesium中的SkyBox (天空盒)是用于星现场景背景的3D立方体贴图。它通常用于模拟天空,
      包括天空的颜色、云朵、星空等。SkyBox是由6个面组成的立方体贴图,每个面都是个矩形纹理映射。
      在Cesium中,SkyBox可以通过设置场景的skyBox属性来使用。具体的参数如下:
      sources: 指定天空盒的六个面的纹理图像的URL数组,通常包括前、后、左、右、上下六个方向的图像。例如:
          positiveX: 表示3D立方体的x轴正方向面
          negativeX:表示3D立方体的x轴负方向面
          positiveY: 表示3D立方体的y轴正方向面
          negativeY: 表示3D立方体的y轴负方向面
          positivez: 表示3D立方体的z轴正方向面
          negativez: 表示3D立方体的z轴负方向面
      其中,positive和negative表示的是正方向和负方向,即对应着3D立方体的两个相对面。
      例如,positiveX表示的是3D立方体在x轴正方向上的面,而negativeX表示的是3D立方体在x轴负方向上的面。
      这样命名的好处是可以避免混淆,同时也符合了OpenGL中的约定。在OpenGL中,通常使用正方向和负方向来表示两个相对方向的面。
      **/
    });
    viewer.scene.globe.show = true//地图显示,默认是true
    console.log(viewer.imgerLaters == viewer.scene.imgerLaters);//输出true

    // //设置视角
    // viewer.scene.camera.setView({//通过scene控制相机对视口进行切换
    //   destination: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000000)//设置经纬度和高度
    // })

    //地球加载一个圆点
    // const entity = viewer.entities.add({//创建一个Entity点对象
    //   position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000000),//设置经纬度和高度
    //   point: {
    //     pixelSize: 100,//设置点大小
    //     color: new Cesium.Color(0, 1, 0, 1)//设定点颜色      
    //   }
    // })
    // viewer.trackedEntity = entity;//将摄像头设置在圆点处(track跟踪)

    //在地球上加载geojson矢量数据
    //DataSource用于将任意数据转换为EntityCollection,这里调用的是geojson数据
    viewer.dataSources.add(
      Cesium.GeoJsonDataSource.load(
        "./data/hubei.json"
      )
    )

    //----------------------------相机系统效果演示,---------------------------
    // const position=Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000000) //定义常量,用于存储飞行目的地的坐标
    //setView 直接跳转,没有过程
    // viewer.camera.setView({//通过相机系统设置视角
    //   destination:position,//设置相机目的地
    //   orientation:{//设置相机视口的方向
    //     heading:Cesium.Math.toRadians(0),//控制视口的水平旋转,也就是沿着y轴旋转,当数值为0时,代表正北方向
    //     pitch:Cesium.Math.toRadians(-90),//控制视口的上下旋转,即沿x轴进行旋转,当数值为-90,代表俯视地面
    //     roll:0,//控制视口的反转角度,即沿着z轴进行旋转,数值为0表示不翻转
    //   },
    // })
    
    // flyTo 过渡跳转,有过程
    // viewer.camera.flyTo({
    //   destination: position,//设置相机目的地
    //   orientation: {//设置相机视口的方向
    //     heading: Cesium.Math.toRadians(0),//控制视口的水平旋转,也就是沿着y轴旋转,当数值为0时,代表正北方向
    //     pitch: Cesium.Math.toRadians(-90),//控制视口的上下旋转,即沿x轴进行旋转,当数值为-90,代表俯视地面
    //     roll: 0,//控制视口的反转角度,即沿着z轴进行旋转,数值为0表示不翻转
    //   },
    //   duration:5//跳转持续时长5秒
    // })

    //lookAt 将视角设置到跳转的设置的目的地上,但是用鼠标任意旋转视角方向,是不会改变其位置的,一般用于锁定某个场景的视角
    // const center = Cesium.Cartesian3.fromDegrees(113.37377, 31.717497) //lookAt所需的视角点只设置经纬度即可,不需要高度
    // const heading=Cesium.Math.toRadians(50)//设置一个水平旋转视口方向的角度
    // const pitch=Cesium.Math.toRadians(-90)//设置垂直旋转视口的角度
    // const range=1000000//设置相机距离目标点的高度
    // viewer.camera.lookAt(center,new Cesium.HeadingPitchRange(heading,pitch,range))//参数一:目标点位置,参数二:用Cesium的HeadingPitchRange方法设定相机方向和高度
    
    //viewBoundingSphere 视角切换效果和setView一样,没有飞行过渡,直接切换,但是它可以给一个指定的目标点,从多个角度更好的观测物体样式和状态,也就是说相机围绕目标点旋转而不是地球
    const position = Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 10000) //定义常量,用于存储飞行目的地的坐标
    const orientation= Cesium.Transforms.headingPitchRollQuaternion(position,new Cesium.HeadingPitchRoll(-90,0,0))//定义模型朝向变量:参数一:位置信息,参数二:旋转角度
    const entity=viewer.entities.add({//使用Entities加载一个glb模型用于观察
      position:position,//模型位置
      orientation:orientation,//模型朝向
      /*model里面的参数
        uri: 表示3D模型的URI,可以是相对路径或绝对路径,也可以是data URI格式。必须指定个URI才能显示3D模型
        scale: 表示缩放比例。默认值为1,表示原始大小,可以通过指定其他值来调整模型的大小
        minimumPixelSize: 表示模型的最小大小(以像素为单位)。如果模型在地球上的投影比此值小,则模型将不可见。默认值为0。
        maximumScale: 表示模型缩放比例的最大值,如果模型的缩放比例大于此值,则模型将不可见。默认值为无穷大。
        incrementallyLoadTextures: 表示是否逐步加载模型纹理。默认值为true,表示模型的纹理将在后台异步加载,直到完全加载为止。如果设置为false,则模型的纹理将在模型加载时同步加载,这可能会导致性能问题。
        runAnimations: 表示是否播放模型的动画。默认值为true,表示模型动画将播放。如果设置为false,则模型将显示静态姿势
        clampAnimations:表示是否在地球上固定模型动画,默认值为false,表示模型动画将在地球上自由移动。如果设置为true,则模型动画将保持相对于地球的固定位置。
        nodeTransformations:表示对模型节点的变换。默认值为什,表示没有节点变换。该参数是一个对象,对象的键是节点名称,值是一个包含变换信息的对象,包括translate.rotate、scale和matrix四个属性。每个属性都是一个3x3或4x4的矩阵,用于描述节点的变换信息。
        articulations: 表示模型的关节,用于模拟模型的骨骼动画。默认值为1,表示没有关节该参数是一个数组,数组中的每个元素都是一个包含关节信息的对象,包括名称、父级名称、最大角度、最小角度和当前角度等属性。
        show: 表示是否显示模型。默认值为true,表示模型将显示。如果设置为false,则模型将不可见。
        distanceDisplayCondition: 表示模型的距离显示条件。默认值为undefined,表示没有距离显示条件。该参数是一个Cesium.DistanceDisplayCondition对象,它有两个属性:near和far,表示距离地球的近和远限制。
        silhouetteColor: 表示模型的描边颜色。默认值为Cesium.Color.RED,表示红色描边。该参数可以是Cesium.Color对象、Cesium.Property对象或表示颜色的字符串。
      **/
      model:{//模型信息
        uri:"./data/car.glb",//模型路径
        minimumPixelSize:100,//模型最小的大小
        maxmumScale:10000,//模型缩放最大比例
        show:true,//模型的显示
        clippingPlanes: new Cesium.ClippingPlaneCollection({//clippingPlanes 对模型进行裁剪
          planes:[new Cesium.ClippingPlane(new Cesium.Cartesian3(0, 1, 0) ,0)],//沿着y轴裁剪
          edgeWidth: 1,
          edgeColor: Cesium.Color.GREEN,//裁剪线为绿色
          enabled: true,
          edgeMaterial: new Cesium.PolylineOutlineMaterialProperty({
            color: Cesium.Color.RED, 
            outlineWidth: 1, 
            outlineColor: Cesium.Color.BLUE
          })
        }),
        //下面的参数会给glb模型加一层膜
        color: Cesium.Color.fromCssColorString('#FF6C37').withAlpha(1),//fromCssColorString:设置膜的颜色,withAlpha:设置透明度,glb模型的透明度也会被影响
        colorBlendMode: Cesium.ColorBlendMode.MIX,//设置模式:这里选择的是混合模式,有下面三个值:HIGHLIGHT、MIX、REPLACE,可以试下三个值分别的效果
        colorBlendAmount: 0.5,//膜和模型混入的程度,只有值为MIX的时候才会生效
        silhouetteColor: Cesium.Color.fromCssColorString('#00FFF0').withAlpha(1),//设置膜边框颜色和透明度
        silhouetteSize: 5,//设置膜边框大小
      }
    })
    viewer.camera.viewBoundingSphere(new Cesium.BoundingSphere(position,20),new Cesium.HeadingPitchRange(0,0,0)) //绑定实体,参数一:设置视点位置BoundingSphere,参数二,相机的朝向HeadingPitchRange。其中new Cesium.BoundingSphere参数一:位置,参数二,和物体的距离
    //------------------------------相机系统效果演示结束------------------------------
  </script>
</body>

</html>

4、相机键盘控制

Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
    viewer = new Cesium.Viewer("cesium-container", {
      imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
      }),
      sceneMode: Cesium.SceneMode.SCENE3D,
      vrButton: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
      timeline: false,
      fullscreenButton: false,
      homeButton: false,
      creditContainer: document.createElement('div'),
      infoBox: true,
      navigationHelpButton: false,
      sceneModePicker: false,
      scene3DOnly: true,
      shouldAnimate: true,//开启动画
    });
 const scene = viewer.scene;
      const ellipsoid = scene.globe.ellipsoid;
      const canvas = viewer.canvas;
      canvas.setAttribute("tabindex", "0"); //使canvas获得键盘焦点
      canvas.onclick = function () {
        canvas.focus();
      };

      const flags = {
        moveForward: false,
        moveBackward: false,
        moveUp: false,
        moveDown: false,
        moveLeft: false,
        moveRight: false,
        lookUp: false,
        lookDown: false,
        lookLeft: false,
        lookRight: false
      };
      //W:前进  S:后退  A:左移  D:右移  
      //上:向上翻转  下:向下翻转  右:向右转动  左:向左转动
      //Q:上升  E:下降
      document.addEventListener(
        "keydown",
        function (e) {
          switch (e.keyCode) {
            case "W".charCodeAt(0):
              flags.moveForward = true
              return
            case "S".charCodeAt(0):
              flags.moveBackward = true
              return
            case "Q".charCodeAt(0):
              flags.moveUp = true
              return
            case "E".charCodeAt(0):
              flags.moveDown = true
              return
            case "D".charCodeAt(0):
              flags.moveRight = true
              return
            case "A".charCodeAt(0):
              flags.moveLeft = true
              return
            case 38:
              flags.lookUp = true
              return
            case 40:
              flags.lookDown = true
              return
            case 37:
              flags.lookLeft = true
              return
            case 39:
              flags.lookRight = true
              return
          }
        },
        false
      );

      document.addEventListener(
        "keyup",
        function (e) {
          switch (e.keyCode) {
            case "W".charCodeAt(0):
              flags.moveForward = false
              return
            case "S".charCodeAt(0):
              flags.moveBackward = false
              return
            case "Q".charCodeAt(0):
              flags.moveUp = false
              return
            case "E".charCodeAt(0):
              flags.moveDown = false
              return
            case "D".charCodeAt(0):
              flags.moveRight = false
              return
            case "A".charCodeAt(0):
              flags.moveLeft = false
              return
            case 38:
              flags.lookUp = false
              return
            case 40:
              flags.lookDown = false
              return
            case 37:
              flags.lookLeft = false
              return
            case 39:
              flags.lookRight = false
              return
          }
        },
        false
      );

      viewer.clock.onTick.addEventListener(function () {
        const camera = viewer.camera;

        const cameraHeight = ellipsoid.cartesianToCartographic(
          camera.position
        ).height;
        const moveRate = cameraHeight / 100.0;

        if (flags.moveForward) {
          camera.moveForward(moveRate);
        }
        if (flags.moveBackward) {
          camera.moveBackward(moveRate);
        }
        if (flags.moveUp) {
          camera.moveUp(moveRate);
        }
        if (flags.moveDown) {
          camera.moveDown(moveRate);
        }
        if (flags.moveLeft) {
          camera.moveLeft(moveRate);
        }
        if (flags.moveRight) {
          camera.moveRight(moveRate);
        }
        if (flags.lookUp) {
          camera.lookUp(0.003);
        }
        if (flags.lookDown) {
          camera.lookDown(0.003);
        }
        if (flags.lookLeft) {
          camera.lookLeft(0.003);
        }
        if (flags.lookRight) {
          camera.lookRight(0.003);
        }
      });

六、地图、地形的加载与剖面分析、地球天气特效

1、地图和地形的加载:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第二课,加载自定义地图</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    //----------------------- 地图、地形的添加--------------------------
    //----------------------两种加载底图服务的方式,第一种就是下面的直接用“imageryProvider”属性值去指定,
    //还有一种是通过viewer.imageryLayers.addImageryProvider(imageryProvider)去调用方法添加
    let esri = new Cesium.ArcGisMapServerImageryProvider({//这里加载的是arcmapserver的地图
      /** ArcGisMapServerImageryProvider的参数详解
       * 
          url: 必需,ArcGIS Server发布的影像服务的URL地址。
          token: 可选,需要进行身份验证时使用的ArcGIS Server令牌。
          layers: 可选,指定要显示的图层,可以是字符串表示单个图层,也可以是数组表示多个图层,例如:'0,1,2'或[0,1,2]。
          enablePickFeatures: 可选,布尔型参数,指示是否启用选择功能,默认为false。
          rectangle: 可选,指定加载影像的矩形区域,可以是Rectangle类型的对象或字符串形式的'west,south,east,north'。
          tilingScheme: 可选,指定影像瓦片的切图方案,可以是WebMercatorTilingScheme或GeographicTilingScheme类型的对象。
          maximumLevel: 可选,指定最大的图层级别(即最大缩放级别),默认为影像地图服务中定义的最大级别。
          show: 可选,布尔型参数,指示是否显示该影像图层,默认为true。
       * 
      */
      url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer",
      layers: '4',
      maximumLevel: 18,
      enablePickFeatures: true//必须开启能够拾取要素
    })//用一个变量存储新地图的信息,这里我们使用的是arcgis地图服务
    let viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表,
      baseLayerPicker: false,//默认的地图按钮设置为隐藏
      // imageryProvider: esri,//viewer内的imageryProvider属性用于设置地图,加载影像底图
      terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
        url:Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
        requestVertexNormals:true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
        requestWaterMask:true,//可以增加水面特效
      }),
    });
    viewer.scene.globe.show = true//地图显示,默认是true
    viewer.scene.globe.depthTestAgainstTerrain = true;//一定要设置这个属性,否则左键点击拾取不到movement.position平面坐标
    
    // var layer=viewer.imageryLayers.addImageryProvider(
    //   new Cesium.IonImageryProvider({assetId:3812})
    // )//测试夜晚地图
    viewer.imageryLayers.addImageryProvider(esri)
      viewer.camera.setView({
        destination: window.Cesium.Cartesian3.fromDegrees(117.16978315861392, 36.93205961529747, 2000)
      });
      viewer.screenSpaceEventHandler.setInputAction(async function onLeftClick(
          movement
        ) {
          // 获取鼠标点击位置的屏幕坐标
          var position = movement.position;
          console.log(viewer.scene.pickPosition(position));
          let cartographic = window.Cesium.Cartographic.fromCartesian(
            viewer.scene.pickPosition(position)
          );
          // 使用pickFeatures方法获取选择的要素信息
          var promise = esri.pickFeatures(
            position.x,
            position.y,
            18,
            cartographic.longitude,
            cartographic.latitude
          );
          let features = await promise;
          console.log(features);
        },
        window.Cesium.ScreenSpaceEventType.LEFT_CLICK);
//----------------------- 地图、地形的添加结束--------------------------

//----------------------- 建筑物的添加使用--------------------------
    // let tileset=viewer.scene.primitives.add(//Viewer中的scene是Cesium虚拟场景中所有3D图形对象和状态的容器,它的primitives属性用于获取大量的基元集合,add方法用于加载数据
    //   new Cesium.Cesium3DTileset({//这里使用的CesiumBDTileset方法,是Cesium用于传输海量异构3D地理空间数据集
    //     url:Cesium.IonResource.fromAssetId(96188)//添加建筑物
    //   })
    // )
    // viewer.camera.setView({//添加相机信息
    //   destination:Cesium.Cartesian3.fromDegrees(121.49,31.23,3000),
    //   orientation:{
    //     heading:0,
    //     pitch:-90,
    //     roll:0
    //   }
    // })

    // //模型加样式,这里先添加一个单一样式
    // tileset.style= new Cesium.Cesium3DTileStyle({//需要使用Cesium的Cesium3DTileStyle方法
    //   color:"color('pink',0.5)",//color属性用于设定模型的颜色和透明度,可以设置颜色根据楼层高度进行变化
    //   show:true
    // })//因为3d模型的样式过于单调,这里我们对一个模型数据样式进行修改,就是修改它的style

    // //模型加样式,根据高度添加不同样式
    // tileset.style = new Cesium.Cesium3DTileStyle({//需要使用Cesium的Cesium3DTileStyle方法
    //   color: {//给不同的高度的建筑物体设置不同的颜色
    //     conditions:[
    //       ["${Height} >= 300", "rgba(45,0,75,0.5)"],
    //       ["${Height} >= 200", "rgb(102,0,75)"],
    //       ["${Height} >= 100", "rgb(170,0,75)"],
    //       ["${Height} >= 50", "rgb(224,0,75)"],
    //       ["${Height} >= 20", "rgb(1,0,75)"],
    //       ["${Height} >= 10", "rgb(100,0,75)"],
    //       ["${Height} >= 5", "rgb(200,0,75)"],
    //       ["true", "rgb(127,59,8)"]
    //     ]
    //   },//color属性用于设定模型的颜色和透明度,可以设置颜色根据楼层高度进行变化
    //   show: '${Height} >= 0'//show属性设置的是高度大于等于0的建筑物才可以显示出来
    // })//因为3d模型的样式过于单调,这里我们对一个模型数据样式进行修改,就是修改它的style
//----------------------- 建筑物的添加使用结束--------------------------
  </script>
</body>

</html>

代码执行效果:
3d地形

水纹波动

2、地形剖面分析

需求:点击两个位置,分析出两个点位之间的地形高度。代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十三课,地形剖面分析</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <script src="../libs/Echarts/echarts.min.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
    #echarts-container {
      position: absolute;
      z-index: 10;
      width: 800px;
      height: 250px;
      background-color: aquamarine;
    }
  </style>
</head>

<body>
  <div id="cesium-container"><div id="echarts-container"></div></div>
  <script>
    //---------------------地形剖面分析代码演示--------------------
   Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
    viewer = new Cesium.Viewer("cesium-container", {
      imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
      }),
      sceneMode: Cesium.SceneMode.SCENE3D,
      vrButton: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
      timeline: false,
      fullscreenButton: false,
      homeButton: false,
      creditContainer: document.createElement('div'),
      infoBox: false,
      navigationHelpButton: false,
      sceneModePicker: false,
      scene3DOnly: true,
      terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
        url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
        requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
        requestWaterMask: true,//可以增加水面特效
      }),
    });
    viewer.scene.globe.depthTestAgainstTerrain = true;

    let startAndEndPoints = [], interNumber = 5000
    const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas);
    handler.setInputAction(function (movement) {
      startAndEndPoints.push(window.viewer.scene.pickPosition(movement.position))
      if (startAndEndPoints.length == 2) {
        var profile = {
          arrHB: [],
          arrPoint: [],
          arrLX: [],
          ponits: [],
          distance: 0
        }
        for (let i = 0; i < startAndEndPoints.length - 1; i++) {
          var point = equidistantInterpolation(startAndEndPoints[i], startAndEndPoints[i + 1])
          profile.arrPoint.push(point)
        }
        for (var j = 0; j < profile.arrPoint.length; j++) {
          for (var m = 0; m < profile.arrPoint[j].length; m++) {
            profile.ponits.push(profile.arrPoint[j][m])
          }
        }
        profile.arrLX.push(0)
        for (var k = 1; k < profile.ponits.length - 1; k++) {
          profile.arrHB.push(profile.ponits[k].hit)
          var disc = distance(profile.ponits[k - 1], profile.ponits[k])
          profile.distance = profile.distance + disc
          profile.arrLX.push(profile.arrLX[k - 1] + disc)
        }
        console.log(profile)
        initChart(profile)
        startAndEndPoints = []
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    function equidistantInterpolation(pointA, pointB) {
      // 插值处理
      var tpLength = getArcLength(pointA, pointB)
      // 插值间距按照输入的插值来进行
      var interpolationNumber = parseInt(tpLength / interNumber)
      var pointAToW84 = worldCoordinateToLonAndLat(pointA)
      var pointBToW84 = worldCoordinateToLonAndLat(pointB)
      var lat1 = Cesium.Math.toRadians(pointAToW84.lat)
      var lon1 = Cesium.Math.toRadians(pointAToW84.lon)
      var lat2 = Cesium.Math.toRadians(pointBToW84.lat)
      var lon2 = Cesium.Math.toRadians(pointBToW84.lon)
      var spaceList = []
      // 插值坐标数组
      for (var i = 0; i < interpolationNumber + 1; i++) {
        var lonRadians = Cesium.Math.lerp(lon1, lon2, i / interpolationNumber)
        var latRadians = Cesium.Math.lerp(lat1, lat2, i / interpolationNumber)
        var lon = Cesium.Math.toDegrees(lonRadians)
        var lat = Cesium.Math.toDegrees(latRadians)
        // 计算插值点坐标
        var point = {
          lon: Number(lon),
          lat: Number(lat)
        }
        point = getGeoCoordinate(point.lat, point.lon)
        point.len = 0
        spaceList.push(point)
      }
      return spaceList
    }

    function getArcLength(pointA, pointB) {
      var lat1 = pointA.x
      var lon1 = pointA.y
      var lat2 = pointB.x
      var lon2 = pointB.y

      var EARTH_RADIUS = 6378137.0
      var PI = Math.PI

      // 弧度
      function getRad(d) {
        return d * PI / 180.0
      }

      var f = getRad((lat1 + lat2) / 2)
      var g = getRad((lat1 - lat2) / 2)
      var l = getRad((lon1 - lon2) / 2)

      var sg = Math.sin(g)
      var sl = Math.sin(l)
      var sf = Math.sin(f)

      var s, c, w, r, d, h1, h2
      // 地球半径
      var a = EARTH_RADIUS
      // 地球扁率
      var fl = 1 / 298.257

      sg = sg * sg
      sl = sl * sl
      sf = sf * sf

      s = sg * (1 - sl) + (1 - sf) * sl
      c = (1 - sg) * (1 - sl) + sf * sl

      w = Math.atan(Math.sqrt(s / c))
      r = Math.sqrt(s * c) / w
      d = 2 * w * a
      h1 = (3 * r - 1) / 2 / c
      h2 = (3 * r + 1) / 2 / s
      return d * (1 + fl * (h1 * sf * (1 - sg) - h2 * (1 - sf) * sg))
    }

    function worldCoordinateToLonAndLat(cartesian3) {
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian3)
      const lat = Cesium.Math.toDegrees(cartographic.latitude)
      const lng = Cesium.Math.toDegrees(cartographic.longitude)
      const alt = cartographic.height
      return { lon: lng, lat: lat, hit: alt }
    }

    function getGeoCoordinate(lat, lon) {
      var terrainheight = viewer.scene.globe.getHeight(window.Cesium.Cartographic.fromDegrees(lon, lat));
      (terrainheight !== undefined) ? terrainheight : 0
      var point = {
        lon: lon,
        lat: lat,
        hit: terrainheight
      }
      return point
    }

    function distance(point1, point2) {
      /** 根据经纬度计算出距离**/
      var strat = Cesium.Cartographic.fromDegrees(point1.lon, point1.lat, point1.hit)
      var end = Cesium.Cartographic.fromDegrees(point2.lon, point2.lat, point2.hit)
      var geodesic = new Cesium.EllipsoidGeodesic()
      geodesic.setEndPoints(strat, end)
      var s = geodesic.surfaceDistance
      // 返回两点之间的距离
      s = (Math.sqrt(Math.pow(s, 2) + Math.pow(strat.height - end.height, 2))) / 1000
      // s = Math.abs(strat.height - end.height) / 1000
      return s
    }

    function initChart(e) {
      function strFormat(str) {
        var strString = String(str)
        var strs = strString.slice(0, strString.indexOf('.') + 3)
        return strs
      }
      var t = e.ponits
      var chartData = {
        dataZoom: [{
          type: 'inside',
          throttle: 50
        }],
        valueAxis: {
          position: 'left',
          axisLine: { // 坐标轴线
            show: true, // 默认显示,属性show控制显示与否
            lineStyle: { // 属性lineStyle控制线条样式
              color: '#48b',
              width: 2,
              type: 'solid'
            }
          }
        },
        grid: {
          x: 50,
          y: 40,
          x2: 80,
          y2: 40,
          backgroundColor: 'rgba(0,0,0,0)',
          borderWidth: 1,
          borderColor: '#ccc'
        },
        tooltip: {
          trigger: 'axis',
          formatter: function (e) {
            // console.log(e, 'kkkkk')
            var a = ''
            if (e.length === 0) return a
            e[0].value
            var r = t[e[0].dataIndex]
            // console.log(r, 'shujuchakan')
            return a += '所在位置&nbsp;' + strFormat(r.lon) + ',' + strFormat(r.lat) + '</label><br />' + e[0].seriesName + '&nbsp;' + strFormat(r.hit) + "&nbsp;<label style='color:" + e[0].color + ";'>"
          }
          /*  */
        },
        xAxis: [{
          name: '距离[km]',
          // nameLocation: 'left',
          type: 'category',
          boundaryGap: false,
          axisLine: {
            show: true,
            symbol: ['none', 'arrow'], // 是否显示轴线箭头
            symbolSize: [6, 6], // 箭头大小
            symbolOffset: [0, 7], // 箭头位置
            lineStyle: {
              width: 1,
              type: 'solid'
            }
          },
          axisTick: { // 坐标轴刻度
            show: true, // 是否显示
            inside: false, // 是否朝内
            length: 3, // 长度
            lineStyle: { // 默认取轴线的样式
              width: 1,
              type: 'solid'
            }
          },
          axisLabel: {
            show: true,
            interval: 'auto'
            // formatter:
          },
          data: e.arrLX

        }],
        yAxis: [{
          name: '高程值[m]',
          type: 'value',
          axisLabel: {
            rotate: 30,
            interval: '50'
          },
          axisLine: { // 坐标轴 轴线
            show: true, // 是否显示
            symbol: ['none', 'arrow'], // 是否显示轴线箭头
            symbolSize: [6, 6], // 箭头大小
            symbolOffset: [0, 7], // 箭头位置
            lineStyle: {
              width: 1,
              type: 'solid'
            }
          },
          axisTick: { // 坐标轴刻度
            show: true, // 是否显示
            inside: false, // 是否朝内
            length: 3, // 长度
            lineStyle: { // 默认取轴线的样式
              width: 1,
              type: 'solid'
            }
          }

        }],
        series: [{
          name: '高程值',
          type: 'line',
          smooth: !0,
          symbol: 'none',
          sampling: 'average',
          zlevel: 1,
          z: 1,
          itemStyle: {
            color: 'rgb(255, 70, 131)'
          },
          areaStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
              offset: 0,
              color: 'rgb(255, 158, 68)'
            },
            {
              offset: 1,
              color: 'rgb(255, 70, 131)'
            }])

          },
          data: e.arrHB
        }]
      }
      echarts.init(document.getElementById("echarts-container")).setOption(chartData)
    }
    //---------------------地形剖面分析效果结束--------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

3、天气特效和场景泛光:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十二课,大气效果</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
    //---------------------大气效果代码演示--------------------
     Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxNzMxZDY2Ni04ZDJlLTRkNjItYjc4Ni0wZWIwMDk1MWE0ZDIiLCJpZCI6MTU4Mzk1LCJpYXQiOjE2OTExMTU3MTV9.dA5HKMX06zvLJEl64qNuw982uxFOtJ966saJsYbHKQs"
      viewer = new Cesium.Viewer("cesium-container", {
        imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
          url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
        }),
        sceneMode: Cesium.SceneMode.SCENE3D,
        vrButton: false,
        animation: false,
        baseLayerPicker: false,
        geocoder: false,
        timeline: false,
        fullscreenButton: false,
        homeButton: false,
        creditContainer: document.createElement('div'),
        infoBox: false,
        navigationHelpButton: false,
        sceneModePicker: false,
        scene3DOnly: true
      });

      viewer.scene.globe.enableLighting = true//开启光照

      //雾
      viewer.scene.fog.enabled = true//开启雾效果
      viewer.scene.fog.minimumBrightness = 0.1//雾效果最小亮度
      viewer.scene.fog.density = 0.0003//浓度

      //地表大气效果
      viewer.scene.globe.showGroundAtmosphere = true//开启地表大气效果
      viewer.scene.globe.atmosphereLightIntensity = 10//设置地表大气亮度

      //天空大气效果
      viewer.scene.skyAtmosphere.show=true
      viewer.scene.skyAtmosphere.atmosphereLightIntensity=50//天空大气效果亮度

      //HDR效果
      viewer.scene.highDynamicRange=true
   	  //场景泛光
      viewer.scene.globe.depthTestAgainstTerrain = true;
      const bloom = viewer.scene.postProcessStages.bloom;
      bloom.enabled = true;
      //设置泛光效果
      bloom.uniforms.glowOnly = false;//是否只显示泛光效果的区域,没泛光的区域为黑色
      bloom.uniforms.contrast = 128;//泛光对比度
      bloom.uniforms.brightness = -0.3;//泛光亮度
      //泛光区域叠加模糊效果
      bloom.uniforms.delta = 1;
      bloom.uniforms.sigma = 2;//设置模糊半径
      bloom.uniforms.stepSize = 1;
    //---------------------大气效果结束--------------------
  </script>
</body>

</html>

执行效果:
在这里插入图片描述

4、动态云层效果

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十五课,动态云层效果</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <script src="../libs/Echarts/echarts.min.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
    //---------------------动态云层效果代码演示--------------------
 Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
  viewer = new Cesium.Viewer("cesium-container", {
    terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
      url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
      requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
      requestWaterMask: true,//可以增加水面特效
    }),
    imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
      url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
    }),
    sceneMode: Cesium.SceneMode.SCENE3D,
    vrButton: false,
    animation: false,
    baseLayerPicker: false,
    geocoder: false,
    timeline: false,
    fullscreenButton: false,
    homeButton: false,
    creditContainer: document.createElement('div'),
    infoBox: true,
    navigationHelpButton: false,
    sceneModePicker: false,
    scene3DOnly: true,
    shouldAnimate: true,//开启动画
  });
  viewer.scene.globe.depthTestAgainstTerrain = true;

  let west = -180, east = 180
  viewer.entities.add({
    rectangle: {
      coordinates: new Cesium.CallbackProperty(() => {
        west -= 1
        east -= 1
        return Cesium.Rectangle.fromDegrees(west, -90, east, 90)
      }),
      height: 20000,
      material: new Cesium.ImageMaterialProperty({
        image: "./data/earth_cloud.png",
        transparent: true
      })
    }
  })
    //---------------------动态云层效果效果结束--------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

5、雷达扫描效果

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十五课,雷达扫描效果</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <script src="../libs/Echarts/echarts.min.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>

  <script>
    //---------------------雷达扫描效果代码演示--------------------
  Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
    viewer = new Cesium.Viewer("cesium-container", {
      terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
        url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
        requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
        requestWaterMask: true,//可以增加水面特效
      }),
      imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
      }),
      sceneMode: Cesium.SceneMode.SCENE3D,
      vrButton: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
      timeline: false,
      fullscreenButton: false,
      homeButton: false,
      creditContainer: document.createElement('div'),
      infoBox: true,
      navigationHelpButton: false,
      sceneModePicker: false,
      scene3DOnly: true,
      shouldAnimate: true,//开启动画
    });
    viewer.scene.globe.depthTestAgainstTerrain = true;

    //--------------------十秒后执行雷达扫描效果--------------------
    setTimeout(() => {
      Object.defineProperties(RadarScanMaterialProperty.prototype, {
        color: Cesium.createPropertyDescriptor('color'),
        speed: Cesium.createPropertyDescriptor('speed')
      })

      Cesium.RadarScanMaterialProperty = RadarScanMaterialProperty;
      Cesium.Material.RadarScanMaterialProperty = 'RadarScanMaterialProperty';
      Cesium.Material.RadarScanMaterialType = 'RadarScanMaterialType';
      Cesium.Material.RadarScanMaterialSource =
        `
                uniform vec4 color;
                uniform float speed;

                #define PI 3.14159265359

                czm_material czm_getMaterial(czm_materialInput materialInput){
                czm_material material = czm_getDefaultMaterial(materialInput);
                vec2 st = materialInput.st;
                vec2 scrPt = st * 2.0 - 1.0;
                float time = czm_frameNumber * speed / 1000.0 ;
                vec3 col = vec3(0.0);
                mat2 rot;
                float theta = -time * 1.0 * PI - 2.2;
                float cosTheta, sinTheta;
                cosTheta = cos(theta);
                sinTheta = sin(theta);
                rot[0][0] = cosTheta;
                rot[0][1] = -sinTheta;
                rot[1][0] = sinTheta;
                rot[1][1] = cosTheta;
                vec2 scrPtRot = rot * scrPt;
                float angle = 1.0 - (atan(scrPtRot.y, scrPtRot.x) / 6.2831 + 0.5);
                float falloff = length(scrPtRot);
                material.alpha = pow(length(col + vec3(.5)),5.0);
                material.diffuse =  (0.5 +  pow(angle, 2.0) * falloff ) *   color.rgb    ;
                return material;
                }

                `

      Cesium.Material._materialCache.addMaterial(Cesium.Material.RadarScanMaterialType, {
        fabric: {
          type: Cesium.Material.RadarScanMaterialType,
          uniforms: {
            color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
            speed: 10.0
          },
          source: Cesium.Material.RadarScanMaterialSource
        },
        translucent: function () {
          return true;
        }
      })

      let rader = viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(113.9236839, 22.528061),
        name: '雷达扫描',
        ellipse: {
          semiMajorAxis: 1000.0,
          semiMinorAxis: 1000.0,
          material: new Cesium.RadarScanMaterialProperty({
            color: new Cesium.Color(1.0, 1.0, 0.0, 0.2),
            speed: 20.0,
          }),
          height: 60,
          heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND,
          outline: true,
          outlineColor: new Cesium.Color(1.0, 1.0, 0.0, 1.0)
        }
      })
      viewer.flyTo(rader)
    }, 10000);

    /*
       ----------------------------------雷达效果工具类-----------------------------------
    */
   class RadarScanMaterialProperty {
      constructor(options) {
        this._definitionChanged = new Cesium.Event();
        this._color = undefined;
        this._speed = undefined;
        this.color = options.color;
        this.speed = options.speed;
      }

      get isConstant() {
        return false;
      }

      get definitionChanged() {
        return this._definitionChanged;
      }

      getType() {
        return Cesium.Material.RadarScanMaterialType;
      }

      getValue(time, result) {
        if (!Cesium.defined(result)) {
          result = {};
        }

        result.color = Cesium.Property.getValueOrDefault(this._color, time, Cesium.Color.RED, result.color);
        result.speed = Cesium.Property.getValueOrDefault(this._speed, time, 10, result.speed);
        return result
      }

      equals(other) {
        return (this === other ||
          (other instanceof RadarScanMaterialProperty &&
            Cesium.Property.equals(this._color, other._color) &&
            Cesium.Property.equals(this._speed, other._speed))
        )
      }
    }
    //export default RadarScanMaterialProperty
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

6、淹没分析

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十六课,淹没分析</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <script src="../libs/Echarts/echarts.min.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>

  <script>
    //---------------------淹没分析效果代码演示--------------------
  Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
    viewer = new Cesium.Viewer("cesium-container", {
      terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
        url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
        requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
        requestWaterMask: true,//可以增加水面特效
      }),
      imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
      }),
      sceneMode: Cesium.SceneMode.SCENE3D,
      vrButton: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
      timeline: false,
      fullscreenButton: false,
      homeButton: false,
      creditContainer: document.createElement('div'),
      infoBox: true,
      navigationHelpButton: false,
      sceneModePicker: false,
      scene3DOnly: true,
      shouldAnimate: true,//开启动画
    });
    viewer.scene.globe.depthTestAgainstTerrain = true;

     let waterMinElevation = 180, waterMaxElevation = 400, positions = Cesium.Cartesian3.fromDegreesArrayHeights([116.64, 36.34, 8000, 116.66, 36.34, 8000, 116.66, 36.32, 8000, 116.64, 36.32, 8000, 116.64, 36.34, 8000])
      let entity = viewer.entities.add({
        polygon: {
          hierarchy: new Cesium.PolygonHierarchy(positions),
          extrudedHeight: new Cesium.CallbackProperty(() => {
            if (waterMinElevation < waterMaxElevation) {
              waterMinElevation += 0.1
            }
            return waterMinElevation
          }),
          material: Cesium.Color.fromCssColorString('#3D81A5').withAlpha(0.7)
        }
      })

      viewer.entities.add({
        polygon: {
          hierarchy: new Cesium.PolygonHierarchy(positions),
          material: Cesium.Color.WHITE.withAlpha(0.3)
        },
        polyline: {
          positions: positions,
          width: 4,
          clampToGround: true
        }
      })

      viewer.flyTo(entity)
    //---------------------淹没分析效果代码结束--------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

七、建筑体添加和使用

代码如下,都有详细注释:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第二课,加载自定义地图</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    //----------------------- 地图、地形的添加--------------------------
    let esri = new Cesium.ArcGisMapServerImageryProvider({
      url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
    })//用一个变量存储新地图的信息,这里我们使用的是arcgis地图服务
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表,
      baseLayerPicker: false,//默认的地图按钮设置为隐藏
      imageryProvider: esri,//viewer内的imageryProvider属性用于设置地图
      terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
        url:Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
        requestVertexNormals:true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
        requestWaterMask:true,//可以增加水面特效
      }),
    });
    viewer.scene.globe.show = true//地图显示,默认是true
    
    // var layer=viewer.imageryLayers.addImageryProvider(
    //   new Cesium.IonImageryProvider({assetId:3812})
    // )//测试夜晚地图
//----------------------- 地图、地形的添加结束--------------------------

//----------------------- 建筑物的添加使用--------------------------
    let tileset=viewer.scene.primitives.add(//Viewer中的scene是Cesium虚拟场景中所有3D图形对象和状态的容器,它的primitives属性用于获取大量的基元集合,add方法用于加载数据
      new Cesium.Cesium3DTileset({//这里使用的CesiumBDTileset方法,是Cesium用于传输海量异构3D地理空间数据集
        url:Cesium.IonResource.fromAssetId(96188)//添加建筑物
      })
    )
    viewer.camera.setView({//添加相机信息
      destination:Cesium.Cartesian3.fromDegrees(121.49,31.23,3000),
      orientation:{
        heading:0,
        pitch:-90,
        roll:0
      }
    })

    //模型加样式,这里先添加一个单一样式
    tileset.style= new Cesium.Cesium3DTileStyle({//需要使用Cesium的Cesium3DTileStyle方法
      color:"color('pink',0.5)",//color属性用于设定模型的颜色和透明度,可以设置颜色根据楼层高度进行变化
      show:true
    })//因为3d模型的样式过于单调,这里我们对一个模型数据样式进行修改,就是修改它的style

    // //模型加样式,根据高度添加不同样式
    // tileset.style = new Cesium.Cesium3DTileStyle({//需要使用Cesium的Cesium3DTileStyle方法
    //   color: {//给不同的高度的建筑物体设置不同的颜色
    //     conditions:[
    //       ["${Height} >= 300", "rgba(45,0,75,0.5)"],
    //       ["${Height} >= 200", "rgb(102,0,75)"],
    //       ["${Height} >= 100", "rgb(170,0,75)"],
    //       ["${Height} >= 50", "rgb(224,0,75)"],
    //       ["${Height} >= 20", "rgb(1,0,75)"],
    //       ["${Height} >= 10", "rgb(100,0,75)"],
    //       ["${Height} >= 5", "rgb(200,0,75)"],
    //       ["true", "rgb(127,59,8)"]
    //     ]
    //   },//color属性用于设定模型的颜色和透明度,可以设置颜色根据楼层高度进行变化
    //   show: '${Height} >= 0'//show属性设置的是高度大于等于0的建筑物才可以显示出来
    // })//因为3d模型的样式过于单调,这里我们对一个模型数据样式进行修改,就是修改它的style
//----------------------- 建筑物的添加使用结束--------------------------
  </script>
</body>

</html>

执行效果如下:
在这里插入图片描述
在这里插入图片描述

八、空间数据加载

1、加载数据

在cesium中一般使用到的空间数据分为矢量数据和栅格数据,前几课我们学习的地形和地图数据的加载,就属于栅格数据。矢量数据则包括几何体的加载,模型、标签等

(1)、加载点、线、面、图标(广告牌)、box盒子 等示例代码

   //地球加载一个圆点
    const entity_point = viewer.entities.add({//创建一个Entity点对象
      position:new Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000),//设置经纬度和高度
      point: {
        pixelSize: 50,//设置点大小
        color: new Cesium.Color(0, 1, 0, 1)//设定点颜色      
      }
    })

    //地球加载一条线
    const entity_line = viewer.entities.add({//创建一个Entity点对象
      polyline: {
        show:true,
        positions:new Cesium.Cartesian3.fromDegreesArray([113.37377, 31.717497, 113.37577, 31.717597]),//线经纬度
        width:5,//线宽
        material:new Cesium.Color(0,0,1,1),//设置颜色
      }
    })

    //地球加载一个面
    const entity_plane = viewer.entities.add({//创建一个Entity点对象
      position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000),//设置经纬度和高度
      plane: {//用于绘制一个面
        plane:new Cesium.Plane(Cesium.Cartesian3.UNIT_Z,0),//确认面的朝向,这里我们设置它朝Z轴平铺
        dimensions:new Cesium.Cartesian2(400,300),//设置面的长度和宽度
        color: new Cesium.Color(0, 1, 0, 1),//设定点颜色  
        material: Cesium.Color.RED.withAlpha(0.5),//颜色为红色,透明度0.5
        outline:true,//是否显示边框
        outlineColor:Cesium.Color.BLACK//边框线为黑色
      }
    })
    
	//加载一个图标(广告牌)
	let entity_tubiao = viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(
          114,
          38,
          100
        ),
        billboard: {
          id:"ggp_id"
          show: true,
          image: "./data/marker.png",
          scale: 1,//比例尺,可以将图标放大
          pixelOffset: new Cesium.Cartesian2(0, 0),//像素偏移,相对于屏幕x、y偏移
          eyeOffset: new Cesium.Cartesian3(0, 0, 0),//相机视角偏移,比如当同一经纬度有俩图标,设置z的属性偏移会显示在前面,xyz三个参数分别控制左右、上下、前后
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //设置图标图片水平方向的位置,比如以此图标水平的中心点为锚点设置在地球上
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,//设置图标图片垂直方向的位置,比如以此图标的垂直最下点为锚点设置在地球上
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,//设置图标图片的高度位置,CLAMP_TO_GROUND是指将图标自动贴在地形的高度上和position无关,RELATIVE_TO_GROUND是图标position的高度,如果position高度设置的0,那么和CLAMP_TO_GROUND效果一样,如果高度为100,则就以高度100,NONE是贴在地球球面,和地形无关
          color: Cesium.Color.CHARTREUSE,//修改图标颜色
          rotation: 0,//图标旋转角度
          sizeInMeters: false,//图标是否随着鼠标缩放进行缩放
          // width:60,//图标宽高
          // height:60,//图标宽高
          // scaleByDistance:new Cesium.NearFarScalar(1000, 2, 5000, 0.1),//设置小于1000米时图标两倍大,大于5000米图标0.1倍大
          // translucencyByDistance:new Cesium.NearFarScalar(1000, 1, 5000, 0.1),//设置小于1000米透明度为1,大于五千米就越来越透明
          // pixelOffsetScaleByDistance:new Cesium.NearFarScalar(1000, 1, 5000, 10),//通过距离进行偏移
          // imageSubRegion: new Cesium.BoundingRectangle(1, 1, 30, 50),//当图标过大时设置图标图片大小
          // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(1000.0, 6000.0),//在1000-6000米时展示图标,其他范围均不显示
          // disableDepthTestDistance:2000,//小于2000关闭深度检测,也就是2000以内图标可以穿透地形
        },
      });

		 //加载box盒子
        viewer.shadows = true//必须设置,否则阴影看不到
        viewer.scene.light = new Cesium.DirectionalLight({//自定义阳光照射方向
          direction: Cesium.Cartesian3.fromElements(-0.2, -0.5, -0.8),
          intensity: 1
        })
        let entity_box =viewer.entities.add({
          name: "Blue box",
          position: position,
          box: {
            show: true,
            heightReference: Cesium.HeightReference.NONE,
            dimensions: new Cesium.Cartesian3(1000.0, 1000.0, 500.0),//尺寸,长宽高
            fill: true,//材质填充,false时就只剩盒子边框
            material: Cesium.Color.BLUE,
            outline: true,
            outlineColor: Cesium.Color.AQUA,//边框颜色
            outlineWidth: 10,
            shadows: Cesium.ShadowMode.ENABLED//阴影,默认是没有阴影
          },
        });

效果展示:
在这里插入图片描述

(2)、加载模型,代码如下:

 const position = Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 10000) //定义常量,用于存储飞行目的地的坐标
    const orientation= Cesium.Transforms.headingPitchRollQuaternion(position,new Cesium.HeadingPitchRoll(-90,0,0))//定义模型朝向变量:参数一:位置信息,参数二:旋转角度
    const entity=viewer.entities.add({//使用Entities加载一个glb模型用于观察
      position:position,//模型位置
      orientation:orientation,//模型朝向
      /*model里面的参数
        uri: 表示3D模型的URI,可以是相对路径或绝对路径,也可以是data URI格式。必须指定个URI才能显示3D模型
        scale: 表示缩放比例。默认值为1,表示原始大小,可以通过指定其他值来调整模型的大小
        minimumPixelSize: 表示模型的最小大小(以像素为单位)。如果模型在地球上的投影比此值小,则模型将不可见。默认值为0。
        maximumScale: 表示模型缩放比例的最大值,如果模型的缩放比例大于此值,则模型将不可见。默认值为无穷大。
        incrementallyLoadTextures: 表示是否逐步加载模型纹理。默认值为true,表示模型的纹理将在后台异步加载,直到完全加载为止。如果设置为false,则模型的纹理将在模型加载时同步加载,这可能会导致性能问题。
        runAnimations: 表示是否播放模型的动画。默认值为true,表示模型动画将播放。如果设置为false,则模型将显示静态姿势
        clampAnimations:表示是否在地球上固定模型动画,默认值为false,表示模型动画将在地球上自由移动。如果设置为true,则模型动画将保持相对于地球的固定位置。
        nodeTransformations:表示对模型节点的变换。默认值为什,表示没有节点变换。该参数是一个对象,对象的键是节点名称,值是一个包含变换信息的对象,包括translate.rotate、scale和matrix四个属性。每个属性都是一个3x3或4x4的矩阵,用于描述节点的变换信息。
        articulations: 表示模型的关节,用于模拟模型的骨骼动画。默认值为1,表示没有关节该参数是一个数组,数组中的每个元素都是一个包含关节信息的对象,包括名称、父级名称、最大角度、最小角度和当前角度等属性。
        show: 表示是否显示模型。默认值为true,表示模型将显示。如果设置为false,则模型将不可见。
        distanceDisplayCondition: 表示模型的距离显示条件。默认值为undefined,表示没有距离显示条件。该参数是一个Cesium.DistanceDisplayCondition对象,它有两个属性:near和far,表示距离地球的近和远限制。
        silhouetteColor: 表示模型的描边颜色。默认值为Cesium.Color.RED,表示红色描边。该参数可以是Cesium.Color对象、Cesium.Property对象或表示颜色的字符串。
      **/
      model:{//模型信息
        uri:"./data/car.glb",//模型路径
        minimumPixelSize:100,//模型最小的大小
        maxmumScale:10000,//模型缩放最大比例
        show:true,//模型的显示,
        clippingPlanes: new Cesium.ClippingPlaneCollection({//clippingPlanes 对模型进行裁剪,效果放在图二
          planes:[new Cesium.ClippingPlane(new Cesium.Cartesian3(0, 1, 0) ,0)],//沿着y轴裁剪
          edgeWidth: 1,
          edgeColor: Cesium.Color.GREEN,//裁剪线为绿色
          enabled: true,
          edgeMaterial: new Cesium.PolylineOutlineMaterialProperty({
            color: Cesium.Color.RED, 
            outlineWidth: 1, 
            outlineColor: Cesium.Color.BLUE
          })
        }),
         //下面的参数会给glb模型加一层膜
        color: Cesium.Color.fromCssColorString('#FF6C37').withAlpha(1),//fromCssColorString:设置膜的颜色,withAlpha:设置透明度,glb模型的透明度也会被影响
        colorBlendMode: Cesium.ColorBlendMode.MIX,//设置模式:这里选择的是混合模式,有下面三个值:HIGHLIGHT、MIX、REPLACE,可以试下三个值分别的效果
        colorBlendAmount: 0.5,//膜和模型混入的程度,只有值为MIX的时候才会生效
        silhouetteColor: Cesium.Color.fromCssColorString('#00FFF0').withAlpha(1),//设置膜边框颜色和透明度
        silhouetteSize: 5,//设置膜边框大小
      }
    }),
    

执行效果:
在这里插入图片描述
模型裁剪的效果如下:
在这里插入图片描述
模型样式设置后的效果如下:
在这里插入图片描述

(3)、加载文字说明(标签的加载),代码如下:

//地球加载一个文本标签 
const entity_txt = viewer.entities.add({//使用Entities加载一个glb模型用于观察
  position: position,//文字位置
  label:{
    text:"我是一段文字",//设置文字内容
    font:"50px Helvetica",//字体
    fillColor:Cesium.Color.YELLOW//字体颜色
  }
})

效果如下:
在这里插入图片描述
(4)、总结:要建立起一个概念,在Cesium中无论载入几何体还是文字都是基于空间位置的。

2、对加载的空间数据进行管理

空间数据管理是三维场景开发的主要内容,其包含对数据的创建、增加、修改、删除等。这里主要学习cesium api的Entity方法,它让开发者专注于数据的呈现,而不必担心底层的可视化机制。
(1)、用Entity方法创建一个多边形,代码如下:

//地球加载一个多边形
const entity_polygon = viewer.entities.add({//创建一个Entity对象
  polygon: {//用于绘制一个多边形,一个多边形由多个点连接组成的
    hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3777, 31.717497, 113.9007, 31.717797, 113.3007, 31.617797]),//fromDegreesArray方法就是让我们录入多个点的位置信息,Cesium则在底层将各个点连接起来,组成一个多边形,这里通过录入三个经纬度坐标位置,绘制一个三角形
    material:Cesium.Color.PINK,//设置颜色
  }
})

效果如下:
在这里插入图片描述
(2)、加载一个图片,这里把图片的朝向指向x轴,可以立起来,代码如下:

  //地球加载一个图片
  const entity_image = viewer.entities.add({//创建一个Entity点对象
    position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 100),//设置经纬度和高度
    plane: {//用于绘制一个面
      plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0),//确认面的朝向,这里我们设置它朝X轴平铺
      dimensions: new Cesium.Cartesian2(200, 150),//设置面的长度和宽度
      material: "./data/a.png",//引入图片
      outline: true,//是否显示边框
      outlineColor: Cesium.Color.GREEN//边框线为黑色
    }
  })

效果如下:
在这里插入图片描述
(3)、将绘制好的多边形在垂直方向拉伸,代码如下:

//地球加载一个多边形并拉伸
const entity_polygon_stretch = viewer.entities.add({//创建一个Entity对象
  polygon: {//用于绘制一个多边形,一个多边形由多个点连接组成的
    hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3777, 31.7174, 113.9007, 31.7177, 113.3007, 31.6177]),//fromDegreesArray方法就是让我们录入多个点的位置信息,Cesium则在底层将各个点连接起来,组成一个多边形,这里通过录入三个经纬度坐标位置,绘制一个三角形
    material: Cesium.Color.YELLOW,//设置颜色
    extrudedHeight: 200,//将绘制好的多边形在垂直方向进行拉升,此属性在绘制多边形的时候可以使用,如果绘制的本身是平面则不可以使用
  }
})

效果如下:
在这里插入图片描述
(4)、除了修改样式以外,还常需对它们进行增、删、改 ,比如 :五秒后将上面的多面体颜色设置为白色,十秒后删除拉伸模型,代码如下:

/** 除了修改样式以外,还常需对它们进行增、删、改 **/
//五秒后颜色设置为白色
setTimeout(() => {
 viewer.entities.getById("polygon_stretch_id").polygon.material = Cesium.Color.WHITE
}, 5000);
setTimeout(() => {//十秒后删除拉伸模型
  viewer.entities.remove(entity_polygon_stretch)
}, 10000);

效果如下:
在这里插入图片描述
移除场景内的所有实体,使用removeAll()方法,代码如下:viewer.entities.removeAll(),效果如下:
在这里插入图片描述

3、空间数据加载的整体代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第三课,空间实体</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表
    });
    //----------------------------空间数据加载,---------------------------
    const position=Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000) //定义常量,用于存储飞行目的地的坐标
    //setView 直接跳转,没有过程
    viewer.camera.setView({//通过相机系统设置视角
      destination:position,//设置相机目的地
      orientation:{//设置相机视口的方向
        heading:Cesium.Math.toRadians(0),//控制视口的水平旋转,也就是沿着y轴旋转,当数值为0时,代表正北方向
        pitch:Cesium.Math.toRadians(-90),//控制视口的上下旋转,即沿x轴进行旋转,当数值为-90,代表俯视地面
        roll:0,//控制视口的反转角度,即沿着z轴进行旋转,数值为0表示不翻转
      },
    })
    //地球加载一个圆点
    const entity_point = viewer.entities.add({//创建一个Entity点对象
      position:new Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000),//设置经纬度和高度
      point: {
        pixelSize: 50,//设置点大小
        color: new Cesium.Color(0, 1, 0, 1)//设定点颜色      
      }
    })

    //地球加载一条线
    const entity_line = viewer.entities.add({//创建一个Entity点对象
      polyline: {
        show:true,
        positions:new Cesium.Cartesian3.fromDegreesArray([113.37377, 31.717497, 113.37577, 31.717597]),//线经纬度
        width:5,//线宽
        material:new Cesium.Color(0,0,1,1),//设置颜色
      }
    })

    //地球加载一个面
    const entity_plane = viewer.entities.add({//创建一个Entity点对象
      position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000),//设置经纬度和高度
      plane: {//用于绘制一个面
        plane:new Cesium.Plane(Cesium.Cartesian3.UNIT_Z,0),//确认面的朝向,这里我们设置它朝Z轴平铺
        dimensions:new Cesium.Cartesian2(400,300),//设置面的长度和宽度
        color: new Cesium.Color(0, 1, 0, 1),//设定点颜色  
        material: Cesium.Color.RED.withAlpha(0.5),//颜色为红色,透明度0.5
        outline:true,//是否显示边框
        outlineColor:Cesium.Color.BLACK//边框线为黑色
      }
    })

    //地球加载一个多边形
    const entity_polygon = viewer.entities.add({//创建一个Entity对象
      polygon: {//用于绘制一个多边形,一个多边形由多个点连接组成的
        hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3777, 31.717497, 113.9007, 31.717797, 113.3007, 31.617797]),//fromDegreesArray方法就是让我们录入多个点的位置信息,Cesium则在底层将各个点连接起来,组成一个多边形,这里通过录入三个经纬度坐标位置,绘制一个三角形
        material:Cesium.Color.PINK,//设置颜色
        //通常除了简单的图形、颜色加载以外,更多时候需要定制化的样式,通过代码看下他的可选配置项
      }
    })

    /** 通常除了简单的图形、颜色加载以外,更多时候需要定制化的样式,通过代码看下他的可选配置项 **/
    //地球加载一个图片
    const entity_image = viewer.entities.add({//创建一个Entity点对象
      position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 100),//设置经纬度和高度
      plane: {//用于绘制一个面
        plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0),//确认面的朝向,这里我们设置它朝X轴平铺
        dimensions: new Cesium.Cartesian2(200, 150),//设置面的长度和宽度
        material: "./data/a.png",//引入图片
        outline: true,//是否显示边框
        outlineColor: Cesium.Color.GREEN//边框线为黑色
      }
    })

    //地球加载一个多边形并拉伸
    const entity_polygon_stretch = viewer.entities.add({//创建一个Entity对象
      id:"polygon_stretch_id",//给几何体加一个id号
      polygon: {//用于绘制一个多边形,一个多边形由多个点连接组成的
        hierarchy: Cesium.Cartesian3.fromDegreesArray([113.3777, 31.7174, 113.9007, 31.7177, 113.3007, 31.6177]),//fromDegreesArray方法就是让我们录入多个点的位置信息,Cesium则在底层将各个点连接起来,组成一个多边形,这里通过录入三个经纬度坐标位置,绘制一个三角形
        material: Cesium.Color.YELLOW,//设置颜色
        extrudedHeight: 200,//将绘制好的多边形在垂直方向进行拉升,此属性在绘制多边形的时候可以使用,如果绘制的本身是平面则不可以使用
      }
    })
    /** 除了修改样式以外,还常需对它们进行增、删、改 **/
    //五秒后颜色设置为白色
    // setTimeout(() => {
    //   viewer.entities.getById("polygon_stretch_id").polygon.material = Cesium.Color.WHITE
    // }, 5000);
    // setTimeout(() => {//十秒后删除拉伸模型
    //   viewer.entities.remove(entity_polygon_stretch)
    // }, 10000);
    //移除场景内的所有实体,使用removeAll()方法
    setTimeout(() => {//五秒后清除所有实体
      viewer.entities.removeAll()
    }, 5000);

    //地球加载一个glb模型
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0))//定义模型朝向变量:参数一:位置信
    const entity_glb = viewer.entities.add({//使用Entities加载一个glb模型用于观察
      position: position,//模型位置
      orientation: orientation,//模型朝向
      model: {//模型信息
        uri: "./data/car.glb",//模型路径
        minimumPixelSize: 100,//模型最小的大小
        maxmumScale: 10000,//模型缩放最大比例
        show: true,//模型的显示
      }
    })

    //地球加载一个文本标签 
    const entity_txt = viewer.entities.add({//使用Entities加载一个glb模型用于观察
      position: position,//文字位置
      label:{
        text:"我是一段文字",//设置文字大小和样式
        font:"50px Helvetica",//字体
        fillColor:Cesium.Color.YELLOW//字体颜色
      }
    })
    // viewer.trackedEntity = entity;//将摄像头设置在圆点处(track跟踪)
    //加载广告牌
    viewer.scene.globe.depthTestAgainstTerrain = true;//是否开启深度检测
    let entity_ggp = viewer.entities.add({
        position: position,
        billboard: {
          show: true,
          image: "./data/marker.png",
          scale: 1,//比例尺,可以将图标放大
          pixelOffset: new Cesium.Cartesian2(0, 0),//像素偏移,相对于屏幕x、y偏移
          eyeOffset: new Cesium.Cartesian3(0, 0, 0),//相机视角偏移,比如当同一经纬度有俩图标,设置z的属性偏移会显示在前面,xyz三个参数分别控制左右、上下、前后
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER, //设置图标图片水平方向的位置,比如以此图标水平的中心点为锚点设置在地球上
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,//设置图标图片垂直方向的位置,比如以此图标的垂直最下点为锚点设置在地球上
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,//设置图标图片的高度位置,CLAMP_TO_GROUND是指将图标自动贴在地形的高度上和position无关,RELATIVE_TO_GROUND是图标position的高度,如果position高度设置的0,那么和CLAMP_TO_GROUND效果一样,如果高度为100,则就以高度100,NONE是贴在地球球面,和地形无关
          color: Cesium.Color.CHARTREUSE,//修改图标颜色
          rotation: 0,//图标旋转角度
          sizeInMeters: false,//图标是否随着鼠标缩放进行缩放
          // width:60,//图标宽高
          // height:60,//图标宽高
          // scaleByDistance:new Cesium.NearFarScalar(1000, 2, 5000, 0.1),//设置小于1000米时图标两倍大,大于5000米图标0.1倍大
          // translucencyByDistance:new Cesium.NearFarScalar(1000, 1, 5000, 0.1),//设置小于1000米透明度为1,大于五千米就越来越透明
          // pixelOffsetScaleByDistance:new Cesium.NearFarScalar(1000, 1, 5000, 10),//通过距离进行偏移
          // imageSubRegion: new Cesium.BoundingRectangle(1, 1, 30, 50),//当图标过大时设置图标图片大小
          // distanceDisplayCondition: new Cesium.DistanceDisplayCondition(1000.0, 6000.0),//在1000-6000米时展示图标,其他范围均不显示
          // disableDepthTestDistance:2000,//小于2000关闭深度检测,也就是2000以内图标可以穿透地形
        },
      });
      
 		//加载box盒子
        viewer.shadows = true//必须设置,否则阴影看不到
        viewer.scene.light = new Cesium.DirectionalLight({//自定义阳光照射方向
          direction: Cesium.Cartesian3.fromElements(-0.2, -0.5, -0.8),
          intensity: 1
        })
        let entity_box =viewer.entities.add({
          name: "Blue box",
          position: position,
          box: {
            show: true,
            heightReference: Cesium.HeightReference.NONE,
            dimensions: new Cesium.Cartesian3(1000.0, 1000.0, 500.0),//尺寸,长宽高
            fill: true,//材质填充,false时就只剩盒子边框
            material: Cesium.Color.BLUE,
            outline: true,
            outlineColor: Cesium.Color.AQUA,//边框颜色
            outlineWidth: 10,
            shadows: Cesium.ShadowMode.ENABLED//阴影,默认是没有阴影
          },
        });
    //------------------------------空间数据加载演示结束------------------------------
  </script>
</body>

</html>

4、对空间模型进行裁剪

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第七课,空间模型平面裁剪</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
      //---------------------对空间数据模型进行裁剪代码演示--------------------
       Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
      var viewer = new Cesium.Viewer("cesium-container", {
          // terrainProvider: new window.Cesium.CesiumTerrainProvider({
          //     url: 'https://tiles.geovis.online/base/v1/terrain?token=fd2cddddcf70866a1a334a47b78b8cba1941af00c93b3a97e49c65ab5182922a',
          //     requestWaterMask: true,
          //     requestVertexNormals: true
          // }),
          imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
            url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
          }),
          sceneMode: Cesium.SceneMode.SCENE3D,
          vrButton: false,
          animation: false,
          baseLayerPicker: false,
          geocoder: false,
          timeline: false,
          fullscreenButton: false,
          homeButton: false,
          creditContainer: document.createElement('div'),
          infoBox: false,
          navigationHelpButton: false,
          sceneModePicker: false,
          scene3DOnly: true,
          // skyBox: new Cesium.SkyBox({
          //   sources: {
          //     positiveX: require('@/assets/images/tycho2t3_80_px.jpg'),
          //     negativeX: require('@/assets/images/tycho2t3_80_mx.jpg'),
          //     positiveY: require('@/assets/images/tycho2t3_80_py.jpg'),
          //     negativeY: require('@/assets/images/tycho2t3_80_my.jpg'),
          //     positiveZ: require('@/assets/images/tycho2t3_80_pz.jpg'),
          //     negativeZ: require('@/assets/images/tycho2t3_80_mz.jpg'),
          //   }
          // })
        });
      //--------------------1、加载官网3dtiles模型、定位-----------------------------
  var tileset = viewer.scene.primitives.add(//发现这里的加载方法并不是之前常用的entities,这是因为3DTiles并不是Entity的一部分,而是属于更加底层的primitives
      new Cesium.Cesium3DTileset({
        url: "./data/示例建筑/tileset.json",//此模型使用的是b3dm格式的瓦片集,主要用于加载批量的模型,除此以外还有pnts格式瓦片集,用于加载点云数据模型;cmpt格式瓦片集,允许一个cmpt文件内嵌多个其他类型的瓦片
        maximumScreenSpaceError: 2,//最大的屏幕空间误差,数字越低,视觉效果越好
        maximumNumberOfLoadedTiles: 1000,//最大加载瓦片个数,用于给定一定的限制,防止数据量过大,占用内存过高
        id: "frq"
      })
    )
    tileset.readyPromise.then((tileset) => {//模型加载完毕后的回调
      viewer.zoomTo(tileset,//将视角转到加载的3dtiles处
        new Cesium.HeadingPitchRange(0.0, -0.5, tileset.boundingSphere.radius * 2.0));//设置相机视角,tileset.boundingSphere.radius是3dtiles的模型包围球
    });
     setTimeout(() => {
       //裁剪平面移动量
       let targetY = 0.0;
       //当前选中的裁剪平面
       let selectedPlane;

       //鼠标左键按下操作句柄
       const downHandler = new Cesium.ScreenSpaceEventHandler(
         viewer.scene.canvas
       );
       downHandler.setInputAction(function (movement) {
         //拾取裁剪面实体
         const pickedObject = viewer.scene.pick(movement.position);
         if (
           Cesium.defined(pickedObject) &&
           Cesium.defined(pickedObject.id) &&
           Cesium.defined(pickedObject.id.plane)
         ) {
           //鼠标左键按下,并拾取到裁剪平面实体时,设置裁剪平面实体的颜色,边线,并将相机控制输入关闭
           selectedPlane = pickedObject.id.plane;
           selectedPlane.material = Cesium.Color.WHITE.withAlpha(0.05);
           selectedPlane.outlineColor = Cesium.Color.YELLOW;
           viewer.scene.screenSpaceCameraController.enableInputs = false;//屏蔽相机的相关操作,避免与裁剪操作冲突
         }
       }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

       //鼠标左键抬起操作句柄
       const upHandler = new Cesium.ScreenSpaceEventHandler(
         viewer.scene.canvas
       );
       upHandler.setInputAction(function () {
         if (Cesium.defined(selectedPlane)) {
           //鼠标左键抬起时,设置裁剪平面实体的颜色、边线、销毁当前选中的裁剪平面实体
           selectedPlane.material = Cesium.Color.RED.withAlpha(0.1);
           selectedPlane.outlineColor = Cesium.Color.WHITE;
           selectedPlane = undefined;
         }
         //恢复相机控制输入
         viewer.scene.screenSpaceCameraController.enableInputs = true;
       }, Cesium.ScreenSpaceEventType.LEFT_UP);

       //鼠标移动操作句柄
       const moveHandler = new Cesium.ScreenSpaceEventHandler(
         viewer.scene.canvas
       );
       moveHandler.setInputAction(function (movement) {
         if (Cesium.defined(selectedPlane)) {
           //鼠标移动后,计算移动量,并将移动量叠加到targetY
           const deltaY = movement.startPosition.y - movement.endPosition.y;
           targetY += deltaY;
         }
       }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

       //为模型数据集设置裁剪平面
       tileset.clippingPlanes = new Cesium.ClippingPlaneCollection({
         planes: [
           //设置一个垂直于Z轴的裁剪平面
           new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 0.0, -1.0), 0)//-1.0露出的是裁剪平面的下半部分,这里如果设置成1,就是露出裁剪平面的上半部分
         ],
         edgeWidth: 3
       });

       var boundingSphere = tileset.boundingSphere;
       var radius = tileset.boundingSphere.radius;

       //遍历数据集的裁剪平面,构建裁剪平面实体
       for (let i = 0; i < tileset.clippingPlanes.length; i++) {
         let plane = tileset.clippingPlanes.get(i)
         viewer.entities.add({
           position: boundingSphere.center,
           plane: {
             //平面的范围(边界)
             dimensions: new Cesium.Cartesian2(
               radius * 2.5,
               radius * 2.5
             ),
             material: Cesium.Color.WHITE.withAlpha(0.1),
             plane: new Cesium.CallbackProperty(
               () => {
                 plane.distance = targetY;
                 return plane;
               },
               false
             ),
             outline: true,
             outlineColor: Cesium.Color.WHITE,
           },
         });
       }
    // viewer.trackedEntity = entity;//将摄像头设置在圆点处(track跟踪)
     }, 3000);
    //---------------------空间数据模型进行裁剪代码演示结束--------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

5、体渲染

(1)、什么是体渲染
Cesium体渲染是一种用于可视化三维数的技术可以将三维数转换为通真的图像或动画,一种用于渲染三维场景中的大量粒子和体积数据的技术。它可以用于显示气象数据、医学影像、烟雾、火等多种类型的数据。

Cesium体渲染的核心是基于GPU的体积渲染技术。它利用OpenGL或WebGL等图形API将体积数据交给GPU进行处理,实现了高效、实时的渲染效果。Cesium体渲染的特点包括:

实时性。Cesium体渲染可以实现实时渲染大量的体积数据,适用于需要实时交互的场景。

真实感。通过逼真的光照和阴影效果,Cesium体渲染可以使渲染出的场景更加真实。

可定制性。Cesium体渲染提供了丰富的用户可定制选项,用户可以自定义颜色、透明度、光照、阴影等等。

跨平台。Cesium体渲染可以在不同的平台上运行,包括桌面应用程序、移动应用程序和Web应用程序等。

总之,Cesium体渲染技术可以为用户提供高效、实时、真实感和可定制性的三维场景渲染体验。

(2)、体染和模型染的区别
体渲染和模型渲染是两种不同的三维渲染技术

1、体染是一种用于可视化三维数据的技术,可以将三维数据转换为通真的图像或动,在体染中通常会使用体元素( Voxel) 来表示三维数据的每个点,然后通过对体元素共行光线追凉或体积光照射等绿作来生成通真的效果,体谊染可以显示一维很的内部结构和特征,例显示医学图像中的器官、显示她质教中的岩层、显示气象数挥中的元层等等。

2、模型渲染是一种用于显示三维模型的技术,可以将三维模型转换为逼真的图像或动画,在模型染中,通常会使用多边形网格来表示三维型的表面,然后通过对多边形网格进行光照、纹理映射等操作来生成逼真的效果。模型渲染可以显示三维模型的外观和形状,例如显示建筑物、人物、车辆等等。因此,体谊染和模型值染是两种不同的三维值染技术,体海染用于可视化三维数唱的内部结构和特征,而模型染用于显示=维模型的外观和形状,在实际应用中,体信染和模型演染可以结合使用,例如在医学图像中显示人体器官的内部结构,或在建筑模型中显示建筑物内部的结构和设施。

九、鼠标交互,数据查询

1、鼠标左键点击图片后提示弹框

代码如下:

var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)//使用Cesium的ScreenSpaceEventHandler方法,创建了一个屏幕控制实例,其中viewer.scene.canvas获取scene下的所有canvas创建的元素
handler.setInputAction(function (movement){//再使用setInputAction方法进行监听,可以监听鼠标相关事件,第一个参数放入回调函数,第二个参数是监听的具体是鼠标的哪个事件
  var pick = viewer.scene.pick(movement.position);//创建一个变量,通过viewer.scene.pick可以获取到点击的对象的位置信息
  if(Cesium.defined(pick) && pick.id.id==="image_id"){
    alert("点击了图片!")
  } //并对其判断,并要同时满足,当前获取对象的id号是否和之前绑定的image_id一致
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
/**
 补充说明:Cesium给我们提供了好几种获取不同对象的方法
//scene.pick返回的是包含给定窗口位置基元的对象,
//scene.drillpick返回的是给定窗口位置所有对象的列表,
//Globe.pick返回的是给定光线和地形的交点,
//际此以外 Cesium还提供了多种鼠标监听可供我们选择:
//鼠标中键点击事件: Cesium.ScreenSpaceEventType.MIDDLE_CLICK
//鼠标移入事件: Cesium.ScreenSpaceEventType.MOUSE_MOVE
//鼠标右击事件: Cesium.ScreenSpaceEventType.RIGHT_CLICK
**/

效果如下:
在这里插入图片描述

2、自定义弹框

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第四课,鼠标交互,数据查询</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表
    });
    //----------------------------空间数据加载,---------------------------
    const position=Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 1000) //定义常量,用于存储飞行目的地的坐标
    //setView 直接跳转,没有过程
    viewer.camera.setView({//通过相机系统设置视角
      destination:position,//设置相机目的地
      orientation:{//设置相机视口的方向
        heading:Cesium.Math.toRadians(0),//控制视口的水平旋转,也就是沿着y轴旋转,当数值为0时,代表正北方向
        pitch:Cesium.Math.toRadians(-90),//控制视口的上下旋转,即沿x轴进行旋转,当数值为-90,代表俯视地面
        roll:0,//控制视口的反转角度,即沿着z轴进行旋转,数值为0表示不翻转
      },
    })

    /** 通常除了简单的图形、颜色加载以外,更多时候需要定制化的样式,通过代码看下他的可选配置项 **/
    //地球加载一个图片
    const entity_image = viewer.entities.add({//创建一个Entity点对象
      id:"image_id",//id标识
      position: Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 100),//设置经纬度和高度
      plane: {//用于绘制一个面
        plane: new Cesium.Plane(Cesium.Cartesian3.UNIT_X, 0),//确认面的朝向,这里我们设置它朝X轴平铺
        dimensions: new Cesium.Cartesian2(200, 150),//设置面的长度和宽度
        material: "./data/a.png",//引入图片
        outline: true,//是否显示边框
        outlineColor: Cesium.Color.GREEN//边框线为黑色
      },
      description:`<div>
        <img width="100%" height:"300px" src="./data/a.png" />
        <h3>自定义点击弹框</h3>
        </div>`,//description属性用于设置选定对象的显示信息,在其中我们可以直接用HTML标签,将其包裹后,可以直接传到页面中,不需要额外设定样式,封装事件
    })

    
    var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)//使用Cesium的ScreenSpaceEventHandler方法,创建了一个屏幕控制实例,其中viewer.scene.canvas获取scene下的所有canvas创建的元素
    handler.setInputAction(function (movement){//再使用setInputAction方法进行监听,可以监听鼠标相关事件,第一个参数放入回调函数,第二个参数是监听的具体是鼠标的哪个事件
      var pick = viewer.scene.pick(movement.position);//创建一个变量,通过viewer.scene.pick可以获取到点击的对象的位置信息
      if(Cesium.defined(pick) && pick.id.id==="image_id"){
        alert("点击了图片!")
      } //并对其判断,并要同时满足,当前获取对象的id号是否和之前绑定的image_id一致
    },Cesium.ScreenSpaceEventType.LEFT_CLICK)
    /**
     补充说明:Cesium给我们提供了好几种获取不同对象的方法
    //scene.pick返回的是包含给定窗口位置基元的对象,
    //scene.drillpick返回的是给定窗口位置所有对象的列表,
    //Globe.pick返回的是给定光线和地形的交点,
    //际此以外 Cesium还提供了多种鼠标监听可供我们选择:
    //鼠标中键点击事件: Cesium.ScreenSpaceEventType.MIDDLE_CLICK
    //鼠标移入事件: Cesium.ScreenSpaceEventType.MOUSE_MOVE
    //鼠标右击事件: Cesium.ScreenSpaceEventType.RIGHT_CLICK
    **/

    //------------------------------空间数据加载演示结束------------------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

总结:其实弹窗显示数据,本质上就是一种空间信息数据的查询,是用户与客户端信息交互的最典型功能

3、分屏卷帘功能

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第八课,分屏卷帘功能</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
    #slider {
      width: 5px;
      height: 100%;
      position: absolute;
      left: 50%;
      background-color: #3370FF;
      z-index: 10;
  }
  </style>
</head>

<body>
  <div id="cesium-container"><div id="slider"></div></div>
  <script>
      //---------------------分屏卷帘功能代码演示--------------------
      Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
        viewer = new Cesium.Viewer("cesium-container", {
          imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
            url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
          }),
          sceneMode: Cesium.SceneMode.SCENE3D,
          vrButton: false,
          animation: false,
          baseLayerPicker: false,
          geocoder: false,
          timeline: false,
          fullscreenButton: false,
          homeButton: false,
          creditContainer: document.createElement('div'),
          infoBox: false,
          navigationHelpButton: false,
          sceneModePicker: false,
          scene3DOnly: true
        });
      let left = viewer.scene.primitives.add(
        new Cesium.Cesium3DTileset({
          url: Cesium.IonResource.fromAssetId(69380),
        })
      );
      //------------开始分屏,主要用到SplitDirection、.scene.splitPosition这两个接口
      left.splitDirection = Cesium.SplitDirection.LEFT;

      let right = viewer.scene.primitives.add(Cesium.createOsmBuildings());
      right.splitDirection = Cesium.SplitDirection.RIGHT;

      viewer.zoomTo(left);

        // Sync the position of the slider with the split position
      const slider = document.getElementById("slider");
      viewer.scene.splitPosition =
        slider.offsetLeft / slider.parentElement.offsetWidth;

      let handler = new Cesium.ScreenSpaceEventHandler(slider);

      let moveActive = false;

      function move(movement) {
        if (!moveActive) {
          return;
        }

        let relativeOffset = movement.endPosition.x;
        let splitPosition =
          (slider.offsetLeft + relativeOffset) /
          slider.parentElement.offsetWidth;
        slider.style.left = `${100.0 * splitPosition}%`;
        viewer.scene.splitPosition = splitPosition;
      }

      handler.setInputAction(function () {
        moveActive = true;
      }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
      handler.setInputAction(function () {
        moveActive = true;
      }, Cesium.ScreenSpaceEventType.PINCH_START);

      handler.setInputAction(move, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
      handler.setInputAction(move, Cesium.ScreenSpaceEventType.PINCH_MOVE);

      handler.setInputAction(function () {
        moveActive = false;
      }, Cesium.ScreenSpaceEventType.LEFT_UP);
      handler.setInputAction(function () {
        moveActive = false;
      }, Cesium.ScreenSpaceEventType.PINCH_END);
    //---------------------分屏卷帘功能演示结束--------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

十、三维数据3dtiles

1、简单加载

(1)、它是Cesium于2016年3月定义的一种三维模型瓦片数据结构。它将海量的三维数据用分块、分层的形式组织起来,这样就很大程度上减轻了浏览器的负担,除此以外还提供了细节层次的LOD功能。在远观时,降低模型的面数和精度,拉近后再将细节加载出来,大大增强了页面的加载速度,更可以用于跨桌面使用,使得Web端和移动应用程序共享。
在这里插入图片描述

(2)、那么3D Tiles与其他数据对比叉有什么特点呢?

其一,它是一个开放式的数据规范,我们可以根据实际需求设定三维模型的大小和范围,此外还能适配多种空间分区方案,如普通网格、四叉树、八叉树等;
这里简单了解一下四叉树和八又树的数据结构:
四叉树是一种树形数据结构,它的每个节点可以有四个子节点,通常把二维空间细分为四个区域,并把该区域里的相关信息存入到四叉树节点中,四叉树的每一个节点代表一个矩形区域,每一个矩形区域叉可划分为四个小矩形区域
在这里插入图片描述
较之四叉树,八又树将场景从二维空间延伸到了三维空间
在这里插入图片描述
树中的子节点只会有八个或者零个,每一个节点同样用于存储数据。
其二,它的异质性支持,可以将不同类型的号维模型数据,如普通模型数据加倾斜摄影数据加自绘几何数据放在一起,转化为同一标准的数据集,让它们可以在同一场景下显示出来。
其三,它是专门为三维可视化设计的,并在其中引入了图形领域的技术,在不满足特定条件的情况下,并不会对场景内的模型做整个渲染,而是只会渲染个轮廓,大大降低了计算量,使得浏览器请求到数据以后,渲染的流程也更加的简单。同时。因为三维模型预先处理成了分块的三维瓦片格式,所以也减少了WebGL绘制请求的数量。
其四,它的可交互性,其支持对加载模型的拾取,和样式的修改,大量加载以后,可以对其中的单独模型进行交互。如高亮显示鼠标悬停处的模型、或进行删除等;也可根据建筑模型的高度和年代,设置不同的显示效果而不需要重新更新代码。

(3)、接下来我们来了解一下,3DTiles的代码究竟是如何构成的

一个简单的3dtiles文件夹结构如下:
在这里插入图片描述
可以看到除了json文件以外,还有三个b3dm格式的文件,它就是我们用于渲染数据的文件。
如上图:3DTiles的格式是由两个部分组成的,其一就是现在看到的tileset.json格式的数据,我们先来了解一下它的格式。
在这里插入图片描述
这里的asset是一个包含整体tileset元素属性的对象,其中的version属性是定义3DTiles版本的字符串,此外这里还可以选填一个

tilesetVersion属性,它可以用于定义特定应用中的版本号。

geometricError:其定义了一个非误差单位,低于这个误差值,瓦片集不会被渲染,这里设定的500,它的单位是米。

root属性用于定义根瓦片,它的子项transform也是一个可选项,它的作用是在加载大量模型或者建筑物的情况下,单个模型的点云瓦片集能在它自己的坐标系中定义,其内的数据是一个4X4的仿射变换矩阵,以列主序存储,用来实现从瓦片局部坐标系到父瓦片或根瓦片坐标系的变换。

root内部也有一个同样的属性,前者是整个瓦片集不被渲染的误差,后者只是当前瓦片集被渲染的误差。content属性通过ur引入文件,这里引入的文件就是瓦片的内容,它的数据是二进制的,其支持的二进制文件格式有:.b3dm、.i3dm、.pnts等。甚至可以在其中再放入一个3DTiles文件,前提是不可以自己引用给自己,content上方的refine属性定义的是LOD细化的方法,简单来说就是瓦片是如何切换的。

下方的children属性比较好理解,因为3DTiles是分级别的,所以每个Tile还会有子Tile、子子Tile。构成3D Tiles的第二个部分就是其引用的瓦片数据文件了。

(4)、来通过代码看一下3dtiles是如何引用的:此模型使用的是b3dm格式的瓦片集,主要用于加载批量的模型,除此以外还有pnts格式瓦片集,用于加载点云数据模型;cmpt格式瓦片集,允许一个cmpt文件内嵌多个其他类型的瓦片

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第五课,加载3dtiles</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
      animation: false,//动画小组件
      timeline: false,//左下角仪表
    });
    //----------------------------加载3dtiles,---------------------------
  var tileset=viewer.scene.primitives.add(//发现这里的加载方法并不是之前常用的entities,这是因为3DTiles并不是Entity的一部分,而是属于更加底层的primitives
    new Cesium.Cesium3DTileset({
      url:"./data/dtiles/tileset.json",//模型地址
      maximumScreenSpaceError:2,//最大的屏幕空间误差,数字越低,视觉效果越好
      maximumNumberOfLoadedTiles:1000,//最大加载瓦片个数,用于给定一定的限制,防止数据量过大,占用内存过高
    })
  )
  
  tileset.readyPromise.then((tileset) => {//模型加载完毕后的回调
   // 调整模型高度,贴合地形,地形是30m精度,只能做到尽可能贴合
      var boundingSphere = tileset.boundingSphere;
      var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
      var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
      var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude,-100);
      var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
      tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
      viewer.zoomTo(tileset,//将视角转到加载的3dtiles处
    viewer.zoomTo(tileset);
  });
    //------------------------------加载3dtiles------------------------------
  </script>
</body>
</html>

2、调整3dtiles模型高度

(1)、直接上代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>加载3dtiles</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    let esri = new Cesium.ArcGisMapServerImageryProvider({
      url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
    })//用一个变量存储新地图的信息,这里我们使用的是arcgis地图服务
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
        animation: true,//动画小组件
        timeline: true,//左下角仪表,
        baseLayerPicker: false,//默认的地图按钮设置为隐藏
        imageryProvider: esri,//viewer内的imageryProvider属性用于设置地图
        terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
          url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
          requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
          requestWaterMask: true,//可以增加水面特效
        }),
      });
    //----------------------------加载3dtiles,---------------------------
    viewer.scene.globe.depthTestAgainstTerrain = true;//开启地形深度检测
    var tileset=viewer.scene.primitives.add(//发现这里的加载方法并不是之前常用的entities,这是因为3DTiles并不是Entity的一部分,而是属于更加底层的primitives
    new Cesium.Cesium3DTileset({
      url:"./data/dtiles/tileset.json",//此模型使用的是b3dm格式的瓦片集,主要用于加载批量的模型,除此以外还有pnts格式瓦片集,用于加载点云数据模型;cmpt格式瓦片集,允许一个cmpt文件内嵌多个其他类型的瓦片
      maximumScreenSpaceError:2,//最大的屏幕空间误差,数字越低,视觉效果越好
      maximumNumberOfLoadedTiles:1000,//最大加载瓦片个数,用于给定一定的限制,防止数据量过大,占用内存过高
    })
  )
  tileset.readyPromise.then((tileset) => {//模型加载完毕后的回调
    viewer.zoomTo(tileset,//将视角转到加载的3dtiles处
    new Cesium.HeadingPitchRange(0.0,-0.5,tileset.boundingSphere.radius * 2.0));//设置相机视角,tileset.boundingSphere.radius是3dtiles的模型包围球
  });
  //调用将模型调高的方法,每秒钟上升一点
  let height=0
  setInterval(() => {
    adjustTitlesetHight(tileset, height++);
  }, 1000);

 function adjustTitlesetHight(tileset,height=10) {
    //计算出模型包围球的中心点(弧度制)
    const cartographic = Cesium.Cartographic.fromCartesian(
      tileset.boundingSphere.center
    );
    //计算与模型包围球中心点经纬度相同的地表点位
    const surface = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      0.0
    );
    //计算调整高度后的模型包围球的中心点
    const offset = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      height
    );
    //计算差向量
    const translation = Cesium.Cartesian3.subtract(
      offset,
      surface,
      new window.Cesium.Cartesian3()
    );
    //修改模型的变换矩阵
    tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
  }
    //------------------------------加载3dtiles------------------------------
  </script>
</body>
</html>

效果如下:
在这里插入图片描述

3、点选3dtiles模型要素高亮

要素拾取、自定义弹窗、后期处理等功能的实现,主要涉及的接口:PostProcessStageLibrarv、PostProcessStageLibrary.createEdgeDetectionStage、 scene.pick。
其中后期处理阶段在Cesium中是指:当我们演染一三维场景时,通常需要经过多个步骤,
首先是将场景中的几何体进行裁、投影和变换等处理,然后将其染到屏幕上,但是,在这个过程中,我们还可以在染结果上添加一些额外的处理步骤. 这些处理步骤通常被称为“后期处理阶段”。在Cesium框架中,后期处理阶段通常用于实现一些高级的图像特效。例如:
1.景深效果,通过模拟摄像机的焦距和光圈大小,可以让场景中的物体产生前景和背景的模湖效果,从而增强逼真度。
2.泛光效果,通过将明亮区域扩散到周围区域,可以营造出一种柔和的光照效果,从而增强亮度感和立体感。
3.环境光遮蔽效果:通过计算场景中每个点受到的环境光线量,可以产生一种更加真实的遮蔽效果,从而增强细节和质感。
需要注意的是,后期处理阶段通常会增加渲染时间,并对GPU性能有一定的要求,因此,在使用后期处理阶段时需要进行合理的优化和配置,以达到最佳的性能和效果。

案例一:效果要求:对3DTiles模型的要素进行拾取,对拾取的要素进行属性信息读取和修改要素颜色

代码如下:

/**
加载3dtiles的代码同上,下面只贴上要素拾取部分代码
*/
 //-------------------对3DTiles模型的要素进行拾取,对拾取的要素进行属性信息读取和修改要素颜色-------------
  //ScreenSpaceEventHandler、ScreenSpaceEventType、 Cesium3DTileset、Cesium3DTile Cesium3DTileFeature
  //请介绍下cesium中Cesium3DTileset、Cesium3DTile、Cesium3DTileFeature这二个类的关系 ?
  //Cesium3DTileset、Cesium3DTile、Cesium3DTileFeature是Cesium中用于加载、染和操作3D Tile数据的重要类,
  //它们之间的关系如下: 1,cesium3DTileset是一个最高级别的类: 表示一个3DTiles数据集、
  //Cesium3DTileset实以通过Cesum的ilese类建,并以通过url或者json数据加载3DTiles。
  //Cesium3DTileset负责管理整个3D Tile数据集,并对其中的每个3D Tile进行加载、解析和渲染。
  //Cesium3DTile是Cesium3DTileset中的一个单独的3D Tile象,它表示一个有几何体和纹理的3D模型,
  //Cesium3DTile对象可以包含多个几何体,每几何体又包含多个几何属性(如位置法线、纹理坐标等) 。
  //cesium3DTile对象负责管理自身的几体和纹理,并且可以通过Cesium3DTleset的方法进行加载、卸载和染。
  //Ceslum3DTilefeature是Cesium3DTle中的一个要素对象,表示 - 3D Tile中的一个具体对象(如建筑、树木、汽车等) ,
  //Cesium3DTileFeature对象可以包含多个属性和几何信息,可以通过Cesium3DTile的方法进行查询和渲染。
  //因此,(esium3DTileset是最高级别的类,包含多Cesium3DTile对象,而每Cesium3DTile对象又可以包合多Cesium3DTileFeature对象,
  //这三个类一起构成了Cesium中3D Tile数据的层次结构,提供了高效的3D模型加载和渲染功能。
   //选中的要素对象
    let selectedFeature
    //交互句柄
    const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
    handler.setInputAction(function (movement) {
      //将上次选中的要素的颜色重置
      if (selectedFeature) {
        selectedFeature.color = Cesium.Color.WHITE
      }
      //拾取要素
      selectedFeature = viewer.scene.pick(movement.position);
      if (selectedFeature && selectedFeature instanceof Cesium.Cesium3DTileFeature) {
        let obj = {}
        //获取要素属性信息
        selectedFeature.getPropertyNames().forEach(name => {
          console.log("打印下name", name)
          obj[name] = selectedFeature.getProperty(name)
        });
        //设置要素颜色
        selectedFeature.color = Cesium.Color.AQUA
        setTimeout(() => {
          alert(JSON.stringify(obj))
        }, 500)
      } 
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    /**
     请详细介绍下你上面说的和orimmitive的区别?
        Cesium3DTileFeature和primitive都是Cesium中用于渲染3D场景的对象,但它们的作用和使用方式有所不同cesium3DTlefeature
        是Ceslum中用于表示3D Tile中-人具体对象的要素对象,它有含了3D Tle中-对象的何和屋性信息。
        Cesium3DTileFeature对象可以讨leset.oetFeatureByName、tleset.aetfeatureBytile tlesetetFeatureBvUniouelD等法来获取,
        通常用于左3D场昌中对3D Tle中的具体对象进行询、操作和primitive则是Cesium中用于表示3D场景中的图元primitive) ,
        它包含了3D场景中的何信园和材质信息,esium中支持多种类型的primtie,例如BoxPrimitiveEIipsoid
        PrimitivePolvgonPrimitive等,每种primitive都有自己的性和法,用于控制其外观和行为,通常情况下,
        用户需要先构造-primitive对象,然后将其添加到3D场景中进行渲染。
        Cesium3DTileFeature对象和orimitve象的关系可以简单理解为,esium3DTleFeature对象是3D Tile中具体一个抽象表示,
        而orimitive对象是3D场景中的-人大图元。在Cesium中,(eslum3DTilefeture对象和ormte对象经常重要进转和结合康用,
        Cesum3DTleFeature对象获信息,并使用这些信息创primitive对象进行染,又例,在选中某个Cesium3DTileFeature对象时,
        通常需要在场景中为其创建-primitive对象以便进行视觉上的反馈熏要注意的是,Cesium3DTlefeature对象和primitive对象
        虽然有相似之处,但它们的使用方式和屋性方法都有所不同,因此在使用时雪要注意区分, 
    */

效果如下图:
在这里插入图片描述
案例二:鼠标经过模型时模型出现蓝色边框,鼠标点击模型,模型出现绿色边框。

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第五课扩展,要素拾取复杂案例</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
   Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
    window.viewer = new window.Cesium.Viewer("cesium-container", {
      terrainProvider: new window.Cesium.CesiumTerrainProvider({
        url: 'https://tiles.geovis.online/base/v1/terrain?token=fd2cddddcf70866a1a334a47b78b8cba1941af00c93b3a97e49c65ab5182922a',
        requestWaterMask: true,
        requestVertexNormals: true
      }),
      imageryProvider: new window.Cesium.ArcGisMapServerImageryProvider({
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
      }),
      sceneMode: window.Cesium.SceneMode.SCENE3D,
      vrButton: false,
      animation: false,
      baseLayerPicker: false,
      geocoder: false,
      timeline: false,
      fullscreenButton: false,
      homeButton: false,
      creditContainer: document.createElement('div'),
      infoBox: true,
      navigationHelpButton: false,
      sceneModePicker: false,
      scene3DOnly: true
    });
    window.viewer.scene.globe.depthTestAgainstTerrain = true;

    // Set the initial camera view to look at Manhattan
    const initialPosition = window.Cesium.Cartesian3.fromDegrees(
      -74.01881302800248,
      40.69114333714821,
      753
    );
    const initialOrientation = new window.Cesium.HeadingPitchRoll.fromDegrees(
      21.27879878293835,
      -21.34390550872461,
      0.0716951918898415
    );
    window.viewer.scene.camera.setView({
      destination: initialPosition,
      orientation: initialOrientation,
      // endTransform: window.Cesium.Matrix4.IDENTITY,
    });

    // Load the NYC buildings tileset
    const tileset = new window.Cesium.Cesium3DTileset({
      url: window.Cesium.IonResource.fromAssetId(75343),
    });
    window.viewer.scene.primitives.add(tileset);

    // HTML overlay for showing feature name on mouseover
    const nameOverlay = document.createElement("div");
    window.viewer.container.appendChild(nameOverlay);
    nameOverlay.className = "backdrop";
    nameOverlay.style.display = "none";
    nameOverlay.style.position = "absolute";
    nameOverlay.style.bottom = "0";
    nameOverlay.style.left = "0";
    nameOverlay.style.color = "white";
    nameOverlay.style["pointer-events"] = "none";
    nameOverlay.style.padding = "4px";
    nameOverlay.style.backgroundColor = "black";

    // Information about the currently selected feature
    const selected = {
      feature: undefined,
      originalColor: new window.Cesium.Color(),
    };

    // An entity object which will hold info about the currently selected feature for infobox display
    const selectedEntity = new window.Cesium.Entity();

    // Get default left click handler for when a feature is not picked on left click
    // const clickHandler = window.viewer.screenSpaceEventHandler.getInputAction(
    //     window.Cesium.ScreenSpaceEventType.LEFT_CLICK
    // );

    // If silhouettes are supported, silhouette features in blue on mouse over and silhouette green on mouse click.
    // If silhouettes are not supported, change the feature color to yellow on mouse over and green on mouse click.
    if (window.Cesium.PostProcessStageLibrary.isSilhouetteSupported(window.viewer.scene)) {
      // Silhouettes are supported
      const silhouetteBlue = window.Cesium.PostProcessStageLibrary.createEdgeDetectionStage();//创建边缘检测
      silhouetteBlue.uniforms.color = window.Cesium.Color.BLUE;
      silhouetteBlue.uniforms.length = 0.01;
      silhouetteBlue.selected = [];//这个selected有哪个模型,哪个模型就是蓝色边框

      const silhouetteGreen = window.Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
      silhouetteGreen.uniforms.color = window.Cesium.Color.LIME;
      silhouetteGreen.uniforms.length = 0.01;
      silhouetteGreen.selected = [];

      window.viewer.scene.postProcessStages.add(
        window.Cesium.PostProcessStageLibrary.createSilhouetteStage([
          silhouetteBlue,
          silhouetteGreen,
        ])
      );

      // Silhouette a feature blue on hover.
      window.viewer.screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
        // If a feature was previously highlighted, undo the highlight
        silhouetteBlue.selected = [];

        // Pick a new feature
        const pickedFeature = window.viewer.scene.pick(movement.endPosition);
        if (!window.Cesium.defined(pickedFeature)) {
          nameOverlay.style.display = "none";
          return;
        }

        // A feature was picked, so show it's overlay content
        nameOverlay.style.display = "block";
        nameOverlay.style.bottom = `${window.viewer.canvas.clientHeight - movement.endPosition.y
          }px`;
        nameOverlay.style.left = `${movement.endPosition.x}px`;
        const name = pickedFeature.getProperty("BIN");
        nameOverlay.textContent = name;

        // Highlight the feature if it's not already selected.
        if (pickedFeature !== selected.feature) {//?????????有问题
          silhouetteBlue.selected = [pickedFeature];
        }
      }, window.Cesium.ScreenSpaceEventType.MOUSE_MOVE);

      // Silhouette a feature on selection and show metadata in the InfoBox.
      window.viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
        // If a feature was previously selected, undo the highlight
        silhouetteGreen.selected = [];

        // Pick a new feature
        const pickedFeature = window.viewer.scene.pick(movement.position);
        if (!window.Cesium.defined(pickedFeature)) {
          // clickHandler(movement);
          return;
        }

        // Select the feature if it's not already selected
        if (silhouetteGreen.selected[0] === pickedFeature) {//???????有问题
          return;
        }

        // Save the selected feature's original color
        const highlightedFeature = silhouetteBlue.selected[0];
        if (pickedFeature === highlightedFeature) {
          silhouetteBlue.selected = [];
        }

        // Highlight newly selected feature
        silhouetteGreen.selected = [pickedFeature];

        // Set feature infobox description//下面是自定义弹框
        const featureName = pickedFeature.getProperty("name");
        selectedEntity.name = featureName;
        selectedEntity.description =
          'Loading <div class="cesium-infoBox-loading"></div>';
        window.viewer.selectedEntity = selectedEntity;
        selectedEntity.description =
          `${'<table class="cesium-infoBox-defaultTable"><tbody>' +
          "<tr><th>BIN</th><td>"
          }${pickedFeature.getProperty("BIN")}</td></tr>` +
          `<tr><th>DOITT ID</th><td>${pickedFeature.getProperty(
            "DOITT_ID"
          )}</td></tr>` +
          `<tr><th>SOURCE ID</th><td>${pickedFeature.getProperty(
            "SOURCE_ID"
          )}</td></tr>` +
          `</tbody></table>`;
      }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK);
    }
            // else {
            //     // Silhouettes are not supported. Instead, change the feature color.

            //     // Information about the currently highlighted feature
            //     const highlighted = {
            //         feature: undefined,
            //         originalColor: new window.Cesium.Color(),
            //     };

            //     // Color a feature yellow on hover.
            //     window.viewer.screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
            //         // If a feature was previously highlighted, undo the highlight
            //         if (window.Cesium.defined(highlighted.feature)) {
            //             highlighted.feature.color = highlighted.originalColor;
            //             highlighted.feature = undefined;
            //         }
            //         // Pick a new feature
            //         const pickedFeature = window.viewer.scene.pick(movement.endPosition);
            //         if (!window.Cesium.defined(pickedFeature)) {
            //             nameOverlay.style.display = "none";
            //             return;
            //         }
            //         // A feature was picked, so show it's overlay content
            //         nameOverlay.style.display = "block";
            //         nameOverlay.style.bottom = `${window.viewer.canvas.clientHeight - movement.endPosition.y
            //             }px`;
            //         nameOverlay.style.left = `${movement.endPosition.x}px`;
            //         let name = pickedFeature.getProperty("name");
            //         if (!window.Cesium.defined(name)) {
            //             name = pickedFeature.getProperty("id");
            //         }
            //         nameOverlay.textContent = name;
            //         // Highlight the feature if it's not already selected.
            //         if (pickedFeature !== selected.feature) {
            //             highlighted.feature = pickedFeature;
            //             window.Cesium.Color.clone(
            //                 pickedFeature.color,
            //                 highlighted.originalColor
            //             );
            //             pickedFeature.color = window.Cesium.Color.YELLOW;
            //         }
            //     }, window.Cesium.ScreenSpaceEventType.MOUSE_MOVE);

            //     // Color a feature on selection and show metadata in the InfoBox.
            //     window.viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
            //         // If a feature was previously selected, undo the highlight
            //         if (window.Cesium.defined(selected.feature)) {
            //             selected.feature.color = selected.originalColor;
            //             selected.feature = undefined;
            //         }
            //         // Pick a new feature
            //         const pickedFeature = window.viewer.scene.pick(movement.position);
            //         if (!window.Cesium.defined(pickedFeature)) {
            //             clickHandler(movement);
            //             return;
            //         }
            //         // Select the feature if it's not already selected
            //         if (selected.feature === pickedFeature) {
            //             return;
            //         }
            //         selected.feature = pickedFeature;
            //         // Save the selected feature's original color
            //         if (pickedFeature === highlighted.feature) {
            //             window.Cesium.Color.clone(
            //                 highlighted.originalColor,
            //                 selected.originalColor
            //             );
            //             highlighted.feature = undefined;
            //         } else {
            //             window.Cesium.Color.clone(pickedFeature.color, selected.originalColor);
            //         }
            //         // Highlight newly selected feature
            //         pickedFeature.color = window.Cesium.Color.LIME;
            //         // Set feature infobox description
            //         const featureName = pickedFeature.getProperty("name");
            //         selectedEntity.name = featureName;
            //         selectedEntity.description =
            //             'Loading <div class="cesium-infoBox-loading"></div>';
            //         window.viewer.selectedEntity = selectedEntity;
            //         selectedEntity.description =
            //             `${'<table class="cesium-infoBox-defaultTable"><tbody>' +
            //             "<tr><th>BIN</th><td>"
            //             }${pickedFeature.getProperty("BIN")}</td></tr>` +
            //             `<tr><th>DOITT ID</th><td>${pickedFeature.getProperty(
            //                 "DOITT_ID"
            //             )}</td></tr>` +
            //             `<tr><th>SOURCE ID</th><td>${pickedFeature.getProperty(
            //                 "SOURCE_ID"
            //             )}</td></tr>` +
            //             `<tr><th>Longitude</th><td>${pickedFeature.getProperty(
            //                 "longitude"
            //             )}</td></tr>` +
            //             `<tr><th>Latitude</th><td>${pickedFeature.getProperty(
            //                 "latitude"
            //             )}</td></tr>` +
            //             `<tr><th>Height</th><td>${pickedFeature.getProperty(
            //                 "height"
            //             )}</td></tr>` +
            //             `<tr><th>Terrain Height (Ellipsoid)</th><td>${pickedFeature.getProperty(
            //                 "TerrainHeight"
            //             )}</td></tr>` +
            //             `</tbody></table>`;
            //     }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK);
            // }
    /**----------------------------加载3dtiles,---------------------------*/
 
    /**------------------------------加载3dtiles结束------------------------------*/
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

4、3dtiles batch table

代码如下:其实代码和点选3dtiles模型要素高亮、鼠标左键点击图片后提示弹框一样

/**
加载3dtiles的代码同上,下面只贴上要素拾取部分代码
*/
  //------------------------------3dtiles batch table代码演示-------------------------------
  //定义样式数组
  let styleArr = [
     //根据建筑名称字段
     {
       color: {
         conditions: [
           ["${building_name} === 'building0'", "color('purple')"],
           ["${building_name} === 'building1'", "color('red')"],
           ["${building_name} === 'building2'", "color('orange')"],
           ["true", "color('blue')"],
         ],
       },
     },
     //根据高度字段
     {
       color: {
         conditions: [
           ["${height} >= 10", "color('purple')"],
           ["${height} >= 6", "color('red')"],
           ["${height} >= 5", "color('orange')"],
           ["true", "color('blue')"],
         ],
       },
     },
     //根据要素类
     {
       color: {
         conditions: [
           ["isClass('door')", "color('orange')"],
           ["true", "color('white')"],
         ],
       },
     }

   ]
   //修改模型的样式
   tileset.style = new Cesium.Cesium3DTileStyle(styleArr[2]);
    //交互句柄
     const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
     handler.setInputAction(function (movement) {
       //拾取要素
       let selectedFeature = viewer.scene.pick(movement.position);
       if (!selectedFeature) return
       let obj = {}
        //获取要素属性信息,低版本(1.89)是getPropertyNames,高版本是getPropertyIds(1.100)
       selectedFeature.getPropertyIds().forEach(name => {
          obj[name] = selectedFeature.getProperty(name)
        });
       setTimeout(() => {
         alert(JSON.stringify(obj))
       }, 500)
     }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

效果如下:
在这里插入图片描述
在这里插入图片描述

5、倾斜模型3dtiles分类渲染(类单体化)

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第九课,倾斜模型3dtiles分类渲染(类单体化)</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
      //---------------------倾斜模型3dtiles分类渲染(类单体化)代码演示--------------------
     Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
      window.viewer = new window.Cesium.Viewer("cesium-container", {
        imageryProvider: new window.Cesium.ArcGisMapServerImageryProvider({
          url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
        }),
        sceneMode: window.Cesium.SceneMode.SCENE3D,
        vrButton: false,
        animation: false,
        baseLayerPicker: false,
        geocoder: false,
        timeline: false,
        fullscreenButton: false,
        homeButton: false,
        creditContainer: document.createElement('div'),
        infoBox: false,
        navigationHelpButton: false,
        sceneModePicker: false,
        scene3DOnly: true
      });

     // A normal b3dm tileset containing photogrammetry
      const tileset = new Cesium.Cesium3DTileset({
        url: Cesium.IonResource.fromAssetId(40866),
      });
      viewer.scene.primitives.add(tileset);
      viewer.zoomTo(tileset);

      // A b3dm tileset used to classify the photogrammetry tileset
      let classificationTileset = new Cesium.Cesium3DTileset({
        url: "./data/Photogrammetry/tileset.json",
        classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
      });
      classificationTileset.style = new Cesium.Cesium3DTileStyle({
        color: "rgba(255, 0, 0, 0.5)",
      });
      viewer.scene.primitives.add(classificationTileset);
      viewer.zoomTo(classificationTileset);
    //---------------------倾斜模型3dtiles分类渲染(类单体化)结束--------------------
  </script>
</body>

</html>

效果如下:其实就是给倾斜数据贴模型,盒子模型紧紧的贴在倾斜表面,而不是简单的正方体包裹着
在这里插入图片描述

6、3dtiles交互

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十课,倾斜模型3dtiles交互</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
      //---------------------倾斜模型3dtiles交互代码演示--------------------
     Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzM2RjYjJlOC01ZTQwLTQwODYtOTEwMy02M2U4OGEzYjQyNGUiLCJpZCI6MjI5NjUsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODI0NTYwMzF9.VG2_T9ry8EnBWAh4msJ3s6m2jW_5hgAZQvfEQDh-WQs"
      window.viewer = new window.Cesium.Viewer("cesium-container", {
        // terrainProvider: new window.Cesium.CesiumTerrainProvider({
        //     url: 'https://tiles.geovis.online/base/v1/terrain?token=fd2cddddcf70866a1a334a47b78b8cba1941af00c93b3a97e49c65ab5182922a',
        //     requestWaterMask: true,
        //     requestVertexNormals: true
        // }),
        imageryProvider: new window.Cesium.ArcGisMapServerImageryProvider({
          url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
        }),
        sceneMode: window.Cesium.SceneMode.SCENE3D,
        vrButton: false,
        animation: false,
        baseLayerPicker: false,
        geocoder: false,
        timeline: false,
        fullscreenButton: false,
        homeButton: false,
        creditContainer: document.createElement('div'),
        infoBox: false,
        navigationHelpButton: false,
        sceneModePicker: false,
        scene3DOnly: true
      });

      const scene = window.viewer.scene;
      if (!scene.pickPositionSupported) {
        window.alert("This browser does not support pickPosition.");
      }

      scene.globe.depthTestAgainstTerrain = true;

      const annotations = scene.primitives.add(new window.Cesium.LabelCollection());

      // Set the initial camera view to look at Manhattan
      const initialPosition = window.Cesium.Cartesian3.fromDegrees(
        -74.01881302800248,
        40.69114333714821,
        753
      );
      const initialOrientation = new window.Cesium.HeadingPitchRoll.fromDegrees(
        21.27879878293835,
        -21.34390550872461,
        0.0716951918898415
      );
      scene.camera.setView({
        destination: initialPosition,
        orientation: initialOrientation,
        // endTransform: window.Cesium.Matrix4.IDENTITY,
      });

      // Load the NYC buildings tileset.
      const tileset = new window.Cesium.Cesium3DTileset({
        url: window.Cesium.IonResource.fromAssetId(75343),
      });
      scene.primitives.add(tileset);
      tileset.style = new window.Cesium.Cesium3DTileStyle({
        meta: {
          description: "'Building ${BIN} has height ${Height}.'",
        },
      });

      const handler = new window.Cesium.ScreenSpaceEventHandler(window.viewer.canvas);

      handler.setInputAction(function (movement) {
        const feature = scene.pick(movement.position);
        if (!window.Cesium.defined(feature)) {
          return;
        }

        const action = 'zoom';
        if (action === "annotate") {
          annotate(movement, feature);
        } else if (action === "properties") {
          printProperties(movement, feature);
        } else if (action === "zoom") {
          zoom(movement, feature);
        }
      }, window.Cesium.ScreenSpaceEventType.RIGHT_CLICK);

      handler.setInputAction(function (movement) {
        const feature = scene.pick(movement.position);
        if (!window.Cesium.defined(feature)) {
          return;
        }

        const action = 'hide';
        if (action === "hide") {
          feature.show = false;
        }
      }, window.Cesium.ScreenSpaceEventType.MIDDLE_CLICK);

      function annotate(movement) {
        if (scene.pickPositionSupported) {
          const cartesian = scene.pickPosition(movement.position);
          if (window.Cesium.defined(cartesian)) {
            const cartographic = window.Cesium.Cartographic.fromCartesian(cartesian);
            const height = `${cartographic.height.toFixed(2)} m`;

            annotations.add({
              position: cartesian,
              text: height,
              showBackground: true,
              font: "14px monospace",
              horizontalOrigin: window.Cesium.HorizontalOrigin.LEFT,
              verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM,
              disableDepthTestDistance: 1000,
            });
          }
        }
      }

      function printProperties(movement, feature) {
        console.log("Properties:");
        const propertyIds = feature.getPropertyIds();
        const length = propertyIds.length;
        for (let i = 0; i < length; ++i) {
          const propertyId = propertyIds[i];
          console.log(`  ${propertyId}: ${feature.getProperty(propertyId)}`);
        }

        // Evaluate feature description
        console.log(
          `Description : ${tileset.style.meta.description.evaluate(feature)}`
        );
      }

      function zoom(movement, feature) {
        const longitude = window.Cesium.Math.toRadians(
          feature.getProperty("Longitude")
        );
        const latitude = window.Cesium.Math.toRadians(
          feature.getProperty("Latitude")
        );
        const height = feature.getProperty("Height");

        const positionCartographic = new window.Cesium.Cartographic(
          longitude,
          latitude,
          height * 0.5
        );
        const position = scene.globe.ellipsoid.cartographicToCartesian(
          positionCartographic
        );

        const camera = scene.camera;
        const heading = camera.heading;
        const pitch = camera.pitch;

        const offset = offsetFromHeadingPitchRange(
          heading,
          pitch,
          height * 2.0
        );

        const transform = window.Cesium.Transforms.eastNorthUpToFixedFrame(position);
        window.Cesium.Matrix4.multiplyByPoint(transform, offset, position);

        camera.flyTo({
          destination: position,
          orientation: {
            heading: heading,
            pitch: pitch,
          },
          easingFunction: window.Cesium.EasingFunction.QUADRATIC_OUT,
        });
      }

      function offsetFromHeadingPitchRange(heading, pitch, range) {
        pitch = window.Cesium.Math.clamp(
          pitch,
          -window.Cesium.Math.PI_OVER_TWO,
          window.Cesium.Math.PI_OVER_TWO
        );
        heading = window.Cesium.Math.zeroToTwoPi(heading) - window.Cesium.Math.PI_OVER_TWO;

        const pitchQuat = window.Cesium.Quaternion.fromAxisAngle(
          window.Cesium.Cartesian3.UNIT_Y,
          -pitch
        );
        const headingQuat = window.Cesium.Quaternion.fromAxisAngle(
          window.Cesium.Cartesian3.UNIT_Z,
          -heading
        );
        const rotQuat = window.Cesium.Quaternion.multiply(
          headingQuat,
          pitchQuat,
          headingQuat
        );
        const rotMatrix = window.Cesium.Matrix3.fromQuaternion(rotQuat);

        const offset = window.Cesium.Cartesian3.clone(window.Cesium.Cartesian3.UNIT_X);
        window.Cesium.Matrix3.multiplyByVector(rotMatrix, offset, offset);
        window.Cesium.Cartesian3.negate(offset, offset);
        window.Cesium.Cartesian3.multiplyByScalar(offset, range, offset);
        return offset;
      }
    //---------------------倾斜模型3dtiles交互结束--------------------
  </script>
</body>

</html>

效果如下:https://sandcastle.cesium.com/index.html?src=3D%20Tiles%20Interactivity.html

7、倾斜模型自定义材质

内容包括倾斜模型分级设色、设置透明度、设置pbr材质等功能的实现,主要涉及的接口 :Cesium3DTileStyle、CustomShader、LightingModel、Cesium3DTileColorBlendMode
透明物体的深度值 ?在 3D 场景中,透明物体的追染需要考虑它们之间的相与遮挡关系,因为透明物体的表面不是完全不透明的,
所以在染时需要考虑到后面的物体对前面的物体的影响。为了正确比渲染透明物体,需要按照它们在场景中的深度值从小到大进行渲染。
透明物的深度盾是指这们距离相机的距富。通常用相机至 办体的富的倒教来表示,深度值越小,距毫相机近,左信染透明物体时。
将这们按照深度值以小到大排席。然后以次渲染。这样可以保证后面的透明物体不会遮挡前面的透明物体,
从而达到正确的渲染效果Scene.pickTranslucentDepth 方法可以帮助我们获取透明物体的深度值,从而实现下确的清顺席
什么是pbr材质 ?
PBR材质(Physically Based Rendering Material) 是一种基于物理的染技术,用于在3D图形中模拟真实世界中的光照和材质反射,
这种染技术使用物理屋性和方程式来计算光线在材质表面的反射和折射,使得渲染结果更加真实和精确
PBR材质通常包括以炀笆并叭捌啊趁挨澳巴边凹板疤抱挨碍氨性: Albedo(反射率): 表面的基础颜色,
它决定了表面在不同光照条件下的颜色表现Roughness(粗精度): 表面的光滑程度,它决定了表面反射光的散射程度,
越光滑的表面反射光越聚集。Metallic(金度): 表面是否具有金屋待性,它决定了表面的反射方式,
金屋表面反射光线类似于面反射,而非金屋表面反射光线则更加散射·Normal(法线): 表面的几何形状,
它决定了表面的光照效果,通过法线贴图可以实现表面的细节和几何形状的变化。PBR材质以在多人3D图形引擎中使用,
Unity.Unreal Engine、three.js等,这以建各种的3D场景,游动,实等。PBR材质可以提高果,使3D场景更加真实和逼真

十一、时间系统、粒子系统

1、时间系统

其在动态数据可视化中发挥了重要的作用,在三维场景的基础上增加了时间维度信息,Cesium初始化是自带时间控件的,默认是会显示当前的时间;Cesium的时间控件Clock由两部分构成,第一部分是Animation控件,控制时间的启动和暂停,第二部分是Timeline控件,控制时间线,如果我们不想显示两个控件,可以在Viewer初始化中将其都设置成false{animation: false,//动画小组件timeline: false,//左下角仪表};我们知道操控时间的Animation控件默认是暂停状态;下面来看一下,如何使用代码修改它们的状态:

viewer.clock.shouldAnimate=true;//设置其clock属性下的shouldAnimate为true,实现初始化页面自动循环播放的效果
viewer.clock.multiplier=1000;//使用它的multiplier属性,设定时间速率为1000,默认时间线是当前时间的24小时
let start=Cesium.JulianDate.fromIso8601('2022-01-05')//给他们设置一个新的时间线,用一个变量存储起始时间,使用JulianDate计算时间差,再通过fromIso8601转换成对应的格式
let end = Cesium.JulianDate.fromIso8601('2022-01-20')//同样的方法设置结束时间
viewer.timeline.zoomTo(start,end)//最后使用timeline的zoomTo方法将时间设置上去

效果如下:时间在自动播放
在这里插入图片描述

2、粒子系统

除了动效以外,Cesium为了实现更加逼真的学维仿真模拟,还加入了粒子系统,用于实现各种特效,如烟花燃放特效、天气效果车辆尾气的特效。Cesium的粒子系统是一种模拟复杂物理效应的图形技术,是由很多的小图像组成的集合,形成一个模糊对象,从而产生特效。

如果我们加载的是粒子类的数据,就需要使用primitives用于加载数据,因为其更接近底层的图形开发,Cesium为我们提供了这个对象用于创建粒子系统,代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第六课,粒子系统的演示</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
  </style>
</head>

<body>
  <div id="puiedu-cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg';
    let esri = new Cesium.ArcGisMapServerImageryProvider({
      url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
    })//用一个变量存储新地图的信息,这里我们使用的是arcgis地图服务
    const viewer = new Cesium.Viewer('puiedu-cesiumContainer', {
        animation: true,//动画小组件
        timeline: true,//左下角仪表,
        baseLayerPicker: false,//默认的地图按钮设置为隐藏
        imageryProvider: esri,//viewer内的imageryProvider属性用于设置地图
        terrainProvider: new Cesium.CesiumTerrainProvider({//此时地球表面是没有比如山脉3d地形的,所以下面加载地形数据放入引入的地形;CesiumTerrainProvider方法是将地形数据,转换为Cesium可以访问的格式
          url: Cesium.IonResource.fromAssetId(1),//url 属性用于放入地形服务器的地址
          requestVertexNormals: true,//requestVertexNormals属性设置为true,可以增加法线,用于提高光照效果
          requestWaterMask: true,//可以增加水面特效
        }),
      });

    //注意:粒子系统是依赖于时间系统的,是根据时间来变换的,所以这里设置下自动播放时间
    viewer.clock.shouldAnimate = true;//设置其clock属性下的shouldAnimate为true,实现初始化页面自动循环播放的效果
    viewer.clock.multiplier = 1000;//使用它的multiplier属性,设定时间速率为1000,默认时间线是当前时间的24小时
    let start = Cesium.JulianDate.fromIso8601('2022-01-05')//给他们设置一个新的时间线,用一个变量存储起始时间,使用JulianDate计算时间差,再通过fromIso8601转换成对应的格式
    let end = Cesium.JulianDate.fromIso8601('2022-01-20')//同样的方法设置结束时间
    viewer.timeline.zoomTo(start, end)//最后使用timeline的zoomTo方法将时间设置上去
    //----------------------------粒子特效,---------------------------
   //地球加载一个glb模型
    const position = Cesium.Cartesian3.fromDegrees(113.37377, 31.717497, 100) //定义常量,用于存储飞行目的地的坐标
    const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(-90, 0, 0))//定义模型朝向变量:参数一:位置信
    const entity_glb = viewer.entities.add({//使用Entities加载一个glb模型用于观察
      position: position,//模型位置
      orientation: orientation,//模型朝向
      model: {//模型信息
        uri: "./data/car.glb",//模型路径
        minimumPixelSize: 100,//模型最小的大小
        maxmumScale: 10000,//模型缩放最大比例
        show: true,//模型的显示
      }
    })
    viewer.camera.viewBoundingSphere(new Cesium.BoundingSphere(position, 20), new Cesium.HeadingPitchRange(0, 0, 0)) //视角绑定实体,参数一:设置视点位置BoundingSphere,参数二,相机的朝向HeadingPitchRange。其中new Cesium.BoundingSphere参数一:位置,参数二,和物体的距离

    viewer.scene.primitives.add(
      new Cesium.ParticleSystem({
        image: "./data/fire.png", //这里的image中放置的就是粒子的样式,通常使用路径的方式引用
        imageSize: new Cesium.Cartesian2(20,20),//imageSize用于设置粒子图片的大小
        startScale: 1.0,//startScale设置的是初始大小的倍数,它是与endScale相关联的
        endScale: 4.0,//这两个属性控制的是,粒子从生成到消亡整个周期,从初始大小,逐渐变化到原来尺寸的四倍,也可以根据需求设置初始很大,然后逐渐缩小
        particleLife: 1.0,//这里设置的是每一个粒子存在的时间,使得粒子随机生成生命周期
        speed:3.0,//粒子速度
        emitter: new Cesium.CircleEmitter(1),//粒子系统发射器,这里设置的是圆形发射器,0.5设置的是圆的半径,粒子将会在圆的范围内随机向上发射粒子,在半径以内随机生成
        // emitter: new Cesium.BoxEmitter(new Cesium.Cartesian3(10,10,10)),//粒子系统发射器,这里设置的是盒体发射器,并在其中设置了盒子的大小,粒子在盒子范围内随机生成
        emissionRate: 5.0,//每秒钟发射粒子的数量,数值越大,性能消耗越大
        lifetime: 16.0,//使用lifetime指定持续时间,这里设置为16秒
        modelMatrix: entity_glb.computeModelMatrix(viewer.clock.startTime, new Cesium.Matrix4()),//设置粒子系统的位置,它将粒子系统从模型转换为世界坐标的 4x4 转换矩阵,这里需要绑定到glb模型上,并用它的这个方法,在指定时间计算实体变换的模型矩阵,其中clock.startTime获取的就是时间控件中的起始时间
      })
    );
    //------------------------------粒子特效结束------------------------------
  </script>
</body>

</html>

效果如下:
在这里插入图片描述

十三、动态数据格式CZML

1、CZML介绍:

CZML是一种用关描述动态场景的JSON架构的语言,主要用于eslum在浏览器中的展示,它以用来述点、线布告板模型以及其他的图元,同时定他们是怎样随时间变化的,Cesium拥有-套富客产端AP,通过CZML采用数得动的方式,不用写代我就以使用通用的Cesium viewerp建出丰富的场景Ceslum与ZML的关系如同G000Earth和KML的关系,CZML和KML都是用来描述场景的数据格式,可以通到很多其他的程序自动生成,或者手写也可以。CZML拥有很多的特性,其中有一些区别于KML的:
1、CZML 是基于JSON的。
2、CZML可以准确的描本值随时间变化的属性,理由,一条在某一时间内是红色的而在另一时间内是蓝色的。同时客户端可以根据时间置%进行差值,加入有一辆车,分别定义了两个不同时间的位置,通过CZML定义的差值算法,客户端可以准确的显示在这两个时间点之间车的位置。所有属性都可以是随时间变化的。
3、CZML通过增是流的方式传送到客户端,在场景显示之前,整个CZML文档重要首先被下载到客户端,在某些情况下,个别客户端可能会加入或富开正在传输的流。(这段的不好)
4、CZML高度优化,旨在解析时更紧凑也更容易,让人工的读写更容易
5、CZMl可扩展,尽管(ZML的主要作用在与拟地缺客户端程序与场景的交流,但它可以很容易的通过扩展来满足其他一些辅助的程序对静态或动态数据的需求,例口,随时间
动态变化在数据就可以用在某些2D的图表程序中。
6、CZML是一个开放的格式。我们希望有更多的程序能使用CZML,同时期待有一天它也能成为OGC一样的标准
7、CZML(Cesium Language)是一种JSON格式,用于描述地球空间数据、事件和属性。它是Cesium 3D地球可视化引擎的一部分,是创建虚拟地球应用程序的一种重要技术。
8、CZML提供了一种简单的方法来在Cesium中显示三维场景中的任何实体,包括线、面、点、模型、文本、图像、标签、水平面和操作杆等。每个实体可以设置位置、旋转、缩放、颜色、外观、事件、时间和其他属性等。这使得Cesium成为一个强大的3D地球可视化引擎,具有处理大量、复杂数据的能力,并且功能强大、可扩展性强。
9、在CZML中,每个实体被称为一个Packet。Packet包含名称、ID、类型、属性、时刻和其他元数据。Packet中的属性使用键值对表示,每个键值对包含一个名称、一个值和一个数据类型。CZML还支持Packet的嵌套和Packet的引用,使得实体之间可以相互关联,并且可以更好地组织数据。

总之,CZML是一种强大的描述地球空间数据、事件和属性的格式,它为Cesium提供了丰富的虚拟地球可视化功能和扩展性。

2、添加czml动态点

代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>cesium第十四课,CZML动态点、动态自定义属性</title>
  <!-- cesium.js引入 -->
  <script src="../libs/Cesium/Cesium.js"></script>
  <script src="../libs/Echarts/echarts.min.js"></script>
  <!-- cesium样式引入 -->
  <link href="../libs/Cesium/Widgets/widgets.css" rel="stylesheet">
  <!-- 导入cesium的类型声明,有这个才会有快捷智能提示(已屏蔽,无效) -->
  <!-- <script src="../libs/Cesium/Cesium.d.ts"></script> -->
  <!-- <reference path="../libs/Cesium/Cesium.d.ts" /> -->
  <style>
    html,
    body {
      margin: 0px;
      padding: 0px;
    }
    #cesium-container {
      width: 100%;
      height: 100%;
    }
  </style>
</head>

<body>
  <div id="cesium-container"></div>
  <script>
    //---------------------代码演示--------------------
  /**
      CZML介绍 ?
      CZML是一种用来达动态场景的JSON架的语言,主要用于Cesium在浏览中的展示,它可以用来达点 线,布告板、模型以及其他的图元,
      同时定义他们是怎样随时间变化的,Cesium拥有 - 套富客户端AP1,通过CZML采用数据驱动的方式,
      不用写代码我就可以使用通用的Cesium viewer梅建出丰富的场景,Cesium与CZML的关系就如同GoodleEarth和KML的关系,
      CZML和KML都是用来描述场景的数据格式,可以通到很多其他的程序自动生成,或者手写也可以,CZML拥有很多的特性,
      其中有一些区别于KML的:
      CZML 是基于JSON的
      CZML可以准确的述值时间变化的量件,理由,一条在某一时间内是红色的而在另一时间内是蓝色的同时客户端
      可以根据时间置进行差值,加入有一辆车,分别定义了两个不同时间的位置,通过CZML定义的差值算法,
      客户端可以准确的显示在这两个时间点之间车的位置,所有属性都可以是随时间变化的。
      CZML通过增量流的方式传送到客户端,在场景显示之前,整个CZML文档需要首先被下载到客户端,
      在某些情况下,个别客户端可能会加入或离开正在传输的流。(这段翻泽的不好)
      CZML高度优化,旨在解析时更紧凑也更容易,让人工的读写更容易
      CZML可扩展,尽管CZML的主要作用在与虚拟地球客产端程序与场景的交流,但它可以很容易的通过扩展来满足
      其他一些辅助的程序对静本或动态数的需求,例如,随时动态变化在数据就可以用在某些2D的图表程序中
      CZML是一个开放的格式。我们希望有更多的程序能使用CZML,同时期待有一天能成为0GC一样的标准
**/
  Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI5MWVkMjllNy03ZjY4LTQ3YmQtOTgxOC05NGQ5YTU0ZjM5ZGEiLCJpZCI6MzU5Nywic2NvcGVzIjpbImFzciIsImdjIl0sImlhdCI6MTUzODE5MTUyM30.dtS2F3-q2fGoA93N7cFl-LCikK-Rjk7v01WWA-RqCxg"
  const viewer  = new Cesium.Viewer("cesium-container", {
    // terrainProvider: new Cesium.CesiumTerrainProvider({
    //   url: 'https://tiles.geovis.online/base/v1/terrain?token=fd2cddddcf70866a1a334a47b78b8cba1941af00c93b3a97e49c65ab5182922a',
    //   requestWaterMask: true,
    //   requestVertexNormals: true
    // }),
    imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
      url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'
    }),
    sceneMode: Cesium.SceneMode.SCENE3D,
    vrButton: false,
    animation: false,
    baseLayerPicker: false,
    geocoder: false,
    timeline: false,
    fullscreenButton: false,
    homeButton: false,
    creditContainer: document.createElement('div'),
    infoBox: true,
    navigationHelpButton: false,
    sceneModePicker: false,
    scene3DOnly: true,
    shouldAnimate: true,//开启动画
  });
  viewer.scene.globe.depthTestAgainstTerrain = true;

  const czml_point = [//pocket
    //必须的节点,document
    {
      id: "document",
      name: "CZML Point - Time Dynamic",
      version: "1.0",
    },
    {
      id: "point",
      availability: "2024-08-04T16:00:00Z/2024-08-04T16:01:00Z",
      position: {
        epoch: "2024-08-04T16:00:00Z",
        cartographicDegrees: [//以下参数分别代表:0秒、15秒、30秒、60秒的经度、纬度、高度
          0,
          -70,
          20,
          150000,
          20,
          -80,
          44,
          150000,
          40,
          -90,
          18,
          150000,
          60,
          -98,
          52,
          150000,
        ],
      },
      point: {
        color: {
          rgba: [255, 255, 255, 128],
        },
        outlineColor: {
          rgba: [255, 0, 0, 128],
        },
        outlineWidth: 3,
        pixelSize: 15,
      },
    },
  ];
  viewer.dataSources.add(Cesium.CzmlDataSource.load(czml_point));
  
  //----------50秒后加载glb CZML------------
  setTimeout(() => {
    const czml_path = [//pocket
      //必须的节点,document
      {
        id: "document",
        name: "CZML Path",
        version: "1.0",
        clock: {
          interval: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
          currentTime: "2012-08-04T10:00:00Z",
          multiplier: 100,
        },
      },
      {
        id: "path",
        name: "path with GPS flight data",
        description:
          "<p>Hang gliding flight log data from Daniel H. Friedman.<br>Icon created by Larisa Skosyrska from the Noun Project</p>",
        availability: "2012-08-04T10:00:00Z/2012-08-04T15:00:00Z",
        path: {
          material: {
            polylineOutline: {
              color: {
                rgba: [255, 0, 255, 255],
              },
              outlineColor: {
                rgba: [0, 255, 255, 255],
              },
              outlineWidth: 5,
            },
          },
          width: 8,
          leadTime: 5,
          trailTime: 50000,
          resolution: 5,
        },
        orientation: {
          "unitQuaternion": [
            0.19134171618254486,
            -0.3314135740355918,
            0.4619397662556433,
            0.8001031451912656,
          ]
        },
        model: {
          gltf: './data/Cesium_Air.glb'
        },
        position: {
          epoch: "2012-08-04T10:00:00Z",
          cartographicDegrees: [
            0,
            -122,
            39.50935,
            8776,
            3000,
            -122,
            39.60918,
            8773,
            4000,
            -122,
            39.70883,
            8772
          ]
        }
      }
    ];
    viewer.dataSources.add(Cesium.CzmlDataSource.load(czml_path)).then((result) => {
      viewer.trackedEntity = result.entities.getById("path");
    })
  }, 50000);
  

  //-----十秒以后加载自定义属性-------
  setTimeout(() => {
    const czml_zdy = [//pocket
      //必须的节点,document
      {
        id: "document",
        name: "CZML Point - Time Dynamic",
        version: "1.0",
        clock: {
          interval: "2012-08-04T16:00:00Z/2012-08-04T16:00:59Z",
          currentTime: "2012-08-04T16:00:00",
          // multiplier: 10,
        },
      },
      {
        id: "point",
        // availability: "2012-08-04T16:00:00Z/2012-08-04T16:05:00Z",
        position: {
          // epoch: "2012-08-04T16:00:00Z",
          cartographicDegrees: [
            '2012-08-04T16:00:00Z',
            -70,
            20,
            150000,
            '2012-08-04T16:00:10Z',
            -80,
            44,
            150000,
            '2012-08-04T16:00:40Z',
            -90,
            18,
            150000,
            '2012-08-04T16:00:59Z',
            -98,
            52,
            150000,
          ],
        },
        point: {
          color: {
            rgba: [255, 255, 255, 128],
          },
          outlineColor: {
            rgba: [255, 0, 0, 128],
          },
          outlineWidth: 3,
          pixelSize: 15,
        },
        properties: {
          //interval方式
          // height: [
          //     {
          //         interval: "2012-08-04T16:00:00Z/2012-08-04T16:00:10Z",
          //         number: 5
          //     },
          //     {
          //         interval: "2012-08-04T16:00:10Z/2012-08-04T16:00:59Z",
          //         number: 6
          //     },
          // ]

          //sample方式
          height: {
            number: [
              "2012-08-04T16:00:00Z", 5,
              "2012-08-04T16:00:59Z", 6
            ]
          },

          // position: {
          //     cartesian: [
          //         0, 4650397.56551457, -3390535.52275848, -4087729.48877329,
          //         300, 3169722.12564676, -2787480.80604407, -5661647.74541255,
          //         600, 1369743.14695017, -1903662.23809705, -6663952.07552171
          //     ]
          // }


          // testRGB: [
          //     {
          //         interval: "2012-08-04T16:00:00Z/2012-08-04T16:00:10Z",
          //         rgba: [255, 255, 255, 255]
          //     },
          //     {
          //         interval: "2012-08-04T16:00:10Z/2012-08-04T16:00:59Z",
          //         rgba: [255, 255, 255, 254]
          //     },
          // ]
        }
      },
    ];
    viewer.dataSources.add(Cesium.CzmlDataSource.load(czml_zdy)).then((result) => {
      setInterval(() => {
        console.log(result.entities.getById('point').properties.height.getValue(viewer.clock.tick()))
      }, 5000)
    });
  }, 10000);
    //---------------------效果结束--------------------
  </script>
</body>

</html>

效果如下:

在这里插入图片描述

十四、特效插件推荐

D3-KIT 基于Cesium三维拓展包 版本:1.5 作者:zhangti
主页: http://www.ztwow.top github:
https://github.com/zhangti0708/cesium-examples 示例地址:
http://zhangticcc.gitee.io/webgis Cesium版本:1.100 版权声明:
1.代码包基于Cesium拓展,部分模块开源已上传github。 2.后续会继续更新拓展,目前该代码包不开源,如需使用: 1)代码包的完整引用 2)此版权信息在控制台输出 我方保留对此版权信息的最终解释权。
在这里插入图片描述

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值