cesium之淹没分析

发现最近成长很多,之前测距测面于我来说,做起来遥不可及,现在不到半天的工作时间便做出了淹没分析,当然也是参考的了

参考:mumu122 - 博客园

前提地图要设置了地形~

1、vue 前端代码

<div class="sub_ana_container" v-show="showFlowTool">
      <div class="saclose" @click="closeFlowTool">x</div>
      <label>最大高度:</label>
      <input type="number" v-model="height_max">
      <label>最小高度:</label>
      <input type="number" v-model="height_min">
      <label>淹没速度:</label>
      <input type="number" v-model="speed">
      <button class="btn" v-on:click="startFlowing()">开始</button>
      <button class="btn" v-on:click="clearFlowing()">清除</button>
      <button class="btn" v-on:click="draw_ymfw()">绘制淹没范围</button>
    </div>

2、css样式

.sub_ana_container {
  position: absolute;
  right: 20%;
  top: 1%;
  width: auto;
  height: auto;
  border: solid 1px;
  padding: 12px;
  color: rgb(221, 218, 218);
  background-color: rgba(6, 89, 161, 0.8);
}
.saclose{
  position: absolute;
  right: 5px;
  top: 3px;
  cursor: pointer;
}
.btn {
  color: white;
  background-color: #555758;
  border: #555758;
  margin: 6px;
}
input {
  width: 100px;
}
label {
  margin: 8px;
  color: rgb(221, 218, 218);
}
input[type="radio"] {
  width: 30px;
}

3、methods方法使用

