森林草原湿地荒漠调查
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

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) { }
}
}