移动曲面法数字高程模型内插程序(C#桌面窗体)

运行环境:Visual Studio 2019/2022。别的不知道可不可以。


一、程序演示

DEM移动拟合插值算法成果演示

二、目的要求

1.算法要求

3c8bd51f3d8e447781c6c5fab02f9777.jpeg

2.参考公式

移动拟合法插值的数学模型:

14878859ea1d4e0485a342ba95557dbf.png

设计矩阵 M:  是一个 n×6 的矩阵,其中 n 是数据点的数量。每行代表一个数据点的坐标值映射到二次多项式项上。

5bb9d0afe2ac44118600cad3ed98c3a3.png

权重矩阵 P:是一个对角矩阵,其对角线元素是每个数据点到目标点(x, y)的距离的倒数平方根的倒数。 

66836609f7b8411c839307ce89a19153.png

97a95410a6194a889062ab1b75d86ee2.png

计算系数 ABCDEF: 使用加权最小二乘法,我们首先计算 eq?MP,然后求其逆 eq?%28M%5E%7BT%7DPM%29%5E%7B-1%7D。最后通过eq?%28M%5E%7BT%7DPM%29%5E%7B-1%7DM%5E%7BT%7DPZ计算出系数向量Z,其中 Z是坐标数据中的 Z值组成的向量。

a136d0868f2c442bba5c95778d24e49f.png

d2b5f9bc948d48aabdc48fabb0068b51.png

具体算法请参考下面给出的代码,代码中有详细注释,应该都能看懂。

三、算法思路

这只是我的思路,不绝对。从最简单的代码开始写,成功运行后,一步步写的更复杂一些。

1.先写简易的版本,插值实现高程值Z的计算,在代码内部输入坐标数据。运算后会显示message弹窗。

2.删除上一步的坐标数据,固定地址读取excel文件,实现算法的运行。并且把excel中的内容在dataview控件中呈现出来。

3.将定死的待算坐标高程xy改为手动输入textBox中任意值。

4.删除固定地址,实现任意表格的导入,通过读取textbox中路径,任意打开excel表格用LinearRegression类中的函数计算里面的数据,得到ABCDEF系数。

5. 运算后textZ文本框会显示Z值

6.算法加入P(P为未知点距离已知点的距离平方的倒数)和M矩阵。(因为一开始的算法我写错了,后来检查出来后,又修改算法。这一步放到第一步中。)

7.完善代码,自定义输入xy坐标等功能

四、算法内容

这是我的源代码文件:

移动曲面法数字高程模型内插算法(C#桌面窗体版)资源-CSDN文库

1.安装程序包

cdafbefc86db4757929de8f6f5e837e4.png

mathnet.numerics包

具体看前辈文章:VisualStudio2022为C#安装mathnet.numerics包_mathnet.numerics下载安装-CSDN博客

好像还需要安装其他程序包,因为时间太久远,我记不清了,大家运行代码的时候,直接复制错误信息在CSDN搜索就能搜索出来需要安装什么包。

欢迎在评论区补充需要安装的包。

2.引用类库

ecb708f5b4f14471a3e5a99330ea4f43.png

3.具体代码

Form.cs

窗体布局

a4c670ac4d34440e81a5b588b2bb4cdb.png

代码

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.Data.OleDb;
using System.Reflection;
using System.IO;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel; // 对于.xlsx文件  
using NPOI.HSSF.UserModel; // 对于.xls文件  
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using DEM插值算法;

namespace DEM插值算法
{
    public partial class Form1 : Form
    {
        private const int MinimumDataPoints = 6; // 假设最小数据点数量是6
                                                  // 将 data 提升为类级别的字段  
        public Form1()
        {
            InitializeComponent();
            comboBox1.Items.Add("移动曲面法");
        }

        //定义方法,读取Excel文件,并返回一个包含三元组(Tuple)的列表,每个三元组包含三个 double 类型的值
        private List<Tuple<double, double, double>> ReadExcelFile(string filePath)
        {
            List<Tuple<double, double, double>> data = new List<Tuple<double, double, double>>();

            using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                // 创建一个XSSFWorkbook实例,用于读取XLSX格式的Excel文件  
                IWorkbook workbook = new XSSFWorkbook(stream); 
                ISheet sheet = workbook.GetSheetAt(0); // 获取第一个工作表  

                if (sheet == null)
                {
                    throw new InvalidOperationException("Excel文件中没有找到工作表。");
                }

                IRow headerRow = sheet.GetRow(0); // 获取标题行

                // 从第二行开始读取数据  
                for (int rowNum = 1; rowNum <= sheet.LastRowNum; rowNum++)
                {
                    IRow row = sheet.GetRow(rowNum);   // 获取当前行  
                    
                    // 如果行不为空,则读取数据  
                    if (row != null) 
                    {
                        double x = 0.0, y = 0.0, z = 0.0;

                        // 获取x、y、z所在列的单元格  
                        ICell cellX = row.GetCell(0); // 假设x在第一列  
                        ICell cellY = row.GetCell(1); // 假设y在第二列  
                        ICell cellZ = row.GetCell(2); // 假设z在第三列  

                        // 如果单元格不为空,则将其转换为double类型并赋值给相应的变量
                        if (cellX != null) x = ConvertCellToDouble(cellX);
                        if (cellY != null) y = ConvertCellToDouble(cellY);
                        if (cellZ != null) z = ConvertCellToDouble(cellZ);

                        // 将读取到的x、y、z值添加到元组列表中
                        data.Add(Tuple.Create(x, y, z));
                    }
                }
            }
            return data;
        }

        //将Apache POI库中的ICell对象转换为double类型,否则无法正常运算
        private double ConvertCellToDouble(ICell cell)
        {
            switch (cell.CellType)
            { 
                case CellType.Numeric:// 如果单元格是数值类型  
                    return cell.NumericCellValue;// 直接返回单元格的数值  
                case CellType.String: // 如果单元格是字符串类型  
                    double result;  // 定义一个double类型的变量,用于存储转换结果  
                    if (double.TryParse(cell.StringCellValue, out result))// 尝试将字符串转换为double类型  
                    {
                        return result;
                    }
                    throw new InvalidOperationException("无法将字符串单元格转换为double类型。");
                default:
                    throw new InvalidOperationException("不支持的单元格类型。");
            }
        }

        private void CalculateCoefficientsButton_Click(object sender, EventArgs e)
        {
            string filePath = textBoxFilePath.Text; // 从textBoxFilePath中获取文件路径  

            // 确保文件路径不是空的  
            if (string.IsNullOrWhiteSpace(filePath))
            {
                MessageBox.Show("请输入有效的文件路径。");
                return;
            }

            try
            {
                // 1. 从Excel文件中读取数据  
                List<Tuple<double, double, double>> data = ReadExcelFile(filePath);

                // 确保读取到了足够的数据点来进行计算  
                if (data.Count < MinimumDataPoints)
                {
                    throw new InvalidOperationException("数据点数量不足以进行计算。");
                }
               if (comboBox1.SelectedIndex == 0)//根据下拉框执行移动拟合算法
               
               {
                    // 2. 调用 LinearRegression 类中的 ComputeABCDE 方法求解  
                    double x1 = double.Parse(textX.Text);
                    double y1 = double.Parse(textY.Text);
                    DenseMatrix coefficients = LinearRegression.ComputeABCDEF(data, x1, y1);

                    // 3. 使用计算得到的系数来计算新的 z 值  
                    // 从两个文本框中获取 x 和 y 的值  
                    if (double.TryParse(textX.Text, out double x) && double.TryParse(textY.Text, out double y))
                    {
                        // 使用 x 和 y 的值计算 z  
                        double z = LinearRegression.CalculateZ(coefficients, x, y);
                      
                        // 4. 输出结果  
                        MessageBox.Show($"对于 x={x} 和 y={y}, z={z}");
                    textZ.Text = $"{z}";
                    }
                    else
                    {
                    // 如果输入的不是有效的数字,显示错误消息  
                        MessageBox.Show("请输入有效的 x 和 y 值。");
                    }
               }
            }
            catch (Exception ex)  // 显示异常信息  
            {
               MessageBox.Show("发生错误:" + ex.Message);
            }
        }

        //打开文件对话框,选择excel文件,读取该文件的内容后,将数据展示在DataGridView控件中
        private void LoadExcelData_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {

                openFileDialog.Title = "已知数据点坐标";
                openFileDialog.Filter = "高程点文件|*csv;*.xls;*.xlsx";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string filePath = openFileDialog.FileName;
                    string connectionString = GetConnectionString(filePath);
                    textBoxFilePath.Text = openFileDialog.FileName;

                    // 使用OleDbConnection对象来建立与Excel文件的连接
                    using (OleDbConnection connection = new OleDbConnection(connectionString))  
                    {
                        try
                        {
                            connection.Open();

                            // 创建一个OleDbDataAdapter对象,用于执行SQL查询并填充数据集  
                            OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", connection);
                            DataSet dataSet = new DataSet();      // 创建一个新的数据集  
                            adapter.Fill(dataSet);   // 使用适配器填充数据集  
                            dataGridView1.DataSource = dataSet.Tables[0];// 将数据集的第一个表绑定到dataGridView1控件上 
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Error loading Excel data: " + ex.Message);
                        }
                    }
                }


            }
        }
     
        private string GetConnectionString(string filePath)
        {
            string connectionString = "";

            if (filePath.EndsWith(".xls"))
            {
                // Excel 97-2003 (.xls)  
                connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filePath + ";Extended Properties='Excel 8.0;HDR=YES;';";
            }
            else if (filePath.EndsWith(".xlsx"))
            {
                // Excel 2007 and above (.xlsx)  
                connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties='Excel 12.0 Xml;HDR=YES;';";
            }

            return connectionString;
        }
    }
}



      
       

LinearRegression.cs

using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra.Factorization;

namespace DEM插值算法
{
    internal class LinearRegression
    {
        //创建的对角权重矩阵P
        public static DenseMatrix ComputeWeightsMatrix(List<Tuple<double, double, double>> data, double x, double y)
        {
            var weights = data.Select(p => 1.0 / (Math.Pow(p.Item1 - x, 2) + Math.Pow(p.Item2 - y, 2))).ToList();

            //静态方法,用于计算线性回归的系数矩阵。它接收一个包含三元组的列表(每个三元组包含三个double类型的值)
            //以及两个double类型的参数x和y。这个方法返回一个DenseMatrix对象,表示系数矩阵。
            var P = DenseMatrix.OfDiagonalVector(DenseVector.OfEnumerable(weights));
            return P;
        }

        //创建M矩阵
        public static DenseMatrix ComputeMMatrix(List<Tuple<double, double, double>> data)
        {
            var M = DenseMatrix.OfArray(new double[data.Count, 6]);
            for (int i = 0; i < data.Count; i++)
            {
                var x = data[i].Item1;
                var y = data[i].Item2;
                M[i, 0] = x * x;      // X^2
                M[i, 1] = x * y;      // XY
                M[i, 2] = y * y;      // Y^2
                M[i, 3] = x;          // X
                M[i, 4] = y;          // Y
                M[i, 5] = 1.0;        // Constant
            }
            return M;
        }
        //计算ABCDEF系数
        public static DenseMatrix ComputeABCDEF(List<Tuple<double, double, double>> data, double x, double y)
        { 
            var P = ComputeWeightsMatrix(data, x, y);//调用ComputeWeightsMatrix方法计算权重矩阵P。
            var M = ComputeMMatrix(data);//调用ComputeMMatrix方法计算M矩阵。
            var MT = M.Transpose();//计算M矩阵的转置矩阵MT。

            var MP = MT * P * M;//计算MP矩阵,即MT、P和M的乘积。
            var MPInv = MP.Inverse();//计算MP矩阵的逆矩阵MPInv。

            if (MPInv == null)
            {
                throw new InvalidOperationException("Failed to compute inverse of matrix MP.");
            }

            var zValues = data.Select(p => p.Item3).ToArray();
            var MTP = MT * P;//计算MTP矩阵,即MT和P的乘积
            var ABCDEF = MPInv * MTP * DenseVector.OfArray(zValues);//计算线性回归的系数向量ABCDEF。
            // 创建一个 1 行 6 列的 DenseMatrix  
            var coefficientsMatrix = DenseMatrix.OfRowArrays(new[] { ABCDEF.ToArray() }); //将系数向量ABCDEF转换为一个1行6列的DenseMatrix对象。

            return coefficientsMatrix;
          
        }

