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();
}
}