一、以下图附合导线为例,图形如下:

二、第一步,读取测量数据,读取界面设计如下:

读取数据文本文件格式如下:
(1)已知点坐标数据格式:

(2)角度观测值数据格式:

(3)边长观测值数据格式:

读取文本文件的代码如下:
void TraverseAdjustment::onReadDataKnown()
{
    //打开文件对话框
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开已知点数据"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));
    //读取文本文件数据
    QTextStream stream(&file);
    pointKnown pk1;
    while (!stream.atEnd())
    {
        QString str = stream.readLine();
        QStringList list=str.split(",");
        pk1.number = list.at(0);
        pk1.x = list.at(1).toDouble();
        pk1.y = list.at(2).toDouble();
        ui.textEdit->append(str);
        vpKnown.push_back(pk1);
    }
    ui.lineEdit->setText(fileName);
} 
说明:需要先创建一个字符串变量用于存储文件对话框返回的路径,然后创建一个文件操作对象,用返回路径进行初始化。使用open()函数打开文本文件,随后设置一个读写流对象,用于逐行读取文本,将单行文本分割后,赋值给创建的结构体对象,然后添加到vecctor中。
三、第二步,计算角度闭合差
观测值为左角时,有
其中n为导线转折角数,这里如图1的附合导线,n=8.
注意1:观测角度值之和应为各个观测角度(DMS度分秒格式)的十进制度数的总和,因此,需要将原始观测值逐一转换成十进制度,再求和。(原始角度观测数据为度分秒格式的)
注意2:等方位角的计算,由于atan()等三角函数返回的是弧度值,因此需要将结果转化成十进制度值再输出。利用坐标反算方法,得到已知边的方位角,如下:

(1)方位角计算代码如下:
double TraverseAdjustment::CoordinateInverse(double xa, double ya, double xb, double yb)
{
    double arfa_ab = atan(abs(yb - ya) / abs(xb - xa));//返回的是弧度值
    arfa_ab = Radian2Angle(arfa_ab);
    if (ya<yb&&xa>xb)
    {
        arfa_ab=180 - arfa_ab;
    }
    if (ya > yb && xa > xb)
    {
        arfa_ab = 180 + arfa_ab;
    }
    if (ya > yb && xa < xb)
    {
        arfa_ab = 360 - arfa_ab;
    }
    return arfa_ab;//返回十进制度数
} 
(2)闭合差计算代码如下:
void TraverseAdjustment::onAngleClosureError()
{
    double arfa_1=0;//角度观测值之和
    for (auto& val : vA)
    {
        double a = val.value;
        a = DMS2Angle(a);
        arfa_1 = arfa_1 + a;
    }
    qDebug() << arfa_1;
    qDebug() << DMS2Angle(1644.2828);
    double arfa_ab;//AB方位角值
    double arfa_cd;//CD方位角值
    arfa_ab = CoordinateInverse(vpKnown.at(0).x, vpKnown.at(0).y, vpKnown.at(1).x, vpKnown.at(1).y);
    arfa_cd = CoordinateInverse(vpKnown.at(2).x, vpKnown.at(2).y, vpKnown.at(3).x, vpKnown.at(3).y);
    qDebug() << arfa_ab;
    qDebug() << arfa_cd;
    qDebug() << DMS2Angle(303.2728);
    qDebug() << DMS2Angle(147.5628);
    Rab = arfa_ab;
    Rcd = arfa_cd;
    n = vA.size();
    double arfa_cd1 = 0;//理论角度值
    arfa_cd1 = arfa_ab - n * 180+arfa_1;
    if (arfa_cd1 > 360)
    {
        int t1 = arfa_cd1 / 360;
        arfa_cd1 = arfa_cd1 - t1 * 360;
    }
    double f_beta = arfa_cd1-arfa_cd;
    f_beta = f_beta * 3600;
    f = f_beta;
    ui.textEdit_2->append("角度闭合差:");
    ui.textEdit_2->append(QString::number(f_beta));
    ui.textEdit_2->append("限差:");
    ui.textEdit_2->append(QString::number(24*sqrt(n)));
    if (f_beta <= 24 * sqrt(n))
    {
        ui.textEdit_2->append("合格");
    }
    else
    {
        ui.textEdit_2->append("不合格");
    }
} 
四、第三步,计算方位角

 
若角度闭合差附合限差,则将角度闭合差均分至各角度观测值(即求改正数),然后使用改正后的角度观测值计算各测站的方位角信息。观测值为左角时,有如下:
以此推算各测站方位角。
(1)方位角计算代码如下:
void TraverseAdjustment::CalculateAzimuth()
{
    //计算角度观测值改正数
    double t1 = -f / n;
    t1 = t1 / 3600;
    Angle a1;
    for (auto& val : vA)
    {
        a1.value = DMS2Angle(val.value)+t1;
        a1.number = val.number;
        a1.number1 = val.number1;
        a1.number2 = val.number2;
        va.push_back(a1);
    }
    ui.textEdit_2->clear();
    ui.textEdit_2->append("方位角结果:");
    Angle a2; double R1 = Rab;
    for (int i = 0; i < va.size(); i++)
    {
        double R = R1 + va.at(i).value-180;
        if (R > 360)
        {
            int t2 = R / 360;
            R = R - t2 * 360;
        }
        a2.number = va.at(i).number;
        a2.number1 = va.at(i).number1;
        a2.number2 = va.at(i).number2;
        a2.value = R;
        ui.textEdit_2->append(QString::number(R));
        vAzi.push_back(a2);
        R1 = R;
    }
} 
五、第四步,计算坐标增量