// 淹没分析------------------------
    closeFlowTool() {
      this.showFlowTool = false
    },
    drawFlowingPolygon() {
      const flowing_draw = new SubmergenceAnalysis()
    },
    draw_polygon() {
      const _this = this
      var handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
      // 取消双击事件-追踪该位置
      viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
        Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
      )
      // 鼠标事件
      handler = new Cesium.ScreenSpaceEventHandler(
        viewer.scene._imageryLayerCollection
      )
      var positions = []
      var tempPoints = []
      var polygon = null
      var cartesian = null
      var floatingPoint

      handler.setInputAction(function (movement) {
        const ray = viewer.camera.getPickRay(movement.endPosition)
        cartesian = viewer.scene.globe.pick(ray, viewer.scene)
        // cartesian = viewer.scene.camera.pickEllipsoid(movement.endPosition, viewer.scene.globe.ellipsoid);
        if (positions.length >= 2) {
          if (!Cesium.defined(polygon)) {
            polygon = new PolygonPrimitive(positions)
          } else {
            positions.pop()
            positions.push(cartesian)
          }
        }
      }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

      handler.setInputAction(function (movement) {
        const ray = viewer.camera.getPickRay(movement.position)
        cartesian = viewer.scene.globe.pick(ray, viewer.scene)
        // cartesian = viewer.scene.camera.pickEllipsoid(movement.position, viewer.scene.globe.ellipsoid);
        if (positions.length == 0) {
          positions.push(cartesian.clone())
        }
        // positions.pop();
        positions.push(cartesian)
        // 在三维场景中添加点
        var cartographic = Cesium.Cartographic.fromCartesian(
          positions[positions.length - 1]
        )
        var longitudeString = Cesium.Math.toDegrees(cartographic.longitude)
        var latitudeString = Cesium.Math.toDegrees(cartographic.latitude)
        var heightString = cartographic.height
        tempPoints.push({
          lon: longitudeString,
          lat: latitudeString,
          hei: heightString
        })
        _this.tempPoints.push(longitudeString)
        _this.tempPoints.push(latitudeString)
        // floatingPoint = viewer.entities.add({
        //   name: '多边形面积',
        //   position: positions[positions.length - 1],
        //   point: {
        //     pixelSize: 5,
        //     color: Cesium.Color.WHITE,
        //     outlineColor: Cesium.Color.DEEPSKYBLUE,
        //     outlineWidth: 3
        //   }
        // })
        // measureFeature_polygon.push(floatingPoint)
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK)

      handler.setInputAction(function (movement) {
        handler.destroy()
        positions.pop()
        // var polyCenter = Cesium.BoundingSphere.fromPoints(positions).center
        // var textArea = getArea(tempPoints) + 'km²'
        // const _label = viewer.entities.add({
        //   name: '多边形面积',
        //   position: polyCenter,
        //   // position: positions[positions.length - 1],
        //   label: {
        //     text: textArea,
        //     font: '16px sans-serif',
        //     fillColor: Cesium.Color.DARKORANGE,
        //     heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
        //   }
        // })
        // measureFeature_polygon.push(_label)
      }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)

      var radiansPerDegree = Math.PI / 180.0 // 角度转化为弧度(rad)
      var degreesPerRadian = 180.0 / Math.PI // 弧度转化为角度

      // 计算多边形面积
      function getArea(points) {
        var res = 0
        // 拆分三角曲面
        for (var i = 0; i < points.length - 2; i++) {
          var j = (i + 1) % points.length
          var k = (i + 2) % points.length
          var totalAngle = Angle(points[i], points[j], points[k])

          var dis_temp1 = distance(positions[i], positions[j])
          var dis_temp2 = distance(positions[j], positions[k])
          res += dis_temp1 * dis_temp2 * Math.abs(Math.sin(totalAngle))
          console.log(res)
        }

        return (res / 1000000.0).toFixed(4)
      }
      function logPosition (position) {
        var ray = viewer.camera.getPickRay(position)
        var cartesian = viewer.scene.globe.pick(ray, viewer.scene)
        var cartographic = Cesium.Ellipsoid.WGS84.cartesianToCartographic(cartesian)
        var long2 = Cesium.Math.toDegrees(cartographic.longitude)
        var lat2 = Cesium.Math.toDegrees(cartographic.latitude)
        console.log(long2 + ',' + lat2)
      }

      /* 角度 */
      function Angle(p1, p2, p3) {
        var bearing21 = Bearing(p2, p1)
        var bearing23 = Bearing(p2, p3)
        var angle = bearing21 - bearing23
        if (angle < 0) {
          angle += 360
        }
        return angle
      }
      /* 方向 */
      function Bearing(from, to) {
        var lat1 = from.lat * radiansPerDegree
        var lon1 = from.lon * radiansPerDegree
        var lat2 = to.lat * radiansPerDegree
        var lon2 = to.lon * radiansPerDegree
        var angle = -Math.atan2(
          Math.sin(lon1 - lon2) * Math.cos(lat2),
          Math.cos(lat1) * Math.sin(lat2) -
            Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2)
        )
        if (angle < 0) {
          angle += Math.PI * 2.0
        }
        angle = angle * degreesPerRadian // 角度
        return angle
      }

      var PolygonPrimitive = (function () {
        function _(positions) {
          this.options = {
            name: '多边形',
            polygon: {
              hierarchy: [],
              // perPositionHeight : true,
              material: new Cesium.Color(0, 124 / 255, 247 / 255, 0.2)
              // material: Cesium.Color.GREEN.withAlpha(0.5)
              // heightReference:20000
            }
          }

          this.hierarchy = { positions }
          this._init()
        }

        _.prototype._init = function () {
          var _self = this
          var _update = function () {
            return _self.hierarchy
          }
          // 实时更新polygon.hierarchy
          this.options.polygon.hierarchy = new Cesium.CallbackProperty(
            _update,
            false
          )
          const _polygon = viewer.entities.add(this.options)
          measureFeature_polygon.push(_polygon)
        }

        return _
      })()

      function distance(point1, point2) {
        var point1cartographic = Cesium.Cartographic.fromCartesian(point1)
        var point2cartographic = Cesium.Cartographic.fromCartesian(point2)
        /** 根据经纬度计算出距离**/
        var geodesic = new Cesium.EllipsoidGeodesic()
        geodesic.setEndPoints(point1cartographic, point2cartographic)
        var s = geodesic.surfaceDistance
        // console.log(Math.sqrt(Math.pow(distance, 2) + Math.pow(endheight, 2)));
        // 返回两点之间的距离
        s = Math.sqrt(
          Math.pow(s, 2) +
            Math.pow(point2cartographic.height - point1cartographic.height, 2)
        )
        return s
      }
    },
    draw_ymfw() {
      this.draw_polygon()
    },
    startFlowing() {
      if (!this.obj) {
        this.obj = new SubmergenceAnalysis(
          viewer,
          true,
          this.height_max,
          this.height_min,
          this.speed,
          true,
          this.tempPoints
        )
      }
      this.obj.tempPoints = this.tempPoints
      this.obj.start()
    },
    clearFlowing() {
      this.obj && this.obj.clear()
      this.clearMeasureFeature()
      this.tempPoints = []
      this.obj = null
    }

4、淹没分析的js文件   submerg-analysis.js

引入js

import SubmergenceAnalysis from '../../public/static/cesium/submerg-analysis'

submerg-analysis.js

