第03章 - 核心架构解析

第03章 - 核心架构解析

3.1 架构概览

3.1.1 分层架构

OGU4Net采用清晰的分层架构设计,自上而下分为四层:

┌─────────────────────────────────────────────────────────────────┐
│                        应用层 (Application)                      │
│           OguLayerUtil / GtTxtUtil / 业务代码                    │
├─────────────────────────────────────────────────────────────────┤
│                        服务层 (Service)                          │
│              GisEngineFactory / GisEngine                       │
├─────────────────────────────────────────────────────────────────┤
│                        引擎层 (Engine)                           │
│          GdalEngine / GdalReader / GdalWriter                   │
├─────────────────────────────────────────────────────────────────┤
│                        基础层 (Foundation)                       │
│      MaxRev.Gdal.Universal (GDAL/OGR/OSR)                      │
└─────────────────────────────────────────────────────────────────┘

3.1.2 核心模块

OGU4Net由以下核心模块组成:

模块 命名空间 职责
配置模块 Configuration GDAL初始化和全局配置
数据源模块 DataSource 高层数据读写API
引擎模块 Engine GIS引擎核心实现
模型模块 Engine.Model 统一数据模型定义
几何模块 Geometry 几何处理工具
异常模块 Exception 异常类型定义
工具模块 Utils 实用工具类

3.1.3 依赖关系图

                    ┌───────────────┐
                    │ OguLayerUtil  │
                    └───────┬───────┘
                            │
                            ▼
                    ┌───────────────┐
                    │GisEngineFactory│
                    └───────┬───────┘
                            │
                            ▼
                    ┌───────────────┐
                    │   GdalEngine  │
                    └───────┬───────┘
                            │
              ┌─────────────┴─────────────┐
              ▼                           ▼
      ┌───────────────┐           ┌───────────────┐
      │  GdalReader   │           │  GdalWriter   │
      └───────┬───────┘           └───────┬───────┘
              │                           │
              └─────────────┬─────────────┘
                            ▼
                    ┌───────────────┐
                    │  GeometryUtil │
                    │    CrsUtil    │
                    └───────┬───────┘
                            │
                            ▼
              ┌─────────────────────────────┐
              │  MaxRev.Gdal.Universal      │
              │  (OSGeo.OGR / OSGeo.OSR)    │
              └─────────────────────────────┘

3.2 配置模块

3.2.1 GdalConfiguration类

GdalConfiguration 是GDAL配置的核心类,负责初始化GDAL运行环境:

namespace OpenGIS.Utils.Configuration;

public static class GdalConfiguration
{
    private static bool _isConfigured;
    private static readonly object _lock = new();
    
    /// <summary>
    /// 配置GDAL - 线程安全,可多次调用
    /// </summary>
    public static void ConfigureGdal()
    {
        lock (_lock)
        {
            if (_isConfigured) return;
            
            // 使用MaxRev.Gdal.Universal自动配置
            GdalBase.ConfigureAll();
            
            // 注册所有驱动
            RegisterAllDrivers();
            
            // 设置配置选项
            SetConfigOptions();
            
            _isConfigured = true;
        }
    }
    
    /// <summary>
    /// 获取GDAL版本
    /// </summary>
    public static string GetGdalVersion()
    {
        EnsureConfigured();
        return Gdal.VersionInfo("RELEASE_NAME");
    }
    
    /// <summary>
    /// 获取支持的驱动列表
    /// </summary>
    public static IList<string> GetSupportedDrivers()
    {
        EnsureConfigured();
        var drivers = new List<string>();
        for (int i = 0; i < Ogr.GetDriverCount(); i++)
        {
            var driver = Ogr.GetDriver(i);
            if (driver != null)
                drivers.Add(driver.GetName());
        }
        return drivers;
    }
    
    /// <summary>
    /// 检查驱动是否可用
    /// </summary>
    public static bool IsDriverAvailable(string driverName)
    {
        EnsureConfigured();
        return Ogr.GetDriverByName(driverName) != null;
    }
}

设计要点:

  1. 单例模式:使用静态类确保全局唯一配置
  2. 线程安全:使用锁机制保证多线程安全
  3. 延迟初始化:首次使用时自动初始化
  4. 幂等性:多次调用不会重复初始化

3.2.2 配置选项

GDAL配置选项通过 SetConfigOptions 方法设置:

public static void SetConfigOptions()
{
    // 设置文件名为UTF-8编码
    Gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
    
    // 设置Shapefile默认编码
    Gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8");
    
    // 强制使用传统GIS坐标顺序 (longitude, latitude)
    Gdal.SetConfigOption("OGR_CT_FORCE_TRADITIONAL_GIS_ORDER", "YES");
    
    // 关闭调试信息
    Gdal.SetConfigOption("CPL_DEBUG", "OFF");
}

3.2.3 LibrarySettings类

LibrarySettings 提供库级别的配置常量:

namespace OpenGIS.Utils.Configuration;

public static class LibrarySettings
{
    /// <summary>
    /// 默认容差值
    /// </summary>
    public const double DefaultTolerance = 0.001;
    
    /// <summary>
    /// 默认缓冲区分段数
    /// </summary>
    public const int DefaultBufferSegments = 30;
    
    /// <summary>
    /// 默认字段长度
    /// </summary>
    public const int DefaultFieldLength = 254;
}

3.3 引擎模块

3.3.1 引擎抽象设计

OGU4Net使用抽象工厂模式设计引擎层,便于扩展:

namespace OpenGIS.Utils.Engine;

/// <summary>
/// GIS引擎抽象基类
/// </summary>
public abstract class GisEngine
{
    /// <summary>
    /// 引擎类型
    /// </summary>
    public abstract GisEngineType EngineType { get; }
    
    /// <summary>
    /// 支持的格式列表
    /// </summary>
    public abstract IList<DataFormatType> SupportedFormats { get; }
    
    /// <summary>
    /// 创建读取器
    /// </summary>
    public abstract ILayerReader CreateReader();
    
    /// <summary>
    /// 创建写入器
    /// </summary>
    public abstract ILayerWriter CreateWriter();
    
    /// <summary>
    /// 检查是否支持指定格式
    /// </summary>
    public virtual bool SupportsFormat(DataFormatType format)
    {
        return SupportedFormats.Contains(format);
    }
}

3.3.2 GisEngineFactory

工厂类负责创建和管理引擎实例:

namespace OpenGIS.Utils.Engine;

public static class GisEngineFactory
{
    // 使用单例模式
    private static readonly GdalEngine _gdalEngineInstance = new();
    
    /// <summary>
    /// 根据引擎类型获取引擎实例
    /// </summary>
    public static GisEngine GetEngine(GisEngineType engineType)
    {
        return engineType switch
        {
            GisEngineType.GDAL => _gdalEngineInstance,
            GisEngineType.GEOTOOLS => _gdalEngineInstance, // 兼容性:重定向到GDAL
            _ => throw new EngineNotSupportedException($"Engine {engineType} not supported")
        };
    }
    
    /// <summary>
    /// 根据数据格式自动选择引擎
    /// </summary>
    public static GisEngine GetEngine(DataFormatType format)
    {
        // 所有格式都使用GDAL引擎
        return _gdalEngineInstance;
    }
    
    /// <summary>
    /// 尝试获取引擎
    /// </summary>
    public static bool TryGetEngine(DataFormatType format, out GisEngine? engine)
    {
        engine = _gdalEngineInstance;
        return true;
    }
}

设计要点:

  1. 单例引擎:引擎实例全局唯一,避免重复创建
  2. 自动选择:根据数据格式自动选择合适的引擎
  3. 向后兼容:GEOTOOLS类型重定向到GDAL

3.3.3 GdalEngine实现

namespace OpenGIS.Utils.Engine;

public class GdalEngine : GisEngine
{
    public override GisEngineType EngineType => GisEngineType.GDAL;
    
    public override IList<DataFormatType> SupportedFormats => new List<DataFormatType>
    {
        DataFormatType.SHP,
        DataFormatType.GEOJSON,
        DataFormatType.FILEGDB,
        DataFormatType.GEOPACKAGE,
        DataFormatType.KML,
        DataFormatType.DXF,
        DataFormatType.POSTGIS
    };
    
    public override ILayerReader CreateReader()
    {
        return new GdalReader();
    }
    
    public override ILayerWriter CreateWriter()
    {
        return new GdalWriter();
    }
}

3.4 读写器接口

3.4.1 ILayerReader接口

namespace OpenGIS.Utils.Engine.IO;

public interface ILayerReader
{
    /// <summary>
    /// 读取图层
    /// </summary>
    /// <param name="path">数据源路径</param>
    /// <param name="layerName">图层名称,null则读取第一个图层</param>
    /// <param name="attributeFilter">属性过滤条件(SQL WHERE子句)</param>
    /// <param name="spatialFilterWkt">空间过滤几何(WKT格式)</param>
    /// <param name="options">附加选项</param>
    OguLayer Read(
        string path,
        string? layerName = null,
        string? attributeFilter = null,
        string? spatialFilterWkt = null,
        Dictionary<string, object>? options = null);
    
    /// <summary>
    /// 获取图层名称列表
    /// </summary>
    IList<string> GetLayerNames(string path);
}

3.4.2 ILayerWriter接口

namespace OpenGIS.Utils.Engine.IO;

public interface ILayerWriter
{
    /// <summary>
    /// 写入图层
    /// </summary>
    /// <param name="layer">图层对象</param>
    /// <param name="path">输出路径</param>
    /// <param name="layerName">图层名称,null则使用layer.Name</param>
    /// <param name="options">附加选项</param>
    void Write(
        OguLayer layer,
        string path,
        string? layerName = null,
        Dictionary<string, object>? options = null);
    
    /// <summary>
    /// 追加要素到已存在的图层
    /// </summary>
    void Append(
        OguLayer layer,
        string path,
        string? layerName = null,
        Dictionary<string, object>? options = null);
}

3.4.3 GdalReader实现要点

public class GdalReader : ILayerReader
{
    static GdalReader()
    {
        // 确保GDAL已初始化
        GdalConfiguration.ConfigureGdal();
    }
    
    public OguLayer Read(string path, string? layerName = null, ...)
    {
        // 1. 打开数据源
        using var dataSource = Ogr.Open(path, 0); // 0 = 只读
        
        // 2. 获取图层
        var ogrLayer = layerName != null
            ? dataSource.GetLayerByName(layerName)
            : dataSource.GetLayerByIndex(0);
        
        // 3. 读取并转换
        return ReadOgrLayer(ogrLayer, attributeFilter, spatialFilterWkt);
    }
    
    private OguLayer ReadOgrLayer(Layer ogrLayer, ...)
    {
        var layer = new OguLayer { Name = ogrLayer.GetName() };
        
        // 读取字段定义
        var layerDefn = ogrLayer.GetLayerDefn();
        for (int i = 0; i < layerDefn.GetFieldCount(); i++)
        {
            var fieldDefn = layerDefn.GetFieldDefn(i);
            layer.AddField(new OguField
            {
                Name = fieldDefn.GetName(),
                DataType = MapOgrFieldType(fieldDefn.GetFieldType()),
                Length = fieldDefn.GetWidth(),
                Precision = fieldDefn.GetPrecision()
            });
        }
        
        // 读取几何类型
        layer.GeometryType = MapOgrGeometryType(ogrLayer.GetGeomType());
        
        // 应用过滤条件
        if (!string.IsNullOrWhiteSpace(attributeFilter))
            ogrLayer.SetAttributeFilter(attributeFilter);
        
        // 读取要素
        int fid = 1;
        Feature? ogrFeature;
        while ((ogrFeature = ogrLayer.GetNextFeature()) != null)
        {
            using (ogrFeature)
            {
                var feature = ReadOgrFeature(ogrFeature, layer.Fields, fid++);
                layer.AddFeature(feature);
            }
        }
        
        return layer;
    }
}

关键设计:

  1. 资源管理:使用using确保DataSource正确释放
  2. 类型映射:OGR类型到OGU类型的映射
  3. 过滤支持:属性过滤和空间过滤
  4. 延迟读取:使用迭代器模式读取要素

3.5 数据源模块

3.5.1 OguLayerUtil设计

OguLayerUtil 是面向应用层的高级API,封装了引擎选择和读写操作:

namespace OpenGIS.Utils.DataSource;

public static class OguLayerUtil
{
    /// <summary>
    /// 读取图层
    /// </summary>
    public static OguLayer ReadLayer(
        DataFormatType format,
        string path,
        string? layerName = null,
        string? attributeFilter = null,
        string? spatialFilterWkt = null,
        GisEngineType? engineType = null,
        Dictionary<string, object>? options = null)
    {
        // 参数验证
        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentException("Path cannot be null or empty", nameof(path));
        
        // 获取引擎
        var engine = engineType.HasValue
            ? GisEngineFactory.GetEngine(engineType.Value)
            : GisEngineFactory.GetEngine(format);
        
        // 创建读取器并读取
        var reader = engine.CreateReader();
        return reader.Read(path, layerName, attributeFilter, spatialFilterWkt, options);
    }
    
