阅读 147

智慧园区简单功能Demo

一、前言

智慧园区业内有较多成熟解决方案,这里仅个人纯原生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
本例使用数据均开源合法,如果这篇文章对你有一些帮助,那我会很开心?

作者:韩无仙

原文链接:https://www.jianshu.com/p/e791c58f3d73

文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