10-高级应用与性能优化
第十章:高级应用与性能优化
10.1 概述
在实际项目中,除了正确使用 geometry-api-net 的 API,还需要考虑性能、可维护性和扩展性。本章将介绍高级使用技巧和性能优化方法。
10.2 架构最佳实践
10.2.1 服务层封装
将几何操作封装为服务,提高代码复用性和可测试性:
public interface IGeometryService
{
bool IsPointInRegion(Point point, Polygon region);
double CalculateDistanceMeters(Point p1, Point p2);
Polygon CreateBuffer(Geometry geometry, double distanceMeters);
Geometry Simplify(Geometry geometry, double tolerance);
}
public class GeometryService : IGeometryService
{
private readonly SpatialReference _defaultSR = SpatialReference.Wgs84();
public bool IsPointInRegion(Point point, Polygon region)
{
if (point == null || region == null)
throw new ArgumentNullException();
return GeometryEngine.Contains(region, point);
}
public double CalculateDistanceMeters(Point p1, Point p2)
{
if (p1 == null || p2 == null)
throw new ArgumentNullException();
return GeometryEngine.GeodesicDistance(p1, p2);
}
public Polygon CreateBuffer(Geometry geometry, double distanceMeters)
{
// 将米转换为度(近似值)
double distanceDegrees = distanceMeters / 111000.0;
return GeometryEngine.Buffer(geometry, distanceDegrees);
}
public Geometry Simplify(Geometry geometry, double tolerance)
{
return GeometryEngine.Simplify(geometry, tolerance);
}
}
// 依赖注入配置
services.AddSingleton<IGeometryService, GeometryService>();
10.2.2 仓储模式
public interface ISpatialRepository<T>
{
IEnumerable<T> FindInRegion(Polygon region);
IEnumerable<T> FindNearby(Point location, double radiusMeters);
T FindNearest(Point location);
}
public class SpatialRepository<T> : ISpatialRepository<T>
where T : ISpatialEntity
{
private readonly List<T> _entities;
public IEnumerable<T> FindInRegion(Polygon region)
{
return _entities.Where(e =>
GeometryEngine.Contains(region, e.Location));
}
public IEnumerable<T> FindNearby(Point location, double radiusMeters)
{
return _entities.Where(e =>
GeometryEngine.GeodesicDistance(location, e.Location) <= radiusMeters);
}
public T FindNearest(Point location)
{
return _entities
.OrderBy(e => GeometryEngine.GeodesicDistance(location, e.Location))
.FirstOrDefault();
}
}
public interface ISpatialEntity
{
Point Location { get; }
}
10.2.3 扩展方法
public static class GeometryExtensions
{
// 链式 API
public static Geometry Buffer(this Geometry geometry, double distance)
{
return GeometryEngine.Buffer(geometry, distance);
}
public static Geometry Simplify(this Geometry geometry, double tolerance)
{
return GeometryEngine.Simplify(geometry, tolerance);
}
public static string ToWkt(this Geometry geometry)
{
return GeometryEngine.GeometryToWkt(geometry);
}
public static string ToGeoJson(this Geometry geometry)
{
return GeometryEngine.GeometryToGeoJson(geometry);
}
// 便捷方法
public static bool IsWithin(this Point point, Polygon polygon)
{
return GeometryEngine.Contains(polygon, point);
}
public static double DistanceToMeters(this Point p1, Point p2)
{
return GeometryEngine.GeodesicDistance(p1, p2);
}
// 验证方法
public static bool IsValidPolygon(this Polygon polygon)
{
if (polygon.IsEmpty || polygon.RingCount == 0)
return false;
var ring = polygon.GetRing(0);
if (ring.Count < 4) // 至少 3 个点 + 闭合点
return false;
// 检查闭合
var first = ring[0];
var last = ring[ring.Count - 1];
return first.Equals(last, 0.0001);
}
}
// 使用
var simplified = polygon
.Simplify(0.001)
.Buffer(0.01);
string wkt = simplified.ToWkt();
10.3 性能优化技巧
10.3.1 包络矩形预过滤
在进行复杂空间操作前,先使用包络矩形进行快速过滤:
public class OptimizedSpatialQuery
{
private List<Polygon> _regions;
private Dictionary<Polygon, Envelope> _envelopeCache;
public OptimizedSpatialQuery(List<Polygon> regions)
{
_regions = regions;
// 预计算所有包络矩形
_envelopeCache = regions.ToDictionary(
r => r,
r => r.GetEnvelope());
}
public List<Polygon> FindContaining(Point point)
{
var results = new List<Polygon>();
foreach (var region in _regions)
{
// 快速过滤:检查点是否在包络内
if (!_envelopeCache[region].Contains(point))
continue;
// 精确检查
if (GeometryEngine.Contains(region, point))
{
results.Add(region);
}
}
return results;
}
public List<Polygon> FindIntersecting(Polygon query)
{
var queryEnvelope = query.GetEnvelope();
return _regions
.Where(r => _envelopeCache[r].Intersects(queryEnvelope)) // 快速过滤
.Where(r => GeometryEngine.Intersects(r, query)) // 精确检查
.ToList();
}
}
10.3.2 对象复用
避免频繁创建几何对象:
public class GeometryPool
{
private readonly Stack<Point> _pointPool = new();
private readonly Stack<Envelope> _envelopePool = new();
public Point RentPoint(double x, double y)
{
if (_pointPool.TryPop(out var point))
{
point.X = x;
point.Y = y;
point.Z = null;
point.M = null;
return point;
}
return new Point(x, y);
}
public void ReturnPoint(Point point)
{
_pointPool.Push(point);
}
public Envelope RentEnvelope(double xMin, double yMin, double xMax, double yMax)
{
if (_envelopePool.TryPop(out var envelope))
{
envelope.XMin = xMin;
envelope.YMin = yMin;
envelope.XMax = xMax;
envelope.YMax = yMax;
return envelope;
}
return new Envelope(xMin, yMin, xMax, yMax);
}
public void ReturnEnvelope(Envelope envelope)
{
_envelopePool.Push(envelope);
}
}
// 使用
var pool = new GeometryPool();
var point = pool.RentPoint(10, 20);
// 使用 point...
pool.ReturnPoint(point);
10.3.3 批量操作
public class BatchProcessor
{
public List<bool> BatchContainsTest(Polygon region, List<Point> points)
{
var envelope = region.GetEnvelope();
var results = new List<bool>(points.Count);
foreach (var point in points)
{
// 快速过滤
if (!envelope.Contains(point))
{
results.Add(false);
continue;
}
results.Add(GeometryEngine.Contains(region, point));
}
return results;
}
public List<double> BatchDistanceCalculation(Point origin, List<Point> targets)
{
// 使用并行处理
return targets
.AsParallel()
.AsOrdered()
.Select(t => GeometryEngine.GeodesicDistance(origin, t))
.ToList();
}
}
10.3.4 缓存策略
public class GeometryCache
{
private readonly Dictionary<string, Geometry> _wktCache = new();
private readonly Dictionary<Geometry, string> _reverseCache = new();
private readonly Dictionary<Geometry, double> _areaCache = new();
private readonly Dictionary<Geometry, Envelope> _envelopeCache = new();
public Geometry ParseWkt(string wkt)
{
if (_wktCache.TryGetValue(wkt, out var cached))
return cached;
var geometry = GeometryEngine.GeometryFromWkt(wkt);
_wktCache[wkt] = geometry;
return geometry;
}
public double GetArea(Geometry geometry)
{
if (_areaCache.TryGetValue(geometry, out var area))
return area;
area = GeometryEngine.Area(geometry);
_areaCache[geometry] = area;
return area;
}
public Envelope GetEnvelope(Geometry geometry)
{
if (_envelopeCache.TryGetValue(geometry, out var envelope))
return envelope;
envelope = geometry.GetEnvelope();
_envelopeCache[geometry] = envelope;
return envelope;
}
public void Clear()
{
_wktCache.Clear();
_reverseCache.Clear();
_areaCache.Clear();
_envelopeCache.Clear();
}
}
10.4 错误处理
10.4.1 验证输入
public static class GeometryValidator
{
public static ValidationResult Validate(Geometry geometry)
{
if (geometry == null)
return ValidationResult.Error("几何对象为空");
if (geometry.IsEmpty)
return ValidationResult.Warning("几何对象为空几何");
if (geometry is Polygon polygon)
{
return ValidatePolygon(polygon);
}
if (geometry is Polyline polyline)
{
return ValidatePolyline(polyline);
}
return ValidationResult.Success();
}
private static ValidationResult ValidatePolygon(Polygon polygon)
{
if (polygon.RingCount == 0)
return ValidationResult.Error("多边形没有环");
var ring = polygon.GetRing(0);
if (ring.Count < 4)
return ValidationResult.Error("多边形环至少需要 4 个点(含闭合点)");
// 检查闭合
var first = ring[0];
var last = ring[ring.Count - 1];
if (!first.Equals(last, 0.0001))
return ValidationResult.Warning("多边形环未闭合");
// 检查面积
if (polygon.Area < 0.00000001)
return ValidationResult.Warning("多边形面积过小");
return ValidationResult.Success();
}
private static ValidationResult ValidatePolyline(Polyline polyline)
{
if (polyline.PathCount == 0)
return ValidationResult.Error("折线没有路径");
var path = polyline.GetPath(0);
if (path.Count < 2)
return ValidationResult.Error("折线路径至少需要 2 个点");
if (polyline.Length < 0.00000001)
return ValidationResult.Warning("折线长度过短");
return ValidationResult.Success();
}
}
public class ValidationResult
{
public bool IsValid { get; }
public string Message { get; }
public ValidationLevel Level { get; }
public static ValidationResult Success() => new(true, null, ValidationLevel.None);
public static ValidationResult Warning(string msg) => new(true, msg, ValidationLevel.Warning);
public static ValidationResult Error(string msg) => new(false, msg, ValidationLevel.Error);
private ValidationResult(bool isValid, string message, ValidationLevel level)
{
IsValid = isValid;
Message = message;
Level = level;
}
}
public enum ValidationLevel { None, Warning, Error }
10.4.2 安全包装
public static class SafeGeometryEngine
{
public static Result<bool> TryContains(Geometry g1, Geometry g2)
{
try
{
if (g1 == null || g2 == null)
return Result<bool>.Failure("输入几何对象为空");
return Result<bool>.Ok(GeometryEngine.Contains(g1, g2));
}
catch (Exception ex)
{
return Result<bool>.Failure($"Contains 操作失败: {ex.Message}");
}
}
public static Result<Geometry> TryParseWkt(string wkt)
{
try
{
if (string.IsNullOrWhiteSpace(wkt))
return Result<Geometry>.Failure("WKT 字符串为空");
var geometry = GeometryEngine.GeometryFromWkt(wkt);
return Result<Geometry>.Ok(geometry);
}
catch (FormatException ex)
{
return Result<Geometry>.Failure($"WKT 格式错误: {ex.Message}");
}
catch (Exception ex)
{
return Result<Geometry>.Failure($"解析失败: {ex.Message}");
}
}
}
public class Result<T>
{
public bool IsSuccess { get; }
public T Value { get; }
public string Error { get; }
public static Result<T> Ok(T value) => new(true, value, null);
public static Result<T> Failure(string error) => new(false, default, error);
private Result(bool success, T value, string error)
{
IsSuccess = success;
Value = value;
Error = error;
}
}
10.5 测试策略
10.5.1 单元测试
using Xunit;
public class GeometryServiceTests
{
private readonly IGeometryService _service;
public GeometryServiceTests()
{
_service = new GeometryService();
}
[Fact]
public void IsPointInRegion_PointInside_ReturnsTrue()
{
// Arrange
var polygon = CreateSquarePolygon(0, 0, 100, 100);
var point = new Point(50, 50);
// Act
bool result = _service.IsPointInRegion(point, polygon);
// Assert
Assert.True(result);
}
[Fact]
public void IsPointInRegion_PointOutside_ReturnsFalse()
{
var polygon = CreateSquarePolygon(0, 0, 100, 100);
var point = new Point(150, 150);
bool result = _service.IsPointInRegion(point, polygon);
Assert.False(result);
}
[Theory]
[InlineData(0, 0, 3, 4, 5)] // 3-4-5 三角形
[InlineData(0, 0, 0, 10, 10)] // 垂直线
[InlineData(0, 0, 10, 0, 10)] // 水平线
public void Distance_KnownValues_ReturnsExpected(
double x1, double y1, double x2, double y2, double expected)
{
var p1 = new Point(x1, y1);
var p2 = new Point(x2, y2);
double distance = GeometryEngine.Distance(p1, p2);
Assert.Equal(expected, distance, precision: 6);
}
[Fact]
public void WktRoundTrip_PreservesGeometry()
{
var original = CreateSquarePolygon(0, 0, 100, 100);
string wkt = GeometryEngine.GeometryToWkt(original);
var parsed = GeometryEngine.GeometryFromWkt(wkt);
Assert.True(GeometryEngine.Equals(original, parsed));
}
private Polygon CreateSquarePolygon(double x1, double y1, double x2, double y2)
{
var polygon = new Polygon();
polygon.AddRing(new List<Point>
{
new Point(x1, y1),
new Point(x2, y1),
new Point(x2, y2),
new Point(x1, y2),
new Point(x1, y1)
});
return polygon;
}
}
10.5.2 性能测试
using BenchmarkDotNet.Attributes;
[MemoryDiagnoser]
public class GeometryBenchmarks
{
private Polygon _polygon;
private List<Point> _testPoints;
[GlobalSetup]
public void Setup()
{
_polygon = CreateComplexPolygon(1000); // 1000 个顶点
_testPoints = GenerateRandomPoints(10000);
}
[Benchmark]
public int ContainsTest_WithPrefilter()
{
var envelope = _polygon.GetEnvelope();
int count = 0;
foreach (var point in _testPoints)
{
if (envelope.Contains(point) &&
GeometryEngine.Contains(_polygon, point))
{
count++;
}
}
return count;
}
[Benchmark]
public int ContainsTest_WithoutPrefilter()
{
int count = 0;
foreach (var point in _testPoints)
{
if (GeometryEngine.Contains(_polygon, point))
{
count++;
}
}
return count;
}
}
10.6 调试技巧
10.6.1 几何可视化
public static class GeometryDebugger
{
public static void PrintGeometry(Geometry geometry, string name = "Geometry")
{
Console.WriteLine($"=== {name} ===");
Console.WriteLine($"Type: {geometry.Type}");
Console.WriteLine($"IsEmpty: {geometry.IsEmpty}");
Console.WriteLine($"Dimension: {geometry.Dimension}");
var envelope = geometry.GetEnvelope();
Console.WriteLine($"Envelope: ({envelope.XMin:F4}, {envelope.YMin:F4}) - ({envelope.XMax:F4}, {envelope.YMax:F4})");
Console.WriteLine($"WKT: {GeometryEngine.GeometryToWkt(geometry)}");
if (geometry is Polygon polygon)
{
Console.WriteLine($"RingCount: {polygon.RingCount}");
Console.WriteLine($"Area: {polygon.Area:F4}");
}
if (geometry is Polyline polyline)
{
Console.WriteLine($"PathCount: {polyline.PathCount}");
Console.WriteLine($"Length: {polyline.Length:F4}");
}
Console.WriteLine();
}
public static string ToSvg(Geometry geometry, double width = 200, double height = 200)
{
var envelope = geometry.GetEnvelope();
double scaleX = width / envelope.Width;
double scaleY = height / envelope.Height;
double scale = Math.Min(scaleX, scaleY);
var sb = new StringBuilder();
sb.AppendLine($"<svg width=\"{width}\" height=\"{height}\" xmlns=\"http://www.w3.org/2000/svg\">");
if (geometry is Polygon polygon)
{
foreach (var ring in polygon.GetRings())
{
var points = string.Join(" ", ring.Select(p =>
$"{(p.X - envelope.XMin) * scale:F2},{height - (p.Y - envelope.YMin) * scale:F2}"));
sb.AppendLine($" <polygon points=\"{points}\" fill=\"blue\" fill-opacity=\"0.3\" stroke=\"blue\" stroke-width=\"1\"/>");
}
}
sb.AppendLine("</svg>");
return sb.ToString();
}
}
10.7 小结
本章介绍了 geometry-api-net 的高级应用和性能优化:
架构最佳实践:
- 服务层封装
- 仓储模式
- 扩展方法
性能优化:
- 包络矩形预过滤
- 对象复用
- 批量操作
- 缓存策略
错误处理:
- 输入验证
- 安全包装
测试策略:
- 单元测试
- 性能测试
在下一章中,我们将通过实战案例,展示如何在实际项目中综合运用这些技术。

浙公网安备 33010602011771号