路径回放,视角跟随

  1. 使用GeoJson生成路径数据
  2. Cesium.DataSources加载
  3. 或取路径坐标信息
  4. 增加时间点间隔
<script>
import { onMounted, ref } from 'vue'
import * as Cesium from 'cesium'

export default {
  name: "App",
  setup() {
    const cesiumContainer = ref();
    onMounted(async () => {
      if (cesiumContainer) {
        viewer = new Cesium.Viewer(cesiumContainer.value, {
          timeline: true, // 时间轴
          animation: true, // 动画控件
        });
        viewer.scene.debugShowFramesPerSecond = true


        //创建DataSource
        var datasource = new Cesium.CustomDataSource("enetiestestdata");
        viewer.dataSources.add(datasource)

        let fileJson = await Cesium.GeoJsonDataSource.load('/src/assets/map.topojson');
        viewer.dataSources.add(fileJson);

        // 路径回放
        let lujingdata = fileJson.entities.values[0].polyline.positions._value;
        var property = new Cesium.SampledPositionProperty();

        let starttime = new Date();
        let stoptime;
        let timestamp = starttime.getTime();

        lujingdata.forEach((pos, index) => {
          let time = new Date(timestamp + index * 5000);
          stoptime = time;
          property.addSample(Cesium.JulianDate.fromDate(time), pos);
        })
        property.setInterpolationOptions({
          interpolationDegree: 0.0001,
          interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
        });


        var entitydd = datasource.entities.add({
          availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({
            start: Cesium.JulianDate.fromDate(starttime),
            stop: Cesium.JulianDate.fromDate(new Date(stoptime))
          })]),

          position: property, // 点集
          // billboard: {
          //   image: "./yingjiwuzi.png",
          //   scale: 0.5,
          //   pixelOffset: new Cesium.Cartesian2(0, -120),
          //   heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          //   clampToGround: true  //是否贴地
          // },
          model: {
            uri: '/src/assets/xiaofangche.gltf',
            scale: 1,
            minimumPixelSize: 70,
            maximumScale: 70
          },
          //朝向  定义模型朝向
          orientation: new Cesium.VelocityOrientationProperty(property),
          path: {
            leadTime: 0,
            resolution: 1,
            material: new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.3,
              color: Cesium.Color.GREEN
            }),
            width: 20
          },
          // 定义
          label: {
                text: "",
                fillColor: Cesium.Color.RED,
                pixelOffset:new Cesium.Cartesian2(0,-30)
            },
        });


        viewer.clock.onTick.addEventListener((tick) => {
                console.log(tick);
                    try {
                    entitydd.position.getValue(tick.currentTime);
                    //  console.log(entitydd.position.getValue(tick.currentTime));
                    //转为经纬度
                
                    var cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(entitydd.position.getValue(tick.currentTime))
                     cartographic.longitude = Cesium.Math.toDegrees(cartographic.longitude)
                    cartographic.latitude = Cesium.Math.toDegrees(cartographic.latitude)
                    // console.log(cartographic);
                    entitydd.label.text = Number(cartographic.longitude).toFixed(4) + "," + Number(cartographic.latitude).toFixed(4);

                    } catch (error) {
                        console.error('-------------------------------------------',error);            
                    }    
        
        })

        // 视角跟踪,追随
        viewer.trackedEntity =entitydd;

        viewer.clock.currentTime = Cesium.JulianDate.fromDate(starttime); //修改时间轴的当前时间
        viewer.clock.stopTime = Cesium.JulianDate.fromDate(new Date(stoptime));
        viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP,
          viewer.clock.shouldAnimate = true; //开始播放
        // viewer.zoomTo(fileJson)




      }
    })
    return {
      cesiumContainer
    }
  }
}
</script>
<!-- Clock的ClockRange属性

CLAMPED:达到终止时间后停止
LOOP_STOP:达到终止时间后重新循环
UNBOUNDED:达到终止时间后继续读秒(默认) -->

