You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

730 lines
32 KiB
C#

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<int> listProcessIds = new List<int>();
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);
}
}
/// <summary>网格化处理线程</summary>
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);
/// <summary>终止网格化处理线程</summary>
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)]
public bool AboutGridProcess()
{
if (ProcessThread != null)
{
ProcessThread.Interrupt();
ProcessThread.Abort();
ProcessThread.Join();
}
return false;
}
// ─────────────────────────────────────────────────────────────────────
// 核心:按参数创建地层图
// ─────────────────────────────────────────────────────────────────────
/// <summary>
/// 按参数创建地层图。
/// </summary>
/// <param name="flag">
/// 处理标识:
/// 0 — 网格化 + 等值线 + 断层内等值线清理 + 断层搭接;
/// 1 — 网格化 + 等值线 + 断层内等值线清理;
/// 2 — 网格化 + 等值线;
/// 3 — 仅网格化。
/// 默认值为 0。
/// </param>
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<DfLine> 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<Coordinate> coordinates = new List<Coordinate>();
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>() { dfLine }, false);
return true;
}
catch
{
return false;
}
}
public bool CreateBorder(DataTable data, string fileName)
{
try
{
List<Coordinate> coordinates = new List<Coordinate>();
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<DfPoint> lstDfPoints = new List<DfPoint>();
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>() { dfLine }, false);
return true;
}
catch
{
return false;
}
}
// ─────────────────────────────────────────────────────────────────────
// 断层搭接
// ─────────────────────────────────────────────────────────────────────
/// <summary>等值线搭接断层(按网格文件读取步长)</summary>
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);
}
/// <summary>等值线搭接断层(直接指定步长)</summary>
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<DfLine> 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);
}
/// <summary>对已存在文件中的等值线做就地断层搭接处理</summary>
public bool LineOfExtend2Fault(string fileName, double dStep,
string curveLayer = "等值线", string faultLayer = "断层")
{
DfdReader curveReader = new DfdReader(fileName);
List<DfLine> dfFaults = curveReader.GetDfdLinesByLayer(faultLayer);
List<LineString> 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<DfLine> 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<DfLine> dfCurves = curveReader.GetDfdLinesByLayer(curveLayer, 1);
List<DfLine> dfFaults = new DfdReader(faultFile).GetDfdLinesByLayer(faultLayer);
List<LineString> cooFaults = BuildFaultGeometries(dfFaults);
List<DfLine> dfLinesNew = new List<DfLine>();
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<LineString> 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;
}
/// <summary>将断层线列表转换为 NTS LineString 集合</summary>
private List<LineString> BuildFaultGeometries(List<DfLine> dfFaults, int minPoints = 2)
{
var result = new List<LineString>();
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<DfLine> dfFaults = curveReader.GetDfdLinesByLayer(faultLayer);
if (dfFaults == null || dfFaults.Count == 0)
return false;
List<Polygon> cooFaults = new List<Polygon>();
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<Polygon> 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<DfLine> 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<DfLine> 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);
}
}
/// <summary>在指定目录及其子目录中递归查找指定名称的文件</summary>
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; }
}
}