《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例

目录

1. 课程实习(设计)内容

2. 实施步骤

2.1. 准备工作

2.2. 编写头文件“Traverse_Calc.h”

2.3. 编写源文件“导线测量计算.cpp”

3. 运行结果

4. 结果分析解读


1. 课程实习(设计)内容

用C语言(或者其他任何编程语言)实现附合导线平差解算程序。

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第1张图片 附合导线略图(与该程序的已知数据无关,仅仅是一个示意图)

2. 实施步骤

2.1. 准备工作

首先,按步骤创建好控制台应用程序,并依次添加源文件和头文件,以备后续写入相应的代码使用,具体步骤如下:

新建Win32控制台应用程序,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第2张图片 新建Win32控制台应用程序

点击下一步,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第3张图片 点击下一步

勾选空项目并点击完成,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第4张图片 勾选空项目并点击完成

新建源文件,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第5张图片 新建源文件

选择C++文件,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第6张图片 选择C++文件

新建头文件,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第7张图片 新建头文件

选择头文件,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第8张图片 选择头文件

这时源文件和头文件都已经创建好了,目前都是空的文件,接下来需要写入相应的代码。下图是创建好的源文件和头文件,如下:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第9张图片 创建好的源文件和头文件

2.2. 编写头文件“Traverse_Calc.h”

在C语言中,头文件是一种包含函数声明、宏定义、结构体和其他类型声明的文件。头文件通常具有.h的扩展名,例如stdio.hstdlib.h等。头文件的作用是提供对函数、变量和其他程序实体的声明,使得这些声明可以在多个源文件中共享并被重复使用。

具体来说,头文件的作用有以下几点:

  1. 提供函数声明:头文件中包含了函数原型的声明,即函数的名称、参数列表和返回类型,这样其他源文件就可以在不需要知道函数具体实现细节的情况下调用这些函数。

  2. 提供类型声明:头文件可以声明结构体、枚举、typedef等自定义类型,从而将类型的定义和使用分离开来,方便程序的模块化设计。

  3. 提供宏定义:头文件中可以包含一些预处理指令,如#define来定义常量、宏函数等,提高程序的可读性和可维护性。

  4. 促进代码重用:通过头文件,可以将需要在多个源文件中共享的声明放在一个地方,避免重复编写相同的声明,提高了代码的重用性和可维护性。

//头文件“Traverse_Calc.h”的代码如下:

//这是一种预处理指令,确保头文件只被包含一次,避免重复定义
#pragma once

//定义了一个常量PI,其值为3.14159265
#define PI 3.14159265

//角度,定义了一个结构体Angle,表示角度,包括度、分和秒
struct Angle
{
	int deg;
	int min;
	double sec;
};

//观测点,定义了一个结构体Point,表示观测点,包括id、x坐标和y坐标
struct Point
{
	int id;
	double x;
	double y;
};

//观测数据,定义了一个结构体ObsData,表示观测数据,包括前点、测站点、后点、角度(度、分、秒)、距离
struct ObsData
{
	int prev;  //前点
	int curr;  //测站点
	int next;  //后点
	int deg;   //角度:度
	int min;   //角度:分
	int sec;   //角度:秒
	double dist;  //距离
};

//函数原型:给出了一系列函数的声明,包括角度和弧度之间的转换、角度计算等操作的函数原型
//这些函数包括初始化函数(init)、输出函数(print)、以及一些角度和观测数据的转换计算函数
double Deg2Rad(double deg) ;
double Rad2Deg(double rad);
double Ang2Deg(Angle a);
int Dms2Deg(double dms, Angle *a);
int Deg2Ang(double deg, Angle *a);
int amu_calc(Point a,Point b,Angle *c);

//初始化
int init_Point(Point *a);
int init_ObsData(ObsData *a);
int init_Angle(Angle *a);

//输出
int print_Angle(Angle a);
int print_KnownPoint(Point a);
int print_AngleAll(Angle *a,int n);
int print_KnownPointAll(Point *a,int n);
int print_ObsData(ObsData a);
int print_ObsDataAll(ObsData *a,int n);

2.3. 编写源文件“导线测量计算.cpp”

在C语言中,源文件是指包含程序源代码的文本文件,通常以.c为扩展名。源文件包含了程序的实现细节,其中包括变量的声明和定义、函数的实现以及其他必要的代码。

