第06章-空间分析算法

第06章:空间分析算法

6.1 空间分析概述

NetTopologySuite 提供了丰富的空间分析算法,用于解决各种 GIS 应用中的空间问题。本章将介绍线性参考、距离运算、三角剖分等高级空间分析功能。

6.2 线性参考

线性参考(Linear Referencing)是一种沿线定位的方法,用于在线性要素上定位点或测量距离。

6.2.1 沿线定位

using NetTopologySuite.Geometries;
using NetTopologySuite.LinearReferencing;

var factory = new GeometryFactory();

// 创建一条线
var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100)
});

Console.WriteLine($"线总长度: {line.Length}");  // 200

// 创建 LengthIndexedLine
var indexedLine = new LengthIndexedLine(line);

// 根据长度获取点
var point25 = indexedLine.ExtractPoint(25);      // 距起点25的位置
var point100 = indexedLine.ExtractPoint(100);    // 距起点100的位置(第一个拐点)
var point150 = indexedLine.ExtractPoint(150);    // 距起点150的位置

Console.WriteLine($"距起点25: ({point25.X}, {point25.Y})");      // (25, 0)
Console.WriteLine($"距起点100: ({point100.X}, {point100.Y})");   // (100, 0)
Console.WriteLine($"距起点150: ({point150.X}, {point150.Y})");   // (100, 50)

// 获取起点和终点位置
var startIndex = indexedLine.StartIndex;  // 0
var endIndex = indexedLine.EndIndex;      // 200

Console.WriteLine($"起点索引: {startIndex}");
Console.WriteLine($"终点索引: {endIndex}");

6.2.2 提取子线

var factory = new GeometryFactory();

var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100),
    new Coordinate(200, 100)
});

var indexedLine = new LengthIndexedLine(line);

// 提取子线段(从位置50到位置150)
var subLine = indexedLine.ExtractLine(50, 150);
Console.WriteLine($"子线: {subLine.AsText()}");
Console.WriteLine($"子线长度: {subLine.Length}");  // 100

// 提取从起点到位置100的线段
var firstPart = indexedLine.ExtractLine(0, 100);
Console.WriteLine($"前半段: {firstPart.AsText()}");

// 提取从位置100到终点的线段
var secondPart = indexedLine.ExtractLine(100, line.Length);
Console.WriteLine($"后半段: {secondPart.AsText()}");

6.2.3 投影到线上

var factory = new GeometryFactory();

var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0)
});

var indexedLine = new LengthIndexedLine(line);

// 将点投影到线上
var pointOnLine = new Coordinate(50, 0);      // 在线上
var pointAboveLine = new Coordinate(50, 20);  // 在线的上方
var pointBeyond = new Coordinate(150, 0);     // 超出线的范围

// 计算投影位置(返回沿线距离)
var index1 = indexedLine.Project(pointOnLine);
var index2 = indexedLine.Project(pointAboveLine);
var index3 = indexedLine.Project(pointBeyond);

Console.WriteLine($"线上点的投影位置: {index1}");      // 50
Console.WriteLine($"线上方点的投影位置: {index2}");    // 50
Console.WriteLine($"超出范围点的投影位置: {index3}");  // 100(线的终点)

// 获取投影点的坐标
var projectedPoint = indexedLine.ExtractPoint(index2);
Console.WriteLine($"投影点坐标: ({projectedPoint.X}, {projectedPoint.Y})");  // (50, 0)

// 判断索引是否有效
Console.WriteLine($"索引50有效: {indexedLine.IsValidIndex(50)}");    // true
Console.WriteLine($"索引150有效: {indexedLine.IsValidIndex(150)}");  // false

6.2.4 使用 LocationIndexedLine

using NetTopologySuite.LinearReferencing;

var factory = new GeometryFactory();

var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100)
});

// LocationIndexedLine 使用分段索引
var locationLine = new LocationIndexedLine(line);

// 创建线性位置
var location = new LinearLocation(0, 0.5);  // 第0段,50%位置
var point = locationLine.ExtractPoint(location);
Console.WriteLine($"位置(0, 0.5)的点: ({point.X}, {point.Y})");  // (50, 0)

