手把手带你开发Cesium三维场景【3D智慧城市警情预警】

news2025/1/20 1:49:33

📢 鸿蒙专栏:想学鸿蒙的,冲

📢 C语言专栏:想学C语言的,冲

📢 VUE专栏:想学VUE的,冲这里

📢 CSS专栏:想学CSS的,冲这里

📢 Krpano专栏:想学VUE的,冲这里

🔔 上述专栏,都在不定期持续更新中!!!!!!!!!!!!!

效果演示

警情模拟示例

✨ 一、 前言

本文主要用于构建Cesium三维地图场景,主要实现了以下功能:

1、初始化三维地图控件

        使用Cesium.Viewer和Cesium.Scene等对象初始化三维地图,设置地图纹理、视角位置、阴影参数等配置信息,进行三维场景的初始化。

2、封装场景操作类

        封装D3类对场景进行管理,实现场景配置、数据加载、事件绑定等功能,以更好地控制三维场景。

3、CSS3渲染标注

        使用CSS3渲染在三维场景中添加Html标注信息。

4、实现视角导航动画

        实现不同场景状态下的视角平滑导航动画,使用flyTo方法实现过渡动画效果。

5、构建交互界面

        使用dat.GUI构建交互界面,可以控制场景效果参数、添加各种内容。

6、警情场景模拟

        实现警情监控、预报、分析、调度四个场景状态的模拟。

7、封装视觉效果方法

        封装添加模型、粒子、标注、墙体等常用场景效果的方法。

二、关键技术要点

1. 初始化三维场景

/**
 * 初始化
 */
D3.prototype.init = function (opt = {}) {

    if (configs.mapDom && configs.mapUrl) {

        Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYTQ2ZjdjNS1jM2E0LTQ1M2EtOWM0My1mODMzNzY3YjYzY2YiLCJpZCI6MjkzMjcsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1OTE5NDIzNjB9.RzKlVTVDTQ9r7cqCo-PDydgUh8Frgw0Erul_BVxiS9c';
		// 添加镜像服务
		// mapbox.satellite 卫星图像
		// mapbox.streets 街道图像
        this._viewer = new Cesium.Viewer(configs.mapDom, configs.mapOptions);

        this._util = new Cesium.Utils(this._viewer)
		//BingMapsImageryProvider  Bing地图影像,可以指定mapStyle,详见BingMapsStyle类
		//  其中可以指定mapStyle,选择多种风格,目前Cesium中支持AERIAL、AERIAL_WITH_LABELS、ROAD、ORDNANCE_SURVEY、COLLINS_BART五种。
        this._viewer.imageryLayers.addImageryProvider(new Cesium.BingMapsImageryProvider({
            url: 'https://dev.virtualearth.net',
            mapStyle: Cesium.BingMapsStyle.AERIAL,
            key: URL_CONFIG.BING_MAP_KEY
        }))

        this._scene = this._viewer.scene

        this._scene.skyBox = this._util.setOneGroundSkyBox()

        // this._util.setSnowEffect()

        this.config(opt) //默认开始配置

        this.loadScene() //加载场景

        // this.addThreeObject() //加载three obj
    } else {

        alert("请配置地图参数")
    }

}

使用Cesium中常用的Viewer、Scene等对象初始化三维视图,主要进行以下配置:

  • 设置地图纹理为Bing地图
  • 配置场景的光照、阴影参数
  • 关闭默认的天空背景、大气效果
  • 开启各种画质优化效果

相关API:

  • Cesium.Viewer:三维场景的主要容器
new Cesium.Viewer(container, options)

        用于构建应用程序的基本小部件。它将所有标准 Cesium 组件组合到一个可重用的包中。小部件总是可以通过使用mixins来扩展,它可以添加对各种应用程序有用的功能。 

NameTypeDescription
containerElement | string包含小部件的DOM元素或ID。
optionsViewer.ConstructorOptions描述初始化选项的对象

✨ Throws:

  1. DeveloperError:文档中不存在id为“container”的元素。 
  2. DeveloperError:选项。当不使用BaseLayerPicker小部件时,selectedImageryProviderViewModel不可用,请指定选项。baseLayer代替。
  3. DeveloperError:选项。当不使用BaseLayerPicker小部件时,selectedTerrainProviderViewModel不可用,请指定选项。terrainProvider代替。

示例:

//使用几个自定义选项和mixins初始化查看器小部件。
try {
  const viewer = new Cesium.Viewer("cesiumContainer", {
    // 从Columbus Viewer开始
    sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
    // 使用Cesium World地形
    terrain: Cesium.Terrain.fromWorldTerrain(),
    // 隐藏底层选择器
    baseLayerPicker: false,
    // 使用 OpenStreetMaps
    baseLayer: new Cesium.ImageryLayer(new Cesium.OpenStreetMapImageryProvider({
      url: "https://tile.openstreetmap.org/"
    })),
    skyBox: new Cesium.SkyBox({
      sources: {
        positiveX: "stars/TychoSkymapII.t3_08192x04096_80_px.jpg",
        negativeX: "stars/TychoSkymapII.t3_08192x04096_80_mx.jpg",
        positiveY: "stars/TychoSkymapII.t3_08192x04096_80_py.jpg",
        negativeY: "stars/TychoSkymapII.t3_08192x04096_80_my.jpg",
        positiveZ: "stars/TychoSkymapII.t3_08192x04096_80_pz.jpg",
        negativeZ: "stars/TychoSkymapII.t3_08192x04096_80_mz.jpg"
      }
    }),
    // 显示 Columbus View map 与 Web Mercator projection
    mapProjection: new Cesium.WebMercatorProjection()
  });
} catch (error) {
  console.log(error);
}

// 添加基本的拖放功能
viewer.extend(Cesium.viewerDragDropMixin);

// 如果在处理删除的文件时遇到错误,则显示弹出警报
viewer.dropError.addEventListener(function(dropHandler, name, error) {
  console.log(error);
  window.alert(error);
});
  • Scene:场景的主要渲染对象
new Cesium.Scene(options)

        所有的3 d图形对象的容器和国家Cesium虚拟场景。一般来说都不是直接创建的;它是由CesiumWidget隐式创建。 

options对象:

NameTypeDefaultDescription
canvasHTMLCanvasElement用于创建场景的HTML cancas元素。
contextOptionsContextOptions环境和WebGL创建属性
creditContainerElement用于显示服务描述信息的HTML元素。
creditViewportElement要在其中显示信用弹出窗口的HTML元素。如果未指定,则视口将作为画布的兄弟添加。
mapProjectionMapProjection在二维和Columbus 视图模式下使用的地图投影。
orderIndependentTranslucencybooleantrue如果此项设置为true,并且使用设备支持,将使用与顺序无关的半透明。
scene3DOnlybooleanfalse如果此项设置为true,将优化三维模式的内存使用和性能,但禁止使用二维或Columbus视图功能。
shadowsbooleanfalse确定阴影是否由太阳投射形成。
mapMode2DMapMode2D确定二维地图是可旋转的或是可以在在水平方向上无限滚动。
requestRenderModebooleanfalse
maximumRenderTimeChangenumber0.0
depthPlaneEllipsoidOffsetnumber0.0
msaaSamplesnumber1

✨ Throws: 

  • D eveloperError : options and options.canvas are required.

✨ 示例:

// 创建场景没有各向异性纹理过滤
const scene = new Cesium.Scene({
  canvas : canvas,
  contextOptions : {
    allowTextureFilterAnisotropic : false
  }
});
  • Cesium.BingMapsImageryProvider:Bing地图服务提供者
new Cesium.BingMapsImageryProvider(options)

要构造一个BingMapsImageryProvider,调用BingMapsImageryProvider. fromurl。不要直接调用构造函数。

        使用必应地图图像REST API提供平铺图像。 

NameTypeDescription
optionsBingMapsImageryProvider.ConstructorOptions描述初始化选项的对象

示例:

const bing = await Cesium.BingMapsImageryProvider.fromUrl(
  "https://dev.virtualearth.net", {
    key: "get-yours-at-https://www.bingmapsportal.com/",
    mapStyle: Cesium.BingMapsStyle.AERIAL
});

2. 场景操作类

/**
 * 场景配置
 * 
 * @param opt
 */
D3.prototype.config = function (opt) {

    if (this._scene) {

        //设置第二重烘焙纹理的效果(明暗程度)
        this._scene.shadowMap.darkness = 1.275;

        //设置环境光
        this._scene.lightSource.ambientLightColor = opt.ambientLightColor || new Cesium.Color(0.7, 0.7, 0.7, 1);

        //深度检测
        this._scene.globe.depthTestAgainstTerrain = true;

        //地面调节
        //this._scene.globe.baseColor = Cesium.Color.BLACK;
        this._scene.globe.globeAlpha = 0.5;
        this._scene.undergroundMode = true;
        this._scene.terrainProvider.isCreateSkirt = false;

        //调节场景环境
        this._scene.sun.show = false;
        this._scene.moon.show = false;
        // this._scene.skyBox.show = false;
        this._scene.skyAtmosphere.show = false;
        this._scene.fxaa = true;

        //开启颜色校正
        this._scene.colorCorrection.show = opt.colorCorrection || false;
        this._scene.colorCorrection.saturation = opt.saturation || 3.1;
        this._scene.colorCorrection.brightness = opt.brightness || 1.8;
        this._scene.colorCorrection.contrast = opt.contrast || 1.2;
        this._scene.colorCorrection.hue = opt.hue || 0;

        //开启泛光和HDR
        this._scene.bloomEffect.show = opt.bloomEffect || false;
        this._scene.hdrEnabled = opt.hdrEnabled || true;
        this._scene.bloomEffect.threshold = 1;
        this._scene.bloomEffect.bloomIntensity = 2;

        //最大距离
        this._scene.screenSpaceCameraController.maximumZoomDistance = 5000.0
    }
}

封装D3类对场景进行管理,其中主要功能有:

  • init:场景初始化,包含场景配置、数据加载等
  • config:场景参数配置,如环境光、阴影等
  • loadScene:加载场景数据,如3Dtiles、模型
  • bindHandle:绑定交互事件
  • closeScene:关闭场景,释放资源

使用面向对象的方式对复杂场景进行模块化管理。

3. 事件处理

/**
 * 事件处理
 */
