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

这篇具有很好参考价值的文章主要介绍了手把手带你开发Cesium三维场景【3D智慧城市警情预警】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

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

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

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

scene.terrainprovider.iscreateskirt,vue,3d,智慧城市,人工智能

​​

效果演示

警情模拟示例

✨ 一、 前言

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

1、初始化三维地图控件

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

2、封装场景操作类

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

3、CSS3渲染标注

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

4、实现视角导航动画

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

5、构建交互界面

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

6、警情场景模拟

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

7、封装视觉效果方法

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

二、关键技术要点

scene.terrainprovider.iscreateskirt,vue,3d,智慧城市,人工智能

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来扩展,它可以添加对各种应用程序有用的功能。 

Name Type Description
container Element | string 包含小部件的DOM元素或ID。
options Viewer.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对象:

Name Type Default Description
canvas HTMLCanvasElement 用于创建场景的HTML cancas元素。
contextOptions ContextOptions 环境和WebGL创建属性
creditContainer Element 用于显示服务描述信息的HTML元素。
creditViewport Element 要在其中显示信用弹出窗口的HTML元素。如果未指定,则视口将作为画布的兄弟添加。
mapProjection MapProjection 在二维和Columbus 视图模式下使用的地图投影。
orderIndependentTranslucency boolean true 如果此项设置为true,并且使用设备支持,将使用与顺序无关的半透明。
scene3DOnly boolean false 如果此项设置为true,将优化三维模式的内存使用和性能,但禁止使用二维或Columbus视图功能。
shadows boolean false 确定阴影是否由太阳投射形成。
mapMode2D MapMode2D 确定二维地图是可旋转的或是可以在在水平方向上无限滚动。
requestRenderMode boolean false
maximumRenderTimeChange number 0.0
depthPlaneEllipsoidOffset number 0.0
msaaSamples number 1

✨ Throws: 

  • DeveloperError : 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提供平铺图像。 

Name Type Description
options BingMapsImageryProvider.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)

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

Name Type Default Description
element HTMLCanvasElement document 要向其中添加事件的元素。
// 处理左击事件
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中提供了许多辅助类和工具,可以大幅简化三维开发。

我们改日再会

 scene.terrainprovider.iscreateskirt,vue,3d,智慧城市,人工智能 文章来源地址https://www.toymoban.com/news/detail-772732.html

到了这里,关于手把手带你开发Cesium三维场景【3D智慧城市警情预警】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手把手带你写一份优秀的开发求职简历(五)技术能力如何凸显优势

    前言 前面的几小节,把 个人信息 和 教育背景 的模块做了讲述,这两个模块处于简历的 第一屏最顶部 ,可以说 HR 会第一眼看见,所以很重要,同时也通过一些讲述告诉求职者从这些方面如何 扬长避短 ,抓住 HR

    2024年02月02日
    浏览(66)
  • 从0到1,手把手带你开发截图工具ScreenCap------001实现基本的截图功能

    从0到1,手把手带你开发windows端的截屏软件ScreenCap 当前版本:ScreenCap---001 支持全屏截图 支持鼠标拖动截图区域 支持拖拽截图 支持保存全屏截图 支持另存截图到其他位置 注:博主所有资源永久免费,若有帮助,请点赞转发是对我莫大的帮助 注:博主本人学习过程的分享,

    2024年02月05日
    浏览(46)
  • 从0到1,手把手带你开发截图工具ScreenCap------002实现设置默认保存的图片位置

    在ScreenCap实现截图功能后增加设置图片默认保存位置的功能 实现选择文件夹作为截图的默认保存位置 注:博主所有资源永久免费,若有帮助,请点赞转发是对我莫大的帮助 注:博主本人学习过程的分享,引用他人的文章皆会标注原作者 注:本人文章非盈利性质,若有侵权请

    2024年02月05日
    浏览(75)
  • Mars3D手把手开发教程(vue3)

    Mars3D三维可视化平台  是火星科技 (opens new window)研发的一款基于 WebGL 技术实现的三维客户端开发平台,基于Cesium (opens new window)优化提升与B/S架构设计,支持多行业扩展的轻量级高效能GIS开发平台,能够免安装、无插件地在浏览器中高效运行,并可快速接入与使用多种GIS数

    2024年04月15日
    浏览(81)
  • 从0到1,手把手带你开发截图工具ScreenCap------003实现最小化程序到托盘运行

    为了方便截图干净,实现最小化程序到托盘运行,简洁,勿扰 实现最小化程序到托盘运行 实现托盘菜单功能 实现回显主窗体 实现托盘开始截屏 实现气泡信息提示 实现托盘程序提示 实现托盘退出程序 封装完好,可复用 注:博主所有资源永久免费,若有帮助,请点赞转发是

    2024年02月05日
    浏览(53)
  • Android:手把手带你入门跨平台UI开发框架Flutter,渣本Android开发小伙如何一步步成为架构师

    3.1 框架结构 Flutter框架主要分为两层:FrameWork层、Engine层,如下图所示: 说明:开发时,主要基于Framework层;运行时,则是运行在 Engine上。每层的具体介绍如下: 3.2 原理概述 开发时,主要基于Framework层;运行时,则是运行在 Engine上 Engine是Flutter的独立虚拟机,由它适配 提

    2024年04月16日
    浏览(71)
  • 【第二趴】uni-app开发工具(手把手带你安装HBuilderX、搭建第一个多端项目初体验)

    聚沙成塔——每天进步一点点,大家好我是几何心凉,不难发现越来越多的前端招聘JD中都加入了uni-app 这一项,它也已经成为前端开发者不可或缺的一项技能了,所以凉哥为大家推出 聚沙成塔【45天玩转uni-app】 专栏,帮助大家对 uni-app 进行学习和理解,uni-app可以通过一套代

    2023年04月23日
    浏览(96)
  • 【JUnit技术专题】「入门到精通系列」手把手+零基础带你玩转单元测试,让你的代码更加“强壮”(实战开发篇)

    本节内容主要介绍JUnit单元测试功能框架,并以实战演练的形式进行讲解。本系列教程主要针对代码编程方式和模型,重点讲解实战代码开发。通过本系列教程的学习,您将能够深入了解JUnit单元测试框架的使用和原理,并掌握如何在实际项目中运用JUnit进行单元测试。 以下是

    2024年02月03日
    浏览(83)
  • 手把手教学RRT*(RRTSTAR)三维算法MATLAB仿真(代码可直接运行,视频手把手教学)

            在我以前的作品里有关于RRT算法的视频和代码,今天主要讲解一下RRT*算法的原理。RRT*算法主要是在RRT算法的基础上加上了重写父节点和随机重连的两个步骤。具体的实现方式我想以视频的方式向大家讲解,个人感觉讲解的十分详细。视频连接在这里,希望大家看

    2024年04月17日
    浏览(50)
  • 手把手带你搞懂AMS启动原理

    彻底搞懂AMS即ActivityManagerService,看这一篇就够了 最近那么多教学视频(特别是搞车载的)都在讲AMS,可能这也跟要快速启动一个app(甚至是提高安卓系统启动速度有关),毕竟作为安卓系统的核心系统服务之一,AMS以及PMS都是很重要的,而我之前在 应用的开端–PackageManag

    2024年02月12日
    浏览(122)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包