// 第1段的位置
var location2 = new LinearLocation(1, 0.5);  // 第1段,50%位置
var point2 = locationLine.ExtractPoint(location2);
Console.WriteLine($"位置(1, 0.5)的点: ({point2.X}, {point2.Y})");  // (100, 50)

// 将点投影到线并获取 LinearLocation
var testPoint = new Coordinate(50, 20);
var projLocation = locationLine.Project(testPoint);
Console.WriteLine($"投影位置: 段{projLocation.SegmentIndex}, 分数{projLocation.SegmentFraction}");

// 提取子线
var startLoc = new LinearLocation(0, 0.25);  // 起始位置
var endLoc = new LinearLocation(1, 0.75);    // 结束位置
var subLine = locationLine.ExtractLine(startLoc, endLoc);
Console.WriteLine($"子线: {subLine.AsText()}");

6.2.5 线性参考应用示例

public class RoadMileageService
{
    private readonly GeometryFactory _factory;
    
    public RoadMileageService()
    {
        _factory = new GeometryFactory();
    }
    
    /// <summary>
    /// 计算道路上两点之间的距离(沿路距离)
    /// </summary>
    public double CalculateRoadDistance(LineString road, Coordinate point1, Coordinate point2)
    {
        var indexedLine = new LengthIndexedLine(road);
        
        var index1 = indexedLine.Project(point1);
        var index2 = indexedLine.Project(point2);
        
        return Math.Abs(index2 - index1);
    }
    
    /// <summary>
    /// 根据里程获取道路上的位置
    /// </summary>
    public Point GetPointByMileage(LineString road, double mileage)
    {
        var indexedLine = new LengthIndexedLine(road);
        var coord = indexedLine.ExtractPoint(mileage);
        return _factory.CreatePoint(coord);
    }
    
    /// <summary>
    /// 将道路分割为等长段
    /// </summary>
    public List<LineString> SplitRoadByDistance(LineString road, double segmentLength)
    {
        var segments = new List<LineString>();
        var indexedLine = new LengthIndexedLine(road);
        var totalLength = road.Length;
        
        double startIndex = 0;
        while (startIndex < totalLength)
        {
            var endIndex = Math.Min(startIndex + segmentLength, totalLength);
            var segment = (LineString)indexedLine.ExtractLine(startIndex, endIndex);
            segments.Add(segment);
            startIndex = endIndex;
        }
        
        return segments;
    }
    
    /// <summary>
    /// 计算点到道路的最近距离及最近点
    /// </summary>
    public (double Distance, Point NearestPoint) FindNearestPointOnRoad(
        LineString road, Coordinate point)
    {
        var indexedLine = new LengthIndexedLine(road);
        var projectedIndex = indexedLine.Project(point);
        var nearestCoord = indexedLine.ExtractPoint(projectedIndex);
        
        var distance = point.Distance(nearestCoord);
        var nearestPoint = _factory.CreatePoint(nearestCoord);
        
        return (distance, nearestPoint);
    }
}

// 使用示例
var service = new RoadMileageService();
var factory = new GeometryFactory();

// 创建道路
var road = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100),
    new Coordinate(200, 100)
});

// 计算两点之间的道路距离
var distance = service.CalculateRoadDistance(
    road, 
    new Coordinate(25, 5),   // 靠近道路起点
    new Coordinate(180, 95)  // 靠近道路终点
);
Console.WriteLine($"道路距离: {distance:F2}");

// 获取里程150处的位置
var point = service.GetPointByMileage(road, 150);
Console.WriteLine($"里程150处: {point.AsText()}");

// 将道路分割为50长度的段
var segments = service.SplitRoadByDistance(road, 50);
Console.WriteLine($"分割段数: {segments.Count}");

6.3 距离运算

6.3.1 基本距离计算

using NetTopologySuite.Geometries;
using NetTopologySuite.Operation.Distance;

var factory = new GeometryFactory();

// 点与点
var point1 = factory.CreatePoint(new Coordinate(0, 0));
var point2 = factory.CreatePoint(new Coordinate(3, 4));
Console.WriteLine($"点与点距离: {point1.Distance(point2)}");  // 5

// 点与线
var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 10), new Coordinate(10, 10)
});
Console.WriteLine($"点与线距离: {point1.Distance(line)}");  // 10

// 点与多边形
var polygon = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(10, 0), new Coordinate(20, 0),
    new Coordinate(20, 10), new Coordinate(10, 10), new Coordinate(10, 0)
});
Console.WriteLine($"点与多边形距离: {point1.Distance(polygon)}");  // 10

// 多边形与多边形
var polygon2 = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(30, 0), new Coordinate(40, 0),
    new Coordinate(40, 10), new Coordinate(30, 10), new Coordinate(30, 0)
});
Console.WriteLine($"多边形间距离: {polygon.Distance(polygon2)}");  // 10

6.3.2 最近点对

using NetTopologySuite.Operation.Distance;

var factory = new GeometryFactory();

var polygon1 = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0, 0), new Coordinate(10, 0),
    new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0)
});

var polygon2 = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(15, 5), new Coordinate(25, 5),
    new Coordinate(25, 15), new Coordinate(15, 15), new Coordinate(15, 5)
});

// 使用 DistanceOp 获取详细信息
var distanceOp = new DistanceOp(polygon1, polygon2);

// 获取距离
var distance = distanceOp.Distance();
Console.WriteLine($"距离: {distance}");  // 5

// 获取最近点对
var nearestPoints = distanceOp.NearestPoints();
Console.WriteLine($"多边形1上最近点: ({nearestPoints[0].X}, {nearestPoints[0].Y})");
Console.WriteLine($"多边形2上最近点: ({nearestPoints[1].X}, {nearestPoints[1].Y})");

// 获取最近点的位置信息
var nearestLocations = distanceOp.NearestLocations();
Console.WriteLine($"位置1几何索引: {nearestLocations[0].GeometryComponentIndex}");
Console.WriteLine($"位置1线段索引: {nearestLocations[0].SegmentIndex}");

6.3.3 Hausdorff 距离

Hausdorff 距离衡量两个几何之间的最大不相似程度:

using NetTopologySuite.Algorithm.Distance;

var factory = new GeometryFactory();

var line1 = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0), new Coordinate(100, 0)
});

var line2 = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 10), new Coordinate(100, 10)
});

var line3 = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 10), new Coordinate(50, 30), new Coordinate(100, 10)
});

// 计算 Hausdorff 距离
var hausdorff1 = DiscreteHausdorffDistance.Distance(line1, line2);
var hausdorff2 = DiscreteHausdorffDistance.Distance(line1, line3);

Console.WriteLine($"line1与line2的Hausdorff距离: {hausdorff1}");  // 10
Console.WriteLine($"line1与line3的Hausdorff距离: {hausdorff2}");  // 30

// 带密度因子的 Hausdorff 距离(更精确)
var hausdorffDense = DiscreteHausdorffDistance.Distance(line1, line3, 0.5);
Console.WriteLine($"密集采样Hausdorff距离: {hausdorffDense}");

6.3.4 Frechet 距离

Frechet 距离考虑了沿曲线行进的顺序:

using NetTopologySuite.Algorithm.Distance;

var factory = new GeometryFactory();

var line1 = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0), new Coordinate(50, 0), new Coordinate(100, 0)
});

var line2 = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 10), new Coordinate(50, 10), new Coordinate(100, 10)
});

// Frechet 距离
var frechet = DiscreteFrechetDistance.Distance(line1, line2);
Console.WriteLine($"Frechet距离: {frechet}");

// 对比 Hausdorff 距离
var hausdorff = DiscreteHausdorffDistance.Distance(line1, line2);
Console.WriteLine($"Hausdorff距离: {hausdorff}");

6.4 三角剖分

6.4.1 Delaunay 三角剖分

using NetTopologySuite.Triangulate;

var factory = new GeometryFactory();

// 创建点集
var points = factory.CreateMultiPoint(new[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(50, 80),
    new Coordinate(0, 100),
    new Coordinate(100, 100),
    new Coordinate(50, 50)
});

// 执行 Delaunay 三角剖分
var triangulator = new DelaunayTriangulationBuilder();
triangulator.SetSites(points);

// 获取三角形
var triangles = triangulator.GetTriangles(factory);
Console.WriteLine($"三角形数量: {triangles.NumGeometries}");

