第14章-高级功能与性能优化

第14章:高级功能与性能优化

14.1 性能优化概述

在处理大量空间数据时,性能优化至关重要。本章介绍 NetTopologySuite 的高级功能和性能优化技巧。

14.2 PreparedGeometry 优化

14.2.1 基本使用

PreparedGeometry 通过预计算几何的空间索引,大幅提高重复空间关系查询的性能。

using NetTopologySuite.Geometries;
using NetTopologySuite.Geometries.Prepared;

var factory = new GeometryFactory();

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

// 创建 PreparedGeometry
var prepared = PreparedGeometryFactory.Prepare(polygon);

// 创建测试点
var points = Enumerable.Range(0, 100000)
    .Select(i => factory.CreatePoint(new Coordinate(
        Random.Shared.NextDouble() * 150 - 25,
        Random.Shared.NextDouble() * 150 - 25)))
    .ToList();

// 性能比较
var sw = System.Diagnostics.Stopwatch.StartNew();

// 使用普通几何
var normalCount = points.Count(p => polygon.Contains(p));
var normalTime = sw.ElapsedMilliseconds;

sw.Restart();

// 使用 PreparedGeometry
var preparedCount = points.Count(p => prepared.Contains(p));
var preparedTime = sw.ElapsedMilliseconds;

Console.WriteLine($"普通方法: {normalTime}ms, 结果: {normalCount}");
Console.WriteLine($"Prepared: {preparedTime}ms, 结果: {preparedCount}");
Console.WriteLine($"性能提升: {(double)normalTime / preparedTime:F1}x");

14.2.2 支持的操作

var prepared = PreparedGeometryFactory.Prepare(polygon);

// 支持的空间关系
bool contains = prepared.Contains(point);
bool containsProperly = prepared.ContainsProperly(point);
bool covers = prepared.Covers(point);
bool coveredBy = prepared.CoveredBy(otherPolygon);
bool intersects = prepared.Intersects(point);
bool within = prepared.Within(otherPolygon);

14.3 空间索引

14.3.1 STRtree 索引

using NetTopologySuite.Index.Strtree;

// 创建索引
var tree = new STRtree<Geometry>();

// 批量添加几何
foreach (var geom in geometries)
{
    tree.Insert(geom.EnvelopeInternal, geom);
}

// 必须在查询前构建
tree.Build();

// 范围查询
var searchEnvelope = new Envelope(0, 50, 0, 50);
var candidates = tree.Query(searchEnvelope);

// 精确筛选
var matches = candidates
    .Where(g => g.Intersects(queryGeometry))
    .ToList();

14.3.2 自定义索引项

public class IndexedFeature
{
    public int Id { get; set; }
    public Geometry Geometry { get; set; }
    public Dictionary<string, object> Properties { get; set; }
}

// 创建索引
var featureIndex = new STRtree<IndexedFeature>();

foreach (var feature in features)
{
    featureIndex.Insert(feature.Geometry.EnvelopeInternal, feature);
}
featureIndex.Build();

// 查询
var results = featureIndex.Query(searchEnvelope)
    .Where(f => f.Geometry.Intersects(queryGeometry))
    .ToList();

14.3.3 Quadtree 四叉树

using NetTopologySuite.Index.Quadtree;

var quadtree = new Quadtree<Geometry>();

// 添加几何
foreach (var geom in geometries)
{
    quadtree.Insert(geom.EnvelopeInternal, geom);
}

// 查询
var results = quadtree.Query(searchEnvelope);

// 删除
quadtree.Remove(geom.EnvelopeInternal, geom);

14.4 并行处理

14.4.1 并行空间查询

using System.Collections.Concurrent;

public class ParallelSpatialQuery
{
    private readonly STRtree<Geometry> _index;
    private readonly PreparedGeometry _preparedQuery;

    public ParallelSpatialQuery(IEnumerable<Geometry> geometries, Geometry queryGeometry)
    {
        _index = new STRtree<Geometry>();
        foreach (var geom in geometries)
        {
            _index.Insert(geom.EnvelopeInternal, geom);
        }
        _index.Build();
        
        _preparedQuery = PreparedGeometryFactory.Prepare(queryGeometry);
    }

    /// <summary>
    /// 并行查询
    /// </summary>
    public List<Geometry> QueryParallel(Envelope extent)
    {
        var candidates = _index.Query(extent);
        var results = new ConcurrentBag<Geometry>();

        Parallel.ForEach(candidates, geom =>
        {
            if (_preparedQuery.Intersects(geom))
            {
                results.Add(geom);
            }
        });

        return results.ToList();
    }
}

14.4.2 并行几何运算

public class ParallelGeometryOperations
{
    /// <summary>
    /// 并行缓冲区计算
    /// </summary>
    public static List<Geometry> ParallelBuffer(
        IEnumerable<Geometry> geometries, 
        double distance)
    {
        return geometries
            .AsParallel()
            .Select(g => g.Buffer(distance))
            .ToList();
    }