轨迹回放的主要思想是让模型的位置随时间来进行变换,并且让视角的中心一直跟着模型移动

  1. trackedEntity: 相机跟踪模型
  2. clock { multiplier: 控制播放速度 shouldAnimate: 设置是否移动 clockRange: 时间戳到终点后停止还是循环 }
  3. SampledPositionProperty: addSample() 用来构造时间和位置的关系 根据这个关系来移动模型和绘制路径
  4. sampleTerrainMostDetailed: 用来获取加载了地形后的真实坐标
  5. Cesium.CallbackProperty: 因为要绘制运动后的轨迹线,所以用回调的方式把走过的路径重新绘制

贴地线Ground

Entity方式贴地

  let line = [105, 36, 116, 38, 118, 36]
    viewer.entities.add({
        polyline: {
            positions: Cesium.Cartesian3.fromDegreesArray(line),//line要加载的线数据
            width: 10,//线宽
            clampToGround: true,//开启贴地
            material: Cesium.Color.RED//颜色
        }
    });
    //或者直接加载线数据地址,然后设置贴地即可
    var promise = Cesium.GeoJsonDataSource.load(urlLine, {
            clampToGround: true,//开启贴地
     });

primitive方式贴地

Primitive方式贴地, 需要使用GroundPolylinePrimitive和GroundPolylineGeometry来创建Primitive与Geometry对象,而不是使用Primitive和PolylineGeometry创建Primitive与Geometry对象,代码如下:

    viewer.scene.primitives.add(new Cesium.GroundPolylinePrimitive({
        geometryInstances: new Cesium.GeometryInstance({
            geometry: new Cesium.GroundPolylineGeometry({
                positions: Cesium.Cartesian3.fromDegreesArray(positions[i]),
                width: 15.0,//线宽
                vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT
            }),
            attributes: {
                color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                    new Cesium.Color.fromCssColorString("#0096ff")),
            }
        }),
        appearance: new Cesium.PolylineColorAppearance({
            translucent: false
        })
    }));
  • Entity方式贴地更方便,但是效率不如primitive

水面效果

primitive

 //1 创建 geometry
        const polygon = new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(
            Cesium.Cartesian3.fromDegreesArray(cixian.flat())
          )
        });
        const geometry = Cesium.PolygonGeometry.createGeometry(polygon);

        //2,创建geometryInstance
        const instance = new Cesium.GeometryInstance({
          geometry: geometry
        });

        //3,创建 material
        var material = new Cesium.Material({
          fabric: {
            type: 'Water',
            uniforms: {
              baseWaterColor: new Cesium.Color( 0,  0, 0, 0.7),
              normalMap: '/src/assets/waterNormals.jpg',
              frequency: 20000,
              animationSpeed: 0.1,
              amplitude: 50,
              specularIntensity: 0.5
            }
          }
        })

        // 4 创建Appearance
        var appearance = new Cesium.EllipsoidSurfaceAppearance({
          material: material,
        })

        // 5创建primitive

        viewer.scene.primitives.add(new Cesium.Primitive({
          id: 'idsss',
          name: 'water_info',
          asynchronous:false,
          geometryInstances: instance,
          appearance: appearance
        }));


        viewer.camera.setView({
          destination:Cesium.Cartesian3.fromDegrees(114.28496124522002, 36.42363208436417,5000)

        })
      

等高线


// 2、开启深度测试

viewer.scene.globe.enableLighting = true;

// 3、设置几个等高线必备的参数

var minHeight = -414.0; // 最小高度-例:最低接近死海高度

var maxHeight = 8777.0; // 最大高度-例:最高接近珠峰高度

var contourColor = Cesium.Color.RED.withAlpha(0.4); // 等高线的颜色

var contourSpacing = 200.0; // 等高线的等间距

var contourWidth = 1.0; // 等高线的宽度 