D3.prototype.bindHandle = function () {
	// ScreenSpaceEventHandler提供ScreenSpaceEventType一种监听用户输入并对用户输入进行分类的方法
	// 这段代码是在Cesium中使用ScreenSpaceEventHandler来处理canvas的点击事件。
	
	// 具体来说:
	
	// _handler 是ScreenSpaceEventHandler的一个实例,用来处理canvas上的屏幕空间事件。
	// 它通过Cesium.ScreenSpaceEventHandler构造函数传入viewer的scene的canvas来创建。
	// 设置了一个左击事件的回调函数,在回调函数中可以获取到点击事件的一些信息,如点击位置的movement对象。
	// 接着使用scene的pick方法根据点击位置拾取到对象。
	// 如果拾取到的对象id名称为“警情分析”,则调用analysis()方法进行后续处理。
	// 所以这段代码的作用是:在Cesium场景canvas上,当点击“警情分析”对象时,调用analysis()方法进行响应。
	
	// ScreenSpaceEventHandler还可以处理其他事件类型,如右击、移动、长按等。事件类型包括:
	
	// LEFT_CLICK
	// RIGHT_CLICK
	// MIDDLE_CLICK
	// WHEEL
	// PINCH_START
	// PINCH_END
	// PINCH_MOVE
	// MOUSE_MOVE 等。

    this._handler = new Cesium.ScreenSpaceEventHandler(this._viewer.scene.canvas)
    this._handler.setInputAction((movement) => {

        // var cartesian = this._util.getCatesian3FromPX(movement.position)

        // console.log(this._util.transformCartesianToWGS84(cartesian))
        var obj = this._scene.pick(movement.position);
        if (obj && obj.id && obj.id.name && "警情分析" == obj.id.name) this.analysis()

    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    // this._viewer.scene.camera.moveEnd.addEventListener((move) => {

    //     console.log(this._util.getCameraPosition())

    // });



    // this._util.getHandelPosition((position,handel)=>{
    //     console.log(position)

    // })

    // this._util.setScanCircleEffect({
    //     position: new Cesium.Cartesian3.fromDegrees(106.50642721790797, 29.658575326606123, 5.0)
    // })

    // this._util.drawLine((value) => {
    //     console.log(value)
    // })
}

主要使用两种事件处理方式:

  • ScreenSpaceEventHandler:处理鼠标交互事件

new Cesium.ScreenSpaceEventHandler(element)

        处理用户输入事件。可以添加自定义函数,以便在用户输入时执行。 

NameTypeDefaultDescription
elementHTMLCanvasElementdocument要向其中添加事件的元素。
// 处理左击事件
this._handler.setInputAction((movement) => {
    // 事件处理逻辑
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  • 相机移动回调函数:处理飞行结束事件

Related API:

  • Cesium.ScreenSpaceEventHandler
  • Camera.moveEnd

 

4. 视角控制

D3.prototype.loadCameraPath = function (callback) {

    this._util.setView({

        position: { x: -2180840.640119748, y: 4381647.215317032, z: 4091216.503229185 },
        orientation: {
            heading: Cesium.Math.toRadians(356.76499726379865),
            pitch: Cesium.Math.toRadians(-22.735599006353922),
            roll: Cesium.Math.toRadians(0.00133659048757427)
        }
    });

    setTimeout(() => {
        this._util.flyTo({
            position: { x: -2178897.313757382, y: 4381397.305312672, z: 4091462.297319925 },
            orientation: {
                heading: Cesium.Math.toRadians(46.527000640600505),
                pitch: Cesium.Math.toRadians(-5.17091508581087),
                roll: Cesium.Math.toRadians(1.90833280887811)
            },
            duration: 5,
            callback: () => {
                this._util.flyTo({
                    position: { x: -2178132.972253719, y: 4380734.091723098, z: 4093209.132147421 },
                    orientation: {
                        heading: Cesium.Math.toRadians(105.62030224024655),
                        pitch: Cesium.Math.toRadians(-21.59096416111003),
                        roll: Cesium.Math.toRadians(359.9987311314987)
                    },
                    duration: 5,
                    callback: () => {
                        this._util.flyTo({
                            position: { x: -2179780.958069727, y: 4379145.05670711, z: 4093251.679035389 },
                            orientation: {
                                heading: Cesium.Math.toRadians(202.12146484437022),
                                pitch: Cesium.Math.toRadians(-4.367558356924628),
                                roll: Cesium.Math.toRadians(0.0006130606451948047)
                            },
                            duration: 5,
                            callback: () => {
                                this._util.flyTo({
                                    position: { x: -2182832.9113919945, y: 4380248.782123272, z: 4093233.182007854 },
                                    orientation: {
                                        heading: Cesium.Math.toRadians(282.56605551019436),
                                        pitch: Cesium.Math.toRadians(-38.5875540173017),
                                        roll: Cesium.Math.toRadians(359.99999999993923)
                                    },
                                    duration: 5,
                                    callback: callback
                                })
                            }
                        })
                    }
                })
            }
        })
    }, 2000)

}

主要通过flyTo方法实现视角的平滑飞行过渡动画。

// fly to 函数
_util.flyTo({
  destination : {
      x : 1.0,
      y : 2.0,
      z : 3.0
  },
  duration : 5
});

设置destination目标位置和orientation朝向,以及duration动画时间实现视角导航动画。

5. CSS3渲染

使用Cesium.Css3Renderer实现CSS3渲染:

// 创建CSS3渲染器
var cssRenderer = new Cesium.Css3Renderer();

// 添加标签层
cssRenderer.addLayer({
    position : [1.0, 2.0, 3.0],
    element : document.getElementById('label')
});

将Html标签元素渲染到指定三维位置。

相关API:

  • Cesium.Css3Renderer

6. 状态管理

使用状态码管理不同的场景状态,在状态切换时校验当前状态。

// 状态码
this._STATECODE = {
  zero : 0,
  one : 1,
  //...
};

// 当前状态  
this._state = this._STATECODE.zero;

// 状态检查
if (this._STATECODE.zero !== this._state) {
  // 错误提示
  return ;
}

 

这样可以确保场景按顺序演示。

三、场景实现要点

1. 警情监控

/**
 * 警情监控
 */
D3.prototype.monitoring = function (callback) {

    if (this._STATECODE.zero !== this._state & this._STATECODE.all !== this._state) {

        this._layers.msg('请先初始化场景 ', {
            time: 2500,
        });
        return false
    }

    var start = () => {

        this.postProcess.push(this._util.setRadarScanEffect({
            position: Cesium.Cartesian3.fromDegrees(116.45141573633816, 39.91324637901737, 10.0),
            color: Cesium.Color.RED,
            radius: 500
        }))

        this._util.flyTo({
            position: { x: -2178693.2564594974, y: 4380220.704344869, z: 4093694.8169494905 },
            orientation: {
                heading: Cesium.Math.toRadians(154.12738034665816),
                pitch: Cesium.Math.toRadians(-36.860896281659365),
                roll: Cesium.Math.toRadians(5.0125541779865026)
            },
            duration: 5,
            callback: () => {

                this._scene.camera.flyCircle(Cesium.Cartesian3.fromDegrees(116.45141573633816, 39.91324637901737, 100.0));

                if (typeof callback === 'function') {

                    setTimeout(() => {

                        callback()
                    }, 3000)
                } else {


                    // update state
                    this._state = this._STATECODE.one
                }
            }
        })
    }

    if (this.postProcess.length > 0) {


        this.closeScene(() => {

            start()
        })
    } else {

        start()
    }


}

主要实现:

  • 添加雷达扫描效果
  • 相机环绕目标点

使用 Cesium.PostProcessStage 添加雷达扫描后期效果:

// 添加雷达扫描特效
scene.postProcessStages.add(Cesium.PostProcessStageLibrary.createRadarScanStage());

并设置相机环绕点飞行实现监控效果:

// 相机环绕点飞行
camera.flyCircle(center);

 

2. 警情预报

/**
 * 
 * 警情预报
 */
D3.prototype.startSceneOne = function (callback) {


    if (this._STATECODE.one !== this._state & this._STATECODE.all !== this._state) {

        this._layers.msg('请先预览完警情监控 ', {
            time: 3000,
        });
        return false
    }
    this._viewer.scene.camera.stopFlyCircle();

    this._util.flyTo({
        position: { x: -2178463.9456453873, y: 4381002.914153181, z: 4093890.133365481 },
        orientation: {
            heading: Cesium.Math.toRadians(128.64858283790525),
            pitch: Cesium.Math.toRadians(-44.65849475098552),
            roll: Cesium.Math.toRadians(0.0023842018258495466)
        },
        duration: 5,
        callback: () => {
            this._util.flyTo({
                position: { x: -2179037.6221276326, y: 4380549.292276369, z: 4092247.909965294 },
                orientation: {
                    heading: Cesium.Math.toRadians(38.73496800382902),
                    pitch: Cesium.Math.toRadians(-21.116761624408735),
                    roll: Cesium.Math.toRadians(0.00018448854527482853)
                },
                duration: 5,
                callback: () => {

                    this._util.flyTo({
                        position: { x: -2179048.567425212, y: 4380181.227054535, z: 4092287.128962158 },
                        orientation: {
                            heading: Cesium.Math.toRadians(57.01126750844415),
                            pitch: Cesium.Math.toRadians(-4.698058614089507),
                            roll: Cesium.Math.toRadians(0.00009319620311191225)
                        },
                        duration: 5,
                        callback: () => {
                            if (typeof callback === 'function') {

                                setTimeout(() => {

                                    callback()
                                }, 3000)
                            } else {

                                // update state
                                this._state = this._STATECODE.tow
                            }
                        }
                    })
                }
            })
        }
    })

    //异常提示
    new Promise((resolve, reject) => {

        this.addDynamicEntity({
            position: Cesium.Cartesian3.fromDegrees(116.450217639056, 39.912527799624065, 130.0),
            model: { lng: 116.450217639056, lat: 39.912527799624065, alt: 130.0 },
            m_color: Cesium.Color.RED.withAlpha(0.5),
            label: true,
            billboard: true,
            text: ' 商务办公楼 ',
            l_font: '14px sans-serif',
            l_fillColor: Cesium.Color.WHITE,
            l_backgroundColor: Cesium.Color.RED,
            l_pixelOffset: new Cesium.Cartesian2(0, -5),
            l_showBackground: false
        })

        var cricleEntity = this._util.createDynamicCricle({
            center: { lng: 116.450217639056, lat: 39.912527799624065, alt: 130.0 },
            material: new Cesium.CircleWaveMaterialProperty({
                color: Cesium.Color.RED,
                count: 3,
                gradient: 0.9
            }),
            height: 100,
            radius: 50,
            rotateAmount: 0.01
        })
        this._viewer.entities.add(cricleEntity)

    })

    // 模拟效果
    new Promise((resolve, reject) => {

        //添加火焰粒子
        this.primitives.push(this._util.setFlameParticle({
            position: Cesium.Cartesian3.fromDegrees(116.4499827986952, 39.91231248171688, 0),
            tx: 0, ty: 0, tz: 50
        }))

        this.primitives.push(this._util.setFlameParticle({
            position: Cesium.Cartesian3.fromDegrees(116.45045144518653, 39.91234434075017, 0),
            tx: 0, ty: 0, tz: 50
        }))
		
		// position:表示模型在场景中的位置,使用经度、纬度和高度来定义。在这个示例中,使用 Cesium.Cartesian3.fromDegrees 方法将经度和纬度转换为笛卡尔坐标系。
		// m_url:表示模型的 URI,即模型数据的地址。在这个示例中,使用了相对路径 'examples/SampleData/gltf/man/walk.gltf'。
		// m_scale:表示模型在三个轴上的缩放比例。在这个示例中,模型将按照原始比例的 3 倍进行缩放。
		// m_minimumPixelSize:以像素为单位,表示模型渲染时的最小尺寸。在这个示例中,指定最小尺寸为 1 个像素。

        //添加人群
        this._viewer.entities.add(this._util.createModel({
            position: Cesium.Cartesian3.fromDegrees(116.45007132823285, 39.91223440231512, 5.0),
            m_url: 'examples/SampleData/gltf/man/walk.gltf',
            m_scale: 3,
            m_minimumPixelSize: 1,
        }))

        this._viewer.entities.add(this._util.createModel({
            position: Cesium.Cartesian3.fromDegrees(116.45026244256015, 39.91226094401238, 5.0),
            m_url: 'examples/SampleData/gltf/man/walk.gltf',
            m_scale: 3,
            m_minimumPixelSize: 1,
        }))

        this._viewer.entities.add(this._util.createModel({
            position: Cesium.Cartesian3.fromDegrees(116.45059442902425, 39.912284437562185, 5.0),
            m_url: 'examples/SampleData/gltf/man/walk.gltf',
            m_scale: 3,
            m_minimumPixelSize: 1,
        }))

    })

}

主要实现:

  • 添加火焰、人群3D模型
  • 显示建筑物异常情况

使用 Entity 系统添加模型:

// 添加3D人群
viewer.entities.add({
  position : ...,
  model : {
    uri : '...',
    scale : ... 
  }
});

并通过添加模型颜色、动画圈等方式显示异常情况。

3. 警情分析

/**
 * 警情分析
 */
D3.prototype.analysis = function (callback) {

    if (this._STATECODE.tow !== this._state & this._STATECODE.all !== this._state) {

        this._layers.msg('请先预览完警情预报', {
            time: 3000,
        });
        return false
    }
    this._viewer.scene.camera.stopFlyCircle();
	
	// 下面这段代码是在Cesium中使用flyTo方法进行视角飞行,并在飞行完成后添加一些视觉效果。
	
	// 具体来看:
	
	// _util.flyTo 是Cesium中的视角飞行方法。
	// position: 设定飞行完成后视角的笛卡尔空间坐标。
	// orientation: 设定飞行完成后视角的方向,包括航向角、俯仰角、滚转角。
	// duration: 整个飞行动画的持续时间,单位是秒。
	// callback: 飞行完成后的回调函数。
	// 在回调函数中添加了CSS3标签、相机环绕飞行的动画效果。
	// 还添加了警示线材质的Entity。
	// 如果传入了callback函数则在动画结束后执行。
	// flyTo除了上述参数,还可以设置:
	
	// maximumHeight: 飞行高度限制。
	// pitchAdjustHeight: 调整相机飞行高度的距离。
	// flyOverLongitude 和 flyOverLatitude: 飞行路线的经纬度。
	// 总之,这段代码实现了一个视角飞行的动画效果,并在飞行后的回调里增添了视觉效果,实现了一个较为复杂的动画场景。
    this._util.flyTo({
        position: { x: -2178835.9901788016, y: 4380941.406850311, z: 4092044.408504874 },
        orientation: {
            heading: Cesium.Math.toRadians(45.453049548959),
            pitch: Cesium.Math.toRadians(-15.610707989693905),
            roll: Cesium.Math.toRadians(359.99999999987216)
        },
        duration: 5,
        callback: () => {
            // css3 实现标牌

            let css3Elements = [];
            this._css3Renderer = new Cesium.Css3Renderer(css3Elements, true) //第三个参数为当标签在地球背面时候会隐藏

            this._css3Renderer.addEntityLayer({
                id: 'labelTip',
                position: [116.450217639056, 39.912527799624065, 130.0],//高度为 boxHeightMax
                element: `<div class='ysc-dynamic-layer ys-css3-box' id='labelTip'>
                       <div class='line'></div>
                       <div class='main' style="font-size:25px">
                            <div class="" style="color:#ff9800">警情分析</div>
                           <div class="">该楼七层发生火灾</div>
                           <div class="">指派救生队伍支援!</div>
                       </div>
                   </div>`,
                offset: [10, -250],
                boxShow: false,
                circleShow: false,
            })

            this._viewer.scene.camera.speedRatio = 0.1
            this._viewer.scene.camera.flyCircle(Cesium.Cartesian3.fromDegrees(116.450217639056, 39.912527799624065, 100.0));

            // 警示线特效
            var warn = [
                116.45121972426787, 39.912280505197565, 30.0,
                116.449751129691, 39.912270436562736, 30.0,
                116.44971753510406, 39.91321324258255, 30.0,
                116.45131361499521, 39.91317812427803, 30.0,
                116.45127073097758, 39.91221994119961, 30.0,
            ]
            this._viewer.entities.add({
                wall: {
                    positions: Cesium.Cartesian3.fromDegreesArrayHeights(warn),
                    material: new Cesium.WarnLinkMaterialProperty({ freely: 'cross', color: Cesium.Color.YELLOW, duration: 1000, count: 1.0, direction: '-' }),
                }
            });

            if (typeof callback === 'function') {

                setTimeout(() => {

                    callback()
                }, 3000)
            } else {
                // update state
                this._state = this._STATECODE.three
            }
        }
    })
}

主要实现:

  • 添加HTML标注
  • 相机环绕目标点

使用CSS3渲染添加HTML标注:

// 添加标注
cssRenderer.addLayer({
  position : [116.xx, 39.xx, 130.0],
  element : document.getElementById('label')
});

 

并设置相机环绕飞行完成对目标区域的分析。

4. 警情调度

/**
 * 警情调度
 */
D3.prototype.scheduling = function () {

    if (this._STATECODE.three !== this._state & this._STATECODE.all !== this._state) {

        this._layers.msg('请先预览完警情分析 ', {
            time: 3000,
        });
        return false
    }
    // 救护车
    var paths = [{ lon: 116.44753798513237, lat: 39.91329913144483, alt: 5.0, time: 0 },
    { lon: 116.44754831320495, lat: 39.91205867447874, alt: 5.0, time: 120 },
    { lon: 116.44919669983119, lat: 39.91207453317339, alt: 5.0, time: 240 },
    { lon: 116.45021742143986, lat: 39.91208239585685, alt: 5.0, time: 360 },
    { lon: 116.45021742143986, lat: 39.91208239585685, alt: 5.0, time: 480 }]


    // 消防车
    var paths2 = [{ lon: 116.4552207886404, lat: 39.91205626109142, alt: 5.0, time: 0 },
    { lon: 116.4531359117942, lat: 39.9120348488425, alt: 5.0, time: 120 },
    { lon: 116.45169757241298, lat: 39.91202452492026, alt: 5.0, time: 240 },
    { lon: 116.45021328751454, lat: 39.91201148288871, alt: 5.0, time: 360 },
    { lon: 116.45021328751454, lat: 39.91201148288871, alt: 5.0, time: 480 }]
    // 添加引导线
    this._util.addMaterialLine({
        positions: this._util.transformWGS84ArrayToCartesianArray(paths),
        width: 50,
        material: new Cesium.PolylineCityLinkMaterialProperty({
            color: Cesium.Color.RED,
            duration: 20000
        }),
        clampToGround: true
    })
    this._util.addMaterialLine({
        positions: this._util.transformWGS84ArrayToCartesianArray(paths2),
        width: 50,
        material: new Cesium.PolylineCityLinkMaterialProperty({
            color: Cesium.Color.RED,
            duration: 20000
        }),
        clampToGround: true
    })


    var addMan = () => {

        this._viewer.clock.multiplier = 1.0
        this._viewer.entities.add(this._util.createModel({
            position: Cesium.Cartesian3.fromDegrees(116.4500554199689, 39.91221044177715, 5.0),
            m_url: 'examples/data/model/xiaofangyuan-run.gltf',
            m_scale: 7,
            m_minimumPixelSize: 1,
        }))
        this._viewer.entities.add(this._util.createModel({
            position: Cesium.Cartesian3.fromDegrees(116.45035101783887, 39.912153073679924, 5.0),
            m_url: 'examples/data/model/xiaofangyuan-run.gltf',
            m_scale: 7,
            m_minimumPixelSize: 1,
        }))
    }

    // 添加车辆
    new Promise(() => {

        this._util.setPathRoaming({
            paths: paths,
            model: true,
            m_url: 'examples/data/model/qiche.gltf',
            m_scale: 0.1,
            m_minimumPixelSize: 1,
            label: true,
            l_text: '救护车',
            l_pixelOffset: new Cesium.Cartesian2(52, -48),
            l_fillColor: Cesium.Color.WHITE,
            l_outlineWidth: 3,
            billboard: true,
            b_img: 'examples/images/Textures/bp2.png',
            b_width: 55,
            b_height: 80,
            b_scale: 2,
            b_pixelOffset: new Cesium.Cartesian2(30, 0)
        })
        this._util.setPathRoaming({
            paths: paths2,
            model: true,
            m_url: 'examples/data/model/qiche.gltf',
            m_scale: 0.1,
            m_minimumPixelSize: 1,
            label: true,
            l_text: '消防车',
            l_pixelOffset: new Cesium.Cartesian2(52, -48),
            l_fillColor: Cesium.Color.WHITE,
            l_outlineWidth: 3,
            billboard: true,
            b_img: 'examples/images/Textures/bp.png',
            b_width: 55,
            b_height: 80,
            b_scale: 2,
            b_pixelOffset: new Cesium.Cartesian2(30, 0)
        })
    })

    //街道漫游
    this._viewer.scene.camera.stopFlyCircle();
    // 远景
    this._util.flyTo({
        position: { x: -2178725.2326817405, y: 4380717.691272014, z: 4092429.139375593 },
        orientation: {
            heading: Cesium.Math.toRadians(74.6359315563435),
            pitch: Cesium.Math.toRadians(-31.233145079364085),
            roll: Cesium.Math.toRadians(0.00021380944582563688)
        },
        duration: 5,
        callback: () => {
            //街道
            this._util.flyTo({
                position: { x: -2178718.4499226217, y: 4380320.631065833, z: 4092296.741367402 },
                orientation: {
                    heading: Cesium.Math.toRadians(83.59293245172353),
                    pitch: Cesium.Math.toRadians(-2.0635543646730805),
                    roll: Cesium.Math.toRadians(359.9999999999991)
                },
                duration: 5,
                callback: () => {
                    //漫游 1
                    this._util.flyTo({
                        position: { x: -2178907.5523918574, y: 4380227.944369431, z: 4092295.2689017 },
                        orientation: {
                            heading: Cesium.Math.toRadians(83.59451489628981),
                            pitch: Cesium.Math.toRadians(-2.0635543646734114),
                            roll: Cesium.Math.toRadians(359.99999999999926)
                        },
                        duration: 15,

                        callback: () => {
                            //漫游 2
                            this._util.flyTo({
                                position: { x: -2179038.3685479737, y: 4380186.554328359, z: 4092297.2180936695 },
                                orientation: {
                                    heading: Cesium.Math.toRadians(56.38807730423329),
                                    pitch: Cesium.Math.toRadians(-11.712623638749156),
                                    roll: Cesium.Math.toRadians(359.9999999999992)
                                },
                                duration: 10,
                                callback: () => {

                                    addMan() // 添加消防员
                                    //漫游 3
                                    this._util.flyTo({
                                        position: { x: -2178996.2169643864, y: 4380316.564571191, z: 4092306.3786329245 },
                                        orientation: {
                                            heading: Cesium.Math.toRadians(64.96210850602627),
                                            pitch: Cesium.Math.toRadians(-21.931507732669104),
                                            roll: Cesium.Math.toRadians(359.9998957985643)
                                        },
                                        duration: 8,
                                        callback: () => {
                                            // 弹出结果信息
                                            this._layers.open({
                                                type: 1
                                                , title: false
                                                , closeBtn: 1
                                                , shade: false
                                                , shadeClose: true
                                                , anim: 2
                                                , area: ['500px', '300px']
                                                , offset: 'auto'
                                                , content: `<div style="color:white"><h2 style="text-align:center;padding:10px">警情报告</h2>
                                                <div style="font-size:15px;padding:20px">
                                                <p style="padding-bottom:20px">xxx大楼七层于29日下午发生火灾,报告如下:</p>
                                                <p>原因:    七层火锅商铺电器泄露引起火灾;</p>
                                                <p>损失:    七层共7家商铺受影响,其中两家紧靠起火地较严重;</p>
                                                <p>出警:    接警后10分钟救护车与消防车到达现场,10分钟灭火救险;</p>
                                                <p>伤亡:    无重大伤亡,5人轻伤.</p>
                                                </div></div>`
                                                , btn: ['确认']
                                                , btn1: function () {
                                                    layer.closeAll();
                                                }
                                            });
                                        }
                                    })
                                }
                            })
                        }
                    })
                }
            })

        }
    })
}

主要实现:

  • 添加车辆模型
  • 实现车辆运动、街道漫游动画

使用 Entity 系统添加车辆模型,配合贝塞尔曲线实现运动动画:

// 添加车辆实体
var car = viewer.entities.add({
  // 贝塞尔曲线位置
  position : Cesium.CallbackProperty(...), 
  model : {...}
})

通过飞行设置视角完成街道漫游动画效果。

✨ 源码

 请点击上方绑定的资源下载

✨ 结语

  1. Cesium是一个功能强大的三维地图引擎,使用它可以快速实现各种三维可视化应用。
  2. 使用面向对象方式进行代码封装,可以很好地模块化复杂的三维场景。
  3. 利用Cesium提供的各种对象和效果实现丰富的场景和动画。
  4. 状态管理可以确保场景动画的顺序展现。
  5. Cesium中提供了许多辅助类和工具,可以大幅简化三维开发。

我们改日再会

  

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW利用视觉引导机开发器人精准抓取

LabVIEW利用视觉引导机开发器人精准抓取 本项目利用单目视觉技术指导多关节机器人精确抓取三维物体的技术。通过改进传统的相机标定方法&#xff0c;结合LabVIEW平台的Vision Development和Vision Builder forAutomated Inspection组件&#xff0c;优化了摄像系统的标定过程&a…

使用机器学习进行语法错误检测/纠正

francescofranco_39234 一、说明 一般的学习&#xff0c;特别是深度学习&#xff0c;促进了自然语言处理。各种模型使人们能够执行机器翻译、文本摘要和情感分析——仅举几个用例。今天&#xff0c;我们将研究另一个流行的用途&#xff1a;我们将使用Gramformer构建一个用于机器…

传统船检已经过时?AR智慧船检来助力!!

想象一下&#xff0c;在茫茫大海中&#xff0c;一艘巨型货轮正缓缓驶过。船上的工程师戴着一副先进的AR眼镜&#xff0c;他们不再需要反复翻阅厚重的手册&#xff0c;一切所需信息都实时显示在眼前。这不是科幻电影的场景&#xff0c;而是智慧船检技术带来的现实变革。那么问题…

VMware之FTP的简介以及搭建计算机端口的介绍

目录 一.FTP的简介 1.1 FTP的作用 二.FTP的搭建 2.1 建立组和用户 2.2 添加角色和功能 2.3 用户绑定组 2.4 配置FTP服务器 2.5 授权 2.5 连接测试 三.计算机端口介绍 3.1 端口分类&#xff1a; 3.2 常见的计算机端口及其用途&#xff1a; 四.附图-思维…

C语言使用蔡勒公式判断日期的星期

引言 在日常编程中&#xff0c;处理日期和时间是一个常见的任务。而了解一个特定日期是星期几&#xff0c;是许多应用程序中的一个基本需求。本篇博客将深入解析一个用于计算星期的 C 语言函数。 代码概览 这个函数使用了蔡勒公式来实现&#xff0c;蔡勒公式&#xff08;Zel…

启明智显开源项目分享|基于Model 3c芯片的86中控面板ZX3D95CM20S-V11项目软硬件全开源

前言&#xff1a; 本文为4寸 480*480 RGB接口IPS全面触屏的86中控面板&#xff08;RT-ThreadLVGL&#xff09;软硬件开源干货内容&#xff0c;该项目是综合性非常强的RTOS系列项目&#xff01;项目主控芯片使用 Model 3c&#xff0c;整体实现了简化版本的86中控面板的功能需求…

EDA巨头千亿级收购案谈判中 | 百能云芯

近期传出消息&#xff0c;电子设计自动化&#xff08;EDA&#xff09;领域的巨头新思科技&#xff08;Synopsys&#xff09;已向计算机辅助工程&#xff08;CAE&#xff09;软件行业的领导者安世&#xff08;Ansys&#xff09;递交了一份收购要约。据悉&#xff0c;新思科技的报…

快速排序:高效分割与递归,排序领域的王者算法

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《数据结构&算法》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! &#x1f4cb; 前言 快速排序这个名词&#xff0c;快排之所以叫快排肯定是有点东西的。他在处理大规模数据集时表现及其…

处理urllib.request.urlopen报错UnicodeEncodeError:‘ascii‘

参考&#xff1a;[Python3填坑之旅]一urllib模块网页爬虫访问中文网址出错 目录 一、报错内容 二、报错截图 三、解决方法 四、实例代码 五、运行截图 六、其他UnicodeEncodeError: ascii codec 问题 一、报错内容 UnicodeEncodeError: ascii codec cant encode charac…

缓存和缓冲的区别

近期被这两个词汇困扰了&#xff0c;感觉有本质的区别&#xff0c;搜了一些资料&#xff0c;整理如下 计算机内部的几个部分图如下 缓存&#xff08;cache&#xff09; https://baike.baidu.com/item/%E7%BC%93%E5%AD%98 提到缓存&#xff08;cache&#xff09;&#xff0c;就…

【LeetCode-剑指offer】--3.比特位计数

3.比特位计数 class Solution {public int[] countBits(int n) {int[] bites new int[n 1];for(int i 0 ; i < n;i){bites[i] Count(i);}return bites;}public int Count(int x){int count 0;while(x > 0){x & (x - 1);count;}return count;} }

【SVN】Windows版合并提交bat文件+自定义菜单快捷键

【工具向】利用bat批处理打开TortoiseGit简化版本管理流程_tortoisegit bat-CSDN博客 start cmd /k "cd C:\YourBranchProj && svn cleanup && svn update && svn merge C:\YourTrunkProj -r 历史版本号:HEAD && svn commit -m "me…

项目接口性能优化方案

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表。会点点Java相关技术栈、帆软报表、低代码平台快速开发。技术尚浅&#xff0c;闭关学习中 &#x1f60…

论文阅读《Restormer: Efficient Transformer for High-Resolution Image Restoration》

论文地址:https://openaccess.thecvf.com/content/CVPR2022/html/Zamir_Restormer_Efficient_Transformer_for_High-Resolution_Image_Restoration_CVPR_2022_paper.html 源码地址:https://github.com/swz30/Restormer 概述 图像恢复任务旨在从受到各种扰动(噪声、模糊、雨滴…

低延时视频技术的应用场景和挑战

编者按 无线网络对人们的生活产生了巨大的影响&#xff0c;而5G技术的引入将彻底改变我们与世界互联互通的方式。在5G时代&#xff0c;实现万物互联离不开低延时技术的应用。 LiveVideoStackCon 2023 深圳站邀请到秒点科技的CEO扶凯&#xff0c;为大家分享低延时技术在物联网、…

家政行业的小程序都需要具备哪些功能?

家政服务小程序&#xff0c;覆盖多城&#xff0c;在线派单 适合行业&#xff1a;家电维修、家政保洁、养生护理、美容美发、预约服务上门等 系统功能&#xff1a;服务管理、商品管理、拼团/秒杀、订单管理、会员管理、派单管理、师傅管理、商家/服务点、财务管理、城市代理、次…

Python算法例30 统计前面比自己小的数

1. 问题描述 给定一个整数数组&#xff08;数组大小为n&#xff0c;元素的取值范围为0~10000&#xff09;&#xff0c;对于数组中的每个元素&#xff0c;计算其前面元素中比它小的元素数量。 2. 问题示例 对于数组[1&#xff0c;2&#xff0c;7&#xff0c;8&#xff0c;5]&…

分享44个PyQt5源码总有一个是你想要的

分享44个PyQt5源码总有一个是你想要的 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1_5H_0Ydg0XUa1fz5Jok51Q?pwd6666 提取码&#xff1a;6666 项目名称 B站直播弹幕姬&#xff…

快速、安全、高效地传输海量小文件

随着互联网技术的不断进步&#xff0c;我们正迈入信息爆炸的时代。在这个时代&#xff0c;企业每天都需要在互联网上传输海量的小文件。与传输常见的大文件相比&#xff0c;海量小文件的传输变得更加困难。接下来&#xff0c;我们将分析海量小文件传输面临的挑战&#xff0c;并…

云原生机器学习平台cube-studio开源项目及代码简要介绍

1. cube-studio介绍 云原生机器学习平台cube-studio介绍&#xff1a;https://juejin.cn/column/7084516480871563272 cube-studio是开源的云原生机器学习平台&#xff0c;目前包含特征平台&#xff0c;支持在/离线特征&#xff1b;数据源管理&#xff0c;支持结构数据和媒体标…