// 遍历三角形
for (int i = 0; i < triangles.NumGeometries; i++)
{
    var triangle = (Polygon)triangles.GetGeometryN(i);
    Console.WriteLine($"三角形{i}: {triangle.AsText()}");
}

// 获取边
var edges = triangulator.GetEdges(factory);
Console.WriteLine($"边数量: {edges.NumGeometries}");

6.4.2 约束 Delaunay 三角剖分

using NetTopologySuite.Triangulate;

var factory = new GeometryFactory();

// 创建点集
var points = new List<Coordinate>
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100),
    new Coordinate(0, 100),
    new Coordinate(50, 50)
};

// 创建约束边(必须保留的边)
var constraintLine = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 50), new Coordinate(100, 50)
});

// 创建约束 Delaunay 三角剖分
var triangulator = new ConformingDelaunayTriangulationBuilder();
triangulator.SetSites(factory.CreateMultiPointFromCoords(points.ToArray()));
triangulator.Constraints = factory.CreateGeometryCollection(new[] { constraintLine });

var triangles = triangulator.GetTriangles(factory);
Console.WriteLine($"约束三角剖分三角形数量: {triangles.NumGeometries}");

6.4.3 Voronoi 图

Voronoi 图是点集的对偶,表示每个点的最近区域:

using NetTopologySuite.Triangulate;

var factory = new GeometryFactory();

// 创建点集
var points = factory.CreateMultiPoint(new[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(50, 80),
    new Coordinate(0, 100),
    new Coordinate(100, 100)
});

// 创建 Voronoi 图
var voronoiBuilder = new VoronoiDiagramBuilder();
voronoiBuilder.SetSites(points);

// 可选:设置裁剪边界
var envelope = new Envelope(-50, 150, -50, 150);
voronoiBuilder.ClipEnvelope = envelope;

// 获取 Voronoi 多边形
var voronoiPolygons = voronoiBuilder.GetDiagram(factory);
Console.WriteLine($"Voronoi多边形数量: {voronoiPolygons.NumGeometries}");

for (int i = 0; i < voronoiPolygons.NumGeometries; i++)
{
    var cell = voronoiPolygons.GetGeometryN(i);
    Console.WriteLine($"单元{i}面积: {cell.Area:F2}");
}

// 获取 Voronoi 边
var voronoiEdges = voronoiBuilder.GetSubdivision().GetVoronoiDiagram(factory);

6.4.4 三角剖分应用

public class SpatialInterpolation
{
    private readonly GeometryFactory _factory;
    
    public SpatialInterpolation()
    {
        _factory = new GeometryFactory();
    }
    
    /// <summary>
    /// 使用 Voronoi 图进行最近邻插值
    /// </summary>
    public double NearestNeighborInterpolate(
        List<(Coordinate Location, double Value)> samplePoints,
        Coordinate targetPoint)
    {
        // 找到最近的采样点
        var nearest = samplePoints
            .OrderBy(p => p.Location.Distance(targetPoint))
            .First();
        
        return nearest.Value;
    }
    
    /// <summary>
    /// 使用 Delaunay 三角网进行线性插值
    /// </summary>
    public double? TriangularInterpolate(
        List<(Coordinate Location, double Value)> samplePoints,
        Coordinate targetPoint)
    {
        // 构建三角网
        var points = _factory.CreateMultiPointFromCoords(
            samplePoints.Select(p => p.Location).ToArray());
        
        var triangulator = new DelaunayTriangulationBuilder();
        triangulator.SetSites(points);
        var triangles = triangulator.GetTriangles(_factory);
        
        // 找到包含目标点的三角形
        var targetPointGeom = _factory.CreatePoint(targetPoint);
        
        for (int i = 0; i < triangles.NumGeometries; i++)
        {
            var triangle = (Polygon)triangles.GetGeometryN(i);
            if (triangle.Contains(targetPointGeom))
            {
                // 获取三角形的三个顶点
                var coords = triangle.Coordinates;
                var v1 = coords[0];
                var v2 = coords[1];
                var v3 = coords[2];
                
                // 获取对应的值
                var value1 = samplePoints.First(p => p.Location.Equals2D(v1)).Value;
                var value2 = samplePoints.First(p => p.Location.Equals2D(v2)).Value;
                var value3 = samplePoints.First(p => p.Location.Equals2D(v3)).Value;
                
                // 重心坐标插值
                return BarycentricInterpolate(
                    v1, v2, v3, 
                    value1, value2, value3, 
                    targetPoint);
            }
        }
        
        return null;  // 目标点不在三角网范围内
    }
    