注意:这里的cos()函数等三角函数的输入值为弧度,应把方位角转化成护弧度在计算,否则结果错误。
(1)计算坐标增量的代码如下:
void TraverseAdjustment::onCalIncrement()
{
    Side s1; ui.textEdit_2->clear();
    ui.textEdit_2->append("X坐标增量:");
    for (int i = 0; i < vAzi.size(); i++)
    {
        s1.number1 = vAzi.at(i).number;
        s1.number2 = vAzi.at(i).number2;
        for (int j = 0; j < vS.size(); j++)
        {
            if (vAzi.at(i).number == vS.at(j).number1 && vAzi.at(i).number2 == vS.at(j).number2)
            {
                double b1 = vAzi.at(i).value;
                b1 = Angle2Radian(b1);
                s1.value = vS.at(j).value * cos(b1);//cos函数输入值需为弧度
                ui.textEdit_2->append(QString::number(s1.value));
                vXIn.push_back(s1);
                s1.value = vS.at(j).value * sin(b1);//sin函数输入值需为弧度
                vYIn.push_back(s1);
                break;
            }
        }
    }
    ui.textEdit_2->append("Y坐标增量:");
    for (auto& val : vYIn)
    {
        ui.textEdit_2->append(QString::number(val.value));
    }
} 
六、第五步,计算坐标改正数

这里只需按照上述公式计算即可。
(1)计算坐标改正数代码如下:
void TraverseAdjustment::onCalCorrection()
{
    ui.textEdit_2->clear();
    ui.textEdit_2->append("X增量改正数:");
    //计算坐标增量之和
    double detax = 0;
    double detay = 0;
    for (auto& val : vXIn)
    {
        double b1 = val.value;
        detax = detax + b1;
    }
    for (auto& val : vYIn)
    {
        double b1 = val.value;
        detay = detay + b1;
    }
    //计算坐标增量闭合差
    fx = detax - (vpKnown.at(2).x - vpKnown.at(1).x);
    fy = detay - (vpKnown.at(2).y - vpKnown.at(1).y);
    //计算坐标改正数
    double s1 = 0;//导线边距离之和
    for (auto& val : vS)
    {
        s1 = s1 + val.value;
    }
    Side s2;
    for (int i = 0; i<vXIn.size(); i++)
    {
        double d;
        for (int j=0; j < vS.size(); j++)
        {
            if (vXIn.at(i).number1 == vS.at(j).number1 && vXIn.at(i).number2 == vS.at(j).number2)
            {
                d = vS.at(j).value;
            }
        }
        s2.number1 = vXIn.at(i).number1;
        s2.number2 = vXIn.at(i).number2;
        s2.value = -fx / s1 * d;
        ui.textEdit_2->append(QString::number(s2.value));
        vX.push_back(s2);
    }
    ui.textEdit_2->append("Y增量改正数:");
    for (int i = 0; i < vYIn.size(); i++)
    {
        double d;
        for (int j = 0; j < vS.size(); j++)
        {
            if (vYIn.at(i).number1 == vS.at(j).number1 && vYIn.at(i).number2 == vS.at(j).number2)
            {
                d = vS.at(j).value;
            }
        }
        s2.number1 = vXIn.at(i).number1;
        s2.number2 = vXIn.at(i).number2;
        s2.value = -fy / s1 * d;
        ui.textEdit_2->append(QString::number(s2.value));
        vY.push_back(s2);
    }
} 
七、第六步,计算未知点坐标值