// 淹没分析
export default class SubmergenceAnalysis {
    constructor(viewer, isTerrain = true, height_max, height_min, speed, map_type, tempPoints) {
        debugger
        this.viewer = viewer
        this.isTerrain = isTerrain
        this.handler = null
        this.tempEntities = []
        this.polygonEntities = []
        this.linePositionList = []

        this.pointArr = []
        this.lineEntities = []

        this.tempPoints = []
        this.extrudedHeight = height_min
        this.height_max = height_max
        this.height_min = height_min
        this.speed = speed
        // 默认是范围图/深度图
        this.map_type = map_type
        this.polygon_degrees = tempPoints
        // this.polygon_degrees = [
        //     117.8396128404095, 24.758688509239484,
        //     118.0106511230382, 24.797143096351466,
        //     117.8429783463796, 24.651905270780798,
        //     118.03641195329993, 24.68701116597361
        //     // 115.8784, 40.0198,
        //     // 115.9473, 40.0381,
        //     // 115.9614, 40.0073,
        //     // 115.9042, 39.9912
        // ]
        this._initViewStatus(this.viewer)
        this._addDisListener()
    }

    _initViewStatus(viewer) {
        var scene = viewer.scene
        scene.globe.depthTestAgainstTerrain = true
        // scene.camera.setView({
        //     // 摄像头的位置
        //     destination: Cesium.Cartesian3.fromDegrees(117.80189427650359, 24.700148777225785, 1500.0),
        //     // destination: Cesium.Cartesian3.fromDegrees(115.9216, 39.9870, 1500.0),
        //     orientation: {
        //         heading: Cesium.Math.toRadians(0.0), // 默认朝北0度,顺时针方向,东是90度
        //         pitch: Cesium.Math.toRadians(-20), // 默认朝下看-90,0为水平看,
        //         roll: Cesium.Math.toRadians(0)// 默认0
        //     }
        // })
        viewer.skyAtmosphere = false
    }

    // 根据矩形范围得到行列数点坐标和高程信息
    _getPoints(xmin, xmax, ymin, ymax) {
        const x_count = 10
        const y_count = 10
        const cartesians = new Array(x_count * y_count)
        const x_d = (xmax - xmin) / x_count
        for (var i = 0; i < x_count; ++i) {
            const start_pt = { x: xmin + i * x_d, y: ymax }
            const end_pt = { x: xmin + i * x_d, y: ymin }
            for (let j = 0; j < y_count; j++) {
                const offset = j / (y_count - 1)
                const x = Cesium.Math.lerp(start_pt.x, end_pt.x, offset)
                const y = Cesium.Math.lerp(start_pt.y, end_pt.y, offset)
                cartesians[j + i * y_count] = Cesium.Cartographic.fromDegrees(x, y)
            }
        }
        return cartesians

    }