    /// <summary>
    /// 异步读取图层
    /// </summary>
    public static Task<OguLayer> ReadLayerAsync(...)
    {
        return Task.Run(() => ReadLayer(...));
    }
    
    /// <summary>
    /// 写入图层
    /// </summary>
    public static void WriteLayer(
        DataFormatType format,
        OguLayer layer,
        string path,
        string? layerName = null,
        GisEngineType? engineType = null,
        Dictionary<string, object>? options = null)
    {
        // 参数验证
        if (layer == null)
            throw new ArgumentNullException(nameof(layer));
        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentException("Path cannot be null or empty", nameof(path));
        
        // 获取引擎
        var engine = engineType.HasValue
            ? GisEngineFactory.GetEngine(engineType.Value)
            : GisEngineFactory.GetEngine(format);
        
        // 创建写入器并写入
        var writer = engine.CreateWriter();
        writer.Write(layer, path, layerName, options);
    }
    
    /// <summary>
    /// 格式转换
    /// </summary>
    public static void ConvertFormat(
        string inputPath,
        DataFormatType inputFormat,
        string outputPath,
        DataFormatType outputFormat,
        GisEngineType? engineType = null,
        string? layerName = null)
    {
        // 读取
        var layer = ReadLayer(inputFormat, inputPath, layerName, engineType: engineType);
        
        // 写入
        WriteLayer(outputFormat, layer, outputPath, layerName, engineType);
    }
}

设计特点:

  1. 静态工具类:无状态,线程安全
  2. 参数灵活:支持可选参数,简化常见用法
  3. 异步支持:提供异步版本方法
  4. 格式转换:一键实现格式转换

3.6 类型映射系统

3.6.1 几何类型映射

// OGR几何类型 → OGU几何类型
private GeometryType MapOgrGeometryType(wkbGeometryType geomType)
{
    // 移除Z/M维度标志
    var flatType = wkbFlatten((int)geomType);
    
    return flatType switch
    {
        wkbGeometryType.wkbPoint => GeometryType.POINT,
        wkbGeometryType.wkbLineString => GeometryType.LINESTRING,
        wkbGeometryType.wkbPolygon => GeometryType.POLYGON,
        wkbGeometryType.wkbMultiPoint => GeometryType.MULTIPOINT,
        wkbGeometryType.wkbMultiLineString => GeometryType.MULTILINESTRING,
        wkbGeometryType.wkbMultiPolygon => GeometryType.MULTIPOLYGON,
        wkbGeometryType.wkbGeometryCollection => GeometryType.GEOMETRYCOLLECTION,
        _ => GeometryType.UNKNOWN
    };
}

// OGU几何类型 → OGR几何类型
private wkbGeometryType MapToOgrGeometryType(GeometryType geomType)
{
    return geomType switch
    {
        GeometryType.POINT => wkbGeometryType.wkbPoint,
        GeometryType.LINESTRING => wkbGeometryType.wkbLineString,
        GeometryType.POLYGON => wkbGeometryType.wkbPolygon,
        GeometryType.MULTIPOINT => wkbGeometryType.wkbMultiPoint,
        GeometryType.MULTILINESTRING => wkbGeometryType.wkbMultiLineString,
        GeometryType.MULTIPOLYGON => wkbGeometryType.wkbMultiPolygon,
        GeometryType.GEOMETRYCOLLECTION => wkbGeometryType.wkbGeometryCollection,
        _ => wkbGeometryType.wkbUnknown
    };
}

3.6.2 字段类型映射

// OGR字段类型 → OGU字段类型
private FieldDataType MapOgrFieldType(FieldType ogrType)
{
    return ogrType switch
    {
        FieldType.OFTInteger => FieldDataType.INTEGER,
        FieldType.OFTInteger64 => FieldDataType.LONG,
        FieldType.OFTReal => FieldDataType.DOUBLE,
        FieldType.OFTString => FieldDataType.STRING,
        FieldType.OFTDate => FieldDataType.DATE,
        FieldType.OFTDateTime => FieldDataType.DATETIME,
        FieldType.OFTBinary => FieldDataType.BINARY,
        _ => FieldDataType.STRING  // 默认为字符串
    };
}

