using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geometry; using KGIS.Framework.Utils.ExtensionMethod; namespace Kingo.PluginServiceInterface.Helper { /// /// 多边形边界对齐工具(针对ArcEngine 10.2.2优化版本) /// 主要功能:调整目标多边形的顶点坐标,使其与相邻几何体的边界对齐 /// 优化重点:空间索引结构、COM对象管理、批量坐标操作 /// public static class PolygonBoundaryAligner { #region 公共常量与接口 private const double DEFAULT_TOLERANCE = 0.001; // 默认捕捉容差(地图单位) private static readonly object _syncRoot = new object(); // 线程安全锁 #endregion #region 主入口方法 /// /// 对齐多边形边界的主方法 /// /// 目标多边形 /// 相邻几何体集合 /// 捕捉容差(默认0.001) /// 调整后的多边形 public static IGeometry AlignPolygonBoundary(IGeometry target, List adjacentGeometries, double tolerance = DEFAULT_TOLERANCE) { // 参数校验 ValidateParameters(target, adjacentGeometries); // 克隆原始几何体(避免修改输入参数) IPolygon originalPolygon = (IPolygon)((IClone)target).Clone(); IPolygon newPolygon = new PolygonClass(); newPolygon.SpatialReference = originalPolygon.SpatialReference; try { // 步骤1:收集相邻几何体的边界要素 List adjacentBoundaries = CollectAdjacentBoundaries(adjacentGeometries); // 步骤2:构建空间索引(顶点索引和线段索引) ITopologicalOperator topological = originalPolygon as ITopologicalOperator; var (vertexIndex, segmentIndex) = BuildSpatialIndexes(adjacentBoundaries, topological.Boundary, tolerance); if (vertexIndex.IsEmpty && segmentIndex.IsEmpty) { return target; //(vertexIndex, segmentIndex) = BuildSpatialIndexes(adjacentBoundaries, tolerance); } // 步骤3:调整多边形顶点 //IPointCollection points = (IPointCollection)originalPolygon; List points = new List(); IGeometryCollection geometryCollection = (IGeometryCollection)originalPolygon; for (int i = 0; i < geometryCollection.GeometryCount; i++) { IRing ring = (IRing)geometryCollection.get_Geometry(i); IPointCollection ringPoints = (IPointCollection)ring; points.Add(ringPoints); } newPolygon = AdjustWithLegacyMethod(points, vertexIndex, segmentIndex, tolerance, adjacentBoundaries); // 步骤4:拓扑验证与简化 EnsureTopologyValidity(ref newPolygon); return (IGeometry)newPolygon; } catch (Exception ex) { throw ex; } finally { // 显式释放COM对象(重要!防止内存泄漏) SafeReleaseComObject(originalPolygon); } } #endregion #region 核心处理逻辑 /// /// 获取调整后的所有环(外环+内环) /// private static IEnumerable GetAdjustedRings(IPolygon polygon, VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex, double tolerance, List boundaries) { IGeometryCollection geomColl = (IGeometryCollection)polygon; for (int i = 0; i < geomColl.GeometryCount; i++) { if (geomColl.get_Geometry(i) is IRing ring && !ring.IsEmpty) { yield return AdjustRingVertices(ring, vertexIndex, segmentIndex, tolerance, boundaries); } } } private static IPolygon AdjustRingVertices(IRing ring, VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex, double tolerance, List boundaries) { // 获取点集合接口(兼容10.2.2) IPointCollection points = (IPointCollection)ring; return AdjustWithLegacyMethod(new List { points }, vertexIndex, segmentIndex, tolerance, boundaries); } /// /// 兼容方法:传统逐个点操作(ArcEngine 10.0 以下备用) /// private static IPolygon AdjustWithLegacyMethod(List allPoints, VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex, double tolerance, List boundaries) { IPolygon newPolygon = new PolygonClass(); IGeometryCollection newGeomColl = (IGeometryCollection)newPolygon; for (int k = 0; k < allPoints.Count; k++) { int pointCount = allPoints[k].PointCount; bool modified = false; var coords = new List<(double X, double Y)>(); for (int i = 0; i < pointCount; i++) { IPoint p = allPoints[k].get_Point(i); //string aa1 = GeometryConvertHelper.ConvertIGeoemtryToWKT(p); try { coords.Add((p.X, p.Y)); } finally { Marshal.ReleaseComObject(p); } } // 坐标调整逻辑 Dictionary keyValuePairs = new Dictionary(); for (int i = 0; i < coords.Count; i++) { var (x, y) = coords[i]; var snappedPoints = vertexIndex.FindAllVertices(x, y, tolerance).Distinct().ToList(); if (snappedPoints != null && snappedPoints.Count == 2) { double distSq0 = (coords[i].X - snappedPoints[0].X) * (coords[i].X - snappedPoints[0].X) + (coords[i].Y - snappedPoints[0].Y) * (coords[i].Y - snappedPoints[0].Y); double distSq1 = (coords[i].X - snappedPoints[1].X) * (coords[i].X - snappedPoints[1].X) + (coords[i].Y - snappedPoints[1].Y) * (coords[i].Y - snappedPoints[1].Y); if (distSq0 > distSq1) { coords[i] = snappedPoints[1]; keyValuePairs.Add(i, snappedPoints[0]); } else { coords[i] = snappedPoints[0]; keyValuePairs.Add(i, snappedPoints[1]); } continue; } var snappedPoint = vertexIndex.FindClosestVertex(x, y, tolerance); if (snappedPoint == null) { snappedPoint = segmentIndex.FindClosestPoint(x, y, tolerance); } else { var currents = coords.Where(current => current.X != x && current.Y != y).ToList(); var coordscoords = currents.FirstOrDefault(kvp => Math.Abs(kvp.X - x) < 0.001 && Math.Abs(kvp.Y - y) < 0.001); if ((coords.Contains(snappedPoint.Value) && coords[0] != snappedPoint.Value) || (coordscoords.X != 0 && coordscoords.Y != 0)) { snappedPoint = segmentIndex.FindClosestPoint(x, y, tolerance); } } if (snappedPoint.HasValue) { if (coords.Contains(snappedPoint.Value) && coords[0] != snappedPoint.Value) { int firstIndex = coords.Select((item, idx) => new { item, idx }). FirstOrDefault(pair => pair.item.X == snappedPoint.Value.X && pair.item.Y == snappedPoint.Value.Y)?.idx ?? -1; if (firstIndex != -1 && i > firstIndex) { coords[i] = (0, 0); continue; } } var currents = coords.Where(current => current.X != x && current.Y != y).ToList(); var coordscoords = currents.FirstOrDefault(kvp => Math.Abs(kvp.X - x) < 0.1 && Math.Abs(kvp.Y - y) < 0.1); //var dSnappedPoint = CalculateDistance(snappedPoint.Value.X, snappedPoint.Value.Y, x, y); //var dCoordscoords = CalculateDistance(coordscoords.X, coordscoords.Y, x, y); //var gap = Math.Abs(dSnappedPoint - dCoordscoords); //if (coordscoords.X != 0 && coordscoords.Y != 0 && dSnappedPoint > dCoordscoords && gap > 0.001) // continue; coords[i] = snappedPoint.Value; modified = true; } } // 更新坐标 if (modified) { allPoints[k].RemovePoints(0, pointCount); IPoint newPoint = new PointClass(); if (keyValuePairs.Count > 0) { foreach (var item in keyValuePairs) { coords.Insert(item.Key, item.Value); } } foreach (var (X, Y) in coords) { if (X == 0 || Y == 0) continue; newPoint.PutCoords(X, Y); allPoints[k].AddPoint(newPoint); } Marshal.ReleaseComObject(newPoint); } newGeomColl.AddGeometry((IGeometry)allPoints[k]); string aa = GeometryConvertHelper.ConvertIGeoemtryToWKT(newPolygon); Addnode(newPolygon, boundaries); } return newPolygon; //IGeometryCollection geomColl = (IGeometryCollection)newPolygon; //for (int i = 0; i < geomColl.GeometryCount; i++) //{ // IGeometry geometry = geomColl.get_Geometry(i); // IRing ring1 = geometry as IRing; // if (ring1 == null || ring1.IsEmpty || ring1.Length < 0.01) continue; // return ring1; //} //return null; } public static double CalculateDistance(double x1, double y1, double x2, double y2) { // 计算坐标差值的平方和 double deltaX = x2 - x1; double deltaY = y2 - y1; double distanceSquared = deltaX * deltaX + deltaY * deltaY; // 开平方根得到距离(使用Math.Sqrt) return Math.Sqrt(distanceSquared); } #endregion #region 空间索引构建 private static (VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex) BuildSpatialIndexes(List boundaries, IGeometry targetBoundary, double tolerance) { double xRange = targetBoundary.Envelope.XMax - targetBoundary.Envelope.XMin; double yRange = targetBoundary.Envelope.YMax - targetBoundary.Envelope.YMin; int totalVertices = boundaries.Sum(g => (g as IPointCollection)?.PointCount ?? 0); double density = totalVertices / (xRange * yRange); // 顶点密度(点/单位面积) double baseCellSize = Math.Max(xRange, yRange) / 100; double cellSize = density > 100 ? baseCellSize / 2 : // 高密度:缩小网格 density < 10 ? baseCellSize * 2 : // 低密度:扩大网格 baseCellSize; var vertexIndex = new VertexSpatialIndex(cellSize); var segmentIndex = new SegmentSpatialIndex(cellSize); double maxDistance = tolerance * 20; // 设定最大距离阈值 int threshold = Math.Max(Environment.ProcessorCount * 100, 1000); // 最小阈值1000 foreach (var geom in boundaries) { if (geom is IPointCollection pointColl) { if (pointColl.PointCount > threshold) { Parallel.For(0, pointColl.PointCount, i => { IPoint point = pointColl.get_Point(i); try { double distance = CalculateDistance(point, targetBoundary); if (distance <= maxDistance) { vertexIndex.AddPoint(point); // 线程安全 } } finally { Marshal.ReleaseComObject(point); } }); } else { for (int i = 0; i < pointColl.PointCount; i++) { IPoint point = pointColl.get_Point(i); try { // 计算点到目标边界的距离 double distance = CalculateDistance(point, targetBoundary); if (distance <= maxDistance) { vertexIndex.AddPoint(point); } } finally { Marshal.ReleaseComObject(point); } } } } if (geom is ISegmentCollection segColl) { for (int i = 0; i < segColl.SegmentCount; i++) { ISegment segment = segColl.get_Segment(i); try { IPolyline polyline1 = new PolylineClass(); polyline1.FromPoint = segment.FromPoint; polyline1.ToPoint = segment.ToPoint; polyline1.SpatialReference = targetBoundary.SpatialReference; double distancepolyline1 = CalculateDistance(polyline1, targetBoundary); if (distancepolyline1 <= maxDistance && distancepolyline1 >= 0) { segmentIndex.AddSegment(segment); } } catch { } finally { Marshal.ReleaseComObject(segment); } } } } return (vertexIndex, segmentIndex); } // 计算几何体到目标边界的距离 private static double CalculateDistance(IGeometry geom, IGeometry targetBoundary) { if (geom == null || targetBoundary == null) return double.MaxValue; //if (geom is IPoint point && targetBoundary is IPolygon polygon) //{ // return CalculateDistanceLocal(point, polygon); // 本地计算 //} // 其他类型回退到COM调用 try { IProximityOperator proximityOp = (IProximityOperator)targetBoundary; return proximityOp.ReturnDistance(geom); } catch { return double.MaxValue; } } #endregion #region 空间索引实现类 /// /// 顶点空间索引(使用双层字典的网格索引) /// 优化点:提高网格查询速度,减少距离计算次数 /// private class VertexSpatialIndex { private readonly Dictionary>> _grid; private readonly double _cellSize; private int _totalPoints = 0; public bool IsEmpty => _totalPoints == 0; public VertexSpatialIndex(double cellSize) { _cellSize = cellSize; _grid = new Dictionary>>(); } /// /// 添加顶点到索引(自动网格化) /// public void AddPoint(IPoint point) { long xCell = (long)(point.X / _cellSize); long yCell = (long)(point.Y / _cellSize); lock (_syncRoot) { if (!_grid.TryGetValue(xCell, out var yDict)) { yDict = new Dictionary>(); _grid[xCell] = yDict; } if (!yDict.TryGetValue(yCell, out var points)) { points = new List<(double X, double Y)>(); yDict[yCell] = points; } // 去重逻辑:检查容差范围内是否已有重复点 bool isDuplicate = false; double toleranceSq = _cellSize * _cellSize; // 基于网格精度的容差 foreach (var p in points) { double dx = p.X - point.X; double dy = p.Y - point.Y; if (dx * dx + dy * dy <= toleranceSq) // 平方距离比较(避免开方) { isDuplicate = true; break; } } if (!isDuplicate) { points.Add((point.X, point.Y)); _totalPoints++; } } } /// /// 查找最近顶点(带容差范围) /// public (double X, double Y)? FindClosestVertex(double x, double y, double tolerance) { long centerX = (long)(x / _cellSize); long centerY = (long)(y / _cellSize); int radius = (int)Math.Ceiling(tolerance / _cellSize); double minDist = tolerance * tolerance; (double X, double Y)? closest = null; // 网格搜索范围扩展 for (long dx = -radius; dx <= radius; dx++) { if (!_grid.TryGetValue(centerX + dx, out var yDict)) continue; for (long dy = -radius; dy <= radius; dy++) { if (!yDict.TryGetValue(centerY + dy, out var points)) continue; foreach (var (pX, pY) in points) { double dxVal = pX - x; double dyVal = pY - y; double distSq = dxVal * dxVal + dyVal * dyVal; if (distSq < minDist) { minDist = distSq; closest = (pX, pY); } } } } return closest; } /// /// 根据坐标获取相邻范围内的所有点 /// /// /// /// /// public List<(double X, double Y)> FindAllVertices(double x, double y, double tolerance) { long centerX = (long)(x / _cellSize); long centerY = (long)(y / _cellSize); int radius = (int)Math.Ceiling(tolerance / _cellSize); var result = new List<(double X, double Y)>(); double toleranceSq = tolerance * tolerance; // 遍历邻近网格 for (long dx = -radius; dx <= radius; dx++) { if (!_grid.TryGetValue(centerX + dx, out var yDict)) continue; for (long dy = -radius; dy <= radius; dy++) { if (!yDict.TryGetValue(centerY + dy, out var points)) continue; // 筛选所有容差内的点 foreach (var (pX, pY) in points) { double distSq = (pX - x) * (pX - x) + (pY - y) * (pY - y); if (distSq <= toleranceSq) { result.Add((pX, pY)); } } } } return result; } } /// /// 线段空间索引(使用网格索引+包围盒预计算) /// 优化点:减少不必要的线段距离计算 /// private class SegmentSpatialIndex { private readonly Dictionary>> _grid; private readonly List _allSegments; private readonly double _cellSize; public bool IsEmpty => _grid.Count == 0; public SegmentSpatialIndex(double cellSize) { _cellSize = cellSize; _grid = new Dictionary>>(); _allSegments = new List(); } /// /// 添加线段到索引(自动计算影响网格) /// public void AddSegment(ISegment segment) { var segData = new SegmentData(segment.FromPoint.X, segment.FromPoint.Y, segment.ToPoint.X, segment.ToPoint.Y); lock (_syncRoot) { _allSegments.Add(segData); // 全局列表存储引用 // 计算网格范围 long minXCell = (long)(Math.Min(segData.StartX, segData.EndX) / _cellSize); long maxXCell = (long)(Math.Max(segData.StartX, segData.EndX) / _cellSize); long minYCell = (long)(Math.Min(segData.StartY, segData.EndY) / _cellSize); long maxYCell = (long)(Math.Max(segData.StartY, segData.EndY) / _cellSize); // 添加到所有覆盖的网格(引用传递,无副本) for (long x = minXCell; x <= maxXCell; x++) { for (long y = minYCell; y <= maxYCell; y++) { if (!_grid.TryGetValue(x, out var yDict)) { yDict = new Dictionary>(); _grid[x] = yDict; } if (!yDict.TryGetValue(y, out var list)) { list = new List(); yDict[y] = list; } // 避免重复添加(若外部可能重复调用AddSegment) if (!list.Contains(segData)) { list.Add(segData); } } } } } /// /// 查找最近线段点(两阶段查询:先网格后全局) /// public (double X, double Y)? FindClosestPoint(double x, double y, double tolerance) { long centerX = (long)(x / _cellSize); long centerY = (long)(y / _cellSize); int radius = (int)Math.Ceiling(tolerance / _cellSize); var candidates = new HashSet(); double minDist = tolerance * tolerance; (double X, double Y)? closest = null; // 阶段1:收集附近网格中的候选线段 for (long dx = -radius; dx <= radius; dx++) { if (!_grid.TryGetValue(centerX + dx, out var yDict)) continue; for (long dy = -radius; dy <= radius; dy++) { if (yDict.TryGetValue(centerY + dy, out var segList)) { foreach (var seg in segList) { candidates.Add(seg); } } } } // 阶段2:如果附近没有候选,则遍历全部线段 if (candidates.Count == 0) { candidates = new HashSet(_allSegments); } // 计算最近点 foreach (var seg in candidates) { var (cx, cy, distSq) = CalculateClosestPoint(x, y, seg); if (distSq < minDist) { minDist = distSq; closest = (cx, cy); } } return closest; } /// /// 计算点到线段的最短距离(向量投影法) /// private static (double X, double Y, double DistanceSq) CalculateClosestPoint( double x, double y, SegmentData seg) { double dx = seg.EndX - seg.StartX; double dy = seg.EndY - seg.StartY; double lengthSq = dx * dx + dy * dy; // 处理零长度线段 if (lengthSq < 1e-10) { double distSq1 = (x - seg.StartX) * (x - seg.StartX) + (y - seg.StartY) * (y - seg.StartY); return (seg.StartX, seg.StartY, distSq1); } // 计算投影参数 double t = ((x - seg.StartX) * dx + (y - seg.StartY) * dy) / lengthSq; t = Math.Max(0, Math.Min(1, t)); // 限制在线段范围内 double projX = seg.StartX + t * dx; double projY = seg.StartY + t * dy; double distSq = (x - projX) * (x - projX) + (y - projY) * (y - projY); return (projX, projY, distSq); } /// /// 线段数据结构(引用类型) /// private sealed class SegmentData { public double StartX { get; } public double StartY { get; } public double EndX { get; } public double EndY { get; } public SegmentData(double sx, double sy, double ex, double ey) { StartX = sx; StartY = sy; EndX = ex; EndY = ey; } } } #endregion #region 辅助方法 /// /// 参数有效性校验 /// private static void ValidateParameters(IGeometry target, List adjacentGeometries) { if (target == null) throw new ArgumentNullException(nameof(target), "目标几何体不能为空"); if (adjacentGeometries == null) throw new ArgumentNullException(nameof(adjacentGeometries), "相邻几何体集合不能为空"); if (target.GeometryType != esriGeometryType.esriGeometryPolygon) throw new ArgumentException("目标几何体必须是多边形类型"); } /// /// 收集相邻几何体的边界要素(多线程安全) /// private static List CollectAdjacentBoundaries(IEnumerable geometries) { var boundaries = new ConcurrentBag(); // 线程安全集合 Parallel.ForEach(geometries.Where(g => g?.IsEmpty == false), geom => { switch (geom.GeometryType) { case esriGeometryType.esriGeometryPolygon: var polyColl = (IGeometryCollection)geom; for (int i = 0; i < polyColl.GeometryCount; i++) { if (polyColl.get_Geometry(i) is IRing ring && !ring.IsEmpty) { ring.SpatialReference = geom.SpatialReference; boundaries.Add(ring); // 线程安全添加 } } break; case esriGeometryType.esriGeometryPolyline: boundaries.Add(geom); break; } }); return boundaries.ToList(); // 转换为普通列表 } /// /// 确保多边形拓扑有效性 /// private static void EnsureTopologyValidity(ref IPolygon polygon) { ITopologicalOperator topoOp = (ITopologicalOperator)polygon; if (!topoOp.IsSimple) { topoOp.Simplify(); } // 二次验证几何完整性 if (polygon.IsEmpty) { throw new InvalidOperationException("拓扑简化导致几何体无效"); } } /// /// 安全释放COM对象(带空值检查) /// private static void SafeReleaseComObject(object comObj) { if (comObj != null && Marshal.IsComObject(comObj)) { Marshal.ReleaseComObject(comObj); } } #endregion #region MyRegion private static void Addnode(IPolygon newPolygon, List adjacentGeometries) { double maxDistance = 0.1; // 设定最大距离阈值 //待插入节点坐标及索引 Dictionary insertionPoints = new Dictionary(); //原始图形的点坐标及索引 Dictionary originalpoints = new Dictionary(); List polylines = new List(); var coords = new List<(double X, double Y)>(); try { IPointCollection points = (IPointCollection)newPolygon; var pointCount = points.PointCount; for (int i = 0; i < pointCount; i++) { IPoint p = points.get_Point(i); try { coords.Add((p.X, p.Y)); originalpoints.Add(i, (p.X, p.Y)); } finally { Marshal.ReleaseComObject(p); } } if (newPolygon is ISegmentCollection segColl) { for (int i = 0; i < segColl.SegmentCount; i++) { ISegment segment = segColl.get_Segment(i); try { IPolyline polyline1 = new PolylineClass(); polyline1.FromPoint = segment.FromPoint; polyline1.ToPoint = segment.ToPoint; polyline1.SpatialReference = newPolygon.SpatialReference; polylines.Add(polyline1); } finally { Marshal.ReleaseComObject(segment); } } } bool isbreak = false; foreach (IGeometry geom in adjacentGeometries) { if (geom is IPointCollection pointColl) { for (int i = 0; i < pointColl.PointCount; i++) { IPoint point = pointColl.get_Point(i); try { foreach (var polyline in polylines) { double distance = CalculateDistance(point, polyline); if (distance == 0 || distance > 1) continue; double distanceToPoint = CalculateDistance(point, polyline.ToPoint); double distanceFromPoint = CalculateDistance(point, polyline.FromPoint); if (distance == distanceToPoint || distance == distanceFromPoint) continue; if ((distance < 0.01 && (distanceToPoint < 0.01) || distanceFromPoint < 0.01)) continue; if (distance <= maxDistance && distance.ToDouble(5) > 0.00001) { if (coords.Contains((point.X, point.Y))) continue; var coordscoords = coords.FirstOrDefault(kvp => Math.Abs(kvp.X - point.X) < 0.1 && Math.Abs(kvp.Y - point.Y) < 0.1); if (coordscoords.X != 0 && coordscoords.Y != 0) continue; IPoint ToPoint = polyline.ToPoint; var keyValuePair2 = originalpoints.FirstOrDefault(kvp => Math.Abs(kvp.Value.X - ToPoint.X) < 0.0001 && Math.Abs(kvp.Value.Y - ToPoint.Y) < 0.0001); var pointindex = keyValuePair2.Equals(default(KeyValuePair)) ? -1 : keyValuePair2.Key; if (insertionPoints.ContainsKey(pointindex)) continue; insertionPoints.Add(pointindex, (point.X, point.Y)); isbreak = true; break; } } if (isbreak) break; } finally { Marshal.ReleaseComObject(point); } } } if (isbreak) break; } if (insertionPoints.Count > 0) { foreach (var item in insertionPoints) coords.Insert(item.Key, item.Value); points.RemovePoints(0, pointCount); IPoint newPoint = new PointClass(); foreach (var (X, Y) in coords) { newPoint.PutCoords(X, Y); points.AddPoint(newPoint); } Marshal.ReleaseComObject(newPoint); Addnode(newPolygon, adjacentGeometries); } } catch (Exception) { throw; } } #endregion } }