using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Text; using System.Threading; using DfdIO; using NetTopologySuite.Geometries; using NetTopologySuite.Operation.Distance; using NetTopologySuite.Precision; using NetTopologySuite.Operation.Relate; using System.Diagnostics; using NetTopologySuite.Algorithm; using System.Runtime.InteropServices; using NeighborInterpolator; using System.Security.Permissions; using System.Data.SqlClient; using NetTopologySuite.Index.Strtree; namespace Construction.BatchCreateMap { public delegate void ProgressEvent(string eventName, int curentStep); public class LayerCreate { private const double Zero = 1E-5; public ProgressEvent ProgressChanged; public static string ExePath { get; set; } = string.Empty; public static string XYZPath => Path.Combine(ExePath, "GridModel.exe"); public static string CurveModelPath => Path.Combine(ExePath, "CurveModel.exe"); private Encoding GB2312 => Encoding.GetEncoding("GB2312"); // ───────────────────────────────────────────────────────────────────── // 进程管理 // ───────────────────────────────────────────────────────────────────── private List listProcessIds = new List(); public void KillProgress() { for (int i = listProcessIds.Count - 1; i >= 0; i--) { try { Process process = Process.GetProcessById(listProcessIds[i]); process.Kill(); process.WaitForExit(500); } catch (ArgumentException) { // 进程已退出,忽略 } catch (System.ComponentModel.Win32Exception) { // 权限不足等系统异常,忽略 } listProcessIds.RemoveAt(i); } } /// 网格化处理线程 public static Thread ProcessThread { get; set; } = null; public static uint ProcessThreadId { get; set; } = 0; [Flags] public enum ThreadAccess : int { TERMINATE = 0x0001, SUSPEND_RESUME = 0x0002, GET_CONTEXT = 0x0008, SET_CONTEXT = 0x0010, SET_INFORMATION = 0x0020, QUERY_INFORMATION = 0x0040, SET_THREAD_TOKEN = 0x0080, IMPERSONATE = 0x0100, DIRECT_IMPERSONATION= 0x0200 } [DllImport("kernel32.dll")] static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); [DllImport("kernel32.dll")] private static extern uint GetCurrentThreadId(); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool TerminateThread(IntPtr hThread, uint dwExitCode); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool SetDllDirectory(string lpPathName); /// 终止网格化处理线程 [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public bool AboutGridProcess() { if (ProcessThread != null) { ProcessThread.Interrupt(); ProcessThread.Abort(); ProcessThread.Join(); } return false; } // ───────────────────────────────────────────────────────────────────── // 核心:按参数创建地层图 // ───────────────────────────────────────────────────────────────────── /// /// 按参数创建地层图。 /// /// /// 处理标识: /// 0 — 网格化 + 等值线 + 断层内等值线清理 + 断层搭接; /// 1 — 网格化 + 等值线 + 断层内等值线清理; /// 2 — 网格化 + 等值线; /// 3 — 仅网格化。 /// 默认值为 0。 /// public bool CreateLayerMapWithPara(GridCreateParameter para, string outputDir, string fileName, int flag = 0) { ProcessThread = null; if (!outputDir.EndsWith("\\")) outputDir += "\\"; string strInput = Convert2AbsolutePath(para.DataRootPath, para.Input); string strOutput = Convert2AbsolutePath(para.DataRootPath, para.Output); string strOutline = Convert2AbsolutePath(para.DataRootPath, para.Outline); strOutline = string.IsNullOrEmpty(strOutline) ? "NULL" : strOutline; string strFaultFile = Convert2AbsolutePath(para.DataRootPath, para.Faultage); strFaultFile = string.IsNullOrEmpty(strFaultFile) ? "NULL" : strFaultFile; string strCurveFile = $"{outputDir}{fileName}.kev"; string strDfgFile = $"{outputDir}{fileName}.dfg"; var ctx = new GridCreateContext { Parameter = para, InputFile = strInput, OutputFile = strOutput, OutlineFile = strOutline, FaultFile = strFaultFile, CurveFile = strCurveFile, DfgFile = strDfgFile, OutputDir = outputDir, FileName = fileName, Flag = flag, ProgressChanged = ProgressChanged, ProcessIds = listProcessIds, GridModelExePath = XYZPath, CurveModelExePath = CurveModelPath, RunPostProcessing = (faultFile) => processAfterGrid(outputDir, fileName, flag, faultFile, strCurveFile, strDfgFile), AppendFile = AppendFile, }; return GridCreatorFactory.Get(para.CreateType).Execute(ctx); } private bool processAfterGrid(string outputDir, string fileName, int flag, string strFaultFile, string strCurveFile, string strDfgFile) { if (flag >= 2) return true; // 断层内等值线清理 if (ClearLineInFault(strCurveFile, "等值线", "断层") == false) return true; // 断层搭接 if (flag >= 1) return true; // 读取网格步长 DfgReader dfg = new DfgReader(strDfgFile); dfg.ReadHead(); double dStep = Math.Max(dfg.DeltX, dfg.DeltY) * 2; List extendCurves = LineOfExtend2Fault(dStep, strCurveFile, strFaultFile, "等值线", "断层"); string strCurveFileBak = CreateBackupFileName(strCurveFile); File.Copy(strCurveFile, strCurveFileBak, true); DfdWriter dfdWriter = new DfdWriter(strCurveFile); dfdWriter.WriteDfg(strDfgFile, "背景", false); dfdWriter.WriteDfdLine(extendCurves, true); if (strFaultFile != null && strFaultFile.Length > 0) AppendFile(strCurveFile, strFaultFile); return true; } // ───────────────────────────────────────────────────────────────────── // 边界生成 // ───────────────────────────────────────────────────────────────────── public bool CreateBorder(string xyzFile, string outFile) { try { List coordinates = new List(); using (StreamReader sr = new StreamReader(xyzFile, GB2312)) { double dX = 0, dY = 0; while (sr.Peek() > -1) { string strLine = sr.ReadLine(); string[] straXY = strLine.Split(','); if (straXY.Length < 3) continue; if (double.TryParse(straXY[0], out dX) && double.TryParse(straXY[1], out dY)) { coordinates.Add(new Coordinate(dX, dY)); } } } var convexHullOp = new ConvexHull(coordinates, GeometryFactory.Default); var ch = convexHullOp.GetConvexHull(); DfLine dfLine = new DfLine(); foreach (Coordinate coor in ch.Coordinates) { dfLine.Points.Add(new DfPoint(coor.X, coor.Y)); } dfLine.Layer = "边界"; DfdWriter dfdWriter = new DfdWriter(outFile); dfdWriter.WriteDfdLine(new List() { dfLine }, false); return true; } catch { return false; } } public bool CreateBorder(DataTable data, string fileName) { try { List coordinates = new List(); double dX = 0, dY = 0; for (int i = 0; i < data.Rows.Count; i++) { if (Double.TryParse(data.Rows[i]["X"] + "", out dX) && Double.TryParse(data.Rows[i]["Y"] + "", out dY)) { coordinates.Add(new Coordinate(dX, dY)); } } GeometryFactory factory = new GeometryFactory(); MultiPoint pts = factory.CreateMultiPointFromCoords(coordinates.ToArray()); Geometry hull = pts.ConvexHull(); List lstDfPoints = new List(); for (int i = 0; i < hull.Coordinates.Count(); i++) { lstDfPoints.Add(new DfPoint(hull.Coordinates[i].X, hull.Coordinates[i].Y)); } DfLine dfLine = new DfLine(lstDfPoints); dfLine.Layer = "边界"; DfdWriter dfdWriter = new DfdWriter(fileName); dfdWriter.WriteDfdLine(new List() { dfLine }, false); return true; } catch { return false; } } // ───────────────────────────────────────────────────────────────────── // 断层搭接 // ───────────────────────────────────────────────────────────────────── /// 等值线搭接断层(按网格文件读取步长) public void Extend2Fault(string gridFile, string curveFile, string faultFile, string outFile, string curveLayer = "等值线", string faultLayer = "断层") { DfgReader dfg = new DfgReader(gridFile); dfg.ReadHead(); double dStep = Math.Max(dfg.DeltX, dfg.DeltY) * 2; WriteExtendedCurves(dStep, curveFile, faultFile, outFile, curveLayer, faultLayer); } /// 等值线搭接断层(直接指定步长) public void Extend2Fault(double dStep, string curveFile, string faultFile, string outFile, string curveLayer = "等值线", string faultLayer = "断层") { WriteExtendedCurves(dStep, curveFile, faultFile, outFile, curveLayer, faultLayer); } private void WriteExtendedCurves(double dStep, string curveFile, string faultFile, string outFile, string curveLayer, string faultLayer) { List extendCurves = LineOfExtend2Fault(dStep, curveFile, faultFile, curveLayer, faultLayer); DfdWriter dfdWriter = new DfdWriter(outFile); dfdWriter.WriteDfdLine(extendCurves, true); if (faultFile != null && faultFile.Length > 0) AppendFile(outFile, faultFile); } /// 对已存在文件中的等值线做就地断层搭接处理 public bool LineOfExtend2Fault(string fileName, double dStep, string curveLayer = "等值线", string faultLayer = "断层") { DfdReader curveReader = new DfdReader(fileName); List dfFaults = curveReader.GetDfdLinesByLayer(faultLayer); List cooFaults = BuildFaultGeometries(dfFaults, minPoints: 4); DfdElementReader elementReader = new DfdElementReader(fileName); string strFileBack = CreateBackupFileName(fileName); StreamWriter sw = new StreamWriter(strFileBack, false, GB2312); string strLayerSub = curveLayer + "\\"; string strLine; while ((strLine = elementReader.ReadLine()) != null) { if ((elementReader.LayerName == curveLayer || elementReader.LayerName.StartsWith(strLayerSub)) && elementReader.CurElementType == DfdIO.Elements.ElementType.ELEMENT_LINE) { DfLine eleLine = elementReader.ReadLineElement(); if (eleLine.Points.Count < 2) { elementReader.CurElementType = DfdIO.Elements.ElementType.ELEMENT_NONE; } else { DfLine lineNew = new DfLine(); Extent2Fault(eleLine, cooFaults, dStep, lineNew); sw.WriteLine(lineNew.ToString(false)); elementReader.CurElementType = DfdIO.Elements.ElementType.ELEMENT_NONE; } } else { sw.WriteLine(strLine); } } sw.Flush(); sw.Close(); elementReader.EndReader(); File.Delete(fileName); File.Copy(strFileBack, fileName); return true; } private List LineOfExtend2Fault(double dStep, string curveFile, string faultFile, string curveLayer = "等值线", string faultLayer = "断层") { DfdReader curveReader = new DfdReader(curveFile); if (faultFile == null || faultFile.Length == 0) return curveReader.GetDfdLinesByLayer(curveLayer, 1); List dfCurves = curveReader.GetDfdLinesByLayer(curveLayer, 1); List dfFaults = new DfdReader(faultFile).GetDfdLinesByLayer(faultLayer); List cooFaults = BuildFaultGeometries(dfFaults); List dfLinesNew = new List(); int nCurvesCount = dfCurves.Count; int nIndex = 0; foreach (DfLine line in dfCurves) { int nPtCount = line.Points.Count; if (nPtCount < 2) { nIndex++; continue; } DfLine lineNew = line.Copy(); double dDistanceStart = dStep, dDistanceEnd = dStep; LineString faultStart = null, faultEnd = null; Coordinate ptStart1 = new Coordinate(line.Points[1].X, line.Points[1].Y); Coordinate ptStart2 = new Coordinate(line.Points[0].X, line.Points[0].Y); Coordinate ptEnd1 = new Coordinate(line.Points[nPtCount - 2].X, line.Points[nPtCount - 2].Y); Coordinate ptEnd2 = new Coordinate(line.Points[nPtCount - 1].X, line.Points[nPtCount - 1].Y); for (int i = 0; i < cooFaults.Count; i++) { double dDist = new DistanceOp(cooFaults[i], new Point(ptStart2)).Distance(); if (dDist <= dDistanceStart) { dDistanceStart = dDist; faultStart = cooFaults[i]; } dDist = new DistanceOp(cooFaults[i], new Point(ptEnd2)).Distance(); if (dDist <= dDistanceEnd) { dDistanceEnd = dDist; faultEnd = cooFaults[i]; } } if (faultStart != null) { var crossPt = GetCrossPt(ptStart1, ptStart2, faultStart, dStep); lineNew.Points.Insert(0, new DfPoint(crossPt.X, crossPt.Y)); } if (faultEnd != null) { var crossPt = GetCrossPt(ptEnd1, ptEnd2, faultEnd, dStep); lineNew.Points.Add(new DfPoint(crossPt.X, crossPt.Y)); } dfLinesNew.Add(lineNew); nIndex++; ProgressChanged?.Invoke("断层搭接", (int)(((double)nIndex / nCurvesCount) * 100)); } return dfLinesNew; } private bool Extent2Fault(DfLine line, List cooFaults, double dStep, DfLine lineDes) { bool bSuccess = false; int nPtCount = line.Points.Count; line.CopyTo(lineDes); double dDistanceStart = dStep, dDistanceEnd = dStep; LineString faultStart = null, faultEnd = null; Coordinate ptStart1 = new Coordinate(line.Points[1].X, line.Points[1].Y); Coordinate ptStart2 = new Coordinate(line.Points[0].X, line.Points[0].Y); Coordinate ptEnd1 = new Coordinate(line.Points[nPtCount - 2].X, line.Points[nPtCount - 2].Y); Coordinate ptEnd2 = new Coordinate(line.Points[nPtCount - 1].X, line.Points[nPtCount - 1].Y); if (ptStart1.Equals2D(ptStart2) || ptEnd1.Equals2D(ptEnd2)) return false; bool bSkipHead = false, bSkipTail = false; for (int i = 0; i < cooFaults.Count; i++) { if (!bSkipHead) { double dDistance = new DistanceOp(cooFaults[i], new Point(ptStart2)).Distance(); if (dDistance <= dDistanceStart) { if (dDistance < Zero) bSkipHead = true; else { dDistanceStart = dDistance; faultStart = cooFaults[i]; } } } if (!bSkipTail) { double dDistance = new DistanceOp(cooFaults[i], new Point(ptEnd2)).Distance(); if (dDistance <= dDistanceEnd) { if (dDistance < Zero) bSkipTail = true; else { dDistanceEnd = dDistance; faultEnd = cooFaults[i]; } } } } if (faultStart != null && !bSkipHead) { var crossPt = GetCrossPt(ptStart1, ptStart2, faultStart, dStep); lineDes.Points.Insert(0, new DfPoint(crossPt.X, crossPt.Y)); bSuccess = true; } if (faultEnd != null && !bSkipTail) { var crossPt = GetCrossPt(ptEnd1, ptEnd2, faultEnd, dStep); lineDes.Points.Add(new DfPoint(crossPt.X, crossPt.Y)); bSuccess = true; } return bSuccess; } /// 将断层线列表转换为 NTS LineString 集合 private List BuildFaultGeometries(List dfFaults, int minPoints = 2) { var result = new List(); foreach (DfLine fault in dfFaults) { int nCount = fault.Points.Count; if (nCount < minPoints) continue; Coordinate[] coordinates = new Coordinate[nCount]; for (int i = 0; i < nCount; i++) coordinates[i] = new Coordinate(fault.Points[i].X, fault.Points[i].Y); result.Add(new LineString(coordinates)); } return result; } // ───────────────────────────────────────────────────────────────────── // 断层内等值线清理 // ───────────────────────────────────────────────────────────────────── public bool ClearLineInFault(string fileName, string curveLayer = "等值线", string faultLayer = "断层") { DfdReader curveReader = new DfdReader(fileName); List dfFaults = curveReader.GetDfdLinesByLayer(faultLayer); if (dfFaults == null || dfFaults.Count == 0) return false; List cooFaults = new List(); foreach (DfLine fault in dfFaults) { if (!fault.IsPolyline) continue; fault.CloseData(); int nCount = fault.Points.Count; if (nCount < 4) continue; Coordinate[] coordinates = new Coordinate[nCount]; for (int i = 0; i < nCount; i++) coordinates[i] = new Coordinate(fault.Points[i].X, fault.Points[i].Y); cooFaults.Add(new Polygon(new LinearRing(coordinates))); } DfdElementReader elementReader = new DfdElementReader(fileName); string strFileBack = CreateBackupFileName(fileName); StreamWriter sw = new StreamWriter(strFileBack, false, GB2312); string strLayerSub = curveLayer + "\\"; string strLine; while ((strLine = elementReader.ReadLine()) != null) { if ((elementReader.LayerName == curveLayer || elementReader.LayerName.StartsWith(strLayerSub)) && elementReader.CurElementType == DfdIO.Elements.ElementType.ELEMENT_LINE) { DfLine eleLine = elementReader.ReadLineElement(); if (eleLine.Points.Count < 2 || isLineInFaults(cooFaults, eleLine)) { elementReader.CurElementType = DfdIO.Elements.ElementType.ELEMENT_NONE; } else { sw.WriteLine(eleLine.ToString(false)); elementReader.CurElementType = DfdIO.Elements.ElementType.ELEMENT_NONE; } } else { sw.WriteLine(strLine); } } sw.Flush(); sw.Close(); elementReader.EndReader(); File.Delete(fileName); File.Copy(strFileBack, fileName); File.Delete(strFileBack); return true; } private bool isLineInFaults(List cooFaults, DfLine eleLine) { int nCount = eleLine.Points.Count; Coordinate[] points = new Coordinate[nCount]; for (int i = 0; i < nCount; i++) points[i] = new Coordinate(eleLine.Points[i].X, eleLine.Points[i].Y); LineString cooLine = new LineString(points); foreach (Polygon polygon in cooFaults) { if (RelateOp.Relate(polygon, cooLine).IsCovers()) return true; } return false; } // ───────────────────────────────────────────────────────────────────── // 等值线搭接断层(就地修改绘图文件) // ───────────────────────────────────────────────────────────────────── public void Curve2Fault(string drawFile, string curveLayer, string faultLayer) { DfdReader faultReader = new DfdReader(drawFile); List dfFaults = faultReader.GetDfdLinesByLayer(faultLayer); string strDfgFile = Path.Combine( Path.GetDirectoryName(drawFile), Path.GetFileNameWithoutExtension(drawFile) + ".cfg"); if (!File.Exists(strDfgFile)) { strDfgFile = Path.Combine( Path.GetDirectoryName(drawFile), Path.GetFileNameWithoutExtension(drawFile) + ".dfg"); } DfgReader dfg = new DfgReader(strDfgFile); dfg.ReadHead(); double dStep = Math.Max(dfg.DeltX, dfg.DeltY) * 2; List extendCurves = LineOfExtend2Fault(dStep, drawFile, drawFile, curveLayer); DfdWriter dfdWriter = new DfdWriter(drawFile); dfdWriter.WriteDfg(strDfgFile, "背景", false); dfdWriter.WriteDfdLine(extendCurves, true); dfdWriter.WriteDfdLine(dfFaults, true); } // ───────────────────────────────────────────────────────────────────── // 几何辅助 // ───────────────────────────────────────────────────────────────────── private Coordinate GetCrossPt(Coordinate pt1, Coordinate pt2, Geometry fault, double offset) { double dR = Math.Sqrt((pt2.Y - pt1.Y) * (pt2.Y - pt1.Y) + (pt2.X - pt1.X) * (pt2.X - pt1.X)); double dT = dR + offset; double dXOffset = pt1.X + (pt2.X - pt1.X) / dR * dT; double dYOffset = pt1.Y + (pt2.Y - pt1.Y) / dR * dT; Coordinate[] linePts = { pt2, new Coordinate(dXOffset, dYOffset) }; LineString linear = new LineString(linePts); var crossPts = EnhancedPrecisionOp.Intersection(linear, fault); if (crossPts.Coordinates.Length == 0) return linePts[0]; if (crossPts.Coordinates.Length == 1) return crossPts.Coordinates[0]; // 返回距离最近的交叉点 double dDistanceNear = double.MaxValue; Coordinate cooNear = crossPts.Coordinates[0]; foreach (Coordinate coor in crossPts.Coordinates) { double dDis1 = (pt1.X - coor.X) * (pt1.X - coor.X) + (pt1.Y - coor.Y) * (pt1.Y - coor.Y); double dDis2 = (pt2.X - coor.X) * (pt2.X - coor.X) + (pt2.Y - coor.Y) * (pt2.Y - coor.Y); if (dDis1 < dDistanceNear) { dDistanceNear = dDis1; cooNear = coor; } if (dDis2 < dDistanceNear) { dDistanceNear = dDis2; cooNear = coor; } } return cooNear; } // ───────────────────────────────────────────────────────────────────── // 文件辅助 // ───────────────────────────────────────────────────────────────────── public void AppendFile(string fileSource, string fileAppend) { string strAppendData; using (StreamReader sr = new StreamReader(fileAppend, GB2312)) { strAppendData = sr.ReadToEnd(); } using (StreamWriter sw = new StreamWriter(fileSource, true, GB2312)) { sw.WriteLine(strAppendData); } } /// 在指定目录及其子目录中递归查找指定名称的文件 public string SearchFolder(string dirName, string layerName) { DirectoryInfo dir = new DirectoryInfo(dirName); try { foreach (DirectoryInfo d in dir.GetDirectories()) { string strFind = SearchFolder(dir + "\\" + d.ToString(), layerName); if (strFind.Length > 0) return strFind; } foreach (FileInfo f in dir.GetFiles("*.*")) { if (Path.GetFileNameWithoutExtension(f.Name) .Equals(layerName, StringComparison.CurrentCultureIgnoreCase)) { return f.FullName; } } } catch (Exception e) { Console.WriteLine(e.Message); } return ""; } // ───────────────────────────────────────────────────────────────────── // 路径辅助 // ───────────────────────────────────────────────────────────────────── public string Convert2RelativePath(string root, string fullPath) { if (string.IsNullOrWhiteSpace(fullPath)) return string.Empty; if (fullPath.StartsWith(root, StringComparison.CurrentCultureIgnoreCase)) return fullPath.Remove(0, root.Length + 1); return fullPath; } public string Convert2AbsolutePath(string root, string relativePath) { if (string.IsNullOrWhiteSpace(relativePath)) return string.Empty; if (string.IsNullOrEmpty(root)) return relativePath; if (!relativePath.StartsWith(root, StringComparison.CurrentCultureIgnoreCase)) return Path.Combine(root, relativePath); return relativePath; } public static string CreateBackupFileName(string sourceFile) { try { if (string.IsNullOrEmpty(sourceFile)) return null; return Path.Combine( Path.GetDirectoryName(sourceFile), Path.GetFileNameWithoutExtension(sourceFile) + "_bak" + Path.GetExtension(sourceFile)); } catch (Exception ex) { Trace.WriteLine(ex.Message); return null; } } } public class LayerXyz { public double X { get; set; } public double Y { get; set; } public double Z { get; set; } } }