// OGU字段类型 → OGR字段类型
private FieldType MapToOgrFieldType(FieldDataType dataType)
{
    return dataType switch
    {
        FieldDataType.INTEGER => FieldType.OFTInteger,
        FieldDataType.LONG => FieldType.OFTInteger64,
        FieldDataType.DOUBLE => FieldType.OFTReal,
        FieldDataType.FLOAT => FieldType.OFTReal,
        FieldDataType.STRING => FieldType.OFTString,
        FieldDataType.DATE => FieldType.OFTDate,
        FieldDataType.DATETIME => FieldType.OFTDateTime,
        FieldDataType.BINARY => FieldType.OFTBinary,
        _ => FieldType.OFTString
    };
}

3.6.3 驱动名称推断

private string InferDriverName(string path, Dictionary<string, object>? options)
{
    // 优先从选项中获取
    if (options?.TryGetValue("driver", out var driverObj) == true)
        return driverObj.ToString() ?? "ESRI Shapefile";
    
    // 根据扩展名推断
    var extension = Path.GetExtension(path).ToLowerInvariant();
    return extension switch
    {
        ".shp" => "ESRI Shapefile",
        ".gdb" => "FileGDB",
        ".gpkg" => "GPKG",
        ".kml" => "KML",
        ".dxf" => "DXF",
        ".geojson" or ".json" => "GeoJSON",
        _ => "ESRI Shapefile"  // 默认
    };
}

3.7 资源管理

3.7.1 GDAL对象生命周期

GDAL/OGR对象需要正确管理以避免内存泄漏:

// 正确的资源管理方式
public OguLayer Read(string path, ...)
{
    OgrDataSource? dataSource = null;
    try
    {
        dataSource = Ogr.Open(path, 0);
        if (dataSource == null)
            throw new DataSourceException($"Failed to open: {path}");
        
        // 处理逻辑...
        return layer;
    }
    finally
    {
        dataSource?.Dispose();
    }
}

3.7.2 要素迭代模式

// 逐个处理要素,及时释放资源
Feature? ogrFeature;
while ((ogrFeature = ogrLayer.GetNextFeature()) != null)
{
    using (ogrFeature)  // 确保每个要素都被释放
    {
        var feature = ConvertFeature(ogrFeature);
        layer.AddFeature(feature);
    }
}

3.7.3 几何对象管理

public void Write(OguLayer layer, string path, ...)
{
    foreach (var oguFeature in layer.Features)
    {
        Feature? ogrFeature = null;
        OSGeo.OGR.Geometry? geometry = null;
        
        try
        {
            ogrFeature = new Feature(layerDefn);
            
            // 创建几何(需要单独管理)
            geometry = OSGeo.OGR.Geometry.CreateFromWkt(oguFeature.Wkt);
            if (geometry != null)
                ogrFeature.SetGeometry(geometry);
            
            // 写入要素
            ogrLayer.CreateFeature(ogrFeature);
        }
        finally
        {
            geometry?.Dispose();  // 先释放几何
            ogrFeature?.Dispose(); // 再释放要素
        }
    }
}

3.8 扩展点设计

3.8.1 添加新引擎

如需添加新的GIS引擎,可以:

  1. 继承 GisEngine 抽象类
  2. 实现 ILayerReaderILayerWriter 接口
  3. GisEngineFactory 中注册
// 示例:添加新引擎
public class MyCustomEngine : GisEngine
{
    public override GisEngineType EngineType => GisEngineType.CUSTOM;
    
    public override IList<DataFormatType> SupportedFormats => 
        new List<DataFormatType> { DataFormatType.CUSTOM };
    
    public override ILayerReader CreateReader() => new MyCustomReader();
    public override ILayerWriter CreateWriter() => new MyCustomWriter();
}

3.8.2 添加新数据格式

添加新格式支持:

  1. DataFormatType 枚举中添加新类型
  2. 在驱动名称推断逻辑中添加映射
  3. 确保GDAL支持该格式的驱动

3.9 小结

本章详细解析了OGU4Net的核心架构:

  1. 分层架构:应用层、服务层、引擎层、基础层清晰分离
  2. 配置模块:线程安全的GDAL初始化和配置管理
  3. 引擎模块:抽象工厂模式,支持扩展
  4. 读写器设计:接口驱动,职责单一
  5. 类型映射:OGR与OGU类型的双向映射
  6. 资源管理:正确管理GDAL对象生命周期

理解这些架构设计,有助于更好地使用和扩展OGU4Net框架。

posted @ 2025-12-03 15:51  我才是银古  阅读(0)  评论(0)    收藏  举报