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.
		
		
		
		
		
			
		
			
				
					
					
						
							273 lines
						
					
					
						
							10 KiB
						
					
					
				
			
		
		
	
	
							273 lines
						
					
					
						
							10 KiB
						
					
					
				using ESRI.ArcGIS.SystemUI; | 
						|
using NetTopologySuite.Geometries; | 
						|
using NetTopologySuite.Index.Strtree; | 
						|
using NetTopologySuite.Operation.Overlay.Snap; | 
						|
using NetTopologySuite.Operation.Union; | 
						|
using System; | 
						|
using System.Collections.Concurrent; | 
						|
using System.Collections.Generic; | 
						|
using System.Linq; | 
						|
using System.Threading.Tasks; | 
						|
 | 
						|
namespace Kingo.PluginServiceInterface.Helper | 
						|
{ | 
						|
 | 
						|
    /// <summary> | 
						|
    /// 边界吸附处理器 | 
						|
    /// </summary> | 
						|
    public static class BoundarySnapper | 
						|
    { | 
						|
        /// <summary> | 
						|
        /// 执行边界吸附操作 | 
						|
        /// </summary> | 
						|
        /// <param name="inputParcels">输入图斑集合</param> | 
						|
        /// <param name="tolerance">吸附容差(单位与坐标一致)</param> | 
						|
        /// <returns>修复后的图斑集合</returns> | 
						|
        public static List<Geometry> SnapBoundaries(List<Geometry> inputParcels, double tolerance = 0.001) | 
						|
        { | 
						|
            // 创建空间索引加速邻近查询 | 
						|
            var spatialIndex = new STRtree<Geometry>(); | 
						|
            foreach (var parcel in inputParcels) | 
						|
            { | 
						|
                spatialIndex.Insert(parcel.EnvelopeInternal, parcel); | 
						|
            } | 
						|
            spatialIndex.Build(); | 
						|
 | 
						|
            // 复制输入数据以避免修改原始集合 | 
						|
            var outputParcels = new List<Geometry>(inputParcels); | 
						|
 | 
						|
            // 遍历每个图斑进行边界吸附 | 
						|
            foreach (var currentParcel in inputParcels) | 
						|
            { | 
						|
                // 查询可能与当前图斑相邻的图斑 | 
						|
                var nearbyCandidates = spatialIndex.Query(currentParcel.EnvelopeInternal); | 
						|
 | 
						|
                // 过滤出实际接触的相邻图斑 | 
						|
                var neighbors = nearbyCandidates | 
						|
                    .Where(p => p != currentParcel && p.Touches(currentParcel)) | 
						|
                    .ToList(); | 
						|
 | 
						|
                foreach (var neighbor in neighbors) | 
						|
                { | 
						|
                    // 使用NTS的GeometrySnapper执行吸附操作 | 
						|
                    var snapper = new GeometrySnapper(currentParcel); | 
						|
                    Geometry snappedGeometry = (Geometry)snapper.SnapTo(neighbor, tolerance); | 
						|
 | 
						|
                    // 如果几何体发生改变,则更新结果集 | 
						|
                    if (!snappedGeometry.EqualsExact(currentParcel)) | 
						|
                    { | 
						|
                        outputParcels.Remove(currentParcel); | 
						|
                        outputParcels.Add(snappedGeometry); | 
						|
                        break; // 处理第一个匹配的邻居后跳出循环 | 
						|
                    } | 
						|
                } | 
						|
            } | 
						|
 | 
						|
            return outputParcels; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    /// <summary> | 
						|
    /// 国土变更调查拓扑修复工具 | 
						|
    /// 功能:边界对齐 + 重叠消除 + 拓扑验证 | 
						|
    /// </summary> | 
						|
    public class TopologyRepairEngine | 
						|
    { | 
						|
        #region 公开接口 | 
						|
        /// <summary> | 
						|
        /// 执行完整拓扑修复流程 | 
						|
        /// </summary> | 
						|
        /// <param name="inputGeometries">输入图斑集合</param> | 
						|
        /// <param name="tolerance">拓扑容差(单位与坐标系一致)</param> | 
						|
        /// <param name="maxIterations">最大修复迭代次数</param> | 
						|
        /// <returns>修复后的拓扑一致图斑集合</returns> | 
						|
        public static List<Geometry> FullRepair(List<Geometry> inputGeometries, double tolerance = 0.001, int maxIterations = 10) | 
						|
        { | 
						|
            // 阶段1:边界对齐处理 | 
						|
            var snapped = SnapBoundaries(inputGeometries, tolerance, maxIterations); | 
						|
            // 阶段2:全局重叠消除 | 
						|
            var cleaned = ResolveOverlaps(snapped, tolerance); | 
						|
            //// 阶段3:最终验证 | 
						|
            //if (!ValidateTopology(cleaned, tolerance)) | 
						|
            //    throw new TopologyException("拓扑修复失败,存在未解决的拓扑错误"); | 
						|
            return cleaned; | 
						|
        } | 
						|
        #endregion | 
						|
 | 
						|
        #region 核心算法 | 
						|
        /// <summary> | 
						|
        /// 多轮次边界吸附处理 | 
						|
        /// </summary> | 
						|
        private static List<Geometry> SnapBoundaries(List<Geometry> input, double tolerance, int maxIterations) | 
						|
        { | 
						|
            List<Geometry> workingSet = input.Select(g => (Geometry)g.Copy()).ToList(); | 
						|
            bool hasChanges; | 
						|
            int iteration = 0; | 
						|
            do | 
						|
            { | 
						|
                hasChanges = false; | 
						|
                var spatialIndex = BuildSTRTree(workingSet); | 
						|
                var nextGeneration = new List<Geometry>(); | 
						|
                foreach (var current in workingSet) | 
						|
                { | 
						|
                    var neighbors = spatialIndex.Query(current.EnvelopeInternal) | 
						|
                        .Where(g => g != current && g.Touches(current)) | 
						|
                        .ToList(); | 
						|
                    Geometry processed = current; | 
						|
                    foreach (var neighbor in neighbors) | 
						|
                    { | 
						|
                        var snapper = new GeometrySnapper(processed); | 
						|
                        Geometry snapped = (Geometry)snapper.SnapTo(neighbor, CalculateDynamicTolerance(tolerance, iteration)); | 
						|
                        if (!snapped.EqualsTopologically(processed)) | 
						|
                        { | 
						|
                            processed = ValidateGeometry(snapped); | 
						|
                            hasChanges = true; | 
						|
                            break; // 单次只处理一个邻居 | 
						|
                        } | 
						|
                    } | 
						|
                    nextGeneration.Add(processed); | 
						|
                } | 
						|
                workingSet = nextGeneration; | 
						|
                iteration++; | 
						|
 | 
						|
            } while (hasChanges && iteration < maxIterations); | 
						|
            return workingSet; | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 全局重叠消除处理 | 
						|
        /// </summary> | 
						|
        private static List<Geometry> ResolveOverlaps(List<Geometry> geometries, double tolerance) | 
						|
        { | 
						|
            var overlaps = DetectOverlaps(geometries, tolerance); | 
						|
            if (overlaps.Count == 0) return geometries; | 
						|
            var validOverlaps = overlaps | 
						|
                                .Where(g => g is Polygon&&!g.IsEmpty) // 仅保留多边形 | 
						|
                                .Where(g => g.IsValid)// 检查有效性 | 
						|
                                .Cast<GeoAPI.Geometries.IGeometry>() | 
						|
                                .ToList(); | 
						|
            var union = CascadedPolygonUnion.Union(validOverlaps); | 
						|
            if(union==null) return geometries; | 
						|
            var result = new List<Geometry>(); | 
						|
 | 
						|
            foreach (var geom in geometries) | 
						|
            { | 
						|
                if (geom.Intersects(union)) | 
						|
                { | 
						|
                    var diff = geom.Difference(union); | 
						|
                    result.AddRange(ExtractValidPolygons((Geometry)diff)); | 
						|
                } | 
						|
                else | 
						|
                { | 
						|
                    result.Add(geom); | 
						|
                } | 
						|
            } | 
						|
            // 递归处理残留重叠 | 
						|
            return ResolveOverlaps(result, tolerance); // 收紧容差 | 
						|
        } | 
						|
        #endregion | 
						|
 | 
						|
        #region 辅助方法 | 
						|
        /// <summary> | 
						|
        /// 构建空间索引 | 
						|
        /// </summary> | 
						|
        private static STRtree<Geometry> BuildSTRTree(IEnumerable<Geometry> geometries) | 
						|
        { | 
						|
            var tree = new STRtree<Geometry>(); | 
						|
            foreach (var g in geometries) tree.Insert(g.EnvelopeInternal, g); | 
						|
            tree.Build(); | 
						|
            return tree; | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 动态容差计算(随迭代次数衰减) | 
						|
        /// </summary> | 
						|
        private static double CalculateDynamicTolerance(double baseTolerance, int iteration) | 
						|
            => baseTolerance * Math.Pow(0.9, iteration); | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 几何体有效性检查与修复 | 
						|
        /// </summary> | 
						|
        private static Geometry ValidateGeometry(Geometry geom) | 
						|
        { | 
						|
            if (!geom.IsValid) | 
						|
            { | 
						|
                // 使用缓冲区法修复常见错误 | 
						|
                var buffered = geom.Buffer(0); | 
						|
                return buffered.IsEmpty ? (Geometry)geom : (Geometry)buffered; | 
						|
            } | 
						|
            return geom; | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 检测所有重叠区域 | 
						|
        /// </summary> | 
						|
        private static List<Geometry> DetectOverlaps(List<Geometry> geometries, double minArea) | 
						|
        { | 
						|
            var overlaps = new List<Geometry>(); | 
						|
            var index = BuildSTRTree(geometries); | 
						|
 | 
						|
            foreach (var geom in geometries) | 
						|
            { | 
						|
                foreach (var other in index.Query(geom.EnvelopeInternal) | 
						|
                    .Where(g => g != geom && g.Intersects(geom))) | 
						|
                { | 
						|
                    var overlap = geom.Intersection(other); | 
						|
                    if (overlap.Area > minArea * minArea) | 
						|
                        overlaps.Add((Geometry)overlap); | 
						|
                } | 
						|
            } | 
						|
            return overlaps; | 
						|
        } | 
						|
 | 
						|
        /// <summary> | 
						|
        /// 从几何集合中提取有效多边形 | 
						|
        /// </summary> | 
						|
        private static IEnumerable<Geometry> ExtractValidPolygons(Geometry geom) | 
						|
        { | 
						|
            if (geom is Polygon p) return new[] { p }; | 
						|
            if (geom is GeometryCollection coll) | 
						|
                return coll.Geometries | 
						|
                    .Where(g => g is Polygon) | 
						|
                    .Select(g => ValidateGeometry((Geometry)g)); | 
						|
            return Enumerable.Empty<Geometry>(); | 
						|
        } | 
						|
        #endregion | 
						|
 | 
						|
        #region 验证逻辑 | 
						|
        /// <summary> | 
						|
        /// 拓扑一致性验证 | 
						|
        /// </summary> | 
						|
        public static bool ValidateTopology(IEnumerable<Geometry> geometries, double tolerance) | 
						|
        { | 
						|
            var index = BuildSTRTree(geometries); | 
						|
            return geometries.All(g => | 
						|
            { | 
						|
                // 检查自身有效性 | 
						|
                if (!g.IsValid) return false; | 
						|
 | 
						|
                // 检查相邻关系 | 
						|
                return index.Query(g.EnvelopeInternal) | 
						|
                    .Where(other => g != other) | 
						|
                    .All(other => | 
						|
                        !g.Intersects(other) || | 
						|
                        (g.Touches(other) && g.Boundary.Distance(other.Boundary) <= tolerance) | 
						|
                    ); | 
						|
            }); | 
						|
        } | 
						|
        #endregion | 
						|
    } | 
						|
 | 
						|
    /// <summary> | 
						|
    /// 自定义拓扑异常 | 
						|
    /// </summary> | 
						|
    public class TopologyException : Exception | 
						|
    { | 
						|
        public TopologyException(string message) : base(message) { } | 
						|
    } | 
						|
 | 
						|
} |