前言
#include<iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#define PI 3.14
using namespace std;
// 将度分秒转十进制度
double convertDMStoDD(string dms) {
istringstream iss(dms);
double degrees, minutes, seconds;
char ch;
iss >> degrees >> ch >> minutes >> ch >> seconds;
return degrees + minutes / 60.0 + seconds / 3600.0;
}
// 读取文件数据
bool readFileData(const string& filepath, map<string, double>& elevationMap, vector<vector<string>>& data) {
ifstream file(filepath);
if (!file.is_open()) {
cerr << "文件无法打开" << endl;
return false;
}
string line;
// 读取第一行
if (getline(file, line)) {
istringstream iss(line);
string point;
double elevation;
while (iss >> point >> elevation) {
elevationMap[point] = elevation;
}
}
// 读取剩余的行
while (getline(file, line)) {
vector<string> row;
istringstream iss(line);
string value;
while (iss >> value) {
row.push_back(value);
}
data.push_back(row);
}
file.close();
return true;
}
// 计算高程
void calculateElevations(map<string, double>& elevationMap, vector<vector<string>>& data) {
for (auto& row : data) {
string from_point = row[0], to_point = row[1];
double instrument_height = stod(row[2]);
double target_height = stod(row[3]);
double distance = stod(row[4]);
double angle = convertDMStoDD(row[5]);
double elevation_diff = distance * tan(angle * PI / 180.0);
double new_elevation = elevationMap[from_point] + elevation_diff + instrument_height - target_height;
//创建新列高程
elevationMap[to_point] = new_elevation;
row.push_back(to_string(new_elevation));
}
}
// 计算闭合差
double calculateClosureError(const vector<vector<string>>& data, const map<string, double>& elevationMap) {
string start_point = data[0][0];
string end_point = data.back()[1];
double start_elevation = elevationMap.find(start_point)->second;
double end_elevation = elevationMap.find(end_point)->second;
double closure_error = end_elevation - start_elevation;
return closure_error;
}
// 分配闭合差
void distributeClosureError(vector<vector<string>>& data, double closure_error) {
double total_distance = 0.0;
for (const auto& row : data) {
total_distance += stod(row[4]);
}
for (auto& row : data) {
double distance = stod(row[4]);
double correction = -closure_error * distance / total_distance;
double new_elevation = stod(row[6]) + correction;
row.push_back(to_string(new_elevation)); // 添加修正后的高程到行中
}
}
// 检验闭合差分配的正确性
bool verifyClosureErrorDistribution(const vector<vector<string>>& data, double closure_error) {
double sum_of_corrections = 0.0;
for (const auto& row : data) {
sum_of_corrections += stod(row[7]) - stod(row[6]); // 计算每个测段的改正数
}
// 检验闭合差分配后的总和是否等于负的闭合差
return fabs(sum_of_corrections + closure_error) < 1e-6;
}
int main() {
string filename = "数据.txt";
string filepath = "C:/Users/Administrator/Desktop/20240413程序选拔/数据.txt";
map<string, double> elevationMap; //点号和对应的高程
vector<vector<string>> data; // 从文件中读取的数据
// 读取文件数据
if (!readFileData(filepath, elevationMap, data)) {
return 1;
}
// 计算高程
calculateElevations(elevationMap, data);
// 计算闭合差
double closure_error = calculateClosureError(data, elevationMap);
cout << "闭合差: " << closure_error << endl;
// 分配闭合差
distributeClosureError(data, closure_error);
// 检验闭合差分配的正确性
bool verify = verifyClosureErrorDistribution(data, closure_error);
cout << "闭合差分配正确性: " << (verify ? "通过" : "未通过") << endl;
// 打印分配闭合差后的高程
for (const auto& row : data) {
for (const auto& val : row) {
cout << val << " ";
}
cout << endl;
}
return 0;
}
1.c#初上手
2024.04.22,介于有C++基础,直接上手,不看教程,先试试语法,对着我的代码
先写个简单的试试,前几天建议像我一样,慢慢研究,一天研究gui和代码,一天复现,一个项目分成两三天来做。我也会标出日期,下面是我在这天做了什么。大家没时间就按自己节奏来。
1.控制台项目
这个项目涵盖了C#的基础语法,包括类、方法、异常处理、循环、条件语句和输入输出。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace project_4_22
{
//计算阶乘的方法
class Factorial
{
public static long Calculate(int number)
{
if (number < 0)
{
throw new ArgumentException("数字必须为非负数");
//在C#中,throw关键字用于抛出一个异常。当一个异常被抛出时,正常的程序执行流程会被中断,并且程序的控制权会转移到最近的异常处理代码(try-catch块)。
}
long result = 1;
for(int i = 1; i <= number; i++)
{
result *= i;
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
//相当于cout
Console.WriteLine("欢迎使用阶乘计算器:");
while (true)
{
//相当于cin
Console.WriteLine("请输入一个非负整数(输入-1退出):");
string input = Console.ReadLine();
/*int.TryParse 是 C# 中 int 类型的一个静态方法,它用于尝试将字符串解析为整数类型 int。
这个方法非常有用,因为它提供了非破坏性的解析方式,即在解析失败时不会抛出异常。
int.TryParse 方法接受两个参数:
input:要解析的字符串。
out number:一个 out 参数,用于接收解析后的整数结果。*/
if(int.TryParse(input,out int number))
{
if (number == -1)
{
break;
}
else if (number < 0)
{
Console.WriteLine("错误:请输入非负整数。");
}
else
{
/*这段代码是一个try-catch块,它在C#中用于异常处理。
* 在这个特定的例子中,它用于调用Factorial类的Calculate方法,这个方法计算一个整数的阶乘。
* 如果Calculate方法在执行过程中抛出了一个ArgumentException异常,
* catch块将会捕获这个异常,并打印出异常的消息。*/
try
{
//从类中调用函数
long factorial = Factorial.Calculate(number);
Console.WriteLine($"{number}!={factorial}");
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
}
}
else
{
Console.WriteLine("错误,请输入有效整数");
}
}
Console.WriteLine("感谢使用阶乘计算器!按任意键退出。");
Console.ReadKey();
}
}
}
做完这个试试gui的,给不懂得同学解释下,gui就是将黑框中的东西做个漂亮的界面展现出来,类似小程序。
2.gui项目
不同的是需要创建新的项目,而且,需要winform,有xml,css的东西反正不影响应该,先别管,(2024.04.29补充,后面证实就是没必要管,但是我觉的以后要深研究的的话还是要学SQL,我一个月速成的三级数据库寄掉了嘤嘤嘤)不知道看这个的有没有了解过gui,先别管,学完一个点一个点,最后能制成一张网,按下面。
这里比较简单,就不截屏了,从右边拖过来 b u t t o n button button和 t e x t b o x textbox textbox,右键属性改下名字,还是有图吧,按钮叫显示时间,文本框叫 t i m e F i l e d timeFiled timeFiled,专业一点,这里大家不要有个误区,这不像你们学的 M A T L A B MATLAB MATLAB中的 g u i gui gui是通过句柄来操控,只需要讲底层逻辑代码改改就行,我这么说你们可能不太明白,先不用管,下面的学生管理系统就很简洁直接。
还是按钮,点属性,然后看见闪电没,点那个,然后创建一个动作,就这双击就行,他就会跳转到这里,那么我们在button中添加代码,见下面完整代码
然后Form1.cs文件代码是这样
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace form0422
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();//这是控件拖进来就自动生成
}
private void button1_Click(object sender, EventArgs e)
{
string timestr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");//获取当前时间
this.timeField.Text = timestr;//显示出来
}
}
}
简单吧,这里的细节就是,编译器自动给我们生成刚刚操作的代码,在Form1下属文件中
运行试试,我就不截图了,可以直接显示时间了,现在已经学会了控制台和gui,让我们上手试试,写个这个试试
开个玩笑,我也写不出来目前,先剖析下竞赛开源的代码,我分享出来,细研究。
3.测绘程序设计
单看好像很难,目前看实际就是很难
2024.4.23
我到最后会把所有文件发出来的
2.学生管理系统
只是写个阶乘的小程序对于了解语法来说还是太过牵强,我们来写个最经典的学生管理系统试试,这里就包括简单类的定义了。
1.控制台
using System;
using System.Collections.Generic;
using System.IO;
namespace StudentManagementSystem
{
class Program
{
static void Main(string[] args)
{
List<Student> students = new List<Student>();
// 欢迎界面
Console.WriteLine("欢迎使用学生管理系统");
while (true)
{
Console.WriteLine("\n请选择一个操作:");
Console.WriteLine("1. 添加学生");
Console.WriteLine("2. 显示所有学生");
Console.WriteLine("3. 保存到文件");
Console.WriteLine("4. 从文件加载");
Console.WriteLine("5. 修改学生信息");
Console.WriteLine("6. 删除学生信息");
Console.WriteLine("7. 退出");
string choice = Console.ReadLine();
switch (choice)
{
case "1":
AddStudent(students);
break;
case "2":
DisplayStudents(students);
break;
case "3":
SaveStudentsToFile(students);
break;
case "4":
LoadStudentsFromFile(students);
break;
case "5":
EditStudentInfo(students);
break;
case "6":
DeleteStudent(students);
break;
case "7":
return;
default:
Console.WriteLine("无效的选择,请重新输入。");
break;
}
}
}
static void AddStudent(List<Student> students)
{
Console.WriteLine("请输入学生信息:");
Console.Write("学号:");
string id = Console.ReadLine();
Console.Write("姓名:");
string name = Console.ReadLine();
Console.Write("性别(输入'男'或'女'):");
string gender = Console.ReadLine();
Console.Write("手机号:");
string phoneNumber = Console.ReadLine();
Student newStudent = new Student { Id = id, Name = name, Gender = gender, PhoneNumber = phoneNumber };
students.Add(newStudent);
Console.WriteLine("学生信息已添加");
}
static void DisplayStudents(List<Student> students)
{
Console.WriteLine("\n学生列表:");
for (int i = 0; i < students.Count; i++)
{
Console.WriteLine($"学号:{students[i].Id}, 姓名:{students[i].Name}, 性别:{students[i].Gender}, 手机号:{students[i].PhoneNumber}");
}
}
static void SaveStudentsToFile(List<Student> students)
{
using (StreamWriter sw = new StreamWriter("students.txt"))
{
foreach (Student student in students)
{
sw.WriteLine($"{student.Id},{student.Name},{student.Gender},{student.PhoneNumber}");
}
}
Console.WriteLine("学生信息已保存到文件。");
}
static void LoadStudentsFromFile(List<Student> students)
{
students.Clear();
if (File.Exists("students.txt"))
{
using (StreamReader sr = new StreamReader("students.txt"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
string[] parts = line.Split(',');
Student student = new Student
{
Id = parts[0],
Name = parts[1],
Gender = parts[2],
PhoneNumber = parts[3]
};
students.Add(student);
}
}
Console.WriteLine("学生信息已从文件加载。");
}
else
{
Console.WriteLine("文件不存在,无法加载学生信息。");
}
}
static void EditStudentInfo(List<Student> students)
{
Console.Write("请输入要修改的学生学号:");
string id = Console.ReadLine();
Student studentToEdit = students.Find(s => s.Id == id);
if (studentToEdit != null)
{
Console.WriteLine("请输入新的学生信息:");
Console.Write("姓名(留空保持不变):");
string name = Console.ReadLine();
Console.Write("性别(留空保持不变):");
string gender = Console.ReadLine();
Console.Write("手机号(留空保持不变):");
string phoneNumber = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(name))
{
studentToEdit.Name = name;
}
if (!string.IsNullOrWhiteSpace(gender))
{
studentToEdit.Gender = gender;
}
if (!string.IsNullOrWhiteSpace(phoneNumber))
{
studentToEdit.PhoneNumber = phoneNumber;
}
Console.WriteLine("学生信息已更新。");
}
else
{
Console.WriteLine("未找到该学号的学生。");
}
}
static void DeleteStudent(List<Student> students)
{
Console.Write("请输入要删除的学生学号:");
string id = Console.ReadLine();
Student studentToDelete = students.Find(s => s.Id == id);
if (studentToDelete != null)
{
students.Remove(studentToDelete);
Console.WriteLine("学生信息已删除。");
}
else
{
Console.WriteLine("未找到该学号的学生。");
}
}
}
class Student
{
public string Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string PhoneNumber { get; set; }
}
}
很简单明了的程序,熟悉下语法就行,以后练得机会多着呢。
2.gui
2024.4.24做个gui试试
拖进来的控件就很简单,大家根据Form.Design.cs文件中的试着自己找找。
部分代码,完整文件可以在资源里面找
using Af.Common;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace studentsManagerSystem
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); // 初始化窗体组件
// 向性别下拉列表框中添加性别选项
sexField.Items.Add("女");
sexField.Items.Add("男");
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
//保存按钮
private void saveButton_Click(object sender, EventArgs e)
{
//创建一个新的Student对象
Student stu = new Student();
//从学号文件框获取并设置学生学号
stu.Id = Convert.ToInt32(idField.Text.Trim());
//从姓名文件框获取并设置学生姓名
stu.Name = nameField.Text.Trim();
//性别下拉框选择性别
stu.Sex = (sexField.SelectedIndex == 1);
//从手机号文件框获取并设置学生手机号
stu.Phone = phoneField.Text.Trim();
//将Student对象转JSON字符串
string jsonStr = JsonConvert.SerializeObject(stu, Formatting.Indented);
//保存
AfTextFile.Write("student.txt", jsonStr, AfTextFile.UTF8);
MessageBox.Show("操作成功");
}
}
}
到今天也是把winform教程浅浅全部看完了
2024.4.25,今天分析下代码,感觉还蛮简单,明天周五一节课试着自己写写试试,大家也尽量自己写,控制在2h内。
3.出租车
(程序设计书的第一题)
这个补充一下,建议先看第二题,我也是研究之后才发现第二题比第一题简单
2024.04.30补
原来源代码中的设计器如果没有的话,可以自己创建个项目,复制Form1中的代码就可以显示了.
1.源程序说明
写代码不要上来就写,可以画个架构,先把原理看懂,也方便优化
参考源程序编程语言为C#,项目名称为TaxiData。项目中主要包含以下类:
( 1 ) FileHelper :源文件读取,以及计算结果输出;
( 2 )Epoch.cs:基本数据结构,包含车辆标识、运营状态、时间、x坐标分量、y坐标分量等信息;
( 3 )Algo.cs:时间转化为简化儒略日算法;
( 4 ) Session. cs:计算每个时段的长度、速度和方位角;
( 5 )SessionList. cs:输出所有时段的速度、方位角、累计距离和首尾直线距离。
看原理的话,就是选择出出勤的出租车,两个相邻点算出角和距离,然后不停地迭代,取得的和就是累计距离,第一个和最后一个点就可以算首尾直线距离,就是位移。
2.gui制作
先拖进来toolstrip,然后从源码中把图片复制添加到资源那个文件中Resources,然后即可以做界面了,别忘了添加富文本框
还有Text都要改,设置成图片加文字的,然后点击闪电,设置动作,这点因为不熟,整了好长时间(苦笑)
别急着设置按钮动作,目前我研究的是先写底层代码比较好,可以将逻辑画出来
3.code
最先做的就是这个日转化
Algo.cs
这见多了就知道是algorithm算法的简称,还是很好理解的,里面定义这个儒略日算法MJD,也没啥好说的,就是对着公式敲,还是用GPT加个解释吧,别忘了时间差,人家也提醒了
//源代码
public static double Mjd(int year, int month, int day, int hour, int min, int sec, int timeZone)
{
double mjd = -678987 + 367.0 * year;
mjd -= Convert.ToInt32(7.0 / 4.0 * (year + Convert.ToInt32((month + 9.0) / 12.0)));
mjd += Convert.ToInt32((275.0 * month) / 9.0);
mjd += day + (hour - timeZone) / 24.0 + min / 1440.0 + sec / 86400.0;
return mjd;
}
//加个解释
public static double Mjd(int year, int month, int day, int hour, int min, int sec, int timeZone)
{
// 初始化mjd变量,使用基础值-678987,这是1858年11月17日的MJD值。
double mjd = -678987;
// 计算年份的贡献,考虑到闰年的影响。
// 367.0 * year计算从1858年以来的整年数。
// Convert.ToInt32(7.0 / 4.0 * (year + Convert.ToInt32((month + 9.0) / 12.0)))
// 是一个闰年调整因子,它计算从1858年以来闰年的数量。
mjd += 367.0 * year;
mjd -= Convert.ToInt32(7.0 / 4.0 * (year + Convert.ToInt32((month + 9.0) / 12.0)));
// 计算月份的贡献。
// Convert.ToInt32((275.0 * month) / 9.0)是基于月份的调整因子。
mjd += Convert.ToInt32((275.0 * month) / 9.0);
// 加上日期的贡献。
mjd += day;
// 计算小时、分钟和秒的贡献,同时考虑时区的影响。
// (hour - timeZone) / 24.0 是时区调整,将本地时间转换为UTC时间。
// min / 1440.0 和 sec / 86400.0 分别是将分钟和秒转换为日的分数。
mjd += (hour - timeZone) / 24.0 + min / 1440.0 + sec / 86400.0;
// 返回计算出的MJD值。
return mjd;
}
Epoch.cs
算法定义好了以后,就可以写Epoch.cs了,为啥叫Epoch,搞机器学习是吧(滑稽笑)
class Epoch
{
// Id属性是一个字符串,用于标识Epoch对象。
public string Id;
// Status属性是一个整数,用于表示Epoch的状态。
public int Status;
// TimeStr属性是一个字符串,用于存储Epoch的时间字符串。
public string TimeStr;
// Mjd属性是一个双精度浮点数,用于存储Epoch的MJD值。
public double Mjd;
// x属性是一个双精度浮点数,用于存储Epoch的x坐标。
public double x;
// y属性是一个双精度浮点数,用于存储Epoch的y坐标。
public double y;
// Parse方法用于解析给定的字符串,并设置Epoch对象的属性。
public void Parse(string line)//这里的参数在下个文件中
{
try
{
// 使用逗号作为分隔符,将字符串line分割成数组buf。
var buf = line.Split(',');
// 设置Id属性,从buf数组的第一个元素获取值。
Id = buf[0];
// 设置Status属性,从buf数组的第二个元素获取值,并转换为整数。
Status = Convert.ToInt32(buf[1]);
// 设置TimeStr属性,从buf数组的第三个元素获取值。
TimeStr = buf[2];
// 设置x属性,从buf数组的第四个元素获取值,并转换为双精度浮点数。
x = Convert.ToDouble(buf[3]);
// 设置y属性,从buf数组的第五个元素获取值,并转换为双精度浮点数。
y = Convert.ToDouble(buf[4]);
// 调用GetMjd方法计算Mjd值。
GetMjd();
}
catch (Exception ex)
{
// 如果发生异常,抛出异常。
throw ex;
}
}
// GetMjd方法用于计算Epoch的MJD值。
private void GetMjd()
{
try
{
// 设置时区为8小时(假设为UTC+8)。
int timeZone = 8;
// 解析TimeStr属性中的日期和时间,并转换为整数。
//这里是提取字符它是原始字符串的一个子字符串,从 startIndex 开始,直到 startIndex + length - 1。然后,这些子字符串被转换为整数类型。
int year = Convert.ToInt32(TimeStr.Substring(0, 4));
int month = Convert.ToInt32(TimeStr.Substring(4, 2));
int day = Convert.ToInt32(TimeStr.Substring(6, 2));
int hour = Convert.ToInt32(TimeStr.Substring(8, 2));
int min = Convert.ToInt32(TimeStr.Substring(10, 2));
int sec = Convert.ToInt32(TimeStr.Substring(12, 2));
// 调用Algo类的Mjd方法计算MJD值。
Mjd = Algo.Mjd(year, month, day, hour, min, sec, timeZone);
}
catch (Exception ex)
{
// 如果发生异常,抛出异常。
throw ex;
}
}
}
Session.cs
然后就是我们的Session了,这些解释都是GPT浅浅盖上一层,不懂得要自己想想,或者问问,还是比较好理解的
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TaxiData
{
/// <summary>
/// 描述Session类的简要说明。
/// </summary>
class Session
{
// Sn属性是一个整数,用于表示Session的顺序号。
public int Sn; // 顺序号
// StartMjd和EndMjd属性是双精度浮点数,分别表示Session的开始和结束MJD值。
public double StartMjd, EndMjd;
// Length属性是一个双精度浮点数,表示Session的长度。
public double Length;
// Velocity属性是一个双精度浮点数,表示Session的速度。
public double Velocity;
// Azimuth属性是一个双精度浮点数,表示Session的方位角。
public double Azimuth;
// 构造函数,接受两个Epoch对象start和end作为参数。
public Session(Epoch start, Epoch end)
{
// 设置Sn属性为0。
Sn = 0;
// 设置StartMjd属性为start对象的Mjd值。
StartMjd = start.Mjd;
// 设置EndMjd属性为end对象的Mjd值。
EndMjd = end.Mjd;
// 调用GetLength方法计算长度。
GetLength(start, end);
// 调用GetVelocity方法计算速度。
GetVelocity();
// 调用GetAzimuth方法计算方位角。
GetAzimuth(start, end);
}
// 私有方法,用于计算方位角。
private void GetAzimuth(Epoch start, Epoch end)
{
// 定义一个小的浮点数,用于判断dx和dy是否接近零。
double eps = 1e-5;
// 计算dx和dy。
double dx = end.x - start.x;
double dy = end.y - start.y;
// 如果dx接近零,判断dy的符号来确定方位角。
if (Math.Abs(dx) < eps)
{
if (Math.Abs(dy) < eps)
Azimuth = 0;
else if (dy > 0)
Azimuth = 0.5 * Math.PI;
else
{
Azimuth = 1.5 * Math.PI;
}
}
// 如果dx不接近零,使用Math.Atan2计算方位角,并考虑dx的符号。
else
{
Azimuth = Math.Atan2(dy, dx);
if (dx < 0)
{
Azimuth += Math.PI;
}
}
// 将方位角转换为度数。
Azimuth *= 180 / Math.PI;
// 如果方位角小于0,调整到0到360度之间。
if (Azimuth < 0)
{
Azimuth += 2 * Math.PI;
}
if (Azimuth > 2 * Math.PI)
{
Azimuth -= 2 * Math.PI;
}
}
// 私有方法,用于计算速度。
private void GetVelocity()
{
// 计算时间差,以小时为单位。
double dt = (EndMjd - StartMjd) * 24;
// 计算速度,单位为km/hour。
Velocity = Length / dt;
}
// 私有方法,用于计算长度,单位为km。
private void GetLength(Epoch start, Epoch end)
{
// 计算dx和dy。
double dx = end.x - start.x;
double dy = end.y - start.y;
// 计算长度,单位为km。
Length = Math.Sqrt(dx * dx + dy * dy) / 1000.0;
}
// 重写ToString方法,返回Session对象的字符串表示。
public override string ToString()
{
// 创建一个字符串变量line,用于存储输出信息。
string line = $"{Sn:00}, {StartMjd:f5}-{EndMjd:f5}, ";
// 添加速度和方位角到line中。
line += $"{Velocity:f3}, {Azimuth:f3}";
// 返回line字符串。
return line;//这里就是上面那个文件出现的line
}
}
}
以及
SessionList.cs
class SessionList
{
// Data属性是一个List<Session>类型的集合,用于存储Session对象。
public List<Session> Data = new List<Session>();
// TotalLength属性用于存储计算出的总长度。
public double TotalLength;
// DirctLength属性用于存储计算出的直线距离。
public double DirctLength;
// 构造函数,接受一个List<Epoch>类型的参数epoches。
public SessionList(List<Epoch> epoches)
{
// 遍历epoches集合,创建Session对象,并添加到Data集合中。
for (int i = 0; i < epoches.Count - 1; i++)
{
Session s = new Session(epoches[i], epoches[i + 1]);
s.Sn = i; // 设置Session对象的序列号。
Data.Add(s);
}
// 调用GetTotalLength方法计算总长度。
GetTotalLength();
// 调用GetDirctLength方法计算直线距离。
GetDirctLength(epoches);
}
// 私有方法,用于计算直线距离。
private void GetDirctLength(List<Epoch> epoches)
{
// 获取epoches集合的元素数量。
int n = epoches.Count;
// 创建一个新的Session对象,使用epoches的第一个和最后一个元素。
Session s = new Session(epoches[0], epoches[n - 1]);
// 将计算出的距离赋值给DirctLength属性。
DirctLength = s.Length;//第一个和最后一个算出来物理意义上的位移
}
// 私有方法,用于计算总长度。
private void GetTotalLength()
{
// 初始化TotalLength为0。
TotalLength = 0;
// 遍历Data集合中的每个Session对象,累加它们的长度。
foreach (var d in Data)
{
TotalLength += d.Length;
}
}
// 重写ToString方法,返回SessionList对象的字符串表示。
public override string ToString()
{
// 创建一个字符串变量line,用于存储输出信息。
string line = "------------速度和方位角计算结果----------\r\n";
// 遍历Data集合中的每个Session对象,将它们的字符串表示添加到line中。
foreach (var d in Data)
{
line += d.ToString() + "\r\n";
}
// 添加距离计算结果的标题。
line += "------------距离计算结果-----------------\r\n";
// 添加总长度和直线距离的值到line中。
line += $"累积距离:{TotalLength:f3} (km)\r\n";
line += $"首尾直线距离: {DirctLength:f3} (km)";
// 返回line字符串。
return line;
}
}
最后是定义了我们的文件操作的
FileHelper.cs
class FileHelper
{
// 静态类,不需要实例化。
// Read方法用于读取文件,并将标识为Id的记录列表返回。
public static List<Epoch> Read(string Id, string pathname)
{
// 创建一个空列表data,用于存储读取的Epoch对象。
var data = new List<Epoch>();
// 尝试块用于捕获并处理可能发生的异常。
try
{
// 创建一个StreamReader对象,用于读取指定路径的文件。
var reader = new StreamReader(pathname);
// 读取并忽略文件的第一行,通常是标题或元数据。
reader.ReadLine();
// 循环读取文件中的每一行,直到到达文件末尾。
while (!reader.EndOfStream)
{
// 读取文件中的下一行。
string line = reader.ReadLine();
// 检查读取的行是否为空。
if (line.Length > 0)
{
// 创建一个新的Epoch对象。
var ep = new Epoch();
// 调用Epoch对象的Parse方法解析行。
ep.Parse(line);
// 检查Epoch对象的Id是否与指定的Id相匹配。
if (Id.Equals(ep.Id))
{
// 如果匹配,将Epoch对象添加到data列表中。
data.Add(ep);
}
}
}
// 关闭StreamReader。
reader.Close();
}
catch (Exception ex)
{
// 如果发生异常,抛出异常。
throw ex;
}
// 返回读取的Epoch对象列表。
return data;
}
// Write方法用于将SessionList对象的数据写入文件。
public static void Write(SessionList data, string filename)
{
// 尝试块用于捕获并处理可能发生的异常。
try
{
// 创建一个StreamWriter对象,用于写入指定路径的文件。
var writer = new StreamWriter(filename);
// 调用SessionList对象的ToString方法获取字符串表示,并写入文件。
writer.Write(data.ToString());
// 关闭StreamWriter。
writer.Close();
}
catch (Exception ex)
{
// 如果发生异常,抛出异常。
throw ex;
}
}
}
打开动作,计算动作等代码如下解释,这个最后写
看了半天的报错,原来少创了Session data;可恶,回来记得加上
private SessionList Data;
// 当用户点击工具栏上的“Open”按钮时调用此方法
private void toolOpen_Click(object sender, EventArgs e)
{
// 显示一个打开文件对话框,让用户选择要打开的文件
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// 调用FileHelper类的Read方法,读取用户选择的文件
// openFileDialog1.FileName是用户选择的文件的完整路径
var epochs = FileHelper.Read("T2", openFileDialog1.FileName);
// 使用读取的数据创建一个新的SessionList对象,并将其赋值给Data变量
Data = new SessionList(epochs);
// 在richTextBox1控件中显示一条消息,提示用户数据读取完成
richTextBox1.Text = "数据读取完成!";
}
}
// 当用户点击工具栏上的“Calculate”按钮时调用此方法
private void toolCal_Click(object sender, EventArgs e)
{
// 将Data对象转换为字符串,并在richTextBox1控件中显示
// 假设SessionList类重写了ToString方法,以提供数据的字符串表示
richTextBox1.Text = Data.ToString();
}
// 当用户点击工具栏上的“Save”按钮时调用此方法
private void toolSave_Click(object sender, EventArgs e)
{
// 显示一个保存文件对话框,让用户选择要保存的文件位置和名称
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
// 调用FileHelper类的Write方法,将Data对象的数据写入到用户指定的文件
FileHelper.Write(Data, saveFileDialog1.FileName);
}
}
// 当用户点击工具栏上的“Help”按钮时调用此方法
private void toolHelp_Click(object sender, EventArgs e)
{
// 创建一个包含版权信息的字符串
string copyright = "《测绘程序设计试题集(试题1 出租车数据计算)》配套程序\n作者:李英冰\n";
copyright += "河南理工大学测绘学院\r\nEMAIL: 2969029950@qq.com\r\n2024.4.25";
richTextBox1.Text = copyright;
}
最后呢,改一下Form1的属性就大功告成了,还是蛮有成就感的,logo忘记换了,无伤大雅。
4.反距离加权插值
1.原理剖析
单看题目好像是个算法的实现,目前感觉比较简单
我靠,看完之后就是简单,比第一题简单,早知道先看第二题下手了,应该和计算机二级水平差不多
2.gui制作
和上面那个一样,改个名字,这个带logo了,没有忘
3.code
Point.cs
这就是定义站点类,设置基本的成员变量及从文件中读取到的line,分割成不同的部分,然后重写ToString,让gui显示数据更好看,其实用表格也行,我感觉啊
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IDW
{
class Point
{
// 公共成员,表示点的ID
public string Id;
// 公共成员,表示点的X坐标
public double X;
// 公共成员,表示点的Y坐标
public double Y;
// 公共成员,表示点的高程
public double H;
// 公共成员,表示点到另一个点的距离
public double Dist;
// 无参构造函数,初始化所有属性为0
public Point()
{
X = Y = H = Dist = 0;
}
// 有参构造函数,初始化ID、X坐标和Y坐标
public Point(string id, double x, double y)
{
Id = id;
X = x;
Y = y;
}
// Parse方法,用于从字符串解析点的属性
public void Parse(string line)
{
var buf = line.Split(','); // 以逗号分隔字符串
Id = buf[0]; // 设置ID
X = Convert.ToDouble(buf[1]); // 设置X坐标
Y = Convert.ToDouble(buf[2]); // 设置Y坐标
H = Convert.ToDouble(buf[3]); // 设置高程
}
// 重写ToString方法,返回点的字符串表示
public override string ToString()
{
return $"{Id} {X:F3} {Y:F3} {H:F3}"; // 格式化输出点的属性
}
}
}
DataEntity.cs
DataEntity
类是一个自定义的数据结构,用于存储和管理一系列的Point
对象。就是创建了个列表,理解成C++中的vector就可以
- 存储点数据:
DataEntity
类包含一个List<Point>
类型的成员Data
,用于存储多个Point
对象。每个Point
对象代表一个地理空间中的一个点,包含该点的ID、X坐标、Y坐标、高程(H)和距离(Dist)信息。 - 管理数据点:
DataEntity
类提供了一系列方法来管理Data
列表中的Point
对象,例如Add
方法用于添加新的Point
对象,索引器允许通过索引访问和修改Data
列表中的Point
对象。 - 提供数据访问:通过
Count
属性,可以轻松获取Data
列表中点的数量。此外,索引器允许开发者通过索引直接访问特定的Point
对象。 - 格式化输出:
ToString
方法被重写,以便于生成一个格式化的字符串,可以用于输出数据点的信息。这对于调试或记录数据非常有用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IDW
{
class DataEntity
{
// 公共成员,用于存储Point对象的列表
public List<Point> Data;
// 只读属性,返回Data列表中元素的个数
public int Count => Data.Count;
// 构造函数,初始化Data列表
public DataEntity()
{
Data = new List<Point>();
}
// 公共方法,用于向Data列表中添加一个Point对象
public void Add(Point pt)
{
Data.Add(pt);
}
//索引器,允许通过索引访问和修改Data列表中的Point对象
public Point this[int i]
{
get { return Data[i]; } // 获取指定索引处的Point对象
set { Data[i] = value; } // 设置指定索引处的Point对象
}
// 重写ToString方法,返回Data列表中所有Point对象的字符串表示
public override string ToString()
{
string res = "测站 X(m) Y(m) H(m)\n"; // 初始化结果字符串
foreach (var d in Data)
{
res += d.ToString() + "\n"; // 将每个Point对象的字符串表示添加到结果字符串中
}
return res; // 返回结果字符串
}
}
}
FileHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
// 命名空间
namespace IDW
{
// FileHelper类,提供文件读写的辅助方法
class FileHelper
{
// Read方法,用于从指定文件读取数据到DataEntity对象
public static DataEntity Read(string filename)
{
DataEntity data = new DataEntity(); // 创建一个新的DataEntity实例
try
{
var reader = new StreamReader(filename); // 创建一个StreamReader实例用于读取文件
while (!reader.EndOfStream) // 当未到达文件末尾时继续读取
{
string line = reader.ReadLine(); // 读取一行文本
if (line.Length > 0) // 如果行不为空
{
Point pt = new Point(); // 创建一个新的Point实例
pt.Parse(line); // 解析行文本到Point实例
data.Add(pt); // 将解析后的Point添加到DataEntity实例
}
}
reader.Close(); // 关闭StreamReader
}
catch (Exception ex) // 捕获并处理可能发生的异常
{
throw ex; // 抛出异常
}
return data; // 返回填充了数据的DataEntity实例
}
// Write方法,用于将文本写入到指定文件
public static void Write(string text, string filename)
{
try
{
var writer = new StreamWriter(filename); // 创建一个StreamWriter实例用于写入文件
writer.Write(text); // 写入文本
writer.Close(); // 关闭StreamWriter
}
catch (Exception ex) // 捕获并处理可能发生的异常
{
throw ex; // 抛出异常
}
}
}
}
Algo.cs
算法实现文件,看完之后只能说C#真的灵活,比C++造轮子造的真好
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IDW
{
class Algo
{
// DataEntity类型的成员变量,用于存储数据点
DataEntity Data;
// N定义了参与计算的最近邻点的数量
private int N = 5;
// 构造函数,初始化DataEntity数据以及N的值
public Algo(DataEntity data, int n)
{
Data = data;
N = n;
}
// 计算两点之间的距离
public double Distance(Point p1, Point p2)
{
double dx = p1.X - p2.X; // x坐标差
double dy = p1.Y - p2.Y; // y坐标差
double ds = Math.Sqrt(dx * dx + dy * dy); // 计算欧氏距离
return ds;
}
// IDW算法的主要实现,返回一个字符串,包含插值点的信息和计算的值
public string Idw(Point pt)
{
string res = $"{pt.Id} {pt.X:f3} {pt.Y:f3} "; // 初始化结果字符串
for (int i = 0; i < Data.Count; i++)
{
double d = Distance(Data[i], pt); // 计算插值点与数据集中每个点的距离
Data[i].Dist = d; // 将距离存储在数据点的Dist属性中
}
var dt = Sort(); // 对数据点根据距离进行排序
double H = GetH(dt); // 根据排序后的数据点计算插值
res += $" {H:f3} "; // 将计算结果添加到结果字符串中
for (int j = 0; j < N; j++)
{
res += $"{dt[j].Id} "; // 将参与计算的最近邻点的ID添加到结果字符串中
}
return res; // 返回结果字符串
}
// 根据排序后的数据点计算插值
private double GetH(DataEntity dt)
{
double over = 0, under = 0;
for (int i = 0; i < N; i++)
{
over += dt[i].H / dt[i].Dist; // 分子部分,加权高度除以距离
under += 1 / dt[i].Dist; // 分母部分,距离的倒数之和
}
return over / under; // 计算最终的插值结果
}
// 对数据点根据距离进行排序
DataEntity Sort()
{
DataEntity dt = Data;
for (int i = 0; i < Data.Count; i++)
{
for (int j = i; j < Data.Count; j++)
{
if (dt[i].Dist > dt[j].Dist) // 如果前一个点的距离大于后一个点的距离
{
var pt = dt[i]; // 交换两个点
dt[i] = dt[j];
dt[j] = pt;
}
}
}
return dt; // 返回排序后的数据点集
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// 命名空间
namespace IDW
{
// Form1类,继承自Form,表示应用程序的主窗口
public partial class Form1 : Form
{
// 公共成员,用于存储插值结果字符串
public string result;
// DataEntity实例,用于存储和管理数据点
DataEntity Data = new DataEntity();
// 构造函数,初始化窗体组件
public Form1()
{
InitializeComponent();
}
// 工具栏中“打开”按钮的点击事件处理方法
private void toolOpen_Click(object sender, EventArgs e)
{
// 显示文件打开对话框,如果用户选择了一个文件,则读取数据并显示在richTextBox1中
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
Data = FileHelper.Read(openFileDialog1.FileName);
richTextBox1.Text = Data.ToString();
}
}
// 工具栏中“计算”按钮的点击事件处理方法
private void toolCal_Click(object sender, EventArgs e)
{
// 初始化结果字符串
string res = "点名 X(m) Y(m) H(m) 参与插值的点列表\r\n";
// 创建Algo实例,用于执行IDW算法
Algo go = new Algo(Data, 5);
// 创建四个待插值的点
var Q1 = new Point("Q1", 4310, 3600);
var Q2 = new Point("Q2", 4330, 3600);
var Q3 = new Point("Q3", 4310, 3620);
var Q4 = new Point("Q4", 4330, 3620);
// 对每个点执行IDW算法,并将结果添加到结果字符串中
res += go.Idw(Q1) + "\r\n";
res += go.Idw(Q2) + "\r\n";
res += go.Idw(Q3) + "\r\n";
res += go.Idw(Q4) + "\r\n";
// 更新公共成员result和richTextBox1的文本
result = res;
richTextBox1.Text = res;
}
// 工具栏中“帮助”按钮的点击事件处理方法
private void toolHelp_Click(object sender, EventArgs e)
{
// 初始化版权信息字符串
string copyright = "《测绘程序设计试题集(试题9 反距离加权插值)》配套程序\n作者:李英冰\n";
copyright += "河南理工大学测绘学院\r\n卢文豪EMAIL: 2969029950@qq.com\r\n2024.4.26";
// 显示版权信息在richTextBox1中
richTextBox1.Text = copyright;
}
// 工具栏中“保存”按钮的点击事件处理方法
private void toolSave_Click(object sender, EventArgs e)
{
// 显示文件保存对话框,如果用户指定了文件名,则将结果保存到文件中
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
FileHelper.Write(result, saveFileDialog1.FileName);
}
}
}
}
完成,还是比较好懂的,如果转不过来,就右键查看代码定义研究或者问
第一次培训的话,讲的C#基础,没必要听。自己练吧家人们,趁这个功夫自己写了第二题
2024.04.27
今天来研究一下竞赛题,也是我这一年的比赛题目之一,这个要用到.NET的内置画图库先来看看,接下来的几道题道都是我这年的赛题。
5.五点光滑法进行曲线拟合
(此处是有简化,但是目录没显示出来)示例代码与书上原理不同
1.原理
这个原理的话,就这里解释的还算清晰,直接对着研究就行了,别的说的都没这个好懂。原理需要深入研究和理解。我觉得这里有一些需要注意的点,每次需要确定五个点,那么首尾两个点就需要补充确切的点。补充的方法是得出近似的ABCD点。接下来,我们需要计算其他参数。通过五个点的xy坐标,我们可以计算出参数ab和w,从而得出五个点中中间点的方向梯度。然后,我们需要构建一个三次拟合曲线,并计算出其参数EF。同样地,我们可以通过点坐标与梯度来计算出参数。三次拟合的公式中,自变量是z,其取值范围在0到1之间。将z的值带入公式中,我们可以在两个点之间得到一个新的坐标点,这样就可以得到m个坐标点。最后,将这些坐标点连接起来,就可以得到拟合曲线。看不懂就多看一会,对照着公式。
梯度这里还是比较好理解的,就是五个点的横纵坐标得出的δ算梯度,指示出方向
文件的话就这些
来看下文件中都是什么
- F o r m 1 Form1 Form1:肯定是定义各种空间的动作
- M y P o i n t MyPoint MyPoint:创建一个点类,包含Id,x,y,并可以算出r就是两点间的距离
- M y c u r v e Mycurve Mycurve:定义拟合时需要的系数类
- P o i n t T o C u r v e PointToCurve PointToCurve:算法实现
2.gui制作
他这个gui稍微复杂的就是菜单栏和工具栏的复用,还有下面页面的切换
我们可以看Design的代码
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend();
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting