using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using NaturalNeighbor; namespace NeighborInterpolator { public class GridCreator { public GridCreator() { } /// /// 网格化生成图形. /// /// The xyz file. /// The output file. /// The border file. /// The cols. /// The rows. /// The insert times. /// The contour step. /// The contour mark step. /// public bool Create(string xyzFile, string outputFile, string borderFile , int cols, int rows , int insertTimes, double contourStep, int contourMarkStep , double xMin = -1.0, double yMin = -1.0, double xMax = -1.0, double yMax = -1.0) { // 读取数据,计算坐标范围 Vector3[] points = this.ReadData(xyzFile).ToArray(); Vector3 xyzMin = new Vector3(); Vector3 xyzMax = new Vector3(); this.CalculateRange(points, ref xyzMin, ref xyzMax); Vector3 rangeMin = xyzMin; Vector3 rangeMax = xyzMax; Vector2 borderMin = new Vector2(); Vector2 borderMax = new Vector2(); Vector2[] borderData = null; bool bHasBorder = !String.IsNullOrEmpty(borderFile) && File.Exists(borderFile); if (bHasBorder) { borderData = ReadBorder(borderFile).ToArray(); if(borderData!=null && borderData.Count() > 0) { CalculateRange2D(borderData, ref borderMin, ref borderMax); rangeMin.X = Math.Min(rangeMin.X, borderMin.X); rangeMin.Y = Math.Min(rangeMin.Y, borderMin.Y); rangeMax.X = Math.Max(rangeMax.X, borderMax.X); rangeMax.Y = Math.Max(rangeMax.Y, borderMax.Y); } else { bHasBorder = false; } } else { if (xMin > 0 && yMin > 0) { rangeMin.X = Math.Min(rangeMin.X, (float)xMin); rangeMin.Y = Math.Min(rangeMin.Y, (float)yMin); rangeMax.X = Math.Max(rangeMax.X, (float)xMax); rangeMax.Y = Math.Max(rangeMax.Y, (float)yMax); borderData = new Vector2[5]; borderData[0] = new Vector2(rangeMin.X, rangeMin.Y); borderData[1] = new Vector2(rangeMin.X, rangeMax.Y); borderData[2] = new Vector2(rangeMax.X, rangeMax.Y); borderData[3] = new Vector2(rangeMax.X, rangeMin.Y); borderData[4] = new Vector2(rangeMin.X, rangeMin.Y); CalculateRange2D(borderData, ref borderMin, ref borderMax); bHasBorder = true; } } double dExpand = 10; if(rangeMin.X < xyzMin.X) { dExpand = xyzMin.X - rangeMin.X; } if (rangeMin.Y < xyzMin.Y) { dExpand = Math.Max(dExpand, xyzMin.Y - rangeMin.Y); } if (rangeMax.X> xyzMax.X) { dExpand = Math.Max(dExpand, rangeMax.X - xyzMax.X); } if (rangeMax.Y > xyzMax.Y) { dExpand = Math.Max(dExpand, rangeMax.Y - xyzMax.Y); } dExpand += 10; // 处理为相对坐标 int nCount = points.Length; for (int i = 0; i < nCount; i++) { points[i].X -= rangeMin.X; points[i].Y -= rangeMin.Y; } Interpolator2d interpolator = Interpolator2d.Create(points.ToArray(), dExpand); // 生成网格的大小 double dWidth = rangeMax.X - rangeMin.X; double dHeight = rangeMax.Y - rangeMin.Y; if (bHasBorder) { dWidth = borderMax.X - borderMin.X; dHeight = borderMax.Y - borderMin.Y; } double scaleX = dWidth / (cols - 1); double scaleY = dHeight / (rows - 1); float dScale = (float)Math.Min(scaleX, scaleY); int nCalCols = (int)Math.Ceiling(dWidth / dScale)+1; int nCalRows = (int)Math.Ceiling(dHeight / dScale)+1; // 构建网格节点 Vector2[] result = new Vector2[nCalRows * nCalCols]; double[] resultZ = new double[nCalRows * nCalCols]; for (int i = 0; i < nCalCols; ++i) { var x = i * dScale; for (int j = 0; j < nCalRows; ++j) { var y = j * dScale; result[i * nCalRows + j] = new Vector2(x, y); //resultZ[i * nCalRows + j] = interpolator.Lookup(result[i * nCalRows + j]); } } interpolator.LookupRange(result, resultZ);//, new ParallelOptions() { } //string strResult = "C:\\GeoIntelligent\\Test\\厚度图数据\\40SO(300)-Result.csv"; //using (StreamWriter sw = new StreamWriter(strResult, false, Encoding.Default)) //{ // for (int i = 0; i < nCalCols; i++) // { // for (int j = 0; j < nCalRows; j++) // { // sw.WriteLine($"{i},{j},{resultZ[i * nCalRows + j]}"); // } // } //} double[] daBorder = null; if (bHasBorder) { daBorder = new double[borderData.Length * 2]; for (int i = 0; i < borderData.Length; i++) { daBorder[i * 2] = borderData[i].X; daBorder[i * 2 + 1] = borderData[i].Y; } } try { IntPtr pEvent = IntPtr.Zero; //if (ProgressChanged != null) //{ // pEvent = Marshal.GetFunctionPointerForDelegate(ProgressChanged); //} Thread newThread = new Thread(new ThreadStart(() => { //SetDllDirectory(System.Windows.Forms.Application.StartupPath); // 生成网格 SetGridData(rangeMin.X, rangeMin.Y, resultZ, nCalCols, nCalRows, (double)dScale, (double)dScale , rangeMin.Z, rangeMax.Z, insertTimes, daBorder, daBorder == null ? 0 : daBorder.Length); // 保存网格,同时生成等值线 SaveDrawFile(outputFile, contourStep, contourMarkStep); DestoryXy(); }), 2621440); newThread.Start(); newThread.Join(); } catch (Exception ex) { Trace.WriteLine(ex.Message); } return true; } /// /// 读取边界文件的数据. /// /// Name of the file. /// private IEnumerable ReadBorder(string fileName) { using (StreamReader rd = new StreamReader(File.Open(fileName, FileMode.Open), Encoding.Default)) { string strLine = string.Empty; while ((strLine = rd.ReadLine()) != null) { if (strLine.StartsWith("Pline.", StringComparison.CurrentCultureIgnoreCase)) { break; } } while ((strLine = rd.ReadLine()) != null) { var parts = strLine.Split(','); if (parts.Length >= 2 && float.TryParse(parts[0], out var x) && float.TryParse(parts[1], out var y)) { yield return new Vector2((float)x, (float)y); } } } } /// /// 读取散点数据. /// /// The filename. /// private IEnumerable ReadData(string filename) { using (StreamReader txt = new StreamReader(File.Open(filename, FileMode.Open), Encoding.Default)) { txt.ReadLine(); // Skip columns var s = txt.ReadLine(); while (s != null) { var parts = s.Split(','); if (parts.Length >= 3 && float.TryParse(parts[0], out var x) && float.TryParse(parts[1], out var y) && float.TryParse(parts[2], out var z)) { yield return new Vector3((float)x, (float)y, (float)z); } s = txt.ReadLine(); } } } /// /// 计算二维数据点的范围. /// /// The points. /// The point minimum. /// The point maximum. private void CalculateRange2D(Vector2[] points, ref Vector2 pointMin, ref Vector2 pointMax) { if (points.Length == 0) { pointMin = pointMax = Vector2.Zero; return; } float minX = float.MaxValue; float minY = float.MaxValue; float maxX = float.MinValue; float maxY = float.MinValue; foreach (Vector2 pt in points) { minX = Math.Min(minX, pt.X); maxX = Math.Max(maxX, pt.X); minY = Math.Min(minY, pt.Y); maxY = Math.Max(maxY, pt.Y); } pointMin.X = minX; pointMin.Y = minY; pointMax.X = maxX; pointMax.Y = maxY; } /// /// 计算坐标范围. /// /// The points. /// The point minimum. /// The point maximum. private void CalculateRange(Vector3[] points, ref Vector3 pointMin, ref Vector3 pointMax) { if (points.Length == 0) { pointMin = pointMax = Vector3.Zero; return; } float minX = float.MaxValue; float minY = float.MaxValue; float minZ = float.MaxValue; float maxX = float.MinValue; float maxY = float.MinValue; float maxZ = float.MinValue; foreach (Vector3 pt in points) { minX = Math.Min(minX, pt.X); maxX = Math.Max(maxX, pt.X); minY = Math.Min(minY, pt.Y); maxY = Math.Max(maxY, pt.Y); minZ = Math.Min(minZ, pt.Z); maxZ = Math.Max(maxZ, pt.Z); } pointMin.X = minX; pointMin.Y = minY; pointMin.Z = minZ; pointMax.X = maxX; pointMax.Y = maxY; pointMax.Z = maxZ; } #if DEBUG const string DLL_FILE = "GridUtility.dll"; #else const string DLL_FILE = "GridUtility.dll"; #endif [DllImport(DLL_FILE, EntryPoint = "SetGridData", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern void SetGridData(double xMin, double yMin, double[] pDataZ , double nCols, int nRows, double dx, double dy , double zMin, double zMax, int insertTimes , double[] borderData, int borderDataSize); [DllImport(DLL_FILE, EntryPoint = "SaveDrawFile", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern bool SaveDrawFile(string outputFile, double contourStep, int contourMarkStep); [DllImport(DLL_FILE, EntryPoint = "Destroy", ExactSpelling = false, CallingConvention = CallingConvention.Cdecl)] public static extern void DestoryXy(); } }