    _getHeights(cartesians, extrudedHeight, callback) {
        debugger
        var terrainProvider = new Cesium.CesiumTerrainProvider({
            url: 'http://localhost:8080/o_lab'
        })
        // 根据地形计算某经纬度点的高度
        var promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartesians)
        Cesium.when(promise, function (updatedPositions) {

            let positions = updatedPositions.filter(d => {
                const cartographic = d
                if (cartographic) {
                    const h_d = extrudedHeight - cartographic.height
                    return h_d > 0
                }
            })
            positions = positions.map(d => {
                const cartographic = d
                const h = extrudedHeight - cartographic.height
                return {
                    x: Cesium.Math.toDegrees(cartographic.longitude),
                    y: Cesium.Math.toDegrees(cartographic.latitude),
                    value: h
                }

            })

            if (callback) {

                callback(positions)
            }
        })
    }

    _addDisListener() {
        const viewer = this.viewer
        const scene = viewer.scene
        const linePositionList = this.linePositionList
        viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
        this.handler = new Cesium.ScreenSpaceEventHandler(scene.canvas)
        // 绘制线
        this._drawLine(linePositionList)
        // this.loadGrandCanyon()
        // 绘制面
        if (this.map_type) {
            this._drawPoly(this.polygon_degrees)
        } else {
            // 得到插值网格
            const bounds = {
                west: 115.8784,
                east: 115.9614,
                south: 39.9912,
                north: 40.0381
            }
            
            const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north)
            this._getHeights(positions_cartesian, this.extrudedHeight, (d) => {
                // this.heatMapObj = new HeatMap(this.viewer, d, bounds)
            })
        }

    }

    _reDraw() {
        this.tempPoints = []
        this.linePositionList.length = 0
        this.areaPositionList.length = 0
        for (const entity of this.tempEntities) {
            this.viewer.entities.remove(entity)
        }
        this.tempEntities = []
    }

    _drawLine(linePositionList) {
        const lineStyle = {
            width: 2,
            material: Cesium.Color.CHARTREUSE
        }

        const entity = this.viewer.entities.add({
            polyline: lineStyle
        })

        entity.polyline.positions = new Cesium.CallbackProperty(function () {
            return linePositionList
        }, false)

        this.polygonEntities.push(entity)
    }

    _drawPoint(point_Cartesian3) {
        const entity =
            this.viewer.entities.add({
                position: point_Cartesian3,
                point: {
                    pixelSize: 10,
                    color: Cesium.Color.GOLD
                    // disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    // heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
                }
            })
        this.tempEntities.push(entity)
    }


    _drawPoly(degrees) {
        const that = this
        const entity =
            this.viewer.entities.add({
                polygon: {
                    hierarchy: {},
                    material: new Cesium.Color.fromBytes(64, 157, 253, 100),
                    perPositionHeight: true

                }
            })
        entity.polygon.hierarchy = new Cesium.PolygonHierarchy(Cesium.Cartesian3.fromDegreesArray(degrees))
        entity.polygon.extrudedHeight = new Cesium.CallbackProperty(() => that.extrudedHeight, false)
        this.polygonEntities.push(entity)
    }

    // 世界坐标转经纬坐标
    _car3ToLatLon(cartesian) {
        const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
        const longitudeString = Cesium.Math.toDegrees(cartographic.longitude)
        const latitudeString = Cesium.Math.toDegrees(cartographic.latitude)
        return {
            lon: longitudeString,
            lat: latitudeString,
            height: cartographic.height
        }
    }


    // 移除整个资源
    remove() {
        const viewer = this.viewer
        for (const tempEntity of this.tempEntities) {
            viewer.entities.remove(tempEntity)
        }
        for (const lineEntity of this.polygonEntities) {
            viewer.entities.remove(lineEntity)
        }
        this.handler.destroy()
    }

    start() {
        const that = this
        this.timer = window.setInterval(() => {
            if ((that.height_max > that.extrudedHeight) && (that.extrudedHeight >= that.height_min)) {
                that.extrudedHeight = that.extrudedHeight + that.speed
            } else {
                that.extrudedHeight = that.height_min
            }
            if (!that.map_type) {
                if (this.heatMapObj) {
                    const bounds = {
                        west: 115.8784,
                        east: 115.9614,
                        south: 39.9912,
                        north: 40.0381
                    }
                    const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north)
                    this._getHeights(positions_cartesian, this.extrudedHeight, (d) => {
                        this.heatMapObj.update(d)
                    })
                }
            }

        }, 500)
        if (that.map_type) {
            that._drawPoly(that.polygon_degrees)
        } else {
            if (this.heatMapObj) { }

        }

    }

    clear() {
        const viewer = this.viewer
        if (this.timer) {
            window.clearInterval(this.timer)
            this.timer = null
        }
        this.extrudedHeight = this.height_min
        if (this.heatMapObj) { this.heatMapObj.show(false) }
        for (const entity of this.polygonEntities) {
            viewer.entities.remove(entity)
        }
        viewer.skyAtmosphere = true
    }

    changeMapType(type) {
        if (!type) {
            if (!this.heatMapObj) {
                // 得到插值网格
                const bounds = {
                    west: 115.8784,
                    east: 115.9614,
                    south: 39.9912,
                    north: 40.0381
                }
                const positions_cartesian = this._getPoints(bounds.east, bounds.west, bounds.south, bounds.north)
                this._getHeights(positions_cartesian, this.extrudedHeight, (d) => {
                    // this.heatMapObj = new HeatMap(this.viewer, d, bounds)
                })
            }

            this.heatMapObj && this.heatMapObj.show(true)
            for (const entity of this.polygonEntities) {
                entity.show = false
            }
        } else {
            this.heatMapObj.show(false)
            for (const entity of this.polygonEntities) {
                entity.show = true
            }
        }
    }

    // 切割一部分地形
    loadGrandCanyon() {
        var globe = this.viewer.scene.globe
        const viewer = this.viewer
        // viewer.skyAtmosphere = false,
        // Pick a position at the Grand Canyon
        var position = Cesium.Cartographic.toCartesian(new Cesium.Cartographic.fromDegrees(115.9165534, 40.0139345, 100))
        var distance = 3000.0
        var boundingSphere = new Cesium.BoundingSphere(position, distance)

        globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
            modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(position),
            planes: [
                new Cesium.ClippingPlane(new Cesium.Cartesian3(1.0, 0.0, 0.0), distance),
                new Cesium.ClippingPlane(new Cesium.Cartesian3(-1.0, 0.0, 0.0), distance),
                new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, 1.0, 0.0), distance),
                new Cesium.ClippingPlane(new Cesium.Cartesian3(0.0, -1.0, 0.0), distance)
            ],

            unionClippingRegions: true
        })
        globe.clippingPlanes.enabled = true
        viewer.camera.viewBoundingSphere(boundingSphere, new Cesium.HeadingPitchRange(0.5, -0.5, boundingSphere.radius * 5.0))
        viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
    }

}

