第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 的空间分析算法:
- 线性参考:沿线定位、子线提取、投影计算
- 距离运算:基本距离、最近点对、Hausdorff 距离、Frechet 距离
- 三角剖分:Delaunay 三角剖分、约束三角剖分、Voronoi 图
- 多边形分解:三角化分解、凸分解
- 空间索引:STRtree、Quadtree 及其应用
- 几何优化:最小外包矩形、最小外包圆、质心计算
6.9 下一步
下一章我们将学习 GeoJSON 数据处理,包括:
- GeoJSON 格式介绍
- 读取和写入 GeoJSON
- Feature 和 FeatureCollection 处理
- 与 ASP.NET Core 集成
相关资源:

浙公网安备 33010602011771号