Cesium 含水三维管道网络模拟

2 篇文章 0 订阅

Cesium 管道实体

  • 目的在于根据数据的点坐标生成三维管网
  • 需要体现出管道内水位情况

为了展示出管道内水位,需要将管道实体透明化,此时仅仅用点、线坐标数据直接生成管网会造成视觉混乱,因此需要将管道单体化并加以处理

  • 分为水平管道与垂直管道两类,在两类管道接洽时避免了重叠现象的出现
  • 使用es5对象思维进行封装

使用方法

垂直管道

VerticalPipe(pipeEntityCollection, id, positions, height, pipeRadius, pipeHeight, waterHeight)

参数解释

param {DataSource} pipeEntityCollection: 添加实体的数据源(viewer或自定义CustomDataSource)

param {String} id: 实体id

param {Array} positions: 实体经纬度 [经度, 纬度]

param {Number} height: 实体据地面高度,默认为0

param {Number} pipeRadius: 管道外径,默认外径0.8、内径为外径的2/3

param {Number} pipeHeight: 管道高度, 默认为1.5

param {Number} waterHeight: 管道内水高, 默认为0


水平管道

HorizontalPipe(pipeEntityCollection, id, positions, cut, status, pipeRadius)

参数解释

param {DataSource} pipeEntityCollection: 添加实体的数据源(viewer或自定义CustomDataSource)

param {String} id: 实体id

param {Array} positions: 组成管道的点集合 […, 经度, 纬度, 高度, …]

param {"single" | "both" | "null"} cut: 为避免水平管道与垂直管道重叠对水平管道进行裁切,默认不裁切

param {1 | 2 | 3 | 4} status: 管道内水位状态, 默认为1

param {Number} pipeRadius: 管道外径,默认为0.4、内径为外径的2/3

实际演示

生成两个垂直管道,中间由一段水平管道相连,代码与实际结果如下:

// 垂直管道
VerticalPipe(pipeEntityCollection, "node-A", [117.145668, 34.2198067], 0, null, null, 0.8);
VerticalPipe(pipeEntityCollection, "node-B", [117.1459503, 34.2195511], 0, null, null, 0.8);
// 水平管道
HorizontalPipe(
  pipeEntityCollection,
  "link",
  [
    117.1456680, 34.2198067, 0, 
    117.1457386, 34.2197631, 0, 
    117.1458055, 34.2197096, 0,
    117.1458516, 34.2196686, 0, 
    117.1459059, 34.2196069, 0, 
    117.1459503,34.21955110, 0
  ],
 	"both",
  2
);

可以看到,虽然垂直管道与水平管道有坐标点重叠,但是展示时两者并无叠置


源码解析

源码下载地址:Cesium垂直+水平含水管道实体-Javascript文档类资源-CSDN文库

大部分代码都是很简单的逻辑,大家自行查看源码即可,这里重点讲解两部分代码

  1. 根据中心点和半径生成圆形的函数
	/**
	 * 管道截面形状计算,根据中心点和半径生成圆形
	 * @param {Array} initialPosition [经度, 纬度]
	 * @param {Number} radius 截面半径
	 * @returns 组成截面的点集合
	 */
	function computeCircle(initialPosition, radius) {
		let Ea = 6378137; //   赤道半径
		let Eb = 6356725; // 极半径
		let positionArr = [];
		//cesium正东是0°
		for (let i = 0; i <= 360; i++) {
			let dx = radius * Math.sin((i * Math.PI) / 180.0);
			let dy = radius * Math.cos((i * Math.PI) / 180.0);

			let ec = Eb + ((Ea - Eb) * (90.0 - initialPosition[1])) / 90.0;
			let ed = ec * Math.cos((initialPosition[1] * Math.PI) / 180);

			let BJD = initialPosition[0] + ((dx / ed) * 180.0) / Math.PI; // 圆弧点经度
			let BWD = initialPosition[1] + ((dy / ec) * 180.0) / Math.PI; // 圆弧点纬度

			positionArr.push(BJD);
			positionArr.push(BWD);
		}
		return positionArr;
	}

关于这段代码可以参照此文,这位大佬写的很好通过经纬度坐标计算距离的方法(经纬度距离计算)ZZ_躬行之的技术博客_51CTO博客

其中dx、dy为根据半径计算出的x、y偏移量

ec为地球中心到当前纬度的半径

initialPosition[1] * Math.PI) / 180是将纬度转换为弧度

而ed则是当前纬度圈的半径

dx/ed、dy/ec为经度和纬度变化值对应的弧度

BJD、BWD则为初始经纬度+变化经纬度=变化后的经纬度

  1. 对水平管道进行裁切的函数
  /**
	 * 避免水平管道与垂直管道重叠,重新计算初末位置
	 * @param {"pipe" | "water"} type  管道或者水体
	 * @param {"single" | "both" | "null"} cut 单侧裁切、两侧裁切、不裁切
	 * @returns {Array} cartesianPositions
	 */
	function computePos(type, cut) {
		let waterPositions = positions.concat();
		let cartesianPositions;
		if (type == "water") {
			for (let i = 2; i < waterPositions.length; i += 3) {
				waterPositions[i] += 0.4 - 0.25;
			}
			cartesianPositions = new Cesium.Cartesian3.fromDegreesArrayHeights(waterPositions);
		} else if (type == "pipe") {
			cartesianPositions = new Cesium.Cartesian3.fromDegreesArrayHeights(positions);
		}
		const start = cartesianPositions[0];
		const second = cartesianPositions[1];
		const end = cartesianPositions[cartesianPositions.length - 1];
		const secondToLast = cartesianPositions[cartesianPositions.length - 2];
		const startLength = Math.sqrt(Math.pow(start.x - second.x, 2) + Math.pow(start.y - second.y, 2) + Math.pow(start.z - second.z, 2));
		const endLength = Math.sqrt(Math.pow(secondToLast.x - end.x, 2) + Math.pow(secondToLast.y - end.y, 2) + Math.pow(secondToLast.z - end.z, 2));
		const startOffsetX = (0.7 / startLength) * (second.x - start.x);
		const startOffsetY = (0.7 / startLength) * (second.y - start.y);
		const startOffsetZ = (0.7 / startLength) * (second.z - start.z);
		const endOffsetX = (0.7 / endLength) * (secondToLast.x - end.x);
		const endOffsetY = (0.7 / endLength) * (secondToLast.y - end.y);
		const endOffsetZ = (0.7 / endLength) * (secondToLast.z - end.z);

		if (cut == "single") {
			start.x += startOffsetX;
			start.y += startOffsetY;
			start.z += startOffsetZ;
		}
		if (cut == "both") {
			start.x += startOffsetX;
			start.y += startOffsetY;
			start.z += startOffsetZ;
			end.x += endOffsetX;
			end.y += endOffsetY;
			end.z += endOffsetZ;
		}

		return cartesianPositions;
	}

这段代码相较于上一部分则容易理解的多,核心思想为空间中的相似三角形

startLength、endLength分别为多段管道中的第一段和最后一段

start、end分别为初始点和终点

公式原理如图所示

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

APDCoder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值