按公式计算即可。
(1)计算坐标值代码如下:
void TraverseAdjustment::onCalCoordinate()
{
    ui.textEdit_2->clear();
    ui.textEdit_2->append("改正后X增量:");
    double x0 = vpKnown.at(1).x;
    double y0 = vpKnown.at(1).y;
    for (int i=0;i<vXIn.size();i++)
    {
        vXIn.at(i).value = vXIn.at(i).value + vX.at(i).value;
        ui.textEdit_2->append(QString::number(vXIn.at(i).value));
    }
    ui.textEdit_2->append("改正后Y增量:");
    for (int i = 0; i < vYIn.size(); i++)
    {
        vYIn.at(i).value = vYIn.at(i).value + vY.at(i).value;
        ui.textEdit_2->append(QString::number(vYIn.at(i).value));
    }
    
    ui.textEdit_2->append("X坐标:");
    pointKnown pk1; int t2 = 0;
    for (auto& val : vXIn)
    {
        pk1.number = val.number1;
        pk1.x = x0 + val.value;
        pk1.y = y0 + vYIn.at(t2).value;
        vp.push_back(pk1);
        x0 = pk1.x;
        y0 = pk1.y;
        if (t2 <= vYIn.size()-2)
        {
            ui.textEdit_2->append(QString::number(pk1.x, 'f', 8));
        }
        t2++;
    }
    ui.textEdit_2->append("Y坐标:");
    vp.pop_back();
    for (auto& val : vp)
    {
        ui.textEdit_2->append(QString::number(val.y,'f',8));
    }
} 
八、程序运行结果如下:






九、上述计算结果均校对过,结果准确。附上完整代码
//.cpp文件
#include "TraverseAdjustment.h"
TraverseAdjustment::TraverseAdjustment(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(onReadDataKnown()));
    connect(ui.pushButton_2, SIGNAL(clicked()), this, SLOT(onReadDataAngle()));
    connect(ui.pushButton_3, SIGNAL(clicked()), this, SLOT(onReadDataSide()));
    connect(ui.pushButton_5, SIGNAL(clicked()), this, SLOT(onAngleClosureError()));
    connect(ui.pushButton_4, SIGNAL(clicked()), this, SLOT(CalculateAzimuth()));
    connect(ui.pushButton_6, SIGNAL(clicked()), this, SLOT(onCalIncrement()));
    connect(ui.pushButton_7, SIGNAL(clicked()), this, SLOT(onCalCorrection()));
    connect(ui.pushButton_8, SIGNAL(clicked()), this, SLOT(onCalCoordinate()));
    ui.lineEdit->setReadOnly(true);
    ui.lineEdit_2->setReadOnly(true);
    ui.lineEdit_3->setReadOnly(true);
    ui.textEdit->setReadOnly(true);
    ui.textEdit_2->setReadOnly(true);
    ui.textEdit->append("读入数据展示:");
}
TraverseAdjustment::~TraverseAdjustment()
{}
void TraverseAdjustment::onCalCoordinate()
{
    ui.textEdit_2->clear();
    ui.textEdit_2->append("改正后X增量:");
    double x0 = vpKnown.at(1).x;
    double y0 = vpKnown.at(1).y;
    for (int i=0;i<vXIn.size();i++)
    {
        vXIn.at(i).value = vXIn.at(i).value + vX.at(i).value;
        ui.textEdit_2->append(QString::number(vXIn.at(i).value));
    }
    ui.textEdit_2->append("改正后Y增量:");
    for (int i = 0; i < vYIn.size(); i++)
    {
        vYIn.at(i).value = vYIn.at(i).value + vY.at(i).value;
        ui.textEdit_2->append(QString::number(vYIn.at(i).value));
    }
    
    ui.textEdit_2->append("X坐标:");
    pointKnown pk1; int t2 = 0;
    for (auto& val : vXIn)
    {
        pk1.number = val.number1;
        pk1.x = x0 + val.value;
        pk1.y = y0 + vYIn.at(t2).value;
        vp.push_back(pk1);
        x0 = pk1.x;
        y0 = pk1.y;
        if (t2 <= vYIn.size()-2)
        {
            ui.textEdit_2->append(QString::number(pk1.x, 'f', 8));
        }
        t2++;
    }
    ui.textEdit_2->append("Y坐标:");
    vp.pop_back();
    for (auto& val : vp)
    {
        ui.textEdit_2->append(QString::number(val.y,'f',8));
    }
}
void TraverseAdjustment::onCalCorrection()
{
    ui.textEdit_2->clear();
    ui.textEdit_2->append("X增量改正数:");
    //计算坐标增量之和
    double detax = 0;
    double detay = 0;
    for (auto& val : vXIn)
    {
        double b1 = val.value;
        detax = detax + b1;
    }
    for (auto& val : vYIn)
    {
        double b1 = val.value;
        detay = detay + b1;
    }
    //计算坐标增量闭合差
    fx = detax - (vpKnown.at(2).x - vpKnown.at(1).x);
    fy = detay - (vpKnown.at(2).y - vpKnown.at(1).y);
    //计算坐标改正数
    double s1 = 0;//导线边距离之和
    for (auto& val : vS)
    {
        s1 = s1 + val.value;
    }
    Side s2;
    for (int i = 0; i<vXIn.size(); i++)
    {
        double d;
        for (int j=0; j < vS.size(); j++)
        {
            if (vXIn.at(i).number1 == vS.at(j).number1 && vXIn.at(i).number2 == vS.at(j).number2)
            {
                d = vS.at(j).value;
            }
        }
        s2.number1 = vXIn.at(i).number1;
        s2.number2 = vXIn.at(i).number2;
        s2.value = -fx / s1 * d;
        ui.textEdit_2->append(QString::number(s2.value));
        vX.push_back(s2);
    }
    ui.textEdit_2->append("Y增量改正数:");
    for (int i = 0; i < vYIn.size(); i++)
    {
        double d;
        for (int j = 0; j < vS.size(); j++)
        {
            if (vYIn.at(i).number1 == vS.at(j).number1 && vYIn.at(i).number2 == vS.at(j).number2)
            {
                d = vS.at(j).value;
            }
        }
        s2.number1 = vXIn.at(i).number1;
        s2.number2 = vXIn.at(i).number2;
        s2.value = -fy / s1 * d;
        ui.textEdit_2->append(QString::number(s2.value));
        vY.push_back(s2);
    }
}
void TraverseAdjustment::onCalIncrement()
{
    Side s1; ui.textEdit_2->clear();
    ui.textEdit_2->append("X坐标增量:");
    for (int i = 0; i < vAzi.size(); i++)
    {
        s1.number1 = vAzi.at(i).number;
        s1.number2 = vAzi.at(i).number2;
        for (int j = 0; j < vS.size(); j++)
        {
            if (vAzi.at(i).number == vS.at(j).number1 && vAzi.at(i).number2 == vS.at(j).number2)
            {
                double b1 = vAzi.at(i).value;
                b1 = Angle2Radian(b1);
                s1.value = vS.at(j).value * cos(b1);//cos函数输入值需为弧度
                ui.textEdit_2->append(QString::number(s1.value));
                vXIn.push_back(s1);
                s1.value = vS.at(j).value * sin(b1);//sin函数输入值需为弧度
                vYIn.push_back(s1);
                break;
            }
        }
    }
    ui.textEdit_2->append("Y坐标增量:");
    for (auto& val : vYIn)
    {
        ui.textEdit_2->append(QString::number(val.value));
    }
}
void TraverseAdjustment::onReadDataKnown()
{
    //打开文件对话框
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开已知点数据"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));
    //读取文本文件数据
    QTextStream stream(&file);
    pointKnown pk1;
    while (!stream.atEnd())
    {
        QString str = stream.readLine();
        QStringList list=str.split(",");
        pk1.number = list.at(0);
        pk1.x = list.at(1).toDouble();
        pk1.y = list.at(2).toDouble();
        ui.textEdit->append(str);
        vpKnown.push_back(pk1);
    }
    ui.lineEdit->setText(fileName);
}
void TraverseAdjustment::onReadDataAngle()
{
    //打开文件对话框
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开角度观测数据"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));
    //读取数据
    QTextStream stream(&file);
    Angle va1;
    while (!stream.atEnd())
    {
        QString str = stream.readLine();
        QStringList list = str.split(",");
        va1.number = list.at(0);
        va1.number1 = list.at(1);
        va1.number2 = list.at(2);
        va1.value = list.at(3).toDouble();
        ui.textEdit->append(str);
        vA.push_back(va1);
    }
    ui.lineEdit_2->setText(fileName);
}
void TraverseAdjustment::onReadDataSide()
{
    //打开文件对话框
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开边长观测数据"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, tr("提示"), tr("打开失败!"));
    //读取数据
    QTextStream stream(&file);
    Side vs1;
    while (!stream.atEnd())
    {
        QString str = stream.readLine();
        QStringList list = str.split(",");
        vs1.number1 = list.at(0);
        vs1.number2 = list.at(1);
        vs1.value = list.at(2).toDouble();
        ui.textEdit->append(str);
        vS.push_back(vs1);
    }
    ui.lineEdit_3->setText(fileName);
}
void TraverseAdjustment::CalculateAzimuth()
{
    //计算角度观测值改正数
    double t1 = -f / n;
    t1 = t1 / 3600;
    Angle a1;
    for (auto& val : vA)
    {
        a1.value = DMS2Angle(val.value)+t1;
        a1.number = val.number;
        a1.number1 = val.number1;
        a1.number2 = val.number2;
        va.push_back(a1);
    }
    ui.textEdit_2->clear();
    ui.textEdit_2->append("方位角结果:");
    Angle a2; double R1 = Rab;
    for (int i = 0; i < va.size(); i++)
    {
        double R = R1 + va.at(i).value-180;
        if (R > 360)
        {
            int t2 = R / 360;
            R = R - t2 * 360;
        }
        a2.number = va.at(i).number;
        a2.number1 = va.at(i).number1;
        a2.number2 = va.at(i).number2;
        a2.value = R;
        ui.textEdit_2->append(QString::number(R));
        vAzi.push_back(a2);
        R1 = R;
    }
}
double TraverseAdjustment::Angle2Radian(double a)
{
    a = a * 0.017453293;
    return a;
}
double TraverseAdjustment::Radian2Angle(double a)
{
    a = a * 57.29578;
    return a;
}
double TraverseAdjustment::DMS2Angle(double a)
{
    int deg; double min, sed;
    deg = int(a);
    min = int((a - deg) * 100);
    sed = ((a - deg) * 100 - min) * 100;
    return deg + min / 60 + sed / 3600;
}
void TraverseAdjustment::onAngleClosureError()
{
    double arfa_1=0;//角度观测值之和
    for (auto& val : vA)
    {
        double a = val.value;
        a = DMS2Angle(a);
        arfa_1 = arfa_1 + a;
    }
    qDebug() << arfa_1;
    qDebug() << DMS2Angle(1644.2828);
    double arfa_ab;//BA方位角值
    double arfa_cd;//CD方位角值
    arfa_ab = CoordinateInverse(vpKnown.at(0).x, vpKnown.at(0).y, vpKnown.at(1).x, vpKnown.at(1).y);
    arfa_cd = CoordinateInverse(vpKnown.at(2).x, vpKnown.at(2).y, vpKnown.at(3).x, vpKnown.at(3).y);
    qDebug() << arfa_ab;
    qDebug() << arfa_cd;
    qDebug() << DMS2Angle(303.2728);
    qDebug() << DMS2Angle(147.5628);
    Rab = arfa_ab;
    Rcd = arfa_cd;
    n = vA.size();
    double arfa_cd1 = 0;//理论角度值
    arfa_cd1 = arfa_ab - n * 180+arfa_1;
    if (arfa_cd1 > 360)
    {
        int t1 = arfa_cd1 / 360;
        arfa_cd1 = arfa_cd1 - t1 * 360;
    }
    double f_beta = arfa_cd1-arfa_cd;
    f_beta = f_beta * 3600;
    f = f_beta;
    ui.textEdit_2->append("角度闭合差:");
    ui.textEdit_2->append(QString::number(f_beta));
    ui.textEdit_2->append("限差:");
    ui.textEdit_2->append(QString::number(24*sqrt(n)));
    if (f_beta <= 24 * sqrt(n))
    {
        ui.textEdit_2->append("合格");
    }
    else
    {
        ui.textEdit_2->append("不合格");
    }
}
double TraverseAdjustment::CoordinateInverse(double xa, double ya, double xb, double yb)
{
    double arfa_ab = atan(abs(yb - ya) / abs(xb - xa));//返回的是弧度值
    arfa_ab = Radian2Angle(arfa_ab);
    if (ya<yb&&xa>xb)
    {
        arfa_ab=180 - arfa_ab;
    }
    if (ya > yb && xa > xb)
    {
        arfa_ab = 180 + arfa_ab;
    }
    if (ya > yb && xa < xb)
    {
        arfa_ab = 360 - arfa_ab;
    }
    return arfa_ab;//返回十进制度数
} 
//.h文件
#pragma once
#include <QtWidgets/QWidget>
#include "ui_TraverseAdjustment.h"
#include<QFileDialog>
#include<QFile>
#include<QMessageBox>
#include<QTextStream>
#include<QStringList>
#pragma execution_character_set("UTF-8")
#include"math.h"
#include<qDebug>
struct pointKnown
{
    QString number;
    double x;
    double y;
};
struct Angle 
{
    QString number;
    QString number1;
    QString number2;
    double value;
};
struct Side
{
    QString number1;
    QString number2;
    double value;
};
class TraverseAdjustment : public QWidget
{
    Q_OBJECT
public:
    TraverseAdjustment(QWidget *parent = nullptr);
    ~TraverseAdjustment();
public:
    std::vector<pointKnown> vpKnown;//已知点坐标
    std::vector<Angle> vA;//角度观测值
    std::vector<Side> vS;//边长观测值
    double f;//角度闭合差
    int n;//导线边数
    std::vector<Angle> va;//添加改正数后的角度值
    std::vector<Angle> vAzi;//方位角值
    double Rab, Rcd;//已知起始、终止边对应方位角
    std::vector<Side> vXIn;//坐标增量X
    std::vector<Side> vYIn;//坐标增量Y
    double fx, fy;//坐标增量闭合差
    std::vector<Side> vX;//X增量改正数
    std::vector<Side> vY;//Y增量改正数
    std::vector<pointKnown> vp;//各测点最终坐标值
public slots:
    void onReadDataKnown();
    void onReadDataAngle();
    void onReadDataSide();
    void onAngleClosureError();
    void CalculateAzimuth();
    void onCalIncrement();
    void onCalCorrection();
    void onCalCoordinate();
public:
    double Angle2Radian(double a);
    double DMS2Angle(double a);
    double Radian2Angle(double a);
    double CoordinateInverse(double xa, double ya, double xb, double yb);
private:
    Ui::TraverseAdjustmentClass ui;
};
                


















