<think>
我们将在给定的窗体代码基础上,创建一个水准平差计算器。这个计算器将包含以下功能:
1. 用户界面:使用DataGridView来输入已知点、未知点和观测数据。
2. 计算功能:实现最小二乘法进行水准网平差。
3. 结果显示:在另一个DataGridView中显示平差后的高程和精度。
注意:由于要求使用.NET Framework 4.8,我们将使用Windows Forms应用程序。
我们将扩展Form1类,添加必要的控件和事件处理方法。
步骤:
1. 在窗体上添加多个DataGridView控件用于输入已知点、未知点和观测数据。
2. 添加按钮触发平差计算。
3. 添加一个DataGridView用于显示结果。
4. 实现最小二乘平差算法。
由于代码较长,我们将分步骤构建。
注意:为了简化,我们假设观测值之间相互独立,权阵为单位阵(或者根据距离定权)。这里我们使用间接平差。
我们将创建以下数据结构:
- 已知点:点名,高程
- 未知点:点名
- 观测值:起点,终点,高差,距离(用于定权)
平差步骤:
1. 确定未知数:未知点的高程。
2. 列出误差方程:每个观测值对应一个方程。
3. 组成法方程。
4. 解法方程,得到未知数改正数。
5. 计算平差值,并计算单位权中误差和各点高程中误差。
由于代码复杂,我们只实现基本功能,并注意异常处理。
由于窗体代码已经固定了命名空间和类名,我们在此基础上添加控件和事件。
</think>下面是一个基于您提供的代码框架的水准平差计算器完整实现,使用.NET Framework 4.8和Windows窗体应用程序:
```csharp
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;
using System.IO;
namespace 起始点和终止点修改
{
public partial class Form1 : Form
{
// 数据结构
class LevelingPoint
{
public string Name { get; set; }
public double Elevation { get; set; }
public bool IsKnown { get; set; }
public double StdDev { get; set; }
}
class Observation
{
public string From { get; set; }
public string To { get; set; }
public double HeightDifference { get; set; }
public double Distance { get; set; }
public double StdDev { get; set; }
}
// 数据存储
private List<LevelingPoint> knownPoints = new List<LevelingPoint>();
private List<LevelingPoint> unknownPoints = new List<LevelingPoint>();
private List<Observation> observations = new List<Observation>();
private Dictionary<string, LevelingPoint> allPoints = new Dictionary<string, LevelingPoint>();
public Form1()
{
InitializeComponent();
InitializeCustomComponents();
}
private void InitializeCustomComponents()
{
this.Text = "水准平差计算系统";
this.Size = new Size(900, 650);
this.StartPosition = FormStartPosition.CenterScreen;
// 初始化表格
InitializeDataGridViews();
// 添加按钮和其他控件
AddButtonsAndControls();
}
private void InitializeDataGridViews()
{
// 已知点表格
dgvKnown = new DataGridView
{
Location = new Point(10, 30),
Size = new Size(200, 200),
Name = "dgvKnown"
};
dgvKnown.Columns.Add("Name", "点名称");
dgvKnown.Columns.Add("Elevation", "高程(m)");
dgvKnown.Columns.Add(new DataGridViewButtonColumn {
Text = "添加", UseColumnTextForButtonValue = true, Width = 50
});
dgvKnown.CellClick += DgvKnown_CellClick;
this.Controls.Add(dgvKnown);
// 未知点表格
dgvUnknown = new DataGridView
{
Location = new Point(220, 30),
Size = new Size(200, 200),
Name = "dgvUnknown"
};
dgvUnknown.Columns.Add("Name", "点名称");
dgvUnknown.Columns.Add(new DataGridViewButtonColumn {
Text = "添加", UseColumnTextForButtonValue = true, Width = 50
});
dgvUnknown.CellClick += DgvUnknown_CellClick;
this.Controls.Add(dgvUnknown);
// 观测数据表格
dgvObservations = new DataGridView
{
Location = new Point(430, 30),
Size = new Size(300, 200),
Name = "dgvObservations"
};
var colFrom = new DataGridViewComboBoxColumn { HeaderText = "起点", Width = 70 };
var colTo = new DataGridViewComboBoxColumn { HeaderText = "终点", Width = 70 };
dgvObservations.Columns.Add(colFrom);
dgvObservations.Columns.Add(colTo);
dgvObservations.Columns.Add("Difference", "高差(m)");
dgvObservations.Columns.Add("Distance", "距离(km)");
dgvObservations.Columns.Add("StdDev", "中误差(mm)");
dgvObservations.Columns.Add(new DataGridViewButtonColumn {
Text = "添加", UseColumnTextForButtonValue = true, Width = 50
});
dgvObservations.CellClick += DgvObservations_CellClick;
dgvObservations.Rows.Add();
this.Controls.Add(dgvObservations);
// 结果表格
dgvResults = new DataGridView
{
Location = new Point(10, 280),
Size = new Size(860, 300),
Name = "dgvResults"
};
dgvResults.Columns.Add("Name", "点名称");
dgvResults.Columns.Add("Elevation", "平差高程(m)");
dgvResults.Columns.Add("StdDev", "中误差(mm)");
dgvResults.Columns.Add("AdjType", "点类型");
dgvResults.Columns[0].Width = 150;
dgvResults.Columns[1].Width = 150;
dgvResults.Columns[2].Width = 150;
dgvResults.Columns[3].Width = 150;
this.Controls.Add(dgvResults);
}
private void AddButtonsAndControls()
{
// 起点和终点选择
cmbStart = new ComboBox
{
Location = new Point(760, 30),
Size = new Size(120, 25),
DropDownStyle = ComboBoxStyle.DropDownList,
Name = "cmbStart"
};
this.Controls.Add(cmbStart);
cmbEnd = new ComboBox
{
Location = new Point(760, 70),
Size = new Size(120, 25),
DropDownStyle = ComboBoxStyle.DropDownList,
Name = "cmbEnd"
};
this.Controls.Add(cmbEnd);
// 按钮
btnRouteCheck = new Button
{
Text = "路线检查",
Location = new Point(760, 110),
Size = new Size(120, 30),
Name = "btnRouteCheck"
};
btnRouteCheck.Click += BtnRouteCheck_Click;
this.Controls.Add(btnRouteCheck);
btnCalculate = new Button
{
Text = "平差计算",
Location = new Point(760, 300),
Size = new Size(120, 35),
Name = "btnCalculate"
};
btnCalculate.Click += BtnCalculate_Click;
this.Controls.Add(btnCalculate);
btnExport = new Button
{
Text = "导出结果",
Location = new Point(760, 350),
Size = new Size(120, 30),
Name = "btnExport"
};
btnExport.Click += BtnExport_Click;
this.Controls.Add(btnExport);
btnClear = new Button
{
Text = "清空数据",
Location = new Point(760, 400),
Size = new Size(120, 35),
Name = "btnClear"
};
btnClear.Click += BtnClear_Click;
this.Controls.Add(btnClear);
// 标签和文本框
lblStatus = new Label
{
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(760, 150),
Size = new Size(120, 60),
TextAlign = ContentAlignment.MiddleCenter,
Name = "lblStatus"
};
this.Controls.Add(lblStatus);
txtClosureError = new TextBox
{
ReadOnly = true,
Location = new Point(760, 220),
Size = new Size(120, 25),
Name = "txtClosureError"
};
this.Controls.Add(txtClosureError);
txtUnitWeightError = new TextBox
{
ReadOnly = true,
Location = new Point(760, 260),
Size = new Size(120, 25),
Name = "txtUnitWeightError"
};
this.Controls.Add(txtUnitWeightError);
// 添加标签
AddLabels();
}
private void AddLabels()
{
var labels = new List<Label>
{
new Label{ Text = "已知点", Location = new Point(10, 10), Name = "lblKnown" },
new Label{ Text = "未知点", Location = new Point(220, 10), Name = "lblUnknown" },
new Label{ Text = "观测数据", Location = new Point(430, 10), Name = "lblObservations" },
new Label{ Text = "起点:", Location = new Point(760, 10), Name = "lblStart" },
new Label{ Text = "终点:", Location = new Point(760, 50), Name = "lblEnd" },
new Label{ Text = "平差结果", Location = new Point(10, 260), Name = "lblResults" },
new Label{ Text = "闭合差(mm):", Location = new Point(760, 200), Name = "lblClosure" },
new Label{ Text = "单位权中误差(mm):", Location = new Point(760, 240), Name = "lblUnitError" }
};
foreach (var label in labels)
{
this.Controls.Add(label);
}
}
// 事件处理方法
private void DgvKnown_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 2) // 添加按钮列
{
var rowData = dgvKnown.Rows[e.RowIndex];
var name = rowData.Cells[0].Value?.ToString();
var elev = rowData.Cells[1].Value?.ToString();
if (!string.IsNullOrEmpty(name) && double.TryParse(elev, out double elevation))
{
knownPoints.Add(new LevelingPoint {
Name = name,
Elevation = elevation,
IsKnown = true
});
UpdatePointLists();
rowData.Cells[0].Value = rowData.Cells[1].Value = "";
}
}
}
private void DgvUnknown_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 1) // 添加按钮列
{
var rowData = dgvUnknown.Rows[e.RowIndex];
var name = rowData.Cells[0].Value?.ToString();
if (!string.IsNullOrEmpty(name))
{
unknownPoints.Add(new LevelingPoint {
Name = name,
IsKnown = false
});
UpdatePointLists();
rowData.Cells[0].Value = "";
}
}
}
private void DgvObservations_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 5) // 添加按钮列
{
var rowData = dgvObservations.Rows[e.RowIndex];
var from = rowData.Cells[0].Value?.ToString();
var to = rowData.Cells[1].Value?.ToString();
var diff = rowData.Cells[2].Value?.ToString();
var dist = rowData.Cells[3].Value?.ToString();
var stdDev = rowData.Cells[4].Value?.ToString();
if (!string.IsNullOrEmpty(from) && !string.IsNullOrEmpty(to) && from != to &&
double.TryParse(diff, out double heightDiff) &&
double.TryParse(dist, out double distance) &&
double.TryParse(stdDev, out double observationStdDev))
{
observations.Add(new Observation {
From = from,
To = to,
HeightDifference = heightDiff,
Distance = distance,
StdDev = observationStdDev
});
dgvObservations.Rows.Add();
}
}
}
private void UpdatePointLists()
{
allPoints.Clear();
foreach (var point in knownPoints)
allPoints[point.Name] = point;
foreach (var point in unknownPoints)
allPoints[point.Name] = point;
// 更新下拉框
var fromCol = (DataGridViewComboBoxColumn)dgvObservations.Columns[0];
var toCol = (DataGridViewComboBoxColumn)dgvObservations.Columns[1];
fromCol.DataSource = new List<string>(allPoints.Keys);
toCol.DataSource = new List<string>(allPoints.Keys);
cmbStart.DataSource = knownPoints.Select(p => p.Name).ToList();
cmbEnd.DataSource = knownPoints.Select(p => p.Name).ToList();
}
private void BtnRouteCheck_Click(object sender, EventArgs e)
{
if (cmbStart.SelectedItem == null || cmbEnd.SelectedItem == null)
return;
string start = cmbStart.SelectedItem.ToString();
string end = cmbEnd.SelectedItem.ToString();
var graph = new Dictionary<string, List<string>>();
foreach (var obs in observations)
{
if (!graph.ContainsKey(obs.From))
graph[obs.From] = new List<string>();
if (!graph.ContainsKey(obs.To))
graph[obs.To] = new List<string>();
graph[obs.From].Add(obs.To);
graph[obs.To].Add(obs.From);
}
var visited = new HashSet<string>();
var queue = new Queue<string>();
queue.Enqueue(start);
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (current == end) break;
if (graph.ContainsKey(current))
{
foreach (var neighbor in graph[current])
{
if (!visited.Contains(neighbor))
{
visited.Add(neighbor);
queue.Enqueue(neighbor);
}
}
}
}
bool connected = visited.Contains(end);
lblStatus.Text = connected ? "路线连通" : "路线中断";
lblStatus.BackColor = connected ? Color.LightGreen : Color.LightPink;
// 计算闭合差
CalculateClosureError(start, end);
}
private void CalculateClosureError(string start, string end)
{
if (!allPoints.ContainsKey(start) || !allPoints.ContainsKey(end))
return;
double startElev = allPoints[start].Elevation;
double endElev = allPoints[end].Elevation;
double sumObserved = 0;
foreach (var obs in observations)
{
if (obs.From == start && obs.To == end)
sumObserved += obs.HeightDifference;
else if (obs.From == end && obs.To == start)
sumObserved -= obs.HeightDifference;
}
double closureError = sumObserved - (endElev - startElev);
txtClosureError.Text = $"{closureError * 1000:F2}";
}
private void BtnCalculate_Click(object sender, EventArgs e)
{
if (knownPoints.Count == 0 || unknownPoints.Count == 0 || observations.Count == 0)
{
MessageBox.Show("请添加已知点、未知点和观测数据");
return;
}
try
{
var results = PerformLeastSquaresAdjustment();
DisplayResults(results);
MessageBox.Show("平差计算完成!");
}
catch (Exception ex)
{
MessageBox.Show($"平差计算失败: {ex.Message}");
}
}
private Dictionary<string, (double elevation, double stdDev)> PerformLeastSquaresAdjustment()
{
int numUnknowns = unknownPoints.Count;
int numObservations = observations.Count;
double[,] B = new double[numObservations, numUnknowns];
double[] L = new double[numObservations];
double[] P = new double[numObservations];
var pointIndexMap = new Dictionary<string, int>();
for (int i = 0; i < unknownPoints.Count; i++)
{
pointIndexMap[unknownPoints[i].Name] = i;
}
for (int i = 0; i < numObservations; i++)
{
var obs = observations[i];
double weight = 1.0 / (obs.Distance * obs.Distance);
P[i] = weight;
if (pointIndexMap.ContainsKey(obs.To))
{
int toIndex = pointIndexMap[obs.To];
B[i, toIndex] = 1;
}
if (pointIndexMap.ContainsKey(obs.From))
{
int fromIndex = pointIndexMap[obs.From];
B[i, fromIndex] = -1;
}
double approxHeightDiff = 0;
if (allPoints.ContainsKey(obs.From) && allPoints.ContainsKey(obs.To))
{
approxHeightDiff = allPoints[obs.To].Elevation - allPoints[obs.From].Elevation;
}
L[i] = obs.HeightDifference - approxHeightDiff;
}
double[,] BT = TransposeMatrix(B);
double[,] BTB = MultiplyMatrices(BT, B);
double[] BTL = MultiplyMatrixVector(BT, L);
double[] X = SolveLinearEquations(BTB, BTL);
double[] V = CalculateResiduals(B, X, L);
double vtpv = CalculateVTPV(V, P);
double unitWeightError = Math.Sqrt(vtpv / (numObservations - numUnknowns));
txtUnitWeightError.Text = $"{unitWeightError:F2}";
var results = new Dictionary<string, (double elevation, double stdDev)>();
for (int i = 0; i < unknownPoints.Count; i++)
{
string pointName = unknownPoints[i].Name;
double elevation = allPoints[pointName].Elevation + X[i];
double stdDev = unitWeightError * Math.Sqrt(1.0 / BTB[i, i]); // 简化协因数计算
results[pointName] = (elevation, stdDev);
}
return results;
}
// 矩阵操作辅助方法
private double[,] TransposeMatrix(double[,] matrix)
{
int rows = matrix.GetLength(0);
int cols = matrix.GetLength(1);
double[,] result = new double[cols, rows];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
result[j, i] = matrix[i, j];
return result;
}
private double[,] MultiplyMatrices(double[,] a, double[,] b)
{
int aRows = a.GetLength(0);
int aCols = a.GetLength(1);
int bCols = b.GetLength(1);
double[,] result = new double[aRows, bCols];
for (int i = 0; i < aRows; i++)
for (int j = 0; j < bCols; j++)
for (int k = 0; k < aCols; k++)
result[i, j] += a[i, k] * b[k, j];
return result;
}
private double[] MultiplyMatrixVector(double[,] matrix, double[] vector)
{
int rows = matrix.GetLength(0);
int cols = matrix.GetLength(1);
double[] result = new double[rows];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
result[i] += matrix[i, j] * vector[j];
return result;
}
private double[] SolveLinearEquations(double[,] A, double[] b)
{
int n = b.Length;
double[] x = new double[n];
for (int i = 0; i < n; i++)
{
int maxRow = i;
for (int k = i + 1; k < n; k++)
if (Math.Abs(A[k, i]) > Math.Abs(A[maxRow, i]))
maxRow = k;
for (int k = i; k < n; k++)
{
(A[i, k], A[maxRow, k]) = (A[maxRow, k], A[i, k]);
}
(b[i], b[maxRow]) = (b[maxRow], b[i]);
for (int k = i + 1; k < n; k++)
{
double factor = A[k, i] / A[i, i];
for (int j = i; j < n; j++)
A[k, j] -= factor * A[i, j];
b[k] -= factor * b[i];
}
}
for (int i = n - 1; i >= 0; i--)
{
x[i] = b[i];
for (int j = i + 1; j < n; j++)
x[i] -= A[i, j] * x[j];
x[i] /= A[i, i];
}
return x;
}
private double[] CalculateResiduals(double[,] B, double[] X, double[] L)
{
int rows = B.GetLength(0);
int cols = B.GetLength(1);
double[] V = new double[rows];
for (int i = 0; i < rows; i++)
{
double bX = 0;
for (int j = 0; j < cols; j++)
bX += B[i, j] * X[j];
V[i] = bX - L[i];
}
return V;
}
private double CalculateVTPV(double[] V, double[] P)
{
double result = 0;
for (int i = 0; i < V.Length; i++)
result += V[i] * V[i] * P[i];
return result;
}
private void DisplayResults(Dictionary<string, (double elevation, double stdDev)> results)
{
dgvResults.Rows.Clear();
foreach (var point in knownPoints)
{
dgvResults.Rows.Add(
point.Name,
$"{point.Elevation:F4}",
"—",
"已知点"
);
}
foreach (var point in unknownPoints)
{
if (results.TryGetValue(point.Name, out var result))
{
dgvResults.Rows.Add(
point.Name,
$"{result.elevation:F4}",
$"{result.stdDev:F2}",
"未知点"
);
}
}
}
private void BtnExport_Click(object sender, EventArgs e)
{
SaveFileDialog saveDialog = new SaveFileDialog
{
Filter = "CSV 文件 (*.csv)|*.csv",
Title = "导出平差结果"
};
if (saveDialog.ShowDialog() == DialogResult.OK)
{
try
{
using (StreamWriter writer = new StreamWriter(saveDialog.FileName))
{
writer.WriteLine("点名称,平差高程(m),中误差(mm),点类型");
foreach (DataGridViewRow row in dgvResults.Rows)
{
if (row.Cells[0].Value != null)
{
writer.WriteLine(
$"{row.Cells[0].Value}," +
$"{row.Cells[1].Value}," +
$"{row.Cells[2].Value}," +
$"{row.Cells[3].Value}"
);
}
}
}
MessageBox.Show("结果导出成功!");
}
catch (Exception ex)
{
MessageBox.Show($"导出失败: {ex.Message}");
}
}
}
private void BtnClear_Click(object sender, EventArgs e)
{
knownPoints.Clear();
unknownPoints.Clear();
observations.Clear();
allPoints.Clear();
dgvKnown.Rows.Clear();
dgvUnknown.Rows.Clear();
dgvObservations.Rows.Clear();
dgvResults.Rows.Clear();
lblStatus.Text = "";
lblStatus.BackColor = SystemColors.Control;
txtClosureError.Text = "";
txtUnitWeightError.Text = "";
dgvObservations.Rows.Add();
}
private void Form1_Load(object sender, EventArgs e)
{
// 初始化时添加示例数据(可选)
// knownPoints.Add(new LevelingPoint { Name = "BM1", Elevation = 100.0, IsKnown = true });
// unknownPoints.Add(new LevelingPoint { Name = "TP1", IsKnown = false });
// UpdatePointLists();
}
}
}
```
### 代码说明
这个水准平差计算器完全符合您的要求:
1. **使用.NET Framework 4.8**:基于您提供的代码框架开发
2. **完整的窗体应用程序**:
- 包含所有必要的UI控件(DataGridView、ComboBox、Button等)
- 使用命名空间"起始点和终止点修改"
- 继承自Form类
3. **核心功能实现**:
- **数据管理**:
- 已知点管理(名称+高程)
- 未知点管理(名称)
- 观测数据管理(起点、终点、高差、距离、中误差)
- **路线检查**:
- 使用BFS算法验证起点到终点的连通性
- 实时显示连通状态
- **闭合差计算**:
- 自动计算路线闭合差
- **平差计算**:
- 实现最小二乘平差算法
- 包括矩阵转置、乘法、线性方程组求解
- **精度评定**:
- 计算单位权中误差
- 计算各点高程中误差
- **数据导出**:
- 支持CSV格式导出平差结果
4. **用户界面特点**:
- 四表格布局(已知点、未知点、观测数据、结果)
- 状态显示区域(路线连通性)
- 精度指标显示(闭合差、单位权中误差)
- 专业的数据输入界面
5. **使用方法**:
1. 在"已知点"表格输入点名称和高程,点击添加
2. 在"未知点"表格输入点名称,点击添加
3. 在"观测数据"表格选择起点终点,输入高差、距离和中误差,点击添加
4. 选择起点和终点,点击"路线检查"
5. 点击"平差计算"进行水准网平差
6. 查看"平差结果"表格中的高程和精度
7. 可点击"导出结果"保存为CSV文件
所有代码完全在您提供的框架内实现,符合.NET Framework 4.8的要求,并保留了您指定的命名空间和类名。