Cesium提供的接口很基础,很专业。因此扩展起来非常灵活,但同时也有一定的门槛。而且目前关于Cesium相关的参考资料很少,因此实现功能后准备自己记录一下过程。也希望为后续的开发者们提供一些参考资料。
填挖方计算

填挖方:填方+挖方
过程:绘制一个多边形区域,设置一个填挖基准面,基于这个区域的地形及基准面高度,计算需要填多少立方的土,挖掉多少立方的土。
原理:将多边形划分为无穷多个很小很小的三角体,这一特性可利用Cesium.PolygonGeometry.granularity特性来实现。然后遍历每一个小三角体,判断每一个三角体是挖掉,还是填平。(注意:因为地形表面起伏的原因,体积计算会有误差。所以整体计算结果仅供参考)
效果图中,绿色的是Cesium.Entity中的围墙-wall,实现时参考Cesium沙盒示例。蓝色的是带有extrudedHeight属性的多边形,高度为基准面高度。
注:整体思路来源于另一篇文章Cesium-地形挖方分析,感谢大神提供思路。
关键代码(typeScript):(该方法不适用与模型上的分析,后续我会介绍模型上填挖方的实现)
private computeCutAndFillVolume1(positions:Cesium.Cartesian3[]):CutAndFillResult{
//result是一个结果类,用于存储填挖方结果
const result = new CutAndFillResult();
if(!this.viewer.terrainProvider.availability){
return result ;
}
//用来记录整块区域的最小高程
let minHeight = 15000;
for(let i = 0; i< positions.length;i ++){
const cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
const height = this.viewer.scene.globe.getHeight(cartographic);
minHeight = Math.min(minHeight,height as number);
}
//由于不好给出基准面高度,所以默认取绘制节点的最低点作为基准。
this.baseHeight = this.baseHeight ==0 ? minHeight:this.baseHeight;
//设置颗粒度(来源于大神的思路)
let granularity = Math.PI/Math.pow(2,11);
granularity = granularity/64;
const polygonGeometry = Cesium.PolygonGeometry.fromPositions({
positions:positions,
vertexFormat:Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
granularity:granularity,
});
const geom = Cesium.PolygonGeometry.createGeometry(polygonGeometry);
let totalCutVolume = 0;
let totalFillVolume = 0;
let maxHeight = 0;
let i0,i1,i2;
let height1,height2,height3;
let bottomP1,bottomP2,bottomP3;
const scratchCartesian = new Cesium.Cartesian3();
let cartographic;
let bottomArea = 0.0;
let totalBottomArea = 0.0;
let subTrianglePositions;
for(let i = 0; i< (geom as Cesium.Geometry).indices.length;i +=3){
i0 = geom?.indices[i];
i1 = geom?.indices[i + 1];
i2 = geom?.indices[i + 2];
subTrianglePositions = geom?.attributes.position.values;
if(subTrianglePositions){
scratchCartesian.x = subTrianglePositions[i0 * 3];
scratchCartesian.y = subTrianglePositions[i0 * 3 + 1];
scratchCartesian.z = subTrianglePositions[i0 * 3 + 2];
}
cartographic = Cesium.Cartographic.fromCartesian(scratchCartesian);
height1 = this.viewer.scene.globe.getHeight(cartographic);
bottomP1 = Cesium.Cartesian3.fromRadians(cartographic.longitude,cartographic.latitude,0);
maxHeight = Math.max(maxHeight,height1 as number);
minHeight = Math.min(minHeight,height1 as number);
if(subTrianglePositions){
scratchCartesian.x = subTrianglePositions[i1 * 3];
scratchCartesian.y = subTrianglePositions[i1 * 3 + 1];
scratchCartesian.z = subTrianglePositions[i1 * 3 + 2];
}
cartographic = Cesium.Cartographic.fromCartesian(scratchCartesian);
height2 = this.viewer.scene.globe.getHeight(cartographic);
bottomP2 = Cesium.Cartesian3.fromRadians(cartographic.longitude,cartographic.latitude,0);
maxHeight = Math.max(maxHeight,height2 as number);
minHeight = Math.min(minHeight,height2 as number);
if(subTrianglePositions){
scratchCartesian.x = subTrianglePositions[i2 * 3];
scratchCartesian.y = subTrianglePositions[i2 * 3 + 1];
scratchCartesian.z = subTrianglePositions[i2 * 3 + 2];
}
cartographic = Cesium.Cartographic.fromCartesian(scratchCartesian);
height3 = this.viewer.scene.globe.getHeight(cartographic);
bottomP3 = Cesium.Cartesian3.fromRadians(cartographic.longitude,cartographic.latitude,0);
maxHeight = Math.max(maxHeight,height3 as number);
minHeight = Math.min(minHeight,height3 as number);
bottomArea = this.computeAreaOfTriangle(bottomP1,bottomP2,bottomP3);
totalBottomArea += bottomArea;
//计算三角体的平均高度
const avgCubeHeight = ((height1 as number) + (height2 as number) + (height3 as number))/3;
//判断是 填方还是挖方
//如果三角体低于基准面,则需要填方
if(avgCubeHeight <= this.baseHeight){
totalFillVolume += bottomArea * (this.baseHeight - avgCubeHeight);
}else { //否则需要挖方
totalCutVolume += bottomArea * (avgCubeHeight - this.baseHeight);
}
//totalCutVolume += bottomArea * ((height1 as number) - minHeight + (height2 as number) - minHeight + (height3 as number) - minHeight)/3;
}
result.minHeight = minHeight;
result.maxHeight = maxHeight;
result.cutVolume = totalCutVolume;
result.fillVolume = totalFillVolume;
result.baseArea = totalBottomArea;
return result;
}
export class CutAndFillResult {
minHeight:number = 0.0;
maxHeight:number = 0.0;
cutVolume:number = 0.0;
fillVolume:number = 0.0;
baseArea:number = 0.0;
}版权声明:本文为lying_19原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。