    /// <summary>
    /// 并行简化
    /// </summary>
    public static List<Geometry> ParallelSimplify(
        IEnumerable<Geometry> geometries,
        double tolerance)
    {
        return geometries
            .AsParallel()
            .Select(g => g.Simplify(tolerance))
            .ToList();
    }

    /// <summary>
    /// 并行空间关系判断
    /// </summary>
    public static List<(int Index, bool Result)> ParallelContains(
        Geometry container,
        IList<Geometry> geometries)
    {
        var prepared = PreparedGeometryFactory.Prepare(container);
        
        return geometries
            .AsParallel()
            .Select((g, i) => (i, prepared.Contains(g)))
            .ToList();
    }
}

14.5 内存优化

14.5.1 几何池化

public class GeometryPool
{
    private readonly ConcurrentDictionary<int, ConcurrentBag<Coordinate[]>> _coordinateArrayPool;
    private readonly GeometryFactory _factory;

    public GeometryPool(int srid = 4326)
    {
        _coordinateArrayPool = new ConcurrentDictionary<int, ConcurrentBag<Coordinate[]>>();
        _factory = new GeometryFactory(new PrecisionModel(), srid);
    }

    /// <summary>
    /// 获取坐标数组
    /// </summary>
    public Coordinate[] RentCoordinates(int size)
    {
        if (_coordinateArrayPool.TryGetValue(size, out var pool) && pool.TryTake(out var array))
        {
            return array;
        }
        return new Coordinate[size];
    }

    /// <summary>
    /// 归还坐标数组
    /// </summary>
    public void ReturnCoordinates(Coordinate[] array)
    {
        var pool = _coordinateArrayPool.GetOrAdd(array.Length, _ => new ConcurrentBag<Coordinate[]>());
        // 清理数组
        Array.Clear(array, 0, array.Length);
        pool.Add(array);
    }
}

14.5.2 流式处理大文件

public class StreamingShapefileProcessor
{
    /// <summary>
    /// 流式处理 Shapefile(避免一次性加载到内存)
    /// </summary>
    public IEnumerable<Feature> StreamFeatures(string path)
    {
        using var reader = Shapefile.OpenRead(path);
        
        while (reader.Read())
        {
            var geometry = reader.Geometry;
            var attributes = new AttributesTable();
            
            for (int i = 0; i < reader.Fields.Count; i++)
            {
                attributes.Add(reader.Fields[i].Name, reader.GetValue(i));
            }
            
            yield return new Feature(geometry, attributes);
        }
    }

    /// <summary>
    /// 分批处理
    /// </summary>
    public void ProcessInBatches(string path, int batchSize, Action<List<Feature>> processor)
    {
        var batch = new List<Feature>(batchSize);
        
        foreach (var feature in StreamFeatures(path))
        {
            batch.Add(feature);
            
            if (batch.Count >= batchSize)
            {
                processor(batch);
                batch.Clear();
            }
        }
        
        if (batch.Count > 0)
        {
            processor(batch);
        }
    }
}

14.6 精度控制

14.6.1 精度模型

// 不同精度模型的影响
var floatingPrecision = new PrecisionModel(PrecisionModels.Floating);
var fixedPrecision = new PrecisionModel(1000000);  // 6位小数

var factoryFloat = new GeometryFactory(floatingPrecision);
var factoryFixed = new GeometryFactory(fixedPrecision);

// 精度对运算的影响
var coord = new Coordinate(116.40739999999999, 39.90419999999999);

var pointFloat = factoryFloat.CreatePoint(coord);
var pointFixed = factoryFixed.CreatePoint(coord);

Console.WriteLine($"浮点: {pointFloat.AsText()}");  // 完整精度
Console.WriteLine($"固定: {pointFixed.AsText()}");  // 6位小数

14.6.2 几何精度降低

using NetTopologySuite.Precision;

// 降低几何精度
var reducer = new GeometryPrecisionReducer(new PrecisionModel(1000));

var original = factory.CreatePolygon(new Coordinate[]
{
    new Coordinate(0.123456789, 0.987654321),
    new Coordinate(10.111111111, 0.222222222),
    new Coordinate(10.333333333, 10.444444444),
    new Coordinate(0.555555555, 10.666666666),
    new Coordinate(0.123456789, 0.987654321)
});

var reduced = reducer.Reduce(original);
Console.WriteLine($"原始: {original.AsText()}");
Console.WriteLine($"降低后: {reduced.AsText()}");

14.7 高级几何操作

14.7.1 NG 覆盖引擎

using NetTopologySuite;
using NetTopologySuite.Geometries;