源文件的作用主要有以下几个方面:

  1. 定义数据和函数:源文件中可以定义全局变量、局部变量以及各种函数。这些定义提供了程序运行所需的数据存储和处理逻辑。

  2. 实现功能模块:源文件可以将相关的函数和数据组织在一起,形成一个功能模块。通过合理地划分和组织源文件,可以提高程序的可读性、可维护性和可扩展性。

  3. 编译生成目标文件:源文件需要经过编译器的编译处理,生成目标文件。目标文件是机器代码的二进制形式,其中包含了源文件中定义的函数的实际执行逻辑。

  4. 链接生成可执行文件:多个源文件可以被链接器链接在一起,生成最终的可执行文件。链接器将目标文件和库文件合并,解决函数之间的引用关系,使得程序能够正确执行。

  5. 代码复用和模块化开发:源文件可以被其他源文件引用和调用,实现代码的复用和模块化开发。通过将相关的代码封装在不同的源文件中,可以提高代码的可维护性和重用性。

//源文件“导线测量计算.cpp”的代码如下:

//math.h是C语言标准库中的头文件,提供了数学函数的声明和定义,例如三角函数、对数函数等
#include 

//stdio.h是C语言标准库中的头文件,提供了输入输出函数的声明和定义,例如printf、scanf等
#include  

//stdlib.h是C语言标准库中的头文件,提供了一些常用的函数,如内存分配函数malloc、随机数生成函数rand等
#include 

//Traverse_Calc.h是一个自定义的头文件,用双引号括起来的头文件名表示该头文件位于当前目录中
//Traverse_Calc.h包含了一些函数原型和数据结构的声明,供源文件中的函数使用
#include "Traverse_Calc.h" 

//这是一个编译器指令,用于禁用编译器的特定警告
//这里的4996表示禁用特定的警告编号4996,该警告通常与使用不安全的或已弃用的函数相关
#pragma warning(disable:4996)

//度到弧度转换
double Deg2Rad(double deg)
{
    return (deg *PI / 180.0);
}

//弧度到度转换
double Rad2Deg(double rad)
{
    return (rad * 180.0 / PI);
}

//度分秒转换为度
double Ang2Deg(Angle a)
{
    return  (a.deg + a.min / 60.0 + a.sec / 3600.0);
}

//度分秒转换为角度
//输入格式:ddd.mmsss --> ddd.dddd
int Dms2Deg(double dms, Angle *a)
{
//  --> ddd.mmsss--> 
    a->deg = (int)dms;
    a->min = ((int)(dms * 100)) % 100;
    a->sec = (dms - a->deg - a->min / 100.0) * 10000;
    return 0;
}

//度转换到角度
int Deg2Ang(double deg, Angle *a)
{
    a->deg = (int)deg;
    a->min = (int)((deg - a->deg) * 60);
    a->sec =  (deg - a->deg - a->min / 60.0) * 3600 ;
    return 0;
}

//坐标方位角计算
int amu_calc(Point a,Point b,Angle *c)
{
    double dx = b.x-a.x;
    double dy = b.y-a.y ;
    double quad;
	double dist =sqrt(dx*dx+dy*dy); 
	double tmp;
    
    if (dx == 0 && dy>0)
	{ 
		c->deg = 90; return 0;
	}

    if (dx == 0 && dy<0)
	{ 
		c->deg = 270 ; return 0;
	}

    quad = atan(fabs(dy / dx));
    if (dx>0 && dy>0) tmp = quad;               //第一象限
    else if (dx>0 && dy<0) tmp = 2 *PI - quad;  //第四象限
    else if (dx<0 && dy>0) tmp = PI - quad ;    //第二象限
    else if (dx<0 && dy<0) tmp = PI + quad;     //第三象限
    Deg2Ang(Rad2Deg(tmp),c);
    return 0; 
}

//读取观测数据的控制参数
int read_travese_para(char *data,int *nKnown, int *nObs, int *nUnkown, int *order)
{
    FILE *in; 
    if ((in = fopen(data, "rt")) == NULL)
	{
        printf("Can not open %s data file\n", data);
        return -1;
    }
    fscanf(in, "%d %d %d %d", nKnown, nObs, nUnkown, order);
    fclose(in);
    return 0;
}