    private double BarycentricInterpolate(
        Coordinate v1, Coordinate v2, Coordinate v3,
        double val1, double val2, double val3,
        Coordinate p)
    {
        // 计算重心坐标
        var det = (v2.Y - v3.Y) * (v1.X - v3.X) + (v3.X - v2.X) * (v1.Y - v3.Y);
        var l1 = ((v2.Y - v3.Y) * (p.X - v3.X) + (v3.X - v2.X) * (p.Y - v3.Y)) / det;
        var l2 = ((v3.Y - v1.Y) * (p.X - v3.X) + (v1.X - v3.X) * (p.Y - v3.Y)) / det;
        var l3 = 1 - l1 - l2;
        
        return l1 * val1 + l2 * val2 + l3 * val3;
    }
    
    /// <summary>
    /// 生成泰森多边形(服务区划分)
    /// </summary>
    public GeometryCollection CreateServiceAreas(
        List<Coordinate> servicePoints,
        Envelope boundary)
    {
        var points = _factory.CreateMultiPointFromCoords(servicePoints.ToArray());
        
        var voronoiBuilder = new VoronoiDiagramBuilder();
        voronoiBuilder.SetSites(points);
        voronoiBuilder.ClipEnvelope = boundary;
        
        return (GeometryCollection)voronoiBuilder.GetDiagram(_factory);
    }
}

6.5 多边形分解

6.5.1 三角化分解

using NetTopologySuite.Triangulate;

var factory = new GeometryFactory();

// 创建复杂多边形
var polygon = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(100, 0),
    new Coordinate(100, 100),
    new Coordinate(50, 50),  // 凹点
    new Coordinate(0, 100),
    new Coordinate(0, 0)
});

// 三角化分解
var triangulator = new DelaunayTriangulationBuilder();
triangulator.SetSites(polygon);

var triangles = triangulator.GetTriangles(factory);

// 只保留在原多边形内的三角形
var innerTriangles = new List<Geometry>();
for (int i = 0; i < triangles.NumGeometries; i++)
{
    var triangle = triangles.GetGeometryN(i);
    if (polygon.Contains(triangle.InteriorPoint))
    {
        innerTriangles.Add(triangle);
    }
}

Console.WriteLine($"内部三角形数量: {innerTriangles.Count}");
var triangulated = factory.CreateGeometryCollection(innerTriangles.ToArray());

6.5.2 凸分解

public class ConvexDecomposition
{
    private readonly GeometryFactory _factory;
    
    public ConvexDecomposition()
    {
        _factory = new GeometryFactory();
    }
    
    /// <summary>
    /// 判断多边形是否为凸多边形
    /// </summary>
    public bool IsConvex(Polygon polygon)
    {
        var hull = polygon.ConvexHull();
        return polygon.Area >= hull.Area * 0.99999;  // 允许小误差
    }
    
    /// <summary>
    /// 使用三角剖分进行凸分解
    /// </summary>
    public List<Polygon> DecomposeToTriangles(Polygon polygon)
    {
        var result = new List<Polygon>();
        
        var triangulator = new DelaunayTriangulationBuilder();
        triangulator.SetSites(polygon);
        var triangles = triangulator.GetTriangles(_factory);
        
        for (int i = 0; i < triangles.NumGeometries; i++)
        {
            var triangle = (Polygon)triangles.GetGeometryN(i);
            if (polygon.Contains(triangle.InteriorPoint))
            {
                result.Add(triangle);
            }
        }
        
        return result;
    }
}

6.6 空间索引

6.6.1 STRtree 空间索引

using NetTopologySuite.Index.Strtree;

var factory = new GeometryFactory();

// 创建大量几何对象
var geometries = Enumerable.Range(0, 10000)
    .Select(i => 
    {
        var x = Random.Shared.NextDouble() * 1000;
        var y = Random.Shared.NextDouble() * 1000;
        return factory.CreatePoint(new Coordinate(x, y));
    })
    .ToList();