// 使用 NG 覆盖引擎(更稳定、更快)
var services = new NtsGeometryServices(
    coordinateSequenceFactory: CoordinateArraySequenceFactory.Instance,
    precisionModel: new PrecisionModel(1000000),
    srid: 4326,
    geometryOverlay: GeometryOverlay.NG,  // 使用 NG 引擎
    coordinateEqualityComparer: new CoordinateEqualityComparer()
);

var factory = services.CreateGeometryFactory();

// 使用 NG 引擎进行叠加分析
var union = polygon1.Union(polygon2);
var intersection = polygon1.Intersection(polygon2);

14.7.2 健壮的几何运算

using NetTopologySuite.Operation.Overlay.Snap;

// 使用 Snap 进行健壮的叠加运算
var snapTolerance = 0.00001;
var union = SnapIfNeededOverlayOp.Union(polygon1, polygon2, snapTolerance);
var intersection = SnapIfNeededOverlayOp.Intersection(polygon1, polygon2, snapTolerance);

14.8 基准测试

14.8.1 性能测试框架

public class GeometryBenchmark
{
    private readonly GeometryFactory _factory;
    private readonly List<Geometry> _testGeometries;
    private readonly Geometry _queryGeometry;

    public GeometryBenchmark(int geometryCount = 10000)
    {
        _factory = new GeometryFactory(new PrecisionModel(), 4326);
        _testGeometries = GenerateRandomPolygons(geometryCount);
        _queryGeometry = _factory.CreatePolygon(new Coordinate[]
        {
            new Coordinate(40, 40), new Coordinate(60, 40),
            new Coordinate(60, 60), new Coordinate(40, 60),
            new Coordinate(40, 40)
        });
    }

    public void RunBenchmarks()
    {
        Console.WriteLine("性能基准测试");
        Console.WriteLine("=".PadRight(50, '='));

        BenchmarkContainsQuery();
        BenchmarkPreparedContainsQuery();
        BenchmarkIndexedQuery();
        BenchmarkBufferOperation();
        BenchmarkUnionOperation();
    }

    private void BenchmarkContainsQuery()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var count = _testGeometries.Count(g => _queryGeometry.Intersects(g));
        sw.Stop();
        Console.WriteLine($"普通相交查询: {sw.ElapsedMilliseconds}ms, 结果: {count}");
    }

    private void BenchmarkPreparedContainsQuery()
    {
        var prepared = PreparedGeometryFactory.Prepare(_queryGeometry);
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var count = _testGeometries.Count(g => prepared.Intersects(g));
        sw.Stop();
        Console.WriteLine($"Prepared查询: {sw.ElapsedMilliseconds}ms, 结果: {count}");
    }

    private void BenchmarkIndexedQuery()
    {
        var tree = new STRtree<Geometry>();
        foreach (var geom in _testGeometries)
        {
            tree.Insert(geom.EnvelopeInternal, geom);
        }
        tree.Build();

        var prepared = PreparedGeometryFactory.Prepare(_queryGeometry);
        var sw = System.Diagnostics.Stopwatch.StartNew();
        
        var candidates = tree.Query(_queryGeometry.EnvelopeInternal);
        var count = candidates.Count(g => prepared.Intersects(g));
        
        sw.Stop();
        Console.WriteLine($"索引+Prepared: {sw.ElapsedMilliseconds}ms, 结果: {count}");
    }

    private void BenchmarkBufferOperation()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var buffers = _testGeometries.Take(1000).Select(g => g.Buffer(1)).ToList();
        sw.Stop();
        Console.WriteLine($"缓冲区运算(1000): {sw.ElapsedMilliseconds}ms");
    }

    private void BenchmarkUnionOperation()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var union = UnaryUnionOp.Union(_testGeometries.Take(100));
        sw.Stop();
        Console.WriteLine($"联合运算(100): {sw.ElapsedMilliseconds}ms");
    }

    private List<Geometry> GenerateRandomPolygons(int count)
    {
        var polygons = new List<Geometry>(count);
        var random = new Random(42);

        for (int i = 0; i < count; i++)
        {
            var x = random.NextDouble() * 100;
            var y = random.NextDouble() * 100;
            var size = random.NextDouble() * 2 + 0.5;

            polygons.Add(_factory.CreatePolygon(new Coordinate[]
            {
                new Coordinate(x, y),
                new Coordinate(x + size, y),
                new Coordinate(x + size, y + size),
                new Coordinate(x, y + size),
                new Coordinate(x, y)
            }));
        }

        return polygons;
    }
}

14.9 本章小结

本章介绍了高级功能与性能优化:

  1. PreparedGeometry:预计算几何索引提升查询性能
  2. 空间索引:STRtree 和 Quadtree 的使用
  3. 并行处理:并行空间查询和几何运算
  4. 内存优化:几何池化和流式处理
  5. 精度控制:精度模型和精度降低
  6. 高级几何操作:NG 覆盖引擎和健壮运算
  7. 基准测试:性能测试框架

14.10 下一步

下一章是实战案例与最佳实践。


相关资源

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