转自:https://blog.csdn.net/sinat_36564005/article/details/109528723
背景:
在使用skyline开发的过程中,有些功能并不满足实际需求,所以我们就需要自己重写设计以及做一些计算,比如模拟水淹过程,水淹洼地分析,以及水淹面积和体积计算等等.
需求整个计算过程:
水淹算法本身并不难,只是种子填充算法的扩展. 通俗点讲,就是像素之间的连通域问题; 难的是在从鼠标选择区域->切割改区域的地形数据->将三维地形数据降维处理->应用算法处理之后->反映射原始数据->
三维数据面片化->构建棱柱体->计算共面非共面的不规则体积以及面积; 最后才能得到自己想要的功能以及实现,哦,对了,这个结果最后还要可视化;
大家看这个过程,算法本身只是占了一小部分.剩下的用程序展现这个东西才是重点所在,大部分难题都是工程问题.以及开发过程中的种种异常以及流畅体验(异步多线程以及多进程等问题)
先展示一个结果,然后具体聊实现过程

这个结果我和其它主流的分析软件计算的结果对比过,相差不大,只不过其它的软件在计算水淹高度的过程中,可能设置了固定的采样间隔,我没有设置,
所以,可能计算的结果更逼近一些.(虽然这个意义感觉并不大而已)
实现:
数据获取我就不写了,可以用GDAL获取一个tif图像,转三维数据;或者直接从地球上裁剪;具体视情况而异
1.拿到数据之后,我们要将三维数据栅格化.就是

-
public bool ElevationData2BitmP(ref List<ElevationPoint3D> ListFaces, int width, int height, double waterHeight, out Bitmap a) -
{ -
a = new Bitmap(width, height); -
if (width < 1 && height < 1) -
{ -
return false; -
} -
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, a.Width, a.Height); -
System.Drawing.Imaging.BitmapData bmpData = a.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, -
System.Drawing.Imaging.PixelFormat.Format24bppRgb); //填充数据. -
int stride = bmpData.Stride; -
int Listnumber = ListFaces.Count; -
//查询的步长. -
int querySride = width * height / Listnumber; -
double lowWaterHeightNumber = 0; -
unsafe -
{ -
byte* pIn = (byte*)bmpData.Scan0.ToPointer(); -
byte* P; -
int R, G, B; -
double temp = 0; -
Random rm = new Random(); -
for (int y = 0, k = 0; y < a.Height; y++) -
{ -
for (int x = 0; x < a.Width; x++, k += querySride) -
{ -
// double dscal = 0; -
P = pIn; -
B = P[0]; -
G = P[1]; -
R = P[2]; -
if (k < Listnumber) -
{ -
temp = ListFaces[k].PxyzConvertImg.Z; -
if (temp >= waterHeight) //背景色,以及过滤色 -
{ -
//dscal = 0; -
P[2] = (byte)(73);//R -
P[1] = (byte)(221);//G -
P[0] = (byte)(93);//B -
} -
else -
{ -
//dscal = 1; -
P[2] = (byte)(0);//R -
P[1] = (byte)(0);//G -
P[0] = (byte)(255);//B -
lowWaterHeightNumber++; -
} -
} -
//P[2] = (byte)(255 * dscal);//R -
//P[1] = (byte)(255 * dscal);//G -
//P[0] = (byte)(255 * dscal);//B -
pIn += 3; -
} -
pIn += stride - a.Width * 3; -
} -
} -
a.UnlockBits(bmpData); -
//得到当前蓝色点. -
return true; -
}
这里解释一下: 这一部分主要是将三维数据映射成二维uv数据,方便后续计算
通过这一步,就可以实现其数据的可视化. 这一步完成之后,就可以设置一不同高度,显示不同的计算结果
2.计算完成之后,将三维数据网格化

这样可以分析计算每一个小面片的具体参数; 注意其需要依据水淹高度, 在底层构建一个虚假的面.和这一层构成棱柱体.

类似这样.
3.计算,主要是利用海伦公式快速计算
-
/// <summary> -
/// 依据传入的面,以及高. 求取拟柱体体积. -
/// </summary> 点的绘制按顺时针计算. -
/// <returns></returns> -
public double CalculateFitting(ref QuadPrism quadranglePrism) -
{ -
double cylinderVolume = 0; -
//以点出发,计算每个点到水平面的距离. -
// double currentPlength -
//1/6*(S0 + 4S1 + S2)*H -
//注意这里的面积,一般都按照梯形面接计算. -
//点1 quadrangleFace.item0, item1 ,item2 , item3. -
// 从L-> R-> RB-> LB -
// item0, item1 item2 item3 -
// Distance[0] Distance[1] Distance[2] Distance[3] -
Point3 currentP = quadranglePrism._bottomFace.pxyz0; -
Point3 RightcurrentP = quadranglePrism._bottomFace.pxyz1; -
Point3 RightBottomcurrentP = quadranglePrism._bottomFace.pxyz2; -
Point3 LeftBottomcurrentP = quadranglePrism._bottomFace.pxyz3; -
//SO面积. -
double S0h = ComputorFunction.calculateD2Distance(ref currentP, ref LeftBottomcurrentP); -
double S0Area = (quadranglePrism.PointToWaterLevelDistance[0] + quadranglePrism.PointToWaterLevelDistance[3]) / 2.0 * S0h; -
//S2面积. -
double S2h = ComputorFunction.calculateD2Distance(ref RightcurrentP, ref RightBottomcurrentP); -
double S2Area = (quadranglePrism.PointToWaterLevelDistance[1] + quadranglePrism.PointToWaterLevelDistance[2]) / 2.0 * S2h; -
//S1面积,按照中值划分,可以知道 -
double S1_upBorder = 0.5 * (quadranglePrism.PointToWaterLevelDistance[0] + quadranglePrism.PointToWaterLevelDistance[1]); -
double S1_BottomBorder = 0.5 * (quadranglePrism.PointToWaterLevelDistance[2] + quadranglePrism.PointToWaterLevelDistance[3]); -
double S1h = ComputorFunction.calculateD2Distance(ref RightcurrentP, ref RightBottomcurrentP); -
double S1Area = (S1_upBorder + S1_BottomBorder) * S1h * 0.5; -
//整个体积的高. -
double Sh = ComputorFunction.calculateD3Distance(ref currentP, ref RightcurrentP); -
//幸普斯规则.数值积分进行拟柱体体积计算. -
cylinderVolume = (S0Area + 4 * S1Area + S2Area) / 6.0 * Sh; -
return cylinderVolume; -
}
4.最后使用循环体,累加计算就可以了


5.这里总结一下:写的过程中,一定要细致,否则特变容易出一些问题
&spm=1001.2101.3001.5002&articleId=109721523&d=1&t=3&u=5a1aa3dd73b2401696600ceddb54a3fa)
6176

被折叠的 条评论
为什么被折叠?