// 创建 STRtree 索引
var tree = new STRtree<Point>();
foreach (var geom in geometries)
{
    tree.Insert(geom.EnvelopeInternal, geom);
}
tree.Build();  // 构建索引

// 范围查询
var searchEnvelope = new Envelope(100, 200, 100, 200);
var candidates = tree.Query(searchEnvelope);
Console.WriteLine($"范围查询候选数量: {candidates.Count}");

// 精确筛选
var searchArea = factory.ToGeometry(searchEnvelope);
var exactMatches = candidates.Where(p => searchArea.Contains(p)).ToList();
Console.WriteLine($"精确匹配数量: {exactMatches.Count}");

// 最近邻查询
var queryPoint = new Coordinate(150, 150);
var nearest = tree.NearestNeighbour(
    new Envelope(queryPoint), 
    factory.CreatePoint(queryPoint),
    new GeometryItemDistance());
Console.WriteLine($"最近点: {nearest?.AsText()}");

6.6.2 Quadtree 四叉树

using NetTopologySuite.Index.Quadtree;

var factory = new GeometryFactory();

// 创建四叉树
var quadtree = new Quadtree<Geometry>();

// 添加几何
var geometries = new List<Geometry>
{
    factory.CreatePoint(new Coordinate(10, 10)),
    factory.CreatePoint(new Coordinate(50, 50)),
    factory.CreatePolygon(new Coordinate[]
    {
        new Coordinate(80, 80), new Coordinate(90, 80),
        new Coordinate(90, 90), new Coordinate(80, 90), new Coordinate(80, 80)
    })
};

foreach (var geom in geometries)
{
    quadtree.Insert(geom.EnvelopeInternal, geom);
}

// 查询
var results = quadtree.Query(new Envelope(0, 60, 0, 60));
Console.WriteLine($"四叉树查询结果数量: {results.Count}");

// 删除
quadtree.Remove(geometries[0].EnvelopeInternal, geometries[0]);

6.6.3 空间索引应用

public class SpatialIndexService
{
    private readonly GeometryFactory _factory;
    private readonly STRtree<Feature> _index;
    
    public SpatialIndexService()
    {
        _factory = new GeometryFactory();
        _index = new STRtree<Feature>();
    }
    
    /// <summary>
    /// 批量添加要素
    /// </summary>
    public void AddFeatures(IEnumerable<Feature> features)
    {
        foreach (var feature in features)
        {
            _index.Insert(feature.Geometry.EnvelopeInternal, feature);
        }
        _index.Build();
    }
    
    /// <summary>
    /// 范围查询
    /// </summary>
    public List<Feature> QueryByExtent(Envelope extent)
    {
        return _index.Query(extent).ToList();
    }
    
    /// <summary>
    /// 几何相交查询
    /// </summary>
    public List<Feature> QueryByGeometry(Geometry queryGeometry)
    {
        // 第一步:使用索引进行粗查询
        var candidates = _index.Query(queryGeometry.EnvelopeInternal);
        
        // 第二步:精确筛选
        return candidates
            .Where(f => queryGeometry.Intersects(f.Geometry))
            .ToList();
    }
    
    /// <summary>
    /// 缓冲区查询
    /// </summary>
    public List<Feature> QueryByBuffer(Geometry center, double distance)
    {
        var buffer = center.Buffer(distance);
        return QueryByGeometry(buffer);
    }
    
    /// <summary>
    /// 最近邻查询
    /// </summary>
    public Feature? FindNearest(Point queryPoint)
    {
        return _index.NearestNeighbour(
            new Envelope(queryPoint.Coordinate),
            queryPoint,
            new FeatureItemDistance()) as Feature;
    }
}

// 自定义距离计算器
public class FeatureItemDistance : IItemDistance<Envelope, Feature>
{
    public double Distance(IBoundable<Envelope, Feature> item1, IBoundable<Envelope, Feature> item2)
    {
        var geom1 = item1.Item?.Geometry ?? item1.Item?.Geometry;
        var geom2 = item2.Item?.Geometry ?? item2.Item?.Geometry;
        
        if (geom1 != null && geom2 != null)
        {
            return geom1.Distance(geom2);
        }
        
        return double.MaxValue;
    }
}

