【MFC】水准网间接平差

目录

  • 1、题目
  • 2、分析及源码
  • 3、随笔

1、题目

如图,A、B、C为已知水准点,HA=20.00m,HB=10.00m,HC=11.00m,观测高差及所在距离如下,试求:
1)各高差平均值;
2)P1、P2点高程平差值及其中误差。
【MFC】水准网间接平差_第1张图片

观测高差 距离
h1 = 2.02m s1 = 10km
h2 = 3.12m s2 = 5km
h3 = 15.21m s3 = 5km
h4 = 14.14m s4 = 5km

2、分析及源码

首先,我们定义如下类:

  • 水准点类:存储点的点号,高程,以及判断该点是否已知;
  • 高差观测值类:用来存储高差观测值和路线长度。
//.h文件
class CLevelPoint//水准点类																		
{
public:
	CLevelPoint(void);
	~CLevelPoint(void);
public:

	CString strID;//点号
	double H;//高程
	bool flag;//判断该点是否已知,1为已知,0为未知
};
class CdHObs//高差观测值类
{
public:
	CdHObs(void);
	~CdHObs(void);
public:
	CLevelPoint *StartPoint, *EndPoint;//起点和终点
	double dH;//高差观测值
	double S;//路线长度
};

//.cpp文件
CLevelPoint::CLevelPoint(void)
{
	strID = _T("");
	H = 0;
	flag = 0;
}
CLevelPoint::~CLevelPoint(void)
{

}
CdHObs::CdHObs(void)
{
	dH = 0;
	S = 0;
}
CdHObs::~CdHObs(void)
{

}

然后声明存储数据的数组,以及用得到的各种方法和计算平差时用得到的变量。