//读取文件数据
//1已知点数据
//2观测数据
int read_travese_data(char *data, Point *pt,ObsData *obs)
{
       FILE *in;
       int i, nKnown, nUnkown, order,nObs;
       if ((in = fopen(data, "rt")) == NULL)
	   {
             printf("Can not open %s data file\n",data);
             return -1;
       }
       fscanf(in,"%d %d %d %d",&nKnown,&nObs,&nUnkown,&order);
       for (i = 0; i < nKnown; i++)
	   {
              fscanf(in,"%d %lf %lf",&pt[i].id,&pt[i].x,&pt[i].y);
       }
       fscanf(in,"%d",&nObs);
       for (i = 0; i < nObs; i++)
	   {
              fscanf(in,"%d %d %d %d %d %d %lf", &obs[i].prev, &obs[i].curr, &obs[i].next, &obs[i].deg, &obs[i].min, &obs[i].sec, &obs[i].dist);
       }

       return 0 ;
}

//角度相加
int Dms_Add(Angle a, Angle b, Angle *c)
{
      c->deg = a.deg + b.deg;
      c->min = a.min + b.min;
      c->sec = a.sec + b.sec;
      if(c->sec>=60)  //0<=sec<60
	  { 
		  c->sec -= 60 ;  c->min += 1;
	  } 
      if (c->min >= 60)  //0<=min<60
	  { 
		  c->min -= 60; c->deg += 1;
	  }
      if (c->deg >= 360)  //0<=deg<360
	  { 
		  c->deg -= 360;
	  } 

      return 0;
}


//角度值加秒改正
int Dms_Addd(Angle a, double sec, Angle *c)
{
    c->deg=a.deg;
    c->min=a.min; 
    c->sec = a.sec + sec;
    if (c->sec >= 60)  //0<=sec<60
	{ 
		c->sec -= 60;  c->min += 1;
	}
    if (c->sec< 1.0E-3)
	{ 
		c->sec += 60; c->min -= 1;
	}
    if (c->min >= 60)  //0<=min<60
	{ 
		c->min -= 60; c->deg += 1;
	}
    if (c->min <= 0)
	{ 
		c->min += 60;  c->deg -= 1;
	}
    if (c->deg >= 360)  //0<=deg<360
	{
		c->deg -= 360;
	}
    if (c->deg <= 0)
	{
		c->deg += 360;
	}
    return 0;
}

//角度加秒改正,自身代回
int Dms_Addd2(Angle *a, double sec)
{
	a->sec=a->sec+sec;
	if(a->sec>=60)
	{
		a->sec-=60;
		a->min+=1;
	}
	else if(fabs(a->sec)<1.0E-3)
	{
		a->sec+=60;
		a->min-=1;
	}
	if(a->min>=60)
	{
		a->min-=60;
		a->deg+=1;
	}
	else if(a->min<0)
	{
		a->min+=60;
		a->deg-=1;
	}
	if(a->deg>=360)
	{
		a->deg-=360;
	}
	else if(a->deg<0)
	{
		a->deg+=360;
	}

	return 0;
}

//两个角度相减
int Dms_Minus(Angle a,Angle b,Angle *c)
{
	c->deg=a.deg-b.deg+360;
	c->min=a.min-b.min;
	c->sec=a.sec-b.sec;

	if(c->sec<0)
	{
		c->sec+=60;
		c->min-=1;
	}

	if(c->min<0)
	{
		c->min+=60;
		c->deg-=1;
	}

	if(c->deg>=360)
	{
		c->deg-=360;
	}

	if(c->deg<0)
	{
		c->deg+=360;
	}

	return 0;
}

//计算容许误差
double Angle_Allow(int n)
{
	return (60*sqrt(n*1.0));
}

//角度赋值
int Angle_copy(Angle src, Angle *des)
{
       des->deg = src.deg;
       des->min = src.min;
       des->sec = src.sec;
       return 0;
}


//角度求和
int Angle_accu(Angle a, double *sum)
{
      *sum += Ang2Deg(a);
      return 0;
}

