美文网首页
智慧园区简单功能Demo

智慧园区简单功能Demo

作者: 韩无仙 | 来源:发表于2021-08-23 11:33 被阅读0次

    一、前言

    智慧园区业内有较多成熟解决方案,这里仅个人纯原生Cesium开发、使用各开源免费数据做出其中部分功能。

    二、获得数据

    地理信息数据来源

    地理空间数据云
    数据资源更新比较稳定,免费数据包括Landsat系列、中巴资源卫星、MODIS数据的各种产品、DEM数字高程数据、EO-1数据、NOAAA VHRR数据产品、Sentinel数据等。账号注册,通过审核直接下载即可。

    中国遥感数据共享网
    国内存档周期最长的数据网站,对Landsat数据免费共享,也可订购国外商业卫星数据。注册账号,通过审核就可直接下载。

    OpenStreetMap
    一个开源的世界地图,可依据开放许可协议自由使用,并且可以由人们自由的进行编辑。

    自行绘制geoJson矢量数据

    模型数据来源

    1、通过revit、bentley、3dmax、maya等模型制作软件制作建筑模型。通过obj2gltf等格式转换工具转换成Cesium可展示格式。

    2、调用Cesium Ion的OSMBuilding服务,展示全球建筑模型数据。

    倾斜摄影数据来源

    无人机航拍照片经过空三演算获得osgb数据。

    三、功能

    全景展示

    需要对应园区模型数据,用Cesium对应的加载方法加载。

    const tileset = new Cesium.Cesium3DTileset({
          url: './NewYork/tileset.json',
        })
        tileset.readyPromise.then((tile) => {
          tileset.style = new Cesium.Cesium3DTileStyle({
            color: {
              conditions: [
                /* 根据模型属性分层设色 */
                ['true', "color('white')"],
                //eg: ['${Height} >=50', "color('red')"],
              ],
            },
          })
          viewer.scene.primitives.add(tileset)
    
    image.png

    多元数据叠加

    路网信息

    async addGeoJson() {
          /**
           * @description: 添加路网信息
           *
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const that = this
          let res = await Cesium.GeoJsonDataSource.load('NYC_roads_draw.json', {
            stroke: Cesium.Color.WHITE,
            fill: Cesium.Color.BLUE.withAlpha(0.3),
            strokeWidth: 2,
          })
          res.show = false
          viewer.dataSources.add(res)
          let entities = res.entities.values
          for (let i = 0, l = entities.length; i < l; i++) {
            let thisLine = entities[i]
            // thisLine.nameID = 'line' + i
            thisLine.polyline.width = 20
            thisLine.polyline.material = new Cesium.PolylineGlowMaterialProperty({
              glowPower: 0.1,
              color: Cesium.Color.BLUE.withAlpha(0.7),
            })
          }
        }
    
    image.png

    管线信息

    地下管线缺少数据,本来用glb矿道数据模拟,效果不佳,采用手绘动态线模拟。动态线PolylineTrailLinkMaterialProperty类在Cesium1.7版本后无法拓展至Cesium类。


    管网.gif
    async addPipe() {
          /**
           * @description: 添加管线(geojson)
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const that = this
          let res = await Cesium.GeoJsonDataSource.load('NYC_pipes_draw.json', {
            stroke: Cesium.Color.WHITE,
            fill: Cesium.Color.BLUE.withAlpha(0.3),
            strokeWidth: 2,
          })
          res.show = false
          viewer.dataSources.add(res)
          let entities = res.entities.values
          for (let i = 0, l = entities.length; i < l; i++) {
            let thisLine = entities[i]
            thisLine.polyline.width = 5
            thisLine.polyline.material = new that.PolylineTrailLinkMaterialProperty(
              Cesium.Color.GREEN,
              3000
            )
          }
          /* 👇若使用gltf模型 */
          // const pipes = addGltf(
          //   [-74.01263164288234, 40.710022989658896, -250],
          //   'pipe',
          //   'ParcLeadMine.glb'
          // )
          // viewer.scene.primitives.add(pipes)
          // pipes.show = false
          // function addGltf(degrees, id, model_url, scale = 1) {
          //   /**
          //    * @author: citrusrlia@foxmail.com
          //    * @description: 添加gltf模型
          //    * @param {degrees:array id:string model_url:string scale:number}
          //    * @return {*}
          //    */
          //   let position = Cesium.Cartesian3.fromDegrees(
          //     degrees[0],
          //     degrees[1],
          //     degrees[2]
          //   )
          //   let heading = 90
          //   let pitch = 0
          //   let roll = 0
          //   let modelMatrix = Cesium.Transforms.headingPitchRollToFixedFrame(
          //     position,
          //     new Cesium.HeadingPitchRoll(heading, pitch, roll)
          //   )
          //   let model = Cesium.Model.fromGltf({
          //     url: model_url,
          //     modelMatrix: modelMatrix,
          //     scale: scale,
          //     id: id + '_model',
          //     maximumScreenSpaceError: 16,
          //     show: true,
          //     allowPicking: true,
          //   })
          //   return model
          // }
        }
    

    流动线PolylineTrailLinkMaterialProperty

    initFlow() {
          /**
           * @description:流动线类,1.70后版本Cesium不可拓展材质类,将材质类添加至vue
           * @param {color:Cesium.color duation:ms}
           * @author: citrusrlia@foxmail.com
           */
          function PolylineTrailLinkMaterialProperty(color, duration) {
            this._definitionChanged = new Cesium.Event()
            this._color = undefined
            this._colorSubscription = undefined
            this.color = color
            this.duration = duration
            this._time = new Date().getTime()
          }
    
          Object.defineProperties(PolylineTrailLinkMaterialProperty.prototype, {
            isConstant: {
              get: function () {
                return false
              },
            },
            definitionChanged: {
              get: function () {
                return this._definitionChanged
              },
            },
            color: Cesium.createPropertyDescriptor('color'),
          })
          PolylineTrailLinkMaterialProperty.prototype.getType = function (time) {
            return 'PolylineTrailLink'
          }
          PolylineTrailLinkMaterialProperty.prototype.getValue = function (
            time,
            result
          ) {
            if (!Cesium.defined(result)) {
              result = {}
            }
            result.color = Cesium.Property.getValueOrClonedDefault(
              this._color,
              time,
              Cesium.Color.WHITE,
              result.color
            )
            result.image = Cesium.Material.PolylineTrailLinkImage
            result.time =
              ((new Date().getTime() - this._time) % this.duration) / this.duration
            return result
          }
          PolylineTrailLinkMaterialProperty.prototype.equals = function (other) {
            return (
              this === other ||
              (other instanceof PolylineTrailLinkMaterialProperty &&
                Cesium.Property.equals(this._color, other._color))
            )
          }
          this.PolylineTrailLinkMaterialProperty = PolylineTrailLinkMaterialProperty
          Cesium.Material.PolylineTrailLinkType = 'PolylineTrailLink'
          Cesium.Material.PolylineTrailLinkImage = 'fade_blue.jpeg'
          Cesium.Material.PolylineTrailLinkSource =
            'czm_material czm_getMaterial(czm_materialInput materialInput)\n\
                                                          {\n\
                                                               czm_material material = czm_getDefaultMaterial(materialInput);\n\
                                                               vec2 st = materialInput.st;\n\
                                                               vec4 colorImage = texture2D(image, vec2(fract(st.s - time), st.t));\n\
                                                               material.alpha = colorImage.a * color.a;\n\
                                                               material.diffuse = (colorImage.rgb+color.rgb)/2.0;\n\
                                                               return material;\n\
                                                           }'
          Cesium.Material._materialCache.addMaterial(
            Cesium.Material.PolylineTrailLinkType,
            {
              fabric: {
                type: Cesium.Material.PolylineTrailLinkType,
                uniforms: {
                  color: new Cesium.Color(1.0, 0.0, 0.0, 0.5),
                  image: Cesium.Material.PolylineTrailLinkImage,
                  time: 0,
                },
                source: Cesium.Material.PolylineTrailLinkSource,
              },
              translucent: function (material) {
                return true
              },
            }
          )
        },
      }
    

    动态边界

    添加动态墙显示园区边界


    光墙_ss.gif
    addBorder() {
          /**
           * @description: 添加园区动态光效墙
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          let alp = 1,
            num = 0
          const border = viewer.entities.add({
            name: 'my-border',
            wall: {
              show: true,
              positions: new Cesium.Cartesian3.fromDegreesArrayHeights([
                -74.0134334564209, 40.71439496334888, 100, -74.01660919189453,
                40.70500981124441, 100, -74.01418447494507, 40.70463567898069, 100,
                -74.00809049606323, 40.71203660068803, 100, -74.0134334564209,
                40.71439496334888, 100,
              ]),
              material: new Cesium.ImageMaterialProperty({
                image: 'fade_blue.jpeg',
                transparent: true,
                color: new Cesium.CallbackProperty(function () {
                  if (num % 2 === 0) {
                    alp -= 0.005
                  } else {
                    alp += 0.005
                  }
    
                  if (alp <= 0.3) {
                    num++
                  } else if (alp >= 1) {
                    num++
                  }
                  return Cesium.Color.WHITE.withAlpha(alp)
                }, false),
              }),
            },
          })
        }
    

    地标信息

    添加地标label,设置对应可视距离。

    addLabel() {
          /**
           * @description: 添加Label 设置label可见距离
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const that = this
          for (let i = 0, l = Object.keys(this.places).length; i < l; i++) {
            viewer.entities.add({
              name: Object.keys(this.places)[i],
              position: Cesium.Cartesian3.fromDegrees(
                this.places[Object.keys(this.places)[i]][0],
                this.places[Object.keys(this.places)[i]][1],
                this.places[Object.keys(this.places)[i]][2]
              ),
              label: {
                text: Object.keys(this.places)[i],
                scale: 0.6,
                fillColor: Cesium.Color.fromCssColorString('#ffffff'), //字体颜色
                backgroundColor: Cesium.Color.fromCssColorString('#666666'), //背景颜色
                showBackground: true, //是否显示背景颜色
                // style: 2, //label样式
                // show: false,
                outlineWidth: 1,
                verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
                horizontalOrigin: Cesium.HorizontalOrigin.LEFT, //水平位置
                pixelOffset: new Cesium.Cartesian2(-50, 0), //偏移
                eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 负值则在更上层
              },
              scaleByDistance: new Cesium.NearFarScalar(100, 2, 500, 0.0),
            })
          }
          let labelArray = [
            [-74.01319334175476, 40.708918331339284, '格林威治街给水管'],
            [-74.01117454037451, 40.708399481792796, '华尔街中央给水管'],
            [-74.0130122995653, 40.707738996075705, '尤特尔街给水管'],
            [-74.01235941779147, 40.70907308085917, '泰晤士街给水管'],
            [-74.01118964381898, 40.7063315127262, '拿索街给水管'],
          ]
          for (let i = 0, l = labelArray.length; i < l; i++) {
            viewer.entities.add({
              name: 'underground_pip_' + i,
              position: Cesium.Cartesian3.fromDegrees(
                labelArray[i][0],
                labelArray[i][1],
                10
              ),
              show: false,
              label: {
                text: labelArray[i][2],
                scale: 0.6,
                fillColor: Cesium.Color.fromCssColorString('#ffffff'), //字体颜色
                backgroundColor: Cesium.Color.fromCssColorString('#666666'), //背景颜色
                showBackground: true, //是否显示背景颜色
                // style: 2, //label样式
                // show: false,
                outlineWidth: 1,
                // clampToGround:true,
                verticalOrigin: Cesium.VerticalOrigin.CENTER, //垂直位置
                horizontalOrigin: Cesium.HorizontalOrigin.LEFT, //水平位置
                pixelOffset: new Cesium.Cartesian2(-50, 0), //偏移
                eyeOffset: new Cesium.Cartesian3(0, 0, -1000), // 负值则在更上层
              },
            })
          }
        }
    

    园区漫游

    自由漫游

    使用wasd控制行动,鼠标左键控制方向。


    自由漫游s.gif
    startRoaming() {
        this.roamingSettings(false);
        /* 自由漫游初始点位 */
        this.cameraFlyTo(
          new Cesium.Cartesian3(
            1333262.809154561,
            -4654847.86027459,
            4137655.768559768
          ),
          0.4234449223726292,
          0,
          6.283167794440473
        );
        let that = this;
        const ellipsoid = that.viewer.scene.globe.ellipsoid;
        let startMousePosition;
        let mousePosition;
        let flags = this.roamingFlags;
        flags.available = true;
        this.handler = new Cesium.ScreenSpaceEventHandler(that.viewer.scene.canvas);
        let handler = this.handler;
        handler.setInputAction(function (movement) {
          flags.looking = true;
          mousePosition = startMousePosition = Cesium.Cartesian3.clone(
            movement.position
          );
        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);
    
        handler.setInputAction(function (movement) {
          mousePosition = movement.endPosition;
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    
        handler.setInputAction(function (position) {
          flags.looking = false;
        }, Cesium.ScreenSpaceEventType.LEFT_UP);
    
        function getFlagForKeyCode(keyCode) {
          switch (keyCode) {
            case "W".charCodeAt(0):
              return "moveForward";
            case "S".charCodeAt(0):
              return "moveBackward";
            case "Q".charCodeAt(0):
              return "moveUp";
            case "E".charCodeAt(0):
              return "moveDown";
            case "D".charCodeAt(0):
              return "moveRight";
            case "A".charCodeAt(0):
              return "moveLeft";
            default:
              return undefined;
          }
        }
    
        document.addEventListener(
          "keydown",
          function (e) {
            let flagName = getFlagForKeyCode(e.keyCode);
            if (typeof flagName !== "undefined") {
              flags[flagName] = true;
            }
          },
          false
        );
    
        document.addEventListener(
          "keyup",
          function (e) {
            let flagName = getFlagForKeyCode(e.keyCode);
            if (typeof flagName !== "undefined") {
              flags[flagName] = false;
            }
          },
          false
        );
    
        that.viewer.clock.onTick.addEventListener(function (clock) {
          let camera = that.viewer.camera;
          if (!flags.available) {
            return;
          }
          if (flags.looking) {
            let width = that.viewer.scene.canvas.clientWidth;
            let height = that.viewer.scene.canvas.clientHeight;
            /* 保证MousePositon存在 */
            if (mousePosition) {
              // Coordinate (0.0, 0.0) will be where the mouse was clicked.
              let x = (mousePosition.x - startMousePosition.x) / width;
              let y = -(mousePosition.y - startMousePosition.y) / height;
              /* 转向系数 */
              let lookFactor = 0.05;
              camera.lookRight(x * lookFactor);
              camera.lookUp(y * lookFactor);
            }
          }
    
          // 移动系数
          let cameraHeight = ellipsoid.cartesianToCartographic(camera.position)
            .height;
          let moveRate = cameraHeight / 20.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);
          }
        });
      }
    

    给定线路漫游

    通过画线绘制漫游路线,通过漫游点列表编辑或删除漫游点,点击开始漫游后跟随所画线路漫游


    漫游点编辑s.gif
    漫游点漫游s.gif
    addPlace(position) {
          /**
           * @description:添加漫游点
           * @param {*}position:Cartisian3
           * @author: citrusrlia@foxmail.com
           */
          let Stop = {}
          //参数传入站点变量
          Stop.position = position
          Stop.time = 5
          Stop.name = '漫游点'
          if (this.allStops.length < 1) {
            Stop.heading = 0
            Stop.pitch = 0
            Stop.roll = 0
          } else {
            Stop.heading = this.getHeading(
              this.allStops.slice(-1)[0].position,
              position
            )
            Stop.pitch = this.getPitch(
              this.allStops.slice(-1)[0].position,
              position
            )
            Stop.roll = 0
          }
    
          //传入飞行站点数组
          this.allStops.push(Stop)
          this.$notify({
            title: '成功',
            message: '添加漫游点成功',
            type: 'success',
          })
    
          let length = this.allStops.length
    
          if (this.allStops.length > 1) {
            // 实体路线
            viewer.entities.add({
              id: 'fly_line_' + (this.allStops.length + 1),
    
              polyline: {
                positions: [
                  this.allStops[length - 2].position,
    
                  this.allStops[length - 1].position,
                ],
                width: 5,
                material: Cesium.Color.BLUE,
              },
              show: this.line_bool,
            })
          }
          /* 动态点参数 */
          let x = 0
          let size = 10
          let isAdd = true
          let isZoom = true
    
          viewer.entities.add({
            position: this.allStops[length - 1].position,
            point: {
              color: new Cesium.CallbackProperty(function () {
                if (isAdd) {
                  x = x + 0.05
                  if (x > 1) {
                    isAdd = false
                  }
                } else {
                  x = x - 0.05
                  if (x < 0) {
                    isAdd = true
                  }
                }
                return Cesium.Color.fromCssColorString('#ffffff')
              }, false),
              pixelSize: new Cesium.CallbackProperty(function () {
                if (isZoom) {
                  size = size + 0.5
                  if (size > 10) {
                    isZoom = false
                  }
                } else {
                  size = size - 0.5
                  if (size < 3) {
                    isZoom = true
                  }
                }
                return size
              }, false),
              outlineWidth: 1,
              outlineColor: new Cesium.CallbackProperty(function () {
                return Cesium.Color.fromCssColorString('#ffffff').withAlpha(0.2)
              }, false),
              disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
            id: 'fly_nail_' + (this.allStops.length + 1),
          })
        },
        cleanPlace() {
          /**
           * @description: 清除所有漫游路线
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          /* 清除所有线路,统一重绘 */
          for (let i = 0, l = viewer.entities._entities._array.length; i < l; i++) {
            if (viewer.entities._entities._array[i].id?.indexOf('fly') > -1) {
              viewer.entities.remove(viewer.entities._entities._array[i])
              i--
              l--
            }
          }
        },
        startFly() {
          /**
           * @description: 开始飞行
           * @param {*} this
           * @author: citrusrlia@foxmail.com
           */
          if (this.allStops.length < 1) {
            this.$notify.error({
              title: '错误',
              message: '请添加至少两个漫游点',
            })
            return
          }
          const that = this
          const scene = viewer.scene
          this.reCalStops()
          if (this.tempFlyPoint) {
            /* 暂停漫游 继续 */
            this.flyIndex = this.tempFlyPoint
            this.tempFlyPoint = undefined
            if (this.flyIndex < this.allStops.length) {
              /* 关闭镜头控制 */
              scene.screenSpaceCameraController.enableRotate = false
              scene.screenSpaceCameraController.enableTranslate = false
              scene.screenSpaceCameraController.enableZoom = false
              scene.screenSpaceCameraController.enableTilt = false
              scene.screenSpaceCameraController.enableLook = false
    
              viewer.camera.setView({
                destination: this.allStops[this.flyIndex - 1].position,
                orientation: {
                  heading: this.allStops[this.flyIndex - 1].heading,
                  pitch: this.allStops[this.flyIndex - 1].pitch,
                  roll: this.allStops[this.flyIndex - 1].roll,
                },
              })
              fly()
            }
          } else {
            this.flyIndex = 1
            if (this.flyIndex < this.allStops.length) {
              scene.screenSpaceCameraController.enableRotate = false
              scene.screenSpaceCameraController.enableTranslate = false
              scene.screenSpaceCameraController.enableZoom = false
              scene.screenSpaceCameraController.enableTilt = false
              scene.screenSpaceCameraController.enableLook = false
              /* 使用setView定位第一个漫游点 */
              viewer.camera.setView({
                destination: this.allStops[0].position,
                orientation: {
                  heading: this.allStops[0].heading,
                  pitch: this.allStops[0].pitch,
                  roll: this.allStops[0].roll,
                },
              })
              fly()
            }
          }
          function changeCameraHeading(stopIndex, duration = 5) {
            /**
             * @description: 转向
             * @param {*}
             * @author: citrusrlia@foxmail.com
             */
            viewer.camera.setView({
              orientation: {
                heading: that.allStops[stopIndex + 1].heading, // 方向
                pitch: that.allStops[stopIndex + 1].pitch, // 倾斜角度
              },
            })
            let count = 1
            let thisHeadingDegree = Cesium.Math.toDegrees(viewer.camera.heading)
            let nextHeadingDegree = Cesium.Math.toDegrees(
              that.allStops[stopIndex + 1].heading
            )
            let angle = nextHeadingDegree - thisHeadingDegree
            let perTurn = Cesium.Math.toRadians(angle / duration)
            let turningCallback = (time, result) => {
              if (count == duration) {
                viewer.scene.postRender.removeEventListener(turningCallback)
                return
              }
              viewer.camera.flyTo({
                destination: that.allStops[stopIndex + 1].position,
                orientation: {
                  heading: perTurn + viewer.camera.heading,
                  pitch: that.allStops[stopIndex + 1].pitch,
                },
                duration: 5,
              })
              count += 1
            }
            // viewer.scene.postRender.addEventListener(turningCallback)
          }
          function fly() {
            /**
             * @description: 飞行主函数
             * @param {*} that
             * @author: citrusrlia@foxmail.com
             */
            if (that.flyIndex >= that.allStops.length) {
              /* 飞行结束 */
              scene.screenSpaceCameraController.enableRotate = true
              scene.screenSpaceCameraController.enableTranslate = true
              scene.screenSpaceCameraController.enableZoom = true
              scene.screenSpaceCameraController.enableTilt = true
              scene.screenSpaceCameraController.enableLook = true
              MyCamera.resetCamera()
            } else {
              let l = that.flyIndex
              if (l >= 1) {
                viewer.camera.flyTo({
                  destination: that.allStops[l].position, // 设置位置
                  orientation: {
                    heading: that.allStops[l - 1].heading, // 方向
                    pitch: that.allStops[l - 1].pitch, // 倾斜角度
                    roll: that.allStops[l - 1].roll,
                  },
                  // easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT,
                  duration: 5, // 设置飞行持续时间,默认会根据距离来计算
                  complete: function () {
                    /* 飞完后执行下一站,调整镜头角度指向下一站 */
                    if (l + 1 < that.allStops.length) {
                      changeCameraHeading(l)
                    }
                    fly()
                  },
                })
              }
              that.flyIndex++
            }
          }
        },
        // 飞行暂停
        pauseFly() {
          this.tempFlyPoint = this.flyIndex
          this.flyIndex = this.allStops.length + 1
        },
        // 飞行停止
        stopFly() {
          this.flyIndex = this.allStops.length + 1
          this.tempFlyPoint = undefined
          viewer.scene.screenSpaceCameraController.enableRotate = true
          viewer.scene.screenSpaceCameraController.enableTranslate = true
          viewer.scene.screenSpaceCameraController.enableZoom = true
          viewer.scene.screenSpaceCameraController.enableTilt = true
          viewer.scene.screenSpaceCameraController.enableLook = true
          MyCamera.resetCamera()
        },
        changeLine() {
          viewer.entities._entities._array.forEach((res) => {
            if (res.id.indexOf('fly') > -1) {
              res.show = !res.show
            }
          })
          this.tempFlyPoint = undefined
        }
    

    视频影像融合

    通过video标签构建dom实体,将其设为entity的材质对象.


    视频融合s.gif
    // video标签,设置autoplay和display:none
    <video ref="video" autoplay loop crossorigin controls style="display: none">
        <source
          src="https://cesium.com/public/SandcastleSampleData/big-buck-bunny_trailer.mp4"
          type="video/mp4"
        />
      </video>
    
    addVideo() {
          /**
           * @description: 添加视频
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const videoElement = this.$refs.video
          viewer.showRenderLoopErrors = false
          viewer.shouldAnimate = true
    
          viewer.entities.add({
            name: 'video-screen',
            id: 'video_screen',
            wall: {
              positions: Cesium.Cartesian3.fromDegreesArrayHeights([
                -74.01595609453216, 40.706172490663725, 120, -74.0162584955552,
                40.70537146061003, 120,
              ]),
              material: videoElement,
              // clampToGround: true,
            },
            show: false,
          })
        }
    

    应急事件

    在地图上显示应急事件(应是传感器实时信息),并可以定位,设置影响范围。


    定位火源_s.gif
    改变火源位置_s.gif
    addParticleSystem() {
          /**
           * @description:添加粒子效果与波纹效果
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const that = this
          const viewModel = {
            emissionRate: 200.0,
            gravity: 0,
            minimumParticleLife: 1.5,
            maximumParticleLife: 1.8,
            minimumSpeed: 7,
            maximumSpeed: 9,
            startScale: 3.0,
            endScale: 1.5,
            particleSize: 2,
          }
          let emitterModelMatrix = new Cesium.Matrix4()
          let translation = new Cesium.Cartesian3()
          let rotation = new Cesium.Quaternion()
          let hpr = new Cesium.HeadingPitchRoll()
          let trs = new Cesium.TranslationRotationScale()
    
          that.entity = viewer.entities.add({
            position: this.particlePoint,
          })
          let gravityScratch = new Cesium.Cartesian3()
          function computeEmitterModelMatrix() {
            //调节粒子的发射方向
            hpr = Cesium.HeadingPitchRoll.fromDegrees(0.0, 0.0, 0.0, hpr)
            //喷泉位置
            trs.translation = Cesium.Cartesian3.fromElements(0, 0, 5.4, translation)
            trs.rotation = Cesium.Quaternion.fromHeadingPitchRoll(hpr, rotation)
    
            return Cesium.Matrix4.fromTranslationRotationScale(
              trs,
              emitterModelMatrix
            )
          }
          /* 重力,火焰特效时关闭,漏水特效时开启 */
          function applyGravity(p, dt) {
            // We need to compute a local up vector for each particle in geocentric space.
            var position = p.position
    
            Cesium.Cartesian3.normalize(position, gravityScratch)
            Cesium.Cartesian3.multiplyByScalar(
              gravityScratch,
              viewModel.gravity * dt,
              gravityScratch
            )
    
            p.velocity = Cesium.Cartesian3.add(
              p.velocity,
              gravityScratch,
              p.velocity
            )
          }
          function computeModelMatrix(entity, time) {
            return entity.computeModelMatrix(time, new Cesium.Matrix4())
          }
          const particleSystem = viewer.scene.primitives.add(
            new Cesium.ParticleSystem({
              show: false,
              image: 'fire4.png',
              startColor: new Cesium.Color(1, 1, 1, 1),
              endColor: new Cesium.Color(0.5, 0, 0, 0),
              startScale: viewModel.startScale,
              endScale: viewModel.endScale,
              minimumParticleLife: viewModel.minimumParticleLife,
              maximumParticleLife: viewModel.maximumParticleLife,
              minimumSpeed: viewModel.minimumSpeed,
              maximumSpeed: viewModel.maximumSpeed,
              imageSize: new Cesium.Cartesian2(
                viewModel.particleSize,
                viewModel.particleSize
              ),
              emissionRate: viewModel.emissionRate,
              lifetime: 6.0,
              //粒子发射器
              emitter: new Cesium.SphereEmitter(2.5),
              emitterModelMatrix: computeEmitterModelMatrix(),
              // updateCallback: applyGravity,
              sizeInMeters: true,
              performance: false,
            })
          )
          viewer.scene.preUpdate.addEventListener(function (scene, time) {
            particleSystem.modelMatrix = computeModelMatrix(that.entity, time)
            // Account for any changes to the emitter model matrix.
            particleSystem.emitterModelMatrix = computeEmitterModelMatrix()
          })
          /**
           * @description: 添加应急点波纹特效
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          let myAxis = 0,
            myAxis2 = 0
          const waveCallback = new Cesium.CallbackProperty((time, result) => {
            myAxis += 1
            if (myAxis >= that.maxendAxis) {
              myAxis = 1
              return 1
            }
            return myAxis
          }, false)
          const waveCallback2 = new Cesium.CallbackProperty((time, result) => {
            myAxis2 += 1
            if (myAxis2 >= that.maxendAxis) {
              myAxis2 = 1
              return 1
            }
            return myAxis2
          }, false)
          viewer.entities.add({
            id: 'waveEffect',
            show: false,
            ellipse: {
              clampToGround: true, // 与outline无法一起启用
              semiMinorAxis: waveCallback,
              semiMajorAxis: waveCallback2,
              // outline: true,
              // outlineColor: Cesium.Color.RED,
              // outlineWidth: 20,
              material: Cesium.Color.RED.withAlpha(0.3),
            },
            position: this.particlePoint,
          })
        }
    changeParticlePoint() {
          /**
           * @description: 改变粒子效果位置
           * @param {*}
           * @author: citrusrlia@foxmail.com
           */
          const that = this
          that.$notify({
            title: '变更应急点',
            message: '移动鼠标改变位置,点击鼠标右键确认。',
            duration: 0,
          })
          let em_handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
          em_handler.setInputAction((e) => {
            let ray = viewer.scene.camera.getPickRay(e.endPosition)
            let cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)
            that.particlePoint = cartesian3
            let waveEntity = viewer.entities.getById('waveEffect')
            waveEntity.position._value = cartesian3
            that.entity.position._value = cartesian3
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          em_handler.setInputAction((e) => {
            let ray = viewer.scene.camera.getPickRay(e.position)
            let cartesian3 = viewer.scene.globe.pick(ray, viewer.scene)
            that.particlePoint = cartesian3
            let waveEntity = viewer.entities.getById('waveEffect')
            waveEntity.position._value = cartesian3
            that.entity.position._value = cartesian3
            em_handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
            em_handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
        }
    

    信息弹窗与边框拾取

    点击模型、标签等弹出信息窗口,鼠标滑过模型有边框拾取。


    handler_s.gif
     init(viewer) {
        /**
         * @description: handler初始化
         * @param {*}
         * @author: citrusrlia@foxmail.com
         */
        const that = this
        this.viewer = viewer
        this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
        this.handler.setInputAction((e) => {
          let pickedObject = that.viewer.scene.pick(e.position)
          let pickPosition = that.viewer.scene.camera.pickEllipsoid(e.position)
          let cartographic = Cesium.Cartographic.fromCartesian(
            pickPosition,
            that.viewer.scene.globe.ellipsoid,
            new Cesium.Cartographic()
          )
          let lat = Cesium.Math.toDegrees(cartographic.latitude)
          let lng = Cesium.Math.toDegrees(cartographic.longitude)
          let height = cartographic.height
          console.log('[Lng=>' + lng + ',Lat=>' + lat + ',H=>' + height + ']')
          /* 选择模型 */
          if (pickedObject && pickedObject.content) {
            that.lastPickedObject &&
              (that.lastPickedObject.color = that.lastPickedColor)
            that.lastPickedObject = pickedObject
            that.lastPickedColor = pickedObject.color
            pickedObject.color = Cesium.Color.RED
          } else {
            that.lastPickedObject &&
              (that.lastPickedObject.color = that.lastPickedColor)
            that.lastPickedObject = null
            that.lastPickedColor = null
          }
          if (
            that.viewer.scene.pickPositionSupported &&
            Cesium.defined(pickedObject) &&
            pickedObject.id
          ) {
            /* todo 判断是否点击primitive */
            if (pickedObject.id.constructor == String) {
            }
          }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
        /* 3Dtile蓝色边框 */
        const silhouetteBlue =
          Cesium.PostProcessStageLibrary.createEdgeDetectionStage()
        silhouetteBlue.uniforms.color = Cesium.Color.BLUE
        silhouetteBlue.uniforms.length = 0.01
        silhouetteBlue.selected = []
        const silhouetteGreen =
          Cesium.PostProcessStageLibrary.createEdgeDetectionStage()
        silhouetteGreen.uniforms.color = Cesium.Color.LIME
        silhouetteGreen.uniforms.length = 0.01
        silhouetteGreen.selected = []
    
        viewer.scene.postProcessStages.add(
          Cesium.PostProcessStageLibrary.createSilhouetteStage([
            silhouetteBlue,
            silhouetteGreen,
          ])
        )
        // Information about the currently selected feature
        const selected = {
          feature: undefined,
          originalColor: new Cesium.Color(),
        }
    
        // An entity object which will hold info about the currently selected feature for infobox display
        const selectedEntity = new Cesium.Entity()
        this.handler.setInputAction((e) => {
          silhouetteBlue.selected = []
          var pickedFeature = viewer.scene.pick(e.endPosition)
          if (pickedFeature !== selected.feature) {
            silhouetteBlue.selected = [pickedFeature]
          }
        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
    
        // this.initMeasure()
        return this
      }
    

    四、 结语

    git地址:https://gitee.com/han_wu_xian/cesium-demo.git
    本例使用数据均开源合法,如果这篇文章对你有一些帮助,那我会很开心😋

    相关文章

      网友评论

          本文标题:智慧园区简单功能Demo

          本文链接:https://www.haomeiwen.com/subject/euwtiltx.html