5、结果

点击绘制淹没范围,在地图上绘制需要进行分析的范围

点击开始,即进行淹没分析

骋天淹没分析系统 骋天淹没分析系统是以三维地理信息系统为基础平台,基于数字高程模型(DEM)格网模型,通过改进迭代种子蔓延算法将淹没分析结果直观在三维地理系统系统上呈现出来。 骋天淹没分析系统应用于水库的库区淹没分析时,设置好起止水位和终止水位,以三维的形式呈现库区淹没区域,根据不同是水深来计算库容量,移民数量、直接经济影响和间接经济影响。可将数据制作成柱状图、饼状图、曲线图等多种多样的统计图;能够根据业务流程和用户要求定制各类表格,进行业务报表输出;还能按某一要素生成范围图、点密度图、分级符号图等,进行专题图分析,形象直观地反映防洪要素的时空变化规律。 骋天淹没分析系统亦可应用于洪水淹没分析时,根据洪水演进过程,配合数字化地图,利用三维模型,计算洪水淹没范围和淹没水深,并动态显示淹没区域并动态显示淹没区域,计算人口、家庭财产、工商、企业、农业、林业、渔业和畜牧业等淹没信息。并可根据预报调度、实时调度和历史调度等不同洪水下泻过程,计算分析洪水淹没损失,显示淹没分布状况,从而得出最佳洪水调度预案,提供泄洪区域内人员撤退、避灾转移和救灾物资供应等最佳行动路线。 广西骋天信息科技有限公司 网站 www.gxchengtian.com
### Cesium 淹没分析教程 Cesium 是一种功能强大的开源地理信息系统 (GIS) 可视化工具,能够用于创建高精度的三维地球模型[^1]。为了实现水位变化的淹没分析,可以利用 Cesium 的 JavaScript API 来动态调整水面高度并实时渲染效果。 以下是基于 Cesium 实现水位淹没分析的一个简单示例: #### 初始化 Cesium 场景 首先需要加载 Cesium 并初始化一个 Viewer 对象作为基础场景框架。 ```javascript // 加载 Cesium 库 var viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain() }); ``` 这段代码设置了地形提供者 `createWorldTerrain`,它允许我们使用全球范围内的高分辨率地形数据来增强真实感。 #### 创建水面几何体 通过定义矩形区域或者椭圆形状覆盖特定地理位置上的水域边界,并为其指定透明度和颜色属性以表现不同深度下的视觉差异。 ```javascript function addWaterLayer(height) { var waterEntity = viewer.entities.add({ name : 'water', rectangle : { coordinates : Cesium.Rectangle.fromDegrees(-75.0, 40.0, -70.0, 45.0), // 定义经纬度坐标框 material : new Cesium.ColorMaterialProperty(Cesium.Color.BLUE.withAlpha(0.6)), height : height, outline : true, outlineColor : Cesium.Color.BLACK } }); } addWaterLayer(0); // 初始设置海平面高度为零米 ``` 此函数接受参数 `height` 表示当前设定的海拔高度值,在实际应用过程中可以根据需求改变该变量从而达到模拟洪水上涨的效果。 #### 动态更新水位 为了让用户交互式体验到不同的淹沒程度,可以通过监听事件或定时器不断修改上述实体的高度属性。 ```javascript viewer.clock.onTick.addEventListener(function(clock){ let timeJulian = clock.currentTime; let secondsSinceEpoch = Cesium.JulianDate.toSecondsSinceEpoch(timeJulian); // 假设每秒增加一厘米水量 var currentHeight = Math.floor((secondsSinceEpoch % 3600)/100)*0.01; if(viewer.entities.getById('water')){ viewer.entities.removeById('water'); } addWaterLayer(currentHeight); }); ``` 以上脚本片段展示了如何随着时间推移逐步升高虚拟海洋表面的位置,形成连续上升的趋势。 ### 注意事项 - 需要确保网络连接正常以便下载必要的资源文件如影像图层、DEM 数据集等。 - 如果项目规模较大,则考虑优化性能问题比如减少不必要的重绘操作次数以及合理分配内存空间给各个组件对象。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值