        //计算待定的Z值
        public static double CalculateZ( DenseMatrix coefficients,double x, double y)
        {
            if (coefficients.RowCount != 1 || coefficients.ColumnCount != 6)
            {
                throw new ArgumentException("Coefficients matrix must be a 1x6 matrix.");
            }

            double A = coefficients[0, 0];
            double B = coefficients[0, 1];
            double C = coefficients[0, 2];
            double D = coefficients[0, 3];
            double E = coefficients[0, 4];
            double F = coefficients[0, 5];

            return A * x * x + B * x * y + C * y * y + D * x + E * y + F;
        }
    }
}

五、算法心得

应当由简入繁,在最简单代码的基础上一步步丰富内容,增加功能,变得复杂高级。

每一步都要运行出来后再进行下一步,不然全写完后再运行,代码太多根本不知道哪出问题了。

  • 49
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 移动曲面拟合是一种数字高程模型内插,它可以用来在给定的测量数据中插值计算未知高程信息。 在 C 语言中实现移动曲面拟合,首先需要准备所需的数据。这通常包括测量的经纬度坐标和高程信息,以及需要插值计算的未知的经纬度坐标。然后,可以使用某种算法来拟合这些数据,例如 Kriging 或最小二乘。最后,使用拟合出的模型来计算未知高程信息。 下面是一个简单的示例,展示了如何使用 Kriging 算法在 C 语言中实现移动曲面拟合: ``` #include <stdio.h> #include <stdlib.h> #include <math.h> #define N 5 // 测量的数量 #define M 1 // 未知的数量 double lon[N], lat[N], ele[N]; // 测量的经纬度坐标和高程信息 double lon_unknown, lat_unknown; // 未知的经纬度坐标 double ele_unknown; // 未知高程信息 double semivariogram(int i, int j) { // 计算 i 和 j 之间的半变异距离 double dx = lon[i] - lon[j]; double dy = lat[i] - lat[j]; return 0.5 * (ele[i] - ele[j]) * (ele[i] - ele[j]) ### 回答2: 移动曲面拟合是一种用于数字高程模型(DEM)内插的方。该方可以通过使用C语言进行实现。 在移动曲面拟合中,我们首先将输入的离散高程数据进行分割成小区域,每个小区域都包含一些离散。然后,我们使用多项式函数来拟合每个小区域的数据。 在C语言中,我们可以使用数组来存储输入的高程数据。我们可以使用二维数组来存储每个小区域内的离散,其中第一个维度表示小区域的索引,第二个维度表示离散的索引。 接下来,我们需要实现一个函数来计算拟合后的曲面。我们可以使用最小二乘来确定多项式函数的系数,以使拟合后的曲面尽可能地与原始数据一致。我们可以通过遍历每个小区域,计算每个小区域的拟合曲面后,在整个DEM上进行内插。 在C语言中,我们可以使用矩阵运算库,如`gsl`来进行最小二乘的计算。该库提供了一些函数,例如`gsl_fit`,可以根据给定的多项式函数和数据,计算出最小二乘拟合的系数。 在内插过程中,我们可以遍历整个DEM的每个像素,对每个像素计算其在所在小区域的拟合曲面上的高程值,然后将其作为内插后的结果。 使用C语言实现移动曲面拟合数字高程模型内插可以很好地处理大量的高程数据,并产生平滑的数字高程模型。通过控制多项式的次数和小区域的大小,我们可以调整内插结果的精度和平滑程度。这种方在地形分析、三维可视化和地图制作等应用中具有广泛的用途。 ### 回答3: 移动曲面拟合是一种常用的数字高程模型内插,主要通过在输入数据附近进行曲面拟合来估计未知位置的高程值。在C语言中,可以使用数学库中的相应函数来完成移动曲面拟合数字高程模型内插。 首先,我们需要定义输入的数据,包括已知位置的坐标和对应的高程值。可以使用数组来存储这些数据。然后,利用循环结构遍历每个未知位置,计算其高程值。 在移动曲面拟合中,通过选取合适的邻域范围,对未知位置附近的已知位置进行拟合。一种常用的方是选择K近邻算法,即选择与未知位置最近的K个已知位置作为邻域。 对于每个未知位置,首先计算其与已知位置的距离,并找到K个最邻近的已知位置。然后,根据这K个已知位置的坐标和对应的高程值,使用曲面拟合算法(如最小二乘)来估计未知位置的高程值。最后,将估计得到的高程值保存在相应的数组中。 在完成所有未知位置的高程值估计后,我们就可以得到一个完整的数字高程模型。可以将结果输出到文件中或进行其他后续处理。 使用C语言实现移动曲面拟合数字高程模型内插,需要熟悉基本的数组操作、循环结构以及数学库函数的使用。对于曲面拟合算法的选择和实现,可以根据具体的需求和数据特来进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值