(十)Cesium通过Turf.js点插值获取范围的高程值,进行无源淹没分析

无源淹没分析比较简单,核心就是抬升面的高度。

这里将面绘制完后,获取最高和最低的高程值,传入表单框中。

点击分析,开始抬升

高程获取

灵感来自:Cesium选定范围取最高、最低高程及填挖方分析 | 开飞机的老张 (kaifeiji.cc)

Turf中文网:Turf.js中文网 (fenxianglu.cn)

过程:从Cesium中获取坐标后传入Turf中进行插值,然后返回插值点坐标,Cesium中通过Cesium.sampleTerrain获取高程值,详细见官网API:Global - Cesium Documentation

高程获取函数:

const minAndMaxHeight =async (positions) => {
    //将笛卡尔坐标转为经纬度坐标,并转为turf.js的Polygon类型
    const lnglats = positions.map(position => [Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).longitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).latitude)])
    console.log(lnglats)
    const turfPolygon = turf.lineString(lnglats) // 转为turf.js的Polygon类型
    const turfExtent = turf.bbox(turfPolygon)  // 获取turfPolygon的边界框
    // 在turfPolygon中按网格取样点,网格间距8米
    const turfSamplePoints = turf.pointGrid(turfExtent, 0.08, {
        units: 'kilometers',
        // mask: turfPolygon,
    });
    // 将turf取样点转为Cesium的取样点
    const cesiumSamplePoints = []
    for (let i = 0; i < turfSamplePoints.features.length; i++) {
        const coord = turfSamplePoints.features[i].geometry.coordinates;
        cesiumSamplePoints.push(Cesium.Cartographic.fromDegrees(coord[0], coord[1]));
    }
    const terrainProvider = await Cesium.createWorldTerrainAsync(); // 获取地形服务
    const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 11, cesiumSamplePoints) // 计算取样点的高度
    let minHeight = 8888
    let maxHeight = 0
    // 计算取样点的最小高度和最大高度
    for (let i = 0; i < updatedPositions.length; i++) {
        const height = updatedPositions[i].height
        if (height < minHeight) {
            minHeight = height
        }
        if (height > maxHeight) {
            maxHeight = height
        }
    }
    console.log(minHeight, maxHeight);
    return { minHeight, maxHeight }
}

提醒:

这里面的数字:0.08是插值间距,越小,插值越密

11是地形缩放等级

这两个数值极大影响计算速度,设置不当,当绘制范围过大,计算量将十分大,会直接崩溃。

下面是该功能所有代码:

<script setup>
import * as turf from '@turf/turf'
import { nextTick, reactive } from 'vue'
import * as Cesium from 'cesium'// Import Cesium
import { useCesiumStore } from '@/stores/useCesiumStore.js'
import { toRaw } from 'vue'
import { ElLoading } from 'element-plus'
import 'element-plus/theme-chalk/el-loading.css'
let viewer  = null
 nextTick(()=>{
     viewer = toRaw(useCesiumStore().viewer)
 })

const formLabelAlign = reactive({ minHeight: 0, maxHeight: 1000, speed: 1 })
let height = 0
let handler = null
let positions = []
let region = null
let activeShapePoints = []  // 存放当前绘制的多边形的点坐标
let activeShape = null  // 存放当前绘制的多边形对象
//获取范围内最小和最大的高程值
const minAndMaxHeight =async (positions) => {
    //将笛卡尔坐标转为经纬度坐标,并转为turf.js的Polygon类型
    const lnglats = positions.map(position => [Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).longitude),
        Cesium.Math.toDegrees(Cesium.Cartographic.fromCartesian(position).latitude)])
    console.log(lnglats)
    const turfPolygon = turf.lineString(lnglats) // 转为turf.js的Polygon类型
    const turfExtent = turf.bbox(turfPolygon)  // 获取turfPolygon的边界框
    // 在turfPolygon中按网格取样点,网格间距8米
    const turfSamplePoints = turf.pointGrid(turfExtent, 0.08, {
        units: 'kilometers',
        // mask: turfPolygon,
    });
    // 将turf取样点转为Cesium的取样点
    const cesiumSamplePoints = []
    for (let i = 0; i < turfSamplePoints.features.length; i++) {
        const coord = turfSamplePoints.features[i].geometry.coordinates;
        cesiumSamplePoints.push(Cesium.Cartographic.fromDegrees(coord[0], coord[1]));
    }
    const terrainProvider = await Cesium.createWorldTerrainAsync(); // 获取地形服务
    const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 11, cesiumSamplePoints) // 计算取样点的高度
    let minHeight = 8888
    let maxHeight = 0
    // 计算取样点的最小高度和最大高度
    for (let i = 0; i < updatedPositions.length; i++) {
        const height = updatedPositions[i].height
        if (height < minHeight) {
            minHeight = height
        }
        if (height > maxHeight) {
            maxHeight = height
        }
    }
    console.log(minHeight, maxHeight);
    return { minHeight, maxHeight }
}

//水位高度更新函数
const updataHeight = () => {
    if (height < formLabelAlign.maxHeight ) {
        height += formLabelAlign.speed
        return height
    }
    else{
        return height
    }
}

//绘制淹没区
const addPolygon = ( hierarchy ) => {
    const addRegion = viewer.entities.add({
            id: 'addRegion',
            name: 'addRegion',
            show: true,
            polygon: {
                hierarchy: hierarchy,
                material: new Cesium.ColorMaterialProperty(Cesium.Color.fromBytes(64, 157, 253, 50))
        }
    })
    return addRegion
}

//绘制区域
const draw = async () => {
    console.log(1);
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
    viewer.entities.remove(region)
    // 监听鼠标左键点击事件
    handler.setInputAction( function (event) {
        const earthPosition = viewer.scene.pickPosition(event.position)
        if (Cesium.defined(earthPosition)){
            positions.push(earthPosition)
            if(activeShapePoints.length === 0){
                activeShapePoints.push(earthPosition)
                const dynamicPositions = new Cesium.CallbackProperty(() => {
                    return new Cesium.PolygonHierarchy(activeShapePoints)
                }, false)
                activeShape = addPolygon(dynamicPositions) //绘制第一个多边形
            }
            //添加当前点到activeShapePoints数组,实时渲染动态图
            activeShapePoints.push(earthPosition)
        }
        else {
            alert('请点击地球上的位置')
            return
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)

    //鼠标移动事件
    handler.setInputAction((event) => {
        if ( activeShapePoints.length > 0 ) {
            const newPosition = viewer.scene.pickPosition(event.endPosition) //获取鼠标移动结束位置的地理坐标
            if (Cesium.defined(newPosition)) {
                activeShapePoints.pop() //删除最后一个点
                activeShapePoints.push(newPosition) //添加新的点
            }
        }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)

    // 监听鼠标右键点击事件
    handler.setInputAction(async function () {
        if (positions.length < 3) {
            return alert('至少需要绘制三个点才能进行分析')
        } 
        viewer.entities.remove(activeShape) //删除多边形
        addPolygon(positions) //绘制淹没区
        const loadingInstance = ElLoading.service({
            lock: true,
            fullscreen: true,
            text: '正在计算地形高度...',
            background: 'rgba(0, 0, 0, 0.7)'
        })
        region = viewer.entities.getById('addRegion')
        const result = await minAndMaxHeight(positions)
        formLabelAlign.minHeight = result.minHeight
        formLabelAlign.maxHeight = result.maxHeight
        nextTick(() => {
            loadingInstance.close()
        })
        activeShape = undefined //清空变量
        activeShapePoints = [] //清空数组
        positions = [] //清空数组
        handler.destroy() //销毁事件处理器
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}

//开始淹没分析
const analysis = () => {
    if (!handler.isDestroyed){
        return alert('请先结束绘制')
    }
    if(formLabelAlign.minHeight === 0 || formLabelAlign.maxHeight === 0 || formLabelAlign.speed === 0 || formLabelAlign.minHeight >= formLabelAlign.maxHeight){
        return alert('请输入正确的水位高度')
    }
    region.polygon.material = new Cesium.ImageMaterialProperty({
        image: 'src/assets/water/river.png',
        repeat: new Cesium.Cartesian2(1, 1),
        transparent: true,
        color: Cesium.Color.WHITE.withAlpha(0.7)
    })
    height = formLabelAlign.minHeight
    region.polygon.extrudedHeight = new Cesium.CallbackProperty(updataHeight, false)
}

//重置区域
const reset = () => {
    viewer.entities.remove(region)
    region = undefined
    positions = []
    activeShapePoints = [] 
    // if(handler && !handler.isDestroyed) handler.destroy()
    if(activeShape) viewer.entities.remove(activeShape)
}

//关闭分析面板
const emits = defineEmits(['isShowInundationAnalysis']) // 定义事件
const closeInundation = () => {
    reset()
    // if (handler.isDestroyed) handler.destroy()
    positions = []
    activeShapePoints = []
    activeShape = undefined
    region = undefined
    formLabelAlign.minHeight = 0
    formLabelAlign.maxHeight = 0
    formLabelAlign.speed = 0
    viewer.entities.remove(viewer.entities.getById('addRegion'))
    emits('isShowInundationAnalysis', false)
}
</script>

<template>
    <div color="#a0cfff" class="inundation-analysis">
        <el-form label-position=right label-width="auto" :model="formLabelAlign">
            <el-form-item label="请输入起始水位高度:">
                <el-input v-model.lazy.number="formLabelAlign.minHeight" type="number" />
            </el-form-item>
            <el-form-item label="请输入终止水位高度:">
                <el-input v-model.lazy.number="formLabelAlign.maxHeight" type="number" />
            </el-form-item>
            <el-form-item label="水位增长速度:">
                <el-input v-model.lazy.number="formLabelAlign.speed" type="number" />
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="draw">
                    绘制区域
                </el-button>
                <el-button @click="analysis" type="primary">分析</el-button>
            </el-form-item>
            <el-form-item>
                <el-button @click="reset">
                    重置
                </el-button>
                <el-button @click="closeInundation">关闭</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>

<style scoped>
.inundation-analysis {
    position: absolute;
    top: 100px;
    left: 20px;
    z-index: 9999;
    /* 背景色为浅蓝,透明度为0.5 */
    background-color: rgba(160, 207, 255, 0.8);
    /* 圆角 */
    border-radius: 10px;
    padding: 10px;
}
.inundation-analysis .el-input{
    width: 100px;
    height: 23px;
}
.inundation-analysis .el-button{
    width: 100px;
    /* 居中 */
    margin-left: 20px;
}
</style>

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值