淹没分析

 /**
     * 淹没分析函数,通过拉伸面的高度来进行分析
     * @param {*} viewer
     * @param {*} targertWaterHeight :目标水位高度
     * @param {*} positions :研究区域底部坐标数组
     * @param {*} waterHeight :当前水位高度
     */
    let induationAnalysis = (viewer, targertWaterHeight, positions, waterHeight) => {
      console.log('start................');
      viewer.entities.add({
        polygon: {
          hierarchy: new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(positions)),
          perPositionHeight: true,
          // 使用回调函数Callback,直接设置extrudedHeight会导致闪烁
          extrudedHeight: new Cesium.CallbackProperty(function () {
            waterHeight += 0.2;
            console.log(waterHeight);
            if (waterHeight > targertWaterHeight) {
              waterHeight = targertWaterHeight;
            }
            return waterHeight;
          }, false),
          material: new Cesium.Color.fromBytes(64, 157, 253, 150),
        }
      });
    }


    const cesiumContainer = ref();
    onMounted(() => {
      if (cesiumContainer) {
        viewer = new Cesium.Viewer(cesiumContainer.value, {
          timeline: true, // 时间轴
          animation: true, // 动画控件

          terrainProvider: Cesium.createWorldTerrain({
            requestVertexNormals: true, //开启地形光照
          })

        });
        // 显示帧率
        viewer.scene.debugShowFramesPerSecond = true

        induationAnalysis(viewer, 5000, vdt.value.flat(), 30)
      }
    }
    )
function setup() {
    let viewer
    //1.创建viewModel对象


    const cesiumContainer = ref();


    let subscribeLayerParameter = (name,mLayer,viewModel)=> {
				//4.监听控件值的变化
				Cesium.knockout.getObservable(viewModel, name).subscribe(function(value) {
					//value值改变后会赋值给imagelayer的相应属性
					console.log(name + ":" + value);
					mLayer[name] = value;
				});
			} 
    onMounted(() => {
      if (cesiumContainer) {
        viewer = new Cesium.Viewer(cesiumContainer.value, {
          //搜索框
          geocoder: false,
          //home键
          homeButton: false,
          // 动画控件
          animation: false,
          //全屏按钮
          fullscreenButton: false,
          //场景模式选择器
          sceneModePicker: false,
          //时间轴
          timeline: false,
          //导航提示
          navigationHelpButton: false,
          //地图选择器
          baseLayerPicker: false,
          terrainProvider: Cesium.createWorldTerrain({
            requestVertexNormals: true, //开启地形光照
          })

        }); 
        // 显示帧率
        viewer.scene.debugShowFramesPerSecond = true
        viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
          url: "/src/assets/sjzTerrain/",
        })




        var elevationConterMaterial = new Cesium.Material({
          fabric: {
            type: 'ElevationContour',
            uniforms: {
              //等高线的颜色
              color: new Cesium.Color(1.0, 0.0, 0.0, 1.0),
              //等高线的间隔
              spacing: 50,
              width: 2,
            }
          }
        });

        viewer.scene.globe.material = elevationConterMaterial;

        var viewModel = {
          alpha: 1.0,
          brightness: 1.0,
          contrast: 1.0,
          hue: 0.0,
          saturation: 1.0,
          gamma: 1.0
        };

        //2.监测viewModel中的属性
        Cesium.knockout.track(viewModel);

        //3.激活属性,将viewModel对象与html控件绑定
        var toolBar = document.getElementById("toolbar");
        Cesium.knockout.applyBindings(viewModel, toolBar);

      
			
			//获取当前地球影像
			var mLayer = viewer.imageryLayers.get(0);
			


      subscribeLayerParameter("alpha",mLayer,viewModel);
			subscribeLayerParameter("brightness",mLayer,viewModel);
			subscribeLayerParameter("contrast",mLayer,viewModel);
			subscribeLayerParameter("hue",mLayer,viewModel);
			subscribeLayerParameter("saturation",mLayer,viewModel);
			subscribeLayerParameter("gamma",mLayer,viewModel);


      }
    }
    )
    return {
      cesiumContainer
    }


  }
 

