第04章-空间关系与谓词操作
第04章:空间关系与谓词操作
4.1 空间关系概述
空间关系是 GIS 中的核心概念,用于描述两个几何对象之间的拓扑关系。NetTopologySuite 实现了完整的 OGC Simple Features Specification 定义的空间关系谓词。
4.1.1 空间关系分类
空间关系可以分为以下几类:
| 分类 | 关系类型 | 说明 |
|---|---|---|
| 相等关系 | Equals | 两个几何在空间上完全相同 |
| 包含关系 | Contains, Within, Covers, CoveredBy | 一个几何包含另一个几何 |
| 相交关系 | Intersects, Crosses, Overlaps | 两个几何有交集 |
| 接触关系 | Touches | 两个几何仅边界接触 |
| 分离关系 | Disjoint | 两个几何没有任何公共点 |
4.1.2 准备测试数据
using NetTopologySuite.Geometries;
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(5, 5), new Coordinate(15, 5),
new Coordinate(15, 15), new Coordinate(5, 15), new Coordinate(5, 5)
});
var polygon3 = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(20, 0), new Coordinate(30, 0),
new Coordinate(30, 10), new Coordinate(20, 10), new Coordinate(20, 0)
});
var polygon4 = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(2, 2), new Coordinate(8, 2),
new Coordinate(8, 8), new Coordinate(2, 8), new Coordinate(2, 2)
});
// 创建测试点
var pointInside = factory.CreatePoint(new Coordinate(5, 5));
var pointOutside = factory.CreatePoint(new Coordinate(20, 20));
var pointOnBoundary = factory.CreatePoint(new Coordinate(0, 5));
// 创建测试线
var line = factory.CreateLineString(new Coordinate[]
{
new Coordinate(-5, 5), new Coordinate(15, 5)
});
4.2 基本空间谓词
4.2.1 Equals - 相等
判断两个几何是否在空间上相等(拓扑相等,不要求坐标顺序相同):
var poly1 = 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 poly2 = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(10, 0), new Coordinate(10, 10),
new Coordinate(0, 10), new Coordinate(0, 0), new Coordinate(10, 0)
});
// 完全不同
var poly3 = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(5, 0),
new Coordinate(5, 5), new Coordinate(0, 5), new Coordinate(0, 0)
});
Console.WriteLine($"poly1.Equals(poly2): {poly1.Equals(poly2)}"); // true(拓扑相等)
Console.WriteLine($"poly1.Equals(poly3): {poly1.Equals(poly3)}"); // false
// 精确相等(坐标完全一致)
Console.WriteLine($"poly1.EqualsExact(poly2): {poly1.EqualsExact(poly2)}"); // false
Console.WriteLine($"poly1.EqualsNormalized(poly2): {poly1.EqualsNormalized(poly2)}"); // true
4.2.2 Disjoint - 分离
判断两个几何是否没有任何公共点:
// polygon1 和 polygon3 不相交
Console.WriteLine($"Disjoint: {polygon1.Disjoint(polygon3)}"); // true
// polygon1 和 polygon2 有重叠
Console.WriteLine($"Disjoint: {polygon1.Disjoint(polygon2)}"); // false
// Disjoint 是 Intersects 的否定
Console.WriteLine($"!Intersects == Disjoint: {!polygon1.Intersects(polygon3) == polygon1.Disjoint(polygon3)}"); // true
4.2.3 Intersects - 相交
判断两个几何是否有至少一个公共点:
// 多边形相交
Console.WriteLine($"polygon1 ∩ polygon2: {polygon1.Intersects(polygon2)}"); // true
// 多边形不相交
Console.WriteLine($"polygon1 ∩ polygon3: {polygon1.Intersects(polygon3)}"); // false
// 点在多边形内
Console.WriteLine($"point in polygon: {polygon1.Intersects(pointInside)}"); // true
// 点在边界上
Console.WriteLine($"point on boundary: {polygon1.Intersects(pointOnBoundary)}"); // true
// 线与多边形相交
Console.WriteLine($"line ∩ polygon: {polygon1.Intersects(line)}"); // true
4.2.4 Touches - 接触
判断两个几何是否仅边界接触(内部不相交):
// 创建仅边界接触的多边形
var touchPoly = 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($"polygon1 touches touchPoly: {polygon1.Touches(touchPoly)}"); // true
// 重叠的多边形不是 Touches
Console.WriteLine($"polygon1 touches polygon2: {polygon1.Touches(polygon2)}"); // false
// 点在边界上
Console.WriteLine($"point on boundary touches: {polygon1.Touches(pointOnBoundary)}"); // true
// 点在内部不是 Touches
Console.WriteLine($"point inside touches: {polygon1.Touches(pointInside)}"); // false
4.2.5 Contains - 包含
判断一个几何是否完全包含另一个几何(边界和内部都在内):
// polygon1 包含 polygon4
Console.WriteLine($"polygon1 contains polygon4: {polygon1.Contains(polygon4)}"); // true
// polygon1 包含内部点
Console.WriteLine($"polygon1 contains pointInside: {polygon1.Contains(pointInside)}"); // true
// polygon1 不包含边界点(Contains 要求在内部)
Console.WriteLine($"polygon1 contains pointOnBoundary: {polygon1.Contains(pointOnBoundary)}"); // false
// polygon1 不包含 polygon2(只是部分重叠)
Console.WriteLine($"polygon1 contains polygon2: {polygon1.Contains(polygon2)}"); // false
4.2.6 Within - 被包含
Within 是 Contains 的逆操作:
// polygon4 在 polygon1 内
Console.WriteLine($"polygon4 within polygon1: {polygon4.Within(polygon1)}"); // true
// pointInside 在 polygon1 内
Console.WriteLine($"pointInside within polygon1: {pointInside.Within(polygon1)}"); // true
// Within 和 Contains 的关系
Console.WriteLine($"A.Contains(B) == B.Within(A): {polygon1.Contains(polygon4) == polygon4.Within(polygon1)}"); // true
4.2.7 Covers - 覆盖
Covers 比 Contains 更宽松,允许被包含的几何在边界上:
// Contains 不包含边界点
Console.WriteLine($"polygon1 contains boundary point: {polygon1.Contains(pointOnBoundary)}"); // false
// Covers 包含边界点
Console.WriteLine($"polygon1 covers boundary point: {polygon1.Covers(pointOnBoundary)}"); // true
// Covers 也包含内部点
Console.WriteLine($"polygon1 covers inside point: {polygon1.Covers(pointInside)}"); // true
4.2.8 CoveredBy - 被覆盖
CoveredBy 是 Covers 的逆操作:
// 边界点被多边形覆盖
Console.WriteLine($"boundary point covered by polygon1: {pointOnBoundary.CoveredBy(polygon1)}"); // true
// CoveredBy 和 Covers 的关系
Console.WriteLine($"A.Covers(B) == B.CoveredBy(A): {polygon1.Covers(pointOnBoundary) == pointOnBoundary.CoveredBy(polygon1)}"); // true
4.2.9 Crosses - 跨越
Crosses 用于不同维度几何之间的相交:
// 线跨越多边形(穿过内部)
var crossingLine = factory.CreateLineString(new Coordinate[]
{
new Coordinate(-5, 5), new Coordinate(15, 5)
});
Console.WriteLine($"line crosses polygon: {crossingLine.Crosses(polygon1)}"); // true
// 线在边界上(不是 Crosses)
var boundaryLine = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(10, 0)
});
Console.WriteLine($"boundary line crosses polygon: {boundaryLine.Crosses(polygon1)}"); // false
// 两条线相交
var line1 = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(10, 10)
});
var line2 = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 10), new Coordinate(10, 0)
});
Console.WriteLine($"line1 crosses line2: {line1.Crosses(line2)}"); // true
4.2.10 Overlaps - 重叠
Overlaps 用于相同维度几何之间的部分重叠:
// 两个多边形部分重叠
Console.WriteLine($"polygon1 overlaps polygon2: {polygon1.Overlaps(polygon2)}"); // true
// 完全包含不是 Overlaps
Console.WriteLine($"polygon1 overlaps polygon4: {polygon1.Overlaps(polygon4)}"); // false
// 不相交不是 Overlaps
Console.WriteLine($"polygon1 overlaps polygon3: {polygon1.Overlaps(polygon3)}"); // false
// 两条线部分重叠
var lineA = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(10, 0)
});
var lineB = factory.CreateLineString(new Coordinate[]
{
new Coordinate(5, 0), new Coordinate(15, 0)
});
Console.WriteLine($"lineA overlaps lineB: {lineA.Overlaps(lineB)}"); // true
4.3 DE-9IM 空间关系模型
4.3.1 DE-9IM 介绍
DE-9IM(Dimensionally Extended Nine-Intersection Model)是一种精确描述两个几何对象空间关系的模型。它使用一个 3x3 的矩阵来描述两个几何的内部(I)、边界(B)、外部(E)之间的相交情况。
几何 B
I B E
几何 A I [x x x]
B [x x x]
E [x x x]
矩阵中的每个值可以是:
T- True,有交集F- False,无交集*- 任意值0- 交集维度为 0(点)1- 交集维度为 1(线)2- 交集维度为 2(面)
4.3.2 获取 DE-9IM 矩阵
// 获取两个多边形的 DE-9IM 矩阵
var matrix = polygon1.Relate(polygon2);
Console.WriteLine($"DE-9IM 矩阵: {matrix}");
// 输出类似: 212101212
// 矩阵字符串解释
var matrixStr = matrix.ToString();
Console.WriteLine($"II: {matrixStr[0]}"); // 内部-内部
Console.WriteLine($"IB: {matrixStr[1]}"); // 内部-边界
Console.WriteLine($"IE: {matrixStr[2]}"); // 内部-外部
Console.WriteLine($"BI: {matrixStr[3]}"); // 边界-内部
Console.WriteLine($"BB: {matrixStr[4]}"); // 边界-边界
Console.WriteLine($"BE: {matrixStr[5]}"); // 边界-外部
Console.WriteLine($"EI: {matrixStr[6]}"); // 外部-内部
Console.WriteLine($"EB: {matrixStr[7]}"); // 外部-边界
Console.WriteLine($"EE: {matrixStr[8]}"); // 外部-外部
// 访问矩阵元素
Console.WriteLine($"内部是否相交: {matrix.IsIntersects()}");
Console.WriteLine($"是否分离: {matrix.IsDisjoint()}");
Console.WriteLine($"是否接触: {matrix.IsTouches(Dimension.Surface, Dimension.Surface)}");
4.3.3 常见空间关系的 DE-9IM 模式
| 空间关系 | DE-9IM 模式 | 说明 |
|---|---|---|
| Equals | TF**FFF | 内部和边界相等 |
| Disjoint | FF*FF**** | 没有任何交集 |
| Touches | FT******* 或 F**T***** 等 | 边界接触 |
| Within | T*F**F*** | A 在 B 内部 |
| Contains | T*****FF* | B 在 A 内部 |
| Overlaps (面) | T*T***T** | 部分重叠 |
| Crosses (线/面) | T*T****** | 线穿过面 |
4.3.4 使用模式匹配
// 自定义空间关系判断
bool CustomRelate(Geometry a, Geometry b, string pattern)
{
return a.Relate(b, pattern);
}
// 判断是否相交(使用模式)
Console.WriteLine($"Intersects: {polygon1.Relate(polygon2, "T********")}");
// 判断是否内部完全相交
Console.WriteLine($"Interior intersects: {polygon1.Relate(polygon2, "T********")}");
// 自定义:判断边界是否接触
Console.WriteLine($"Boundary touches: {polygon1.Relate(polygon2, "****T****")}");
// 自定义:判断是否有二维交集
Console.WriteLine($"2D intersection: {polygon1.Relate(polygon2, "2********")}");
4.3.5 IntersectionMatrix 详解
var matrix = polygon1.Relate(polygon2);
// 矩阵属性
Console.WriteLine($"是否相交: {matrix.IsIntersects()}");
Console.WriteLine($"是否分离: {matrix.IsDisjoint()}");
Console.WriteLine($"是否接触: {matrix.IsTouches(Dimension.Surface, Dimension.Surface)}");
Console.WriteLine($"是否相等: {matrix.IsEquals(Dimension.Surface, Dimension.Surface)}");
Console.WriteLine($"是否包含: {matrix.IsContains()}");
Console.WriteLine($"是否被包含: {matrix.IsWithin()}");
Console.WriteLine($"是否覆盖: {matrix.IsCovers()}");
Console.WriteLine($"是否被覆盖: {matrix.IsCoveredBy()}");
// 获取特定位置的维度
var interiorIntersection = matrix.Get(Location.Interior, Location.Interior);
Console.WriteLine($"内部交集维度: {interiorIntersection}");
// 检查是否匹配模式
Console.WriteLine($"匹配 T*F**FFF*: {matrix.Matches("T*F**FFF*")}");
4.4 距离计算
4.4.1 基本距离计算
var factory = new GeometryFactory();
var point1 = factory.CreatePoint(new Coordinate(0, 0));
var point2 = factory.CreatePoint(new Coordinate(3, 4));
// 点与点之间的距离
var distance = point1.Distance(point2);
Console.WriteLine($"两点距离: {distance}"); // 5
// 点与多边形之间的距离
var polygon = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(10, 10), new Coordinate(20, 10),
new Coordinate(20, 20), new Coordinate(10, 20), new Coordinate(10, 10)
});
Console.WriteLine($"点到多边形距离: {point1.Distance(polygon)}");
// 点在多边形内部时,距离为0
var insidePoint = factory.CreatePoint(new Coordinate(15, 15));
Console.WriteLine($"内部点到多边形距离: {insidePoint.Distance(polygon)}"); // 0
// 线与线之间的距离
var line1 = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(10, 0)
});
var line2 = factory.CreateLineString(new Coordinate[]
{
new Coordinate(0, 5), new Coordinate(10, 5)
});
Console.WriteLine($"两线距离: {line1.Distance(line2)}"); // 5
4.4.2 判断是否在指定距离内
// 使用 IsWithinDistance 方法(比 Distance 更高效)
var isWithin5 = point1.IsWithinDistance(polygon, 20);
Console.WriteLine($"是否在20单位内: {isWithin5}"); // true
var isWithin1 = point1.IsWithinDistance(polygon, 5);
Console.WriteLine($"是否在5单位内: {isWithin1}"); // false
// 对于大量几何的距离查询,IsWithinDistance 更高效
// 因为它可以在确定结果后立即返回,不需要计算精确距离
4.4.3 最近点计算
using NetTopologySuite.Operation.Distance;
var point = factory.CreatePoint(new Coordinate(0, 0));
var polygon = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(10, 10), new Coordinate(20, 10),
new Coordinate(20, 20), new Coordinate(10, 20), new Coordinate(10, 10)
});
// 获取最近点对
var distanceOp = new DistanceOp(point, polygon);
var nearestPoints = distanceOp.NearestPoints();
Console.WriteLine($"点1最近位置: ({nearestPoints[0].X}, {nearestPoints[0].Y})");
Console.WriteLine($"点2最近位置: ({nearestPoints[1].X}, {nearestPoints[1].Y})");
Console.WriteLine($"距离: {distanceOp.Distance()}");
// 获取最近点所在的几何位置
var nearestLocations = distanceOp.NearestLocations();
Console.WriteLine($"位置1几何索引: {nearestLocations[0].GeometryComponentIndex}");
Console.WriteLine($"位置2几何索引: {nearestLocations[1].GeometryComponentIndex}");
4.5 空间关系的实际应用
4.5.1 点在多边形内判断(选址分析)
public class LocationAnalysisService
{
private readonly GeometryFactory _factory;
public LocationAnalysisService()
{
_factory = new GeometryFactory(new PrecisionModel(), 4326);
}
/// <summary>
/// 判断商店是否在配送范围内
/// </summary>
public bool IsInDeliveryArea(double storeLon, double storeLat, Polygon deliveryArea)
{
var storeLocation = _factory.CreatePoint(new Coordinate(storeLon, storeLat));
return deliveryArea.Contains(storeLocation);
}
/// <summary>
/// 筛选在指定区域内的所有位置
/// </summary>
public List<Point> FilterLocationsInArea(IEnumerable<Point> locations, Polygon area)
{
return locations.Where(p => area.Contains(p)).ToList();
}
/// <summary>
/// 判断点是否在任意一个区域内
/// </summary>
public bool IsInAnyArea(Point point, IEnumerable<Polygon> areas)
{
return areas.Any(area => area.Contains(point));
}
}
// 使用示例
var service = new LocationAnalysisService();
var factory = new GeometryFactory(new PrecisionModel(), 4326);
// 定义配送范围
var deliveryArea = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(116.3, 39.8), new Coordinate(116.5, 39.8),
new Coordinate(116.5, 40.0), new Coordinate(116.3, 40.0),
new Coordinate(116.3, 39.8)
});
// 判断商店是否在配送范围内
var inRange = service.IsInDeliveryArea(116.4, 39.9, deliveryArea);
Console.WriteLine($"商店在配送范围内: {inRange}");
4.5.2 缓冲区分析(服务范围)
public class ServiceAreaAnalysis
{
private readonly GeometryFactory _factory;
public ServiceAreaAnalysis()
{
_factory = new GeometryFactory();
}
/// <summary>
/// 计算服务点的覆盖范围
/// </summary>
public Geometry CalculateServiceArea(Point servicePoint, double radius)
{
return servicePoint.Buffer(radius);
}
/// <summary>
/// 计算多个服务点的联合覆盖范围
/// </summary>
public Geometry CalculateCombinedServiceArea(IEnumerable<Point> servicePoints, double radius)
{
var buffers = servicePoints.Select(p => p.Buffer(radius));
var union = buffers.First();
foreach (var buffer in buffers.Skip(1))
{
union = union.Union(buffer);
}
return union;
}
/// <summary>
/// 分析服务覆盖重叠区域
/// </summary>
public Geometry FindOverlapArea(Geometry area1, Geometry area2)
{
return area1.Intersection(area2);
}
/// <summary>
/// 查找未覆盖区域
/// </summary>
public Geometry FindUncoveredArea(Geometry totalArea, Geometry coveredArea)
{
return totalArea.Difference(coveredArea);
}
}
// 使用示例
var analysis = new ServiceAreaAnalysis();
var factory = new GeometryFactory();
// 创建服务点
var hospital1 = factory.CreatePoint(new Coordinate(100, 100));
var hospital2 = factory.CreatePoint(new Coordinate(150, 100));
// 计算5km服务半径的覆盖范围
var serviceArea1 = analysis.CalculateServiceArea(hospital1, 50);
var serviceArea2 = analysis.CalculateServiceArea(hospital2, 50);
// 查找重叠服务区域
var overlap = analysis.FindOverlapArea(serviceArea1, serviceArea2);
Console.WriteLine($"重叠区域面积: {overlap.Area}");
// 计算联合服务范围
var combined = analysis.CalculateCombinedServiceArea(
new[] { hospital1, hospital2 }, 50);
Console.WriteLine($"联合覆盖面积: {combined.Area}");
4.5.3 相交分析(用地分析)
public class LandUseAnalysis
{
private readonly GeometryFactory _factory;
public LandUseAnalysis()
{
_factory = new GeometryFactory();
}
/// <summary>
/// 分析道路与地块的交叉情况
/// </summary>
public AnalysisResult AnalyzeRoadIntersection(Polygon parcel, LineString road)
{
var result = new AnalysisResult();
result.DoesIntersect = parcel.Intersects(road);
if (result.DoesIntersect)
{
result.Intersection = parcel.Intersection(road);
result.IntersectionLength = result.Intersection.Length;
// 判断相交类型
if (road.Within(parcel))
{
result.IntersectionType = "道路完全在地块内";
}
else if (road.Crosses(parcel))
{
result.IntersectionType = "道路穿过地块";
}
else if (road.Touches(parcel))
{
result.IntersectionType = "道路与地块边界接触";
}
}
return result;
}
/// <summary>
/// 计算地块被道路分割后的部分
/// </summary>
public Geometry[] SplitParcelByRoad(Polygon parcel, LineString road, double roadWidth)
{
// 创建道路缓冲区
var roadBuffer = road.Buffer(roadWidth / 2);
// 从地块中减去道路
var remaining = parcel.Difference(roadBuffer);
// 返回分割后的各部分
if (remaining is GeometryCollection collection)
{
return collection.Geometries;
}
return new[] { remaining };
}
}
public class AnalysisResult
{
public bool DoesIntersect { get; set; }
public Geometry? Intersection { get; set; }
public double IntersectionLength { get; set; }
public string IntersectionType { get; set; } = "";
}
// 使用示例
var landAnalysis = new LandUseAnalysis();
var factory = new GeometryFactory();
// 创建地块
var parcel = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(0, 0), new Coordinate(100, 0),
new Coordinate(100, 100), new Coordinate(0, 100), new Coordinate(0, 0)
});
// 创建道路
var road = factory.CreateLineString(new Coordinate[]
{
new Coordinate(-20, 50), new Coordinate(120, 50)
});
// 分析交叉情况
var result = landAnalysis.AnalyzeRoadIntersection(parcel, road);
Console.WriteLine($"道路是否穿过地块: {result.DoesIntersect}");
Console.WriteLine($"交叉类型: {result.IntersectionType}");
Console.WriteLine($"交叉长度: {result.IntersectionLength}");
// 分割地块
var splitParts = landAnalysis.SplitParcelByRoad(parcel, road, 10);
Console.WriteLine($"分割后部分数量: {splitParts.Length}");
4.5.4 邻近分析
public class ProximityAnalysis
{
private readonly GeometryFactory _factory;
public ProximityAnalysis()
{
_factory = new GeometryFactory();
}
/// <summary>
/// 查找指定距离内的所有要素
/// </summary>
public List<T> FindNearbyFeatures<T>(
Geometry searchGeometry,
IEnumerable<T> candidates,
Func<T, Geometry> geometrySelector,
double maxDistance)
{
return candidates
.Where(c => searchGeometry.IsWithinDistance(geometrySelector(c), maxDistance))
.ToList();
}
/// <summary>
/// 查找最近的N个要素
/// </summary>
public List<(T Feature, double Distance)> FindNearestN<T>(
Geometry searchGeometry,
IEnumerable<T> candidates,
Func<T, Geometry> geometrySelector,
int n)
{
return candidates
.Select(c => (Feature: c, Distance: searchGeometry.Distance(geometrySelector(c))))
.OrderBy(x => x.Distance)
.Take(n)
.ToList();
}
/// <summary>
/// 按距离分组
/// </summary>
public Dictionary<string, List<T>> GroupByDistance<T>(
Geometry centerGeometry,
IEnumerable<T> items,
Func<T, Geometry> geometrySelector,
double[] distanceBreaks)
{
var result = new Dictionary<string, List<T>>();
foreach (var item in items)
{
var distance = centerGeometry.Distance(geometrySelector(item));
var category = GetDistanceCategory(distance, distanceBreaks);
if (!result.ContainsKey(category))
{
result[category] = new List<T>();
}
result[category].Add(item);
}
return result;
}
private string GetDistanceCategory(double distance, double[] breaks)
{
for (int i = 0; i < breaks.Length; i++)
{
if (distance <= breaks[i])
{
return i == 0
? $"0-{breaks[i]}"
: $"{breaks[i-1]}-{breaks[i]}";
}
}
return $">{breaks[^1]}";
}
}
// 使用示例
var proximity = new ProximityAnalysis();
var factory = new GeometryFactory();
// 创建中心点
var center = factory.CreatePoint(new Coordinate(0, 0));
// 创建候选点
var candidates = Enumerable.Range(1, 100)
.Select(i => factory.CreatePoint(new Coordinate(i * 10, i * 5)))
.ToList();
// 查找100单位距离内的点
var nearby = proximity.FindNearbyFeatures(center, candidates, p => p, 100);
Console.WriteLine($"100单位内的点数量: {nearby.Count}");
// 查找最近的5个点
var nearest = proximity.FindNearestN(center, candidates, p => p, 5);
foreach (var (feature, distance) in nearest)
{
Console.WriteLine($"点: {feature.AsText()}, 距离: {distance:F2}");
}
// 按距离分组
var groups = proximity.GroupByDistance(
center, candidates, p => p,
new[] { 100.0, 200.0, 300.0, 500.0 });
foreach (var group in groups)
{
Console.WriteLine($"距离 {group.Key}: {group.Value.Count} 个点");
}
4.6 性能优化
4.6.1 使用 PreparedGeometry
对于重复的空间关系查询,使用 PreparedGeometry 可以显著提高性能:
using NetTopologySuite.Geometries.Prepared;
var factory = new GeometryFactory();
// 创建一个复杂多边形
var complexPolygon = factory.CreatePolygon(/* 大量坐标点 */);
// 准备几何(预计算空间索引)
var preparedPolygon = PreparedGeometryFactory.Prepare(complexPolygon);
// 创建大量测试点
var testPoints = Enumerable.Range(0, 10000)
.Select(i => factory.CreatePoint(new Coordinate(
Random.Shared.NextDouble() * 100,
Random.Shared.NextDouble() * 100)))
.ToList();
// 使用 PreparedGeometry 进行查询(快速)
var startTime = DateTime.Now;
var containedPoints = testPoints
.Where(p => preparedPolygon.Contains(p))
.ToList();
var preparedTime = DateTime.Now - startTime;
Console.WriteLine($"PreparedGeometry 耗时: {preparedTime.TotalMilliseconds}ms");
Console.WriteLine($"包含的点数量: {containedPoints.Count}");
// PreparedGeometry 支持的操作
// preparedPolygon.Contains(geometry)
// preparedPolygon.ContainsProperly(geometry)
// preparedPolygon.Covers(geometry)
// preparedPolygon.CoveredBy(geometry)
// preparedPolygon.Intersects(geometry)
// preparedPolygon.Within(geometry)
4.6.2 使用空间索引
using NetTopologySuite.Index.Strtree;
// 创建 STRtree 空间索引
var index = new STRtree<Geometry>();
// 添加几何到索引
var polygons = Enumerable.Range(0, 1000)
.Select(i => factory.CreatePolygon(new Coordinate[]
{
new Coordinate(i * 10, 0), new Coordinate(i * 10 + 10, 0),
new Coordinate(i * 10 + 10, 10), new Coordinate(i * 10, 10),
new Coordinate(i * 10, 0)
}))
.ToList();
foreach (var polygon in polygons)
{
index.Insert(polygon.EnvelopeInternal, polygon);
}
// 必须在查询前构建索引
index.Build();
// 使用索引进行范围查询
var searchEnvelope = new Envelope(50, 150, 0, 10);
var candidates = index.Query(searchEnvelope);
Console.WriteLine($"索引查询结果数量: {candidates.Count}");
// 进一步筛选精确相交的几何
var searchArea = factory.CreatePolygon(new Coordinate[]
{
new Coordinate(50, 0), new Coordinate(150, 0),
new Coordinate(150, 10), new Coordinate(50, 10), new Coordinate(50, 0)
});
var exactMatches = candidates.Where(g => g.Intersects(searchArea)).ToList();
Console.WriteLine($"精确匹配数量: {exactMatches.Count}");
4.7 本章小结
本章详细介绍了 NetTopologySuite 的空间关系与谓词操作:
- 基本空间谓词:Equals、Disjoint、Intersects、Touches、Contains、Within、Covers、CoveredBy、Crosses、Overlaps
- DE-9IM 模型:深入理解九交模型及其模式匹配
- 距离计算:距离查询和最近点计算
- 实际应用:选址分析、缓冲区分析、相交分析、邻近分析
- 性能优化:PreparedGeometry 和空间索引的使用
4.8 下一步
下一章我们将学习几何运算与叠加分析,包括:
- 布尔运算(Union、Intersection、Difference、SymmetricDifference)
- 缓冲区操作
- 凸包和质心计算
- 几何简化和验证
相关资源:

浙公网安备 33010602011771号