如图,A、B、C为已知水准点,HA=20.00m,HB=10.00m,HC=11.00m,观测高差及所在距离如下,试求:
1)各高差平均值;
2)P1、P2点高程平差值及其中误差。
观测高差 | 距离 |
---|---|
h1 = 2.02m | s1 = 10km |
h2 = 3.12m | s2 = 5km |
h3 = 15.21m | s3 = 5km |
h4 = 14.14m | s4 = 5km |
首先,我们定义如下类:
//.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窗口如下图。
运行结果如下图
文件读取为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);//关闭文件
}
读取的文件格式如下:
输出的文件格式如下(只截取了部分):
文中用到的计算矩阵的代码已上传,需要的自行下载。
相比测边网,水准网比较简单。其实两者的过程差的不多,理解一下就很容易懂了。供交流学习使用,欢迎大家点评。