class CLevelAdjust//实现水准网平差
{
public:
	CLevelAdjust(void);
	~CLevelAdjust(void);

private:
	CLevelPoint *pUnknownPoint;//未知水准点数组
	int iUnknownPointCount;//未知水准点个数
	CLevelPoint *pKnownPoint;//已知水准点数组
	int iKnownPointCount;//已知水准点个数
	CdHObs *pdHObs;//高差观测值数组
	int idHObsCount;//高差观测值个数

private:
	CLevelPoint *SearchKnownPointUsingID(CString strID);//根据点号查找已知点,返回该点指针
	CLevelPoint *SearchUnknownPointUsingID(CString strID);//根据点号查找未知点,返回该点指针
	CLevelPoint *SearchPointUsingID(CString strID);//根据点号查找点,返回该点指针

private:
	CString *CLevelAdjust::SplitString(CString str, char split, int iSubStrs);//字符串分割函数

public:
	void SetKnownPointSize(int size);//设置已知点数组大小
	void SetUnknownPointSize(int size);//设置未知点数组大小
	void SetdHObsSize(int size);//设置高差观测值数组大小

public:
	BOOL LoadObsData(const CString& strFileName);//导入观测数据函数
	void CalculateApproximateH(void);//求待测点近似高程
	void RigorousAdjust(const CString& strFileName);//间接平差计算主函数
	void RigorousAdjust1(const CString& strFileName);

private:
	void FormErrorEquation();//组成误差方程
	void CLevelAdjust::Weight();//组成权阵
	void EquationCompute();//计算法方程
	void Accuracy_Assessment();//精度评定
	void OutMatrixToFile(const CMatrix& mat, CStdioFile& SF);//把矩阵输出到文件中

private:
	/*l为常数项矩阵,B为系数矩阵,P为权阵,BT为B的转置,NBB为法方程系数阵,NBBl为NBB的逆矩阵
	x为未知数近似值的改正数,v为观测值改正数,vT为v的转置,r为vtPv,Qxx为协因数阵*/
	CMatrix l, B, P, BT, NBB, NBBl, x, v, vT, r, Qxx;
	double r0;//r0为单位权中误差
	double *Qx = new double[iUnknownPointCount];//*Qx为未知点高程中误差
};
CLevelAdjust::CLevelAdjust(void)
{

}
CLevelAdjust::~CLevelAdjust(void)
{
	//释放动态数组内存
	if (pUnknownPoint != NULL)
	{
		delete[] pUnknownPoint;
		pUnknownPoint = NULL;
	}
	if(pKnownPoint != NULL)
	{
		delete[] pKnownPoint;
		pKnownPoint = NULL;
	}
	if (pdHObs != NULL)
	{
		delete[] pdHObs;
		pdHObs = NULL;
	}
}
//设置已知点数组大小
void CLevelAdjust::SetKnownPointSize(int size)
{
	pKnownPoint = new CLevelPoint[size];//已知点数组
}
//设置未知点数组大小
void CLevelAdjust::SetUnknownPointSize(int size)
{
	pUnknownPoint = new CLevelPoint[size];//已知点数组
}
//设置高差观测值数组大小
void CLevelAdjust::SetdHObsSize(int size)
{
	pdHObs = new CdHObs[size];
}
//根据点号从已知点数组中找到控制点,并返回该点指针
CLevelPoint *CLevelAdjust::SearchKnownPointUsingID(CString strID)
{
	for (int i = 0; i < iKnownPointCount; i++)
	{
		if (strID == pKnownPoint[i].strID)//当点号与已知点点号相同时,返回该点的指针
			return &pKnownPoint[i];
	}
	return NULL;
}
//根据点号从未知点数组中找到控制点,并返回该点指针
CLevelPoint *CLevelAdjust::SearchUnknownPointUsingID(CString strID)
{
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		if (strID == pUnknownPoint[i].strID)//当点号与已知点点号相同时,返回该点的指针
			return &pUnknownPoint[i];
	}
	return NULL;
}
//根据点号从未知点和已知点中找到控制点,并返回该点指针
CLevelPoint *CLevelAdjust::SearchPointUsingID(CString strID)
{
	CLevelPoint *pCP = NULL;//定义一个空指针
	pCP = SearchKnownPointUsingID(strID);//试探该点是否已知
	if (pCP == NULL)//如果pCP为空,则pCP为未知点
		pCP = SearchUnknownPointUsingID(strID);
	return pCP;
}
//字符串分割函数
CString *CLevelAdjust::SplitString(CString str, char split, int iSubStrs)
{
	int iPos = 0;//分割符位置
	int iNums = 0;//分割符的总数
	CString strTemp = str;
	CString strRight;
	//计算子字符串的数量
	while (iPos != -1)
	{
		iPos = strTemp.Find(split);
		if (iPos == -1)
			break;
		strRight = strTemp.Mid(iPos + 1, str.GetLength());
		strTemp = strRight;
		iNums++;
	}
	if (iNums == 0)//没有找到分隔符
	{
		//子字符串数就是字符串本身
		iSubStrs = 1;
		return NULL;
	}
	//子字符串数组
	iSubStrs = iNums + 1;//子字符串的数量=分割符数量+1
	CString* pStrSplit;
	pStrSplit = new CString[iSubStrs];
	strTemp = str;
	CString strLeft;
	for (int i = 0; i < iNums; i++)
	{
		iPos = strTemp.Find(split);
		//左子串
		strLeft = strTemp.Left(iPos);
		//右子串
		strRight = strTemp.Mid(iPos + 1, strTemp.GetLength());
		strTemp = strRight;
		pStrSplit[i] = strLeft;
	}
	pStrSplit[iNums] = strTemp;
	return pStrSplit;
}
//导入观测数据函数
BOOL CLevelAdjust::LoadObsData(const CString& strFileName)
{
	CStdioFile sf;//创建文件对象
	//以读的方式打开文件
	if (!sf.Open(strFileName, CFile::modeRead))
		return false;
	CString strLine;
	BOOL bEOF = sf.ReadString(strLine);//读取第一行,已知点个数

	iKnownPointCount = _ttoi(strLine);//存取已知点个数
	SetKnownPointSize(iKnownPointCount);//设置已知点数组大小
	//开始读取已知点数据
	int n = 0;
	for (int i = 0; i < iKnownPointCount; i++)//根据已知点个数,读取已知点数据
	{
		sf.ReadString(strLine);
		CString *pstrData = SplitString(strLine, ',', n);
		pKnownPoint[i].strID = pstrData[0];//存取已知点点号
		pKnownPoint[i].H = _tstof(pstrData[1]);//存取已知点高程
		pKnownPoint[i].flag = 1;//标记为已知点
		delete[] pstrData;
		pstrData = NULL;
	}
	//开始读取未知点数据
	sf.ReadString(strLine);//读取未知点个数
	iUnknownPointCount = _ttoi(strLine);//存取未知点个数
	SetUnknownPointSize(iUnknownPointCount);//设置未知点数组大小
	sf.ReadString(strLine);
	CString *pstrData1 = SplitString(strLine, ',', n);
	//读取并保存未知点点号
	for (int i = 0; i < iUnknownPointCount; i++)//根据未知点个数,读取未知点数据
	{
		pUnknownPoint[i].strID = pstrData1[i];//存取未知点点号
	}
	delete[] pstrData1;
	pstrData1 = NULL;
	//开始读取高差观测值数据
	sf.ReadString(strLine);//读取高差观测值个数
	idHObsCount = _ttoi(strLine);//存取高差观测值个数
	SetdHObsSize(idHObsCount);//设置高差观测值数组大小
	//读取并保存高差观测值数据
	for (int i = 0; i < idHObsCount; i++)
	{
		sf.ReadString(strLine);
		CString *pstrData2 = SplitString(strLine, ',', n);
		pdHObs[i].StartPoint = SearchPointUsingID(pstrData2[0]);//存取起点信息
		pdHObs[i].EndPoint = SearchPointUsingID(pstrData2[1]);//存取终点信息
		pdHObs[i].dH = _tstof(pstrData2[2]);//存取观测高差
		pdHObs[i].S = _tstof(pstrData2[3]);//存取路线长度
		delete[] pstrData2;
		pstrData2 = NULL;
	}
	sf.Close();
	return true;
}
//求待测点近似高程
void CLevelAdjust::CalculateApproximateH(void)
{
	double sum = 0.0;//sum为所有未知点flag的和,当sum与未知点个数相等时,则所有未知点高程均以算出
	while (sum != iUnknownPointCount)
	{
		for (int i = 0; i < iUnknownPointCount; i++)
		{
			if (pUnknownPoint[i].flag == 1)//判断该未知点是否已经算出
				continue;//若已计算,则跳出此次for循环
			for (int j = 0; j < idHObsCount; j++)
			{
				if (pUnknownPoint[i].strID == pdHObs[j].StartPoint->strID
					&& pdHObs[j].EndPoint->flag == 1
					&& pUnknownPoint[i].flag == 0)//当Xi为观测起点且终点高程为已知值时且Xi点高程未知
				{
					pUnknownPoint[i].H = pdHObs[j].EndPoint->H - pdHObs[j].dH;//计算Xi
					pUnknownPoint[i].flag = 1;//标记Xi已算出
				}
				else if (pUnknownPoint[i].strID == pdHObs[j].EndPoint->strID
					&& pdHObs[j].StartPoint->flag == 1
					&& pUnknownPoint[i].flag == 0)//当Xi为观测终点且起点高程为已知值时且Xi点高程未知
				{
					pUnknownPoint[i].H = pdHObs[j].StartPoint->H + pdHObs[j].dH;//计算Xi
					pUnknownPoint[i].flag = 1;//标记Xi已算出
				}
			}
			sum = sum + pUnknownPoint[i].flag;
		}
	}
}
//组成误差方程
void CLevelAdjust::FormErrorEquation()
{
	l.SetSize(idHObsCount, 1);//设置常数项l的大小
	for (int i = 0; i < idHObsCount; i++)
	{
		double l0 = pdHObs[i].EndPoint->H - pdHObs[i].StartPoint->H;//终点近似高程-起点近似高程
		l(i, 0) = l0 - pdHObs[i].dH  ;//计算l中各项的值
	}
	B.SetSize(idHObsCount, iUnknownPointCount);//设置系数B的大小
	{
		for (int i = 0; i < idHObsCount; i++)
		{
			for (int j = 0; j < iUnknownPointCount; j++)
			{
				B(i, j) = 0;//先给B初值
			}
		}
		for (int i = 0; i < idHObsCount; i++)//根据每一观测值的终点和起始点,确定X1,X2,X3的系数
		{
			for (int j = 0; j < iUnknownPointCount; j++)
			{
				if (pdHObs[i].EndPoint->strID == pUnknownPoint[j].strID)//如果终点是X,那么X的系数为1
				{
					B(i, j) = 1;
					break;
				}
			}
			for (int j = 0; j < iUnknownPointCount; j++)
			{
				if (pdHObs[i].StartPoint->strID == pUnknownPoint[j].strID)
				{
					B(i, j) = -1;//如果起点是X,那么X的系数为-1
					break;
				}
			}
		}

	}

}
//组成权阵
void CLevelAdjust::Weight()
{
	P.SetSize(idHObsCount, idHObsCount);//设置权阵P的大小
	for (int i = 0; i < idHObsCount; i++)
	{
		for (int j = 0; j < iUnknownPointCount; j++)
		{
			P(i, j) = 0;//先给P初值
		}
	}
	for (int i = 0; i < idHObsCount; i++)
	{
		P(i, i) = 1 / pdHObs[i].S;//按水准路线长度定权
	}
}
//组成法方程
void CLevelAdjust::EquationCompute()
{
	BT = ~B;//计算B的转置
	NBB = BT*P*B;//计算NBB
	NBBl = NBB.Inv();//计算NBB的逆矩阵
	x = -1*NBBl*BT*P*l;//计算x
}
//精度评定
void CLevelAdjust::Accuracy_Assessment()
{
	v = B*x + l;//计算改正数v
	vT = ~v;
	r = vT*P*v;
	r0 = sqrt(r(0, 0)*1.0 / (idHObsCount - iUnknownPointCount));//计算单位权中误差r0
	Qxx = NBB.Inv();//计算协因数阵
	for (int i = 0; i < iUnknownPointCount; i++)//计算未知点高程中误差
	{
		Qx[i] = sqrt(Qxx(i, i))*r0;
	}
}
//把矩阵输出到文件中
void CLevelAdjust::OutMatrixToFile(const CMatrix& mat, CStdioFile& SF)
{
	CString strLine, strTmp;
	for (int i = 0; i < mat.Row(); i++)
	{
		strLine.Empty();
		for (int j = 0; j < mat.Col(); j++)
		{
			strTmp.Format(_T("%8.4f"), mat(i, j));
			strLine = strLine + strTmp;
		}
		SF.WriteString(strLine + _T("\r\n"));
	}
}
void CLevelAdjust::RigorousAdjust(const CString& strFileName)
{
	//开始输出间接平差结果
	CStdioFile SF;
	CString strLine;
	setlocale(LC_ALL,"");
	if (!SF.Open(strFileName, CFile::modeCreate | CFile::modeWrite))
		return;
	CalculateApproximateH();
	FormErrorEquation();
	Weight();
	EquationCompute();
	Accuracy_Assessment();
	SF.WriteString(_T("平差后高程值(m):\r\n"));
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		strLine.Format(_T("%s,%.4lf\n"),pUnknownPoint[i].strID, pUnknownPoint[i].H + x(i, 0));
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	strLine.Format(_T("单位权中误差:%.lf(mm)\r\n"),r0*1000.0);
	SF.WriteString(strLine);
	SF.WriteString(_T("\n"));
	SF.WriteString(_T("未知点高程中误差(mm):\r\n"));
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		strLine.Format(_T("%.1lf\n"), (double)(Qx[i]*1000));
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	SF.Close();
}
void CLevelAdjust::RigorousAdjust1(const CString& strFileName)
{
	//开始输出间接平差结果
	CStdioFile SF;
	CString strLine;
	setlocale(LC_ALL, "");
	if (!SF.Open(strFileName, CFile::modeCreate | CFile::modeWrite))
		return;
	SF.WriteString(_T("——水准网间接平差结果——\n"));
	strLine.Format(_T("已知点个数:%d\n"), iKnownPointCount);
	SF.WriteString(strLine);
	strLine.Format(_T("已知点点号及个数:\n"));
	SF.WriteString(strLine);
	for (int i = 0; i < iKnownPointCount; i++)
	{
		strLine.Format(_T("%s,%.4lf\n"), pKnownPoint[i].strID, pKnownPoint[i].H );
		SF.WriteString(strLine);
	}
	strLine.Format(_T("未知点个数:%d\n"), iUnknownPointCount);
	SF.WriteString(strLine);
	SF.WriteString(_T("未知点近似高程:\r\n"));
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		strLine.Format(_T("%s,%.4lf\n"), pUnknownPoint[i].strID, pUnknownPoint[i].H);
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	SF.WriteString(_T("B矩阵:\r\n"));
	OutMatrixToFile(B, SF);
	SF.WriteString(_T("常数项l矩阵:\r\n"));
	OutMatrixToFile(l, SF);
	SF.WriteString(_T("\r\nP矩阵:\r\n"));
	OutMatrixToFile(P, SF);
	SF.WriteString(_T("N矩阵:\r\n"));
	OutMatrixToFile(NBB, SF);
	SF.WriteString(_T("N矩阵的逆矩阵:\r\n"));
	OutMatrixToFile(NBBl, SF);
	SF.WriteString(_T("x矩阵:\r\n"));
	OutMatrixToFile(x, SF);
	SF.WriteString(_T("\r\n"));
	SF.WriteString(_T("观测值残差(mm):\r\n"));
	OutMatrixToFile(v*1000.0, SF);
	strLine.Format(_T("单位权中误差:%.lf(mm)\r\n"), r0*1000.0);
	SF.WriteString(strLine);
	SF.WriteString(_T("Qxx矩阵:\r\n"));
	OutMatrixToFile(Qxx, SF);
	SF.WriteString(_T("\r\n"));
	SF.WriteString(_T("未知点高程中误差(mm):\r\n"));
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		strLine.Format(_T("%.1lf\n"), (double)(Qx[i] * 1000));
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	SF.WriteString(_T("平差后高差观测值(m):\r\n"));
	for (int i = 0; i < idHObsCount; i++)
	{
		strLine.Format(_T("%.4lf\n"), pdHObs[i].dH + v(i, 0));
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	SF.WriteString(_T("平差后未知点高程值(m):\r\n"));
	for (int i = 0; i < iUnknownPointCount; i++)
	{
		strLine.Format(_T("%s,%.4lf\n"), pUnknownPoint[i].strID, pUnknownPoint[i].H + x(i, 0));
		SF.WriteString(strLine);
	}
	SF.WriteString(_T("\r\n"));
	SF.Close();
}

MFC窗口如下图。
【MFC】水准网间接平差_第2张图片
运行结果如下图
【MFC】水准网间接平差_第3张图片
文件读取为Button1,平差计算为Button2,结果输出为Button3。我这里写的方法是在文件读取时就进行了平差计算并输出结果,而在平差计算时就是打开输出的结果文件。

void CLevelAdjustDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	CFileDialog dlgFile(TRUE, _T("txt"), NULL,
		OFN_EXPLORER, _T("(文本文件)|*.dat")); //创建打开文件对话框
	if (dlgFile.DoModal() == IDCANCEL) return;//如果选择取消按钮,则退出
	CString strFileName = dlgFile.GetPathName();//获取选择的文件的名称
	CLevelAdjust la;
	la.LoadObsData(strFileName);
	la.RigorousAdjust(_T("水准网间接平差计算结果.txt"));
	la.RigorousAdjust1(_T("水准网间接平差计算总结果.txt"));
	setlocale(LC_ALL, "");	//设置语言环境
	CStdioFile sf;  //创建文件对象
	//以读的形式打开文件,如果打开失败则返回
	if (!sf.Open(strFileName, CFile::modeRead)) return;
	CString strLine;
	strFileContent.Empty();
	BOOL bEOF = sf.ReadString(strLine);//读取第一行
	strFileContent += strLine + _T("\r\n");
	while (bEOF)
	{
		bEOF = sf.ReadString(strLine);
		if (bEOF)
		strFileContent +=strLine+ _T("\r\n");
	}
	sf.Close();
	UpdateData(FALSE);//关闭文件
}
void CLevelAdjustDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	CString strFileName = (_T("水准网间接平差计算结果.txt"));//获取选择的文件的名称
	setlocale(LC_ALL, "");	//设置语言环境
	CStdioFile sf;  //创建文件对象
	//以读的形式打开文件,如果打开失败则返回
	if (!sf.Open(strFileName, CFile::modeRead)) return;
	CString strLine;
	strFileOutput.Empty();
	BOOL bEOF = sf.ReadString(strLine);//读取第一行
	strFileOutput += strLine + _T("\r\n");
	while (bEOF)
	{
		bEOF = sf.ReadString(strLine);
		if (bEOF)
			strFileOutput += strLine + _T("\r\n");
	}
	sf.Close();
	UpdateData(FALSE);//关闭文件
}
void CLevelAdjustDlg::OnBnClickedButton3()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(TRUE);
	CString strFileName = (_T("水准网间接平差计算总结果.txt"));//获取选择的文件的名称
	setlocale(LC_ALL, "");	//设置语言环境
	CStdioFile sf;  //创建文件对象
	//以读的形式打开文件,如果打开失败则返回
	if (!sf.Open(strFileName, CFile::modeRead)) return;
	CString strLine;
	strFileOutput1.Empty();
	BOOL bEOF = sf.ReadString(strLine);//读取第一行
	strFileOutput1 += strLine + _T("\r\n");
	while (bEOF)
	{
		bEOF = sf.ReadString(strLine);
		if (bEOF)
			strFileOutput1 += strLine + _T("\r\n");
	}
	sf.Close();
	UpdateData(FALSE);//关闭文件
}

读取的文件格式如下:
【MFC】水准网间接平差_第4张图片
输出的文件格式如下(只截取了部分):
【MFC】水准网间接平差_第5张图片
【MFC】水准网间接平差_第6张图片
文中用到的计算矩阵的代码已上传,需要的自行下载。

3、随笔

相比测边网,水准网比较简单。其实两者的过程差的不多,理解一下就很容易懂了。供交流学习使用,欢迎大家点评。

你可能感兴趣的:(MFC,c++,mfc)