地图等高线晕眩效果


        var viewer = new Cesium.Viewer('cesiumContainer', {
            imageryProvider: new Cesium.UrlTemplateImageryProvider({
                url: "https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
            }),
            terrainProvider: Cesium.createWorldTerrain({
                requestVertexNormals: true,
                requestWaterMask: true
            })
        });

        viewer.camera.setView({
            destination: new Cesium.Cartesian3(-2495709.521843174, -4391600.804712465, 3884463.7192916023),
            orientation: {
                heading: 1.7183056487769202,
                pitch: -0.06460370548034144,
                roll: 0.0079181631783527
            }
        });
  // 开启光照
        viewer.scene.globe.enableLighting = true;

        // 创建一个拥有高程阴影和等高线的组合样式
        function getElevationContourMaterial() {
            return new Cesium.Material({
                fabric: {
                    type: 'ElevationColorContour',
                    materials: {
                        contourMaterial: {
                            type: 'ElevationContour'
                        },
                        elevationRampMaterial: {
                            type: 'ElevationRamp'
                        }
                    },
                    components: {
                        diffuse: 'contourMaterial.alpha == 0.0 ? elevationRampMaterial.diffuse : contourMaterial.diffuse',
                        alpha: 'max(contourMaterial.alpha, elevationRampMaterial.alpha)'
                    }
                },
                translucent: false
            });
        }

        var minHeight = -414.0; // 最低接近死海高度
        var maxHeight = 8777.0; // 最高接近珠峰高度
        var contourColor = Cesium.Color.RED.withAlpha(0.4); // 等高线的颜色
        var contourSpacing = 150.0; // 等高线的等间距
        var contourWidth = 1.0; // 等高线的宽度

        // 1、高程阴影
        // var material = Cesium.Material.fromType('ElevationRamp');
        // var shadingUniforms = material.uniforms;
        // shadingUniforms.minimumHeight = minHeight;
        // shadingUniforms.maximumHeight = maxHeight;
        // shadingUniforms.image = getColorRamp();

        // 2、等高线
        // var material = Cesium.Material.fromType('ElevationContour');
        // var contourUniforms = material.uniforms;
        // contourUniforms.width = contourWidth;
        // contourUniforms.spacing = contourSpacing;
        // contourUniforms.color = contourColor;

        // 3、高程阴影+等高线
        var material = getElevationContourMaterial();
        var shadingUniforms = material.materials.elevationRampMaterial.uniforms;
        shadingUniforms.minimumHeight = minHeight;
        shadingUniforms.maximumHeight = maxHeight;
        shadingUniforms.image = getColorRamp();

        var contourUniforms = material.materials.contourMaterial.uniforms;
        contourUniforms.width = contourWidth;
        contourUniforms.spacing = contourSpacing;
        contourUniforms.color = contourColor;

        viewer.scene.globe.material = material;

        function getColorRamp() {
            var ramp = document.createElement('canvas');
            ramp.width = 100;
            ramp.height = 1;
            var ctx = ramp.getContext('2d');
            var values = [0.0, 0.045, 0.1, 0.15, 0.37, 0.54, 1.0];
            var grd = ctx.createLinearGradient(0, 0, 100, 0);
            grd.addColorStop(values[0], 'rgba(0,0,0,0.8)'); //black
            grd.addColorStop(values[1], 'rgba(39,71,224,0.8)'); //blue
            grd.addColorStop(values[2], 'rgba(211,59,125,0.8)'); //pink
            grd.addColorStop(values[3], 'rgba(211,48,56,0.8)'); //red
            grd.addColorStop(values[4], 'rgba(255,151,66,0.8)'); //orange
            grd.addColorStop(values[5], 'rgba(255,215,0,0.8)'); //yellow
            grd.addColorStop(values[6], 'rgba(255,255,255,0.8)'); //white
            ctx.fillStyle = grd;
            ctx.fillRect(0, 0, 100, 1);
            return ramp;
        }