//角度改正,计算改正数并输出
int Angle_Cali(Angle st, Angle *Obs, int n,Angle en, double *fb,Angle *Var_Obs)
{
	//st:起始角
	//Obs:观测角
	//n:点数
	//en:结束角
	//fb:容许误差
	//Var_Obs:观测值改正数
	int i;
	Angle A180={180,0,0};
	Angle tmp1={0,0,0};
	Angle tmp2={0,0,0};
	Angle tmp4={0,0,0};
	double tmp3,fsum ;
	double fallow=Angle_Allow(n); 

	fsum =0.0;
	//printf ("\n近似方位角:\n\n");
	for (i = 0; i < n ; i++)
	{
		Angle_accu(Obs[i],&fsum);
	}

	//printf (fsum=%f\n",fsum);
	//printf ("\n\n");
	tmp3 = fsum +  Ang2Deg(st)-(n*180)-Ang2Deg(en);  
	tmp3 *=  3600 ; 
	//printf ("f_closal=%.1f\n",tmp3);
	*fb=tmp3;  //角度闭合差,返回出来
	if (fabs(tmp3) < fallow)
	{ 
		//printf ("\n\n改正观测角\n");
		tmp3 /= n ;
		for(i=0;iid =  99;  //99表示临时的,过渡的点
	a->x  = 0.0;
	a->y  = 0.0;
	return 0;
}

//初始化观测数据
int init_Obsdata(ObsData *a)
{
	a->prev = 77;  //点号编制原则,20个点左右
	a->curr  = 99;
	a->next = 88;
	a->deg = 0;
	a->min = 0;
	a->sec = 0;
	a->dist = 0;
	
	return 0;
}

//初始化角度
int init_Angle(Angle *a)
{
	a->deg = 0 ;
	a->min = 0 ;
	a->sec = 0 ;
	return 0;
}

//输出角度
int print_Angle(Angle a)
{
	printf("%03d %02d %02d\n",a.deg,a.min,(int)(a.sec+0.5));
	return 0;
}

//输出已知点
int print_KnownPoint(Point a)
{
	printf("%2d %10.4f %10.4f\n",a.id,a.x,a.y);
	return 0;
}

//输出全部观测角度
int print_AngleAll(Angle *a, int n)
{
	for (int i = 0; i

在源文件中有一句代码的作用是读取已知数据(观测数据和起算数据),这句代码如下:

char *data="ppt_demo.txt";

已知数据(观测数据和起算数据)如下:

 4 5 3 1
0 843.40 1264.29
1 640.93 1068.44
5 589.97 1307.87
6 793.61 1399.19
 5
 0  1  2 114 17 00 82.17 
 1  2  3 146 59 30 77.28 
 2  3  4 135 11 30 89.64 
 3  4  5 145 38 30 79.84 
 4  5  6 158 00 00  0.00

这些数据的具体含义和解释如下:

 4 5 3 1                   //4是已知点数(分别用来计算定向的坐标方位角),5是观测数据的组数(即测站数),3是未知点数,1表征观测值是左角还是右角
0 843.40 1264.29           //已知点数据,0是点号
1 640.93 1068.44           //已知点数据,1是点号
5 589.97 1307.87           //已知点数据,5是点号
6 793.61 1399.19           //已知点数据,6是点号
 5                         //5是观测数据的组数(即测站数)
 0  1  2 114 17 00 82.17   //第一个测站的观测数据,0  1  2分别表示前视点号,测站点号,后视点号,114 17 00分别表示观测的水平角度分秒数据,82.17表示观测的水平距离数据
 1  2  3 146 59 30 77.28   //第二个测站的观测数据,1  2  3分别表示前视点号,测站点号,后视点号,146 59 30分别表示观测的水平角度分秒数据,77.28表示观测的水平距离数据
 2  3  4 135 11 30 89.64   //第三个测站的观测数据,2  3  4分别表示前视点号,测站点号,后视点号,135 11 30分别表示观测的水平角度分秒数据,89.64表示观测的水平距离数据
 3  4  5 145 38 30 79.84   //第四个测站的观测数据,3  4  5分别表示前视点号,测站点号,后视点号,145 38 30分别表示观测的水平角度分秒数据,79.84表示观测的水平距离数据
 4  5  6 158 00 00  0.00   //第五个测站的观测数据,4  5  6分别表示前视点号,测站点号,后视点号,158 00 00分别表示观测的水平角度分秒数据,0.00是为了保持一致性而添加的一个没有实际意义的数据

运行程序之前只需要把原始(不带注释)的已知数据复制粘贴到一个名为“ppt_demo”的文本文件(.txt文件)中,并把这个文本文件复制粘贴到程序的源文件所在的文件夹目录里,这样源文件的代码就能成功读取这个数据了。不知道源文件在哪里的可以参考下图:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第10张图片 “导线测量计算”文件夹
《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第11张图片 已知数据
《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第12张图片 已知数据的内容

3. 运行结果

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第13张图片 运行结果
《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第14张图片 运行结果

4. 结果分析解读

仅仅运行出来这个结果并不是那么难,只要有一定的C语言基础再花一点时间就应该能做出来,但是如果想要看得懂源代码中的每一个函数所起的作用和运行结果是什么意思,仅有C语言知识是远远不够的,这其中涉及到测绘工程领域的专业应用问题,需要对附合导线的平差计算方法和过程比较熟悉才能把这个程序彻底吃透。

下图是本例中的附合导线示意图,仅仅是示意图,其中已知点的个数、待定点的个数是与本例完全符合的,水平角测量的是左角还是右角在上述的已知数据文件和源代码中已作说明,这里展示的全部假设为左角,再次提醒,仅仅是示意图,如下所示:

《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例_第15张图片 本例附合导线示意图

对于不了解测绘工程专业知识的小伙伴来说,可能会觉得这个运行结果看起来很繁杂甚至很乱,事实上这个运行结果是非常简明和有条理的,该运行结果的具体解读如下所示:

①“程序所有者:”就不多说了。

②紧邻其后的一堆数字正是上述所给出的已知数据。

③“Known Angles”是两个角度,格式为“度 分 秒”,之所以有两个已知的角度数据,是因为本例是一个附合导线的算例,第一个角度是起算边的坐标方位角,第二个角度是附合边的坐标方位角。

④“Observed Amuzith Angles”是根据已知数据中的水平角观测值求算出来的各个导线边的坐标方位角改正后的值,之所以有5个值,是因为如上图的示意图所示,从起始边向附合边推算,最终要附合到终点的那条附合边上,因此一共有5条导线边的坐标方位角数据,起算边的坐标方位角是起算数据,不包含在这5个数据中。从运行结果中可以看出附合边的已知坐标方位角是“024 09 12”,即24°9′12″,而这5个数据的最后一个即推算到附合边时的坐标方位角正是“024 09 12”,因此这是经过角度闭合差分配后的角度数据,也就是经过改正后的数据。

⑤“Approx Coordinate Increments”是x、y坐标增量的计算值,共有4个数据,因为该附合导线需要从1号导线点附合到5号导线点,之间一共经过了4次坐标增量的传递,因此这里有4个数据。

⑥“Statistical Information”是一些在计算过程中需要用到的中间数据,其中“Dist Sum”是导线全长,在分配横纵坐标增量闭合差的时候需要用到(按边长成比例反号分配)。“Dx Sum”是x坐标的总增量,“Dy Sum”是y坐标的总增量,用起算点(1号导线点)的已知坐标数据依次加上横纵坐标增量的总和即可推算出附合终点(5号导线点)的横纵坐标。“fx”是x坐标增量闭合差,“fy”是y坐标增量闭合差,计算方法是用算出来的附合终点(5号导线点)的横纵坐标分别对应与已知数据中的5号导线点的横纵坐标相减,其理论值都是0,但是各种误差是不可避免的,因此实际上这两个值不会是0,但是如果测量精度满足相应要求,那么这两个数值比较接近0。“fs”是导线全长闭合差,计算方法是“fx的平方加上fy的平方再开根号”。“K”是导线全长相对闭合差的分母(一般化成“1/K”的形式),这个数据是评价导线测量精度的一个重要指标,本例设计的限差是“1/4000”,而计算结果显示该附合导线的全长相对闭合差是“1/8275”,显然小于限差,因此该附合导线的测量精度是符合要求的,否则应重新进行外业测量。

⑦“Calibrated Coordinate”是改正后的x、y坐标增量,此时x坐标增量闭合差“fx”、y坐标增量闭合差“fy”已经被平差分配掉了,即“fx”、“fy”现在已经都是0了。

⑧“Final Coordinates”是最终计算得到的3个待求导线点的横纵坐标,前面的是x坐标,后面的是y坐标,最前面的“2”,“3”,“4”代表的是导线点的编号。如上面的示意图所示,带三角形的点是已知点,中间的不带三角形的点是待求点,此程序的功能就是通过已知数据来计算出准确的待求点的横纵坐标,这里已经实现了。

至此,“《数据结构与测绘程序设计》课程实习(设计)——以附合导线平差解算程序为例”已经全部完成了。

你可能感兴趣的:(测绘工程,数据结构与测绘程序设计,课程实习,课程设计)