You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							811 lines
						
					
					
						
							36 KiB
						
					
					
				
			
		
		
	
	
							811 lines
						
					
					
						
							36 KiB
						
					
					
				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 | 
						|
{ | 
						|
    /// <summary> | 
						|
    /// 多边形边界对齐工具(针对ArcEngine 10.2.2优化版本) | 
						|
    /// 主要功能:调整目标多边形的顶点坐标,使其与相邻几何体的边界对齐 | 
						|
    /// 优化重点:空间索引结构、COM对象管理、批量坐标操作 | 
						|
    /// </summary> | 
						|
    public static class PolygonBoundaryAligner | 
						|
    { | 
						|
        #region 公共常量与接口 | 
						|
        private const double DEFAULT_TOLERANCE = 0.001;  // 默认捕捉容差(地图单位) | 
						|
        private static readonly object _syncRoot = new object();  // 线程安全锁 | 
						|
        #endregion | 
						|
 | 
						|
        #region 主入口方法 | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 对齐多边形边界的主方法 | 
						|
        /// </summary> | 
						|
        /// <param name="target">目标多边形</param> | 
						|
        /// <param name="adjacentGeometries">相邻几何体集合</param> | 
						|
        /// <param name="tolerance">捕捉容差(默认0.001)</param> | 
						|
        /// <returns>调整后的多边形</returns> | 
						|
        public static IGeometry AlignPolygonBoundary(IGeometry target, List<IGeometry> 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<IGeometry> 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<IPointCollection> points = new List<IPointCollection>(); | 
						|
                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 核心处理逻辑 | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 获取调整后的所有环(外环+内环) | 
						|
        /// </summary> | 
						|
        private static IEnumerable<IPolygon> GetAdjustedRings(IPolygon polygon, VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex, double tolerance, List<IGeometry> 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<IGeometry> boundaries) | 
						|
        { | 
						|
            // 获取点集合接口(兼容10.2.2) | 
						|
            IPointCollection points = (IPointCollection)ring; | 
						|
            return AdjustWithLegacyMethod(new List<IPointCollection> { points }, vertexIndex, segmentIndex, tolerance, boundaries); | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 兼容方法:传统逐个点操作(ArcEngine 10.0 以下备用) | 
						|
        /// </summary> | 
						|
        private static IPolygon AdjustWithLegacyMethod(List<IPointCollection> allPoints, VertexSpatialIndex vertexIndex, SegmentSpatialIndex segmentIndex, double tolerance, List<IGeometry> 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<int, (double X, double Y)> keyValuePairs = new Dictionary<int, (double X, double Y)>(); | 
						|
                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<IGeometry> 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 空间索引实现类 | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 顶点空间索引(使用双层字典的网格索引) | 
						|
        /// 优化点:提高网格查询速度,减少距离计算次数 | 
						|
        /// </summary> | 
						|
        private class VertexSpatialIndex | 
						|
        { | 
						|
            private readonly Dictionary<long, Dictionary<long, List<(double X, double Y)>>> _grid; | 
						|
            private readonly double _cellSize; | 
						|
            private int _totalPoints = 0; | 
						|
            public bool IsEmpty => _totalPoints == 0; | 
						|
            public VertexSpatialIndex(double cellSize) | 
						|
            { | 
						|
                _cellSize = cellSize; | 
						|
                _grid = new Dictionary<long, Dictionary<long, List<(double X, double Y)>>>(); | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 添加顶点到索引(自动网格化) | 
						|
            /// </summary> | 
						|
            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<long, List<(double X, double Y)>>(); | 
						|
                        _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++; | 
						|
                    } | 
						|
                } | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 查找最近顶点(带容差范围) | 
						|
            /// </summary> | 
						|
            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; | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 根据坐标获取相邻范围内的所有点 | 
						|
            /// </summary> | 
						|
            /// <param name="x"></param> | 
						|
            /// <param name="y"></param> | 
						|
            /// <param name="tolerance"></param> | 
						|
            /// <returns></returns> | 
						|
            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; | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 线段空间索引(使用网格索引+包围盒预计算) | 
						|
        /// 优化点:减少不必要的线段距离计算 | 
						|
        /// </summary> | 
						|
        private class SegmentSpatialIndex | 
						|
        { | 
						|
            private readonly Dictionary<long, Dictionary<long, List<SegmentData>>> _grid; | 
						|
            private readonly List<SegmentData> _allSegments; | 
						|
            private readonly double _cellSize; | 
						|
            public bool IsEmpty => _grid.Count == 0; | 
						|
            public SegmentSpatialIndex(double cellSize) | 
						|
            { | 
						|
                _cellSize = cellSize; | 
						|
                _grid = new Dictionary<long, Dictionary<long, List<SegmentData>>>(); | 
						|
                _allSegments = new List<SegmentData>(); | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 添加线段到索引(自动计算影响网格) | 
						|
            /// </summary> | 
						|
            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<long, List<SegmentData>>(); | 
						|
                                _grid[x] = yDict; | 
						|
                            } | 
						|
                            if (!yDict.TryGetValue(y, out var list)) | 
						|
                            { | 
						|
                                list = new List<SegmentData>(); | 
						|
                                yDict[y] = list; | 
						|
                            } | 
						|
                            // 避免重复添加(若外部可能重复调用AddSegment) | 
						|
                            if (!list.Contains(segData)) | 
						|
                            { | 
						|
                                list.Add(segData); | 
						|
                            } | 
						|
                        } | 
						|
                    } | 
						|
                } | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 查找最近线段点(两阶段查询:先网格后全局) | 
						|
            /// </summary> | 
						|
            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<SegmentData>(); | 
						|
                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<SegmentData>(_allSegments); | 
						|
                } | 
						|
                // 计算最近点 | 
						|
                foreach (var seg in candidates) | 
						|
                { | 
						|
                    var (cx, cy, distSq) = CalculateClosestPoint(x, y, seg); | 
						|
                    if (distSq < minDist) | 
						|
                    { | 
						|
                        minDist = distSq; | 
						|
                        closest = (cx, cy); | 
						|
                    } | 
						|
                } | 
						|
                return closest; | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 计算点到线段的最短距离(向量投影法) | 
						|
            /// </summary> | 
						|
            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); | 
						|
            } | 
						|
 | 
						|
            /// <summary> | 
						|
            /// 线段数据结构(引用类型) | 
						|
            /// </summary> | 
						|
            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 辅助方法 | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 参数有效性校验 | 
						|
        /// </summary> | 
						|
        private static void ValidateParameters(IGeometry target, List<IGeometry> 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("目标几何体必须是多边形类型"); | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 收集相邻几何体的边界要素(多线程安全) | 
						|
        /// </summary> | 
						|
        private static List<IGeometry> CollectAdjacentBoundaries(IEnumerable<IGeometry> geometries) | 
						|
        { | 
						|
            var boundaries = new ConcurrentBag<IGeometry>(); // 线程安全集合 | 
						|
            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(); // 转换为普通列表 | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 确保多边形拓扑有效性 | 
						|
        /// </summary> | 
						|
        private static void EnsureTopologyValidity(ref IPolygon polygon) | 
						|
        { | 
						|
            ITopologicalOperator topoOp = (ITopologicalOperator)polygon; | 
						|
            if (!topoOp.IsSimple) | 
						|
            { | 
						|
                topoOp.Simplify(); | 
						|
            } | 
						|
 | 
						|
            // 二次验证几何完整性 | 
						|
            if (polygon.IsEmpty) | 
						|
            { | 
						|
                throw new InvalidOperationException("拓扑简化导致几何体无效"); | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 安全释放COM对象(带空值检查) | 
						|
        /// </summary> | 
						|
        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<IGeometry> adjacentGeometries) | 
						|
        { | 
						|
            double maxDistance = 0.1; // 设定最大距离阈值 | 
						|
            //待插入节点坐标及索引 | 
						|
            Dictionary<int, (double X, double Y)> insertionPoints = new Dictionary<int, (double X, double Y)>(); | 
						|
            //原始图形的点坐标及索引 | 
						|
            Dictionary<int, (double X, double Y)> originalpoints = new Dictionary<int, (double X, double Y)>(); | 
						|
            List<IPolyline> polylines = new List<IPolyline>(); | 
						|
            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<int, (double X, double Y)>)) ? -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 | 
						|
    } | 
						|
} |