// Feature 类(简化版)
public class Feature
{
    public Geometry Geometry { get; set; }
    public Dictionary<string, object> Attributes { get; set; }
}

6.7 几何优化算法

6.7.1 最小外包矩形

using NetTopologySuite.Algorithm;

var factory = new GeometryFactory();

// 创建不规则多边形
var polygon = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0, 0),
    new Coordinate(10, 5),
    new Coordinate(15, 0),
    new Coordinate(20, 10),
    new Coordinate(10, 15),
    new Coordinate(0, 0)
});

// 轴对齐最小外包矩形(AABB)
var aabb = polygon.Envelope;
Console.WriteLine($"AABB: {aabb.AsText()}");
Console.WriteLine($"AABB面积: {aabb.Area}");

// 最小面积外包矩形(旋转矩形)
var minRect = MinimumDiameter.GetMinimumRectangle(polygon);
Console.WriteLine($"最小矩形: {minRect.AsText()}");
Console.WriteLine($"最小矩形面积: {minRect.Area}");

// 最小直径
var minDiameter = new MinimumDiameter(polygon);
Console.WriteLine($"最小直径: {minDiameter.Diameter}");
Console.WriteLine($"最小直径线: {minDiameter.SupportingSegment.AsText()}");

6.7.2 最小外包圆

using NetTopologySuite.Algorithm;

var factory = new GeometryFactory();

var points = factory.CreateMultiPoint(new[]
{
    new Coordinate(0, 0),
    new Coordinate(10, 0),
    new Coordinate(5, 10),
    new Coordinate(2, 5)
});

// 计算最小外包圆
var minCircle = new MinimumBoundingCircle(points);

Console.WriteLine($"圆心: ({minCircle.Centre.X}, {minCircle.Centre.Y})");
Console.WriteLine($"半径: {minCircle.Radius}");

// 获取圆的几何表示
var circle = minCircle.GetCircle();
Console.WriteLine($"外包圆: {circle.AsText()}");

// 获取定义圆的极点
var extremalPoints = minCircle.GetExtremalPoints();
Console.WriteLine($"极点数量: {extremalPoints.Length}");

6.7.3 质心和重心计算

using NetTopologySuite.Algorithm;

var factory = new GeometryFactory();

// 多边形质心
var polygon = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0, 0), new Coordinate(10, 0),
    new Coordinate(10, 10), new Coordinate(0, 10), new Coordinate(0, 0)
});

// 使用 Geometry.Centroid 属性
var centroid1 = polygon.Centroid;
Console.WriteLine($"质心: {centroid1.AsText()}");

// 使用 Centroid 算法类
var centroidAlgo = new Centroid(polygon);
Console.WriteLine($"质心坐标: ({centroidAlgo.CentroidPoint.X}, {centroidAlgo.CentroidPoint.Y})");

// 线的质心
var line = factory.CreateLineString(new Coordinate[]
{
    new Coordinate(0, 0), new Coordinate(10, 10), new Coordinate(20, 0)
});
Console.WriteLine($"线的质心: {line.Centroid.AsText()}");

// 多点的质心
var multiPoint = factory.CreateMultiPoint(new[]
{
    new Coordinate(0, 0),
    new Coordinate(10, 0),
    new Coordinate(10, 10),
    new Coordinate(0, 10)
});
Console.WriteLine($"多点的质心: {multiPoint.Centroid.AsText()}");

6.8 本章小结

本章介绍了 NetTopologySuite 的空间分析算法:

  1. 线性参考:沿线定位、子线提取、投影计算
  2. 距离运算:基本距离、最近点对、Hausdorff 距离、Frechet 距离
  3. 三角剖分:Delaunay 三角剖分、约束三角剖分、Voronoi 图
  4. 多边形分解:三角化分解、凸分解
  5. 空间索引:STRtree、Quadtree 及其应用
  6. 几何优化:最小外包矩形、最小外包圆、质心计算

6.9 下一步

下一章我们将学习 GeoJSON 数据处理,包括:

  • GeoJSON 格式介绍
  • 读取和写入 GeoJSON
  • Feature 和 FeatureCollection 处理
  • 与 ASP.NET Core 集成

相关资源

posted @ 2025-12-29 10:22  我才是银古  阅读(1)  评论(0)    收藏  举报