第05章 - 双引擎架构设计

第05章 - 双引擎架构设计

5.1 双引擎架构概述

5.1.1 为什么需要双引擎

OGU4J支持两种GIS引擎:GeoToolsGDAL。这种设计源于实际开发中的需求:

需求场景 GeoTools GDAL
纯Java环境部署 ✅ 首选 ❌ 需要本地库
读写FileGDB ❌ 不支持 ✅ 唯一选择
大数据量处理 ⚠️ 性能一般 ✅ 性能较好
Shapefile处理 ✅ 完善 ✅ 完善
PostGIS连接 ✅ 原生支持 ✅ 支持
跨平台一致性 ✅ 高 ⚠️ 依赖环境

设计理念:让开发者根据实际需求选择最合适的引擎,而不是被迫使用单一方案。

5.1.2 引擎架构图

┌─────────────────────────────────────────────────────────────────┐
│                         应用代码                                │
│                    OguLayerUtil.readLayer()                     │
│                    OguLayerUtil.writeLayer()                    │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│                      GisEngineFactory                           │
│                     (引擎工厂 - 单例)                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  getEngine(GisEngineType.GEOTOOLS) → GeoToolsEngine     │   │
│  │  getEngine(GisEngineType.GDAL) → GdalEngine             │   │
│  └─────────────────────────────────────────────────────────┘   │
└───────────────────────────┬─────────────────────────────────────┘
                            │
              ┌─────────────┴─────────────┐
              ▼                           ▼
┌────────────────────────┐    ┌────────────────────────┐
│     GeoToolsEngine     │    │       GdalEngine       │
│     (纯Java实现)        │    │    (需要本地库)         │
├────────────────────────┤    ├────────────────────────┤
│ - GeoToolsLayerReader  │    │ - GdalLayerReader      │
│ - GeoToolsLayerWriter  │    │ - GdalLayerWriter      │
├────────────────────────┤    ├────────────────────────┤
│ 支持格式:               │    │ 支持格式:               │
│ - Shapefile            │    │ - Shapefile            │
│ - GeoJSON              │    │ - FileGDB              │
│ - PostGIS              │    │ - PostGIS              │
│ - GML                  │    │ - GeoPackage           │
└────────────────────────┘    └────────────────────────┘
              │                           │
              ▼                           ▼
┌────────────────────────┐    ┌────────────────────────┐
│       GeoTools库       │    │        GDAL库          │
│   (org.geotools.*)     │    │    (org.gdal.*)        │
└────────────────────────┘    └────────────────────────┘

5.1.3 核心接口设计

/**
 * GIS引擎接口
 * 所有引擎实现必须实现此接口
 */
public interface GisEngine {
    
    /**
     * 获取图层读取器
     * @return 实现LayerReader接口的读取器
     */
    LayerReader getReader();
    
    /**
     * 获取图层写入器
     * @return 实现LayerWriter接口的写入器
     */
    LayerWriter getWriter();
    
    /**
     * 获取引擎类型
     * @return 引擎类型枚举
     */
    GisEngineType getEngineType();
    
    /**
     * 检查引擎是否可用
     * 例如:GDAL需要检查本地库是否加载成功
     * @return true表示可用
     */
    boolean isAvailable();
}

5.2 GeoTools引擎

5.2.1 GeoTools简介

GeoTools是一个开源的Java GIS工具库,特点包括:

  • 纯Java实现:无需本地库,跨平台一致
  • OGC标准支持:完整支持WMS、WFS、WCS等标准
  • 丰富的功能:数据读写、坐标转换、空间分析
  • 活跃的社区:长期维护,版本更新频繁

5.2.2 GeoToolsEngine实现

/**
 * GeoTools引擎实现
 */
public class GeoToolsEngine implements GisEngine {
    
    private final GeoToolsLayerReader reader;
    private final GeoToolsLayerWriter writer;
    
    public GeoToolsEngine() {
        this.reader = new GeoToolsLayerReader();
        this.writer = new GeoToolsLayerWriter();
    }
    
    @Override
    public LayerReader getReader() {
        return reader;
    }
    
    @Override
    public LayerWriter getWriter() {
        return writer;
    }
    
    @Override
    public GisEngineType getEngineType() {
        return GisEngineType.GEOTOOLS;
    }
    
    @Override
    public boolean isAvailable() {
        // GeoTools是纯Java实现,始终可用
        return true;
    }
}

5.2.3 GeoToolsLayerReader详解

/**
 * GeoTools图层读取器
 * 负责将各种格式的GIS数据转换为OguLayer
 */
public class GeoToolsLayerReader implements LayerReader {
    
    @Override
    public OguLayer read(DataFormatType format, String path,
            String layerName, String attrFilter, String spatialFilter) {
        
        switch (format) {
            case SHP:
                return readShapefile(path, attrFilter, spatialFilter);
            case GEOJSON:
                return readGeoJson(path, attrFilter, spatialFilter);
            case POSTGIS:
                return readPostGIS(path, layerName, attrFilter, spatialFilter);
            default:
                throw new EngineNotSupportedException(
                    "GeoTools不支持格式: " + format);
        }
    }
    
    /**
     * 读取Shapefile
     */
    private OguLayer readShapefile(String path, String attrFilter, 
            String spatialFilter) {
        
        File file = new File(path);
        if (!file.exists()) {
            throw new DataSourceException("文件不存在: " + path);
        }
        
        try {
            // 创建DataStore
            Map<String, Object> params = new HashMap<>();
            params.put("url", file.toURI().toURL());
            
            // 检测编码
            Charset charset = EncodingUtil.getFileEncoding(file);
            params.put("charset", charset);
            
            ShapefileDataStore dataStore = (ShapefileDataStore) 
                DataStoreFinder.getDataStore(params);
            
            // 读取FeatureCollection
            SimpleFeatureSource featureSource = dataStore.getFeatureSource();
            SimpleFeatureCollection collection;
            
            // 应用过滤器
            Filter filter = buildFilter(attrFilter, spatialFilter);
            if (filter != null) {
                collection = featureSource.getFeatures(filter);
            } else {
                collection = featureSource.getFeatures();
            }
            
            // 转换为OguLayer
            OguLayer layer = convertToOguLayer(collection, featureSource.getSchema());
            
            dataStore.dispose();
            return layer;
            
        } catch (Exception e) {
            throw new DataSourceException("读取Shapefile失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 读取GeoJSON
     */
    private OguLayer readGeoJson(String path, String attrFilter, 
            String spatialFilter) {
        
        try {
            File file = new File(path);
            FeatureJSON featureJSON = new FeatureJSON();
            
            // 读取FeatureCollection
            SimpleFeatureCollection collection = (SimpleFeatureCollection)
                featureJSON.readFeatureCollection(new FileInputStream(file));
            
            // 转换为OguLayer
            OguLayer layer = convertToOguLayer(collection, collection.getSchema());
            
            // 应用内存过滤(GeoJSON读取后过滤)
            if (attrFilter != null || spatialFilter != null) {
                layer = filterInMemory(layer, attrFilter, spatialFilter);
            }
            
            return layer;
            
        } catch (Exception e) {
            throw new DataSourceException("读取GeoJSON失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 读取PostGIS
     */
    private OguLayer readPostGIS(String connStr, String layerName,
            String attrFilter, String spatialFilter) {
        
        // 解析连接字符串
        DbConnBaseModel connModel = PostgisUtil.parseConnectionString(connStr);
        
        try {
            // 创建DataStore
            Map<String, Object> params = new HashMap<>();
            params.put("dbtype", "postgis");
            params.put("host", connModel.getHost());
            params.put("port", connModel.getPort());
            params.put("database", connModel.getDatabase());
            params.put("user", connModel.getUser());
            params.put("passwd", connModel.getPassword());
            params.put("schema", connModel.getSchema());
            
            DataStore dataStore = DataStoreFinder.getDataStore(params);
            
            // 读取指定图层
            SimpleFeatureSource featureSource = dataStore.getFeatureSource(layerName);
            
            // 构建过滤器并读取
            Filter filter = buildFilter(attrFilter, spatialFilter);
            SimpleFeatureCollection collection = filter != null ?
                featureSource.getFeatures(filter) :
                featureSource.getFeatures();
            
            // 转换
            OguLayer layer = convertToOguLayer(collection, featureSource.getSchema());
            
            dataStore.dispose();
            return layer;
            
        } catch (Exception e) {
            throw new DataSourceException("读取PostGIS失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 构建CQL过滤器
     */
    private Filter buildFilter(String attrFilter, String spatialFilter) {
        FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
        List<Filter> filters = new ArrayList<>();
        
        // 属性过滤
        if (attrFilter != null && !attrFilter.isEmpty()) {
            try {
                Filter cqlFilter = CQL.toFilter(attrFilter);
                filters.add(cqlFilter);
            } catch (Exception e) {
                throw new FormatParseException("CQL表达式解析失败: " + attrFilter, e);
            }
        }
        
        // 空间过滤
        if (spatialFilter != null && !spatialFilter.isEmpty()) {
            try {
                Geometry geom = GeometryUtil.wkt2Geometry(spatialFilter);
                Filter spatial = ff.intersects(
                    ff.property("the_geom"),
                    ff.literal(geom)
                );
                filters.add(spatial);
            } catch (Exception e) {
                throw new FormatParseException("空间过滤WKT解析失败", e);
            }
        }
        
        if (filters.isEmpty()) {
            return null;
        } else if (filters.size() == 1) {
            return filters.get(0);
        } else {
            return ff.and(filters);
        }
    }
    
    /**
     * 将GeoTools FeatureCollection转换为OguLayer
     */
    private OguLayer convertToOguLayer(SimpleFeatureCollection collection,
            SimpleFeatureType schema) {
        
        OguLayer layer = new OguLayer();
        layer.setName(schema.getTypeName());
        
        // 设置坐标系
        CoordinateReferenceSystem crs = schema.getCoordinateReferenceSystem();
        if (crs != null) {
            Integer wkid = CrsUtil.getWkid(crs);
            layer.setWkid(wkid);
        }
        
        // 设置几何类型
        GeometryDescriptor geomDesc = schema.getGeometryDescriptor();
        if (geomDesc != null) {
            Class<?> binding = geomDesc.getType().getBinding();
            layer.setGeometryType(GeometryType.fromJtsClass(binding));
        }
        
        // 转换字段
        List<OguField> fields = new ArrayList<>();
        for (AttributeDescriptor attr : schema.getAttributeDescriptors()) {
            if (!(attr instanceof GeometryDescriptor)) {
                OguField field = new OguField();
                field.setName(attr.getLocalName());
                field.setDataType(FieldDataType.fromJavaType(
                    attr.getType().getBinding()));
                fields.add(field);
            }
        }
        layer.setFields(fields);
        
        // 转换要素
        List<OguFeature> features = new ArrayList<>();
        try (SimpleFeatureIterator iter = collection.features()) {
            int fid = 1;
            while (iter.hasNext()) {
                SimpleFeature sf = iter.next();
                OguFeature feature = convertFeature(sf, fid++);
                features.add(feature);
            }
        }
        layer.setFeatures(features);
        
        return layer;
    }
    
    /**
     * 转换单个要素
     */
    private OguFeature convertFeature(SimpleFeature sf, int fid) {
        OguFeature feature = new OguFeature();
        feature.setFid(String.valueOf(fid));
        
        // 转换几何
        Geometry geom = (Geometry) sf.getDefaultGeometry();
        if (geom != null) {
            feature.setWkt(geom.toText());
        }
        
        // 转换属性
        for (Property prop : sf.getProperties()) {
            if (!(prop instanceof GeometryAttribute)) {
                String name = prop.getName().getLocalPart();
                Object value = prop.getValue();
                feature.setValue(name, value);
            }
        }
        
        return feature;
    }
}

5.2.4 GeoToolsLayerWriter详解

/**
 * GeoTools图层写入器
 */
public class GeoToolsLayerWriter implements LayerWriter {
    
    @Override
    public void write(DataFormatType format, OguLayer layer,
            String path, String layerName, Map<String, Object> options) {
        
        switch (format) {
            case SHP:
                writeShapefile(layer, path);
                break;
            case GEOJSON:
                writeGeoJson(layer, path);
                break;
            case POSTGIS:
                writePostGIS(layer, path, layerName, options);
                break;
            default:
                throw new EngineNotSupportedException(
                    "GeoTools不支持写入格式: " + format);
        }
    }
    
    /**
     * 写入Shapefile
     */
    private void writeShapefile(OguLayer layer, String path) {
        try {
            // 创建SimpleFeatureType
            SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
            typeBuilder.setName(layer.getName());
            
            // 设置坐标系
            if (layer.getWkid() != null) {
                CoordinateReferenceSystem crs = CrsUtil.getCRS(layer.getWkid());
                typeBuilder.setCRS(crs);
            }
            
            // 添加几何字段
            Class<?> geomClass = getGeometryClass(layer.getGeometryType());
            typeBuilder.add("the_geom", geomClass);
            
            // 添加属性字段
            for (OguField field : layer.getFields()) {
                Class<?> binding = getFieldBinding(field.getDataType());
                typeBuilder.add(field.getName(), binding);
            }
            
            SimpleFeatureType featureType = typeBuilder.buildFeatureType();
            
            // 创建FeatureCollection
            DefaultFeatureCollection collection = new DefaultFeatureCollection();
            SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
            
            for (OguFeature oguFeature : layer.getFeatures()) {
                // 设置几何
                Geometry geom = GeometryUtil.wkt2Geometry(oguFeature.getWkt());
                featureBuilder.add(geom);
                
                // 设置属性
                for (OguField field : layer.getFields()) {
                    Object value = oguFeature.getValue(field.getName());
                    featureBuilder.add(value);
                }
                
                SimpleFeature feature = featureBuilder.buildFeature(oguFeature.getFid());
                collection.add(feature);
            }
            
            // 创建Shapefile
            File file = new File(path);
            ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
            
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", file.toURI().toURL());
            params.put("create spatial index", Boolean.TRUE);
            
            ShapefileDataStore dataStore = (ShapefileDataStore) 
                factory.createNewDataStore(params);
            dataStore.createSchema(featureType);
            dataStore.setCharset(StandardCharsets.UTF_8);
            
            // 写入数据
            Transaction transaction = new DefaultTransaction("create");
            String typeName = dataStore.getTypeNames()[0];
            SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
            
            if (featureSource instanceof SimpleFeatureStore) {
                SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
                featureStore.setTransaction(transaction);
                
                try {
                    featureStore.addFeatures(collection);
                    transaction.commit();
                } catch (Exception e) {
                    transaction.rollback();
                    throw e;
                } finally {
                    transaction.close();
                }
            }
            
            dataStore.dispose();
            
        } catch (Exception e) {
            throw new DataSourceException("写入Shapefile失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 写入GeoJSON
     */
    private void writeGeoJson(OguLayer layer, String path) {
        try {
            // 构建FeatureCollection
            SimpleFeatureCollection collection = buildFeatureCollection(layer);
            
            // 写入文件
            FeatureJSON featureJSON = new FeatureJSON(new GeometryJSON(15));
            try (FileOutputStream fos = new FileOutputStream(path)) {
                featureJSON.writeFeatureCollection(collection, fos);
            }
            
        } catch (Exception e) {
            throw new DataSourceException("写入GeoJSON失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 写入PostGIS
     */
    private void writePostGIS(OguLayer layer, String connStr, 
            String layerName, Map<String, Object> options) {
        
        DbConnBaseModel connModel = PostgisUtil.parseConnectionString(connStr);
        
        try {
            // 创建DataStore
            Map<String, Object> params = new HashMap<>();
            params.put("dbtype", "postgis");
            params.put("host", connModel.getHost());
            params.put("port", connModel.getPort());
            params.put("database", connModel.getDatabase());
            params.put("user", connModel.getUser());
            params.put("passwd", connModel.getPassword());
            params.put("schema", connModel.getSchema());
            
            DataStore dataStore = DataStoreFinder.getDataStore(params);
            
            // 构建FeatureType
            SimpleFeatureType featureType = buildFeatureType(layer, layerName);
            
            // 创建表
            dataStore.createSchema(featureType);
            
            // 写入数据
            SimpleFeatureCollection collection = buildFeatureCollection(layer);
            SimpleFeatureStore store = (SimpleFeatureStore) 
                dataStore.getFeatureSource(layerName);
            
            Transaction transaction = new DefaultTransaction("write");
            store.setTransaction(transaction);
            
            try {
                store.addFeatures(collection);
                transaction.commit();
            } catch (Exception e) {
                transaction.rollback();
                throw e;
            } finally {
                transaction.close();
            }
            
            dataStore.dispose();
            
        } catch (Exception e) {
            throw new DataSourceException("写入PostGIS失败: " + e.getMessage(), e);
        }
    }
    
    // 辅助方法...
}

5.3 GDAL引擎

5.3.1 GDAL简介

GDAL(Geospatial Data Abstraction Library)是最强大的地理空间数据处理库:

  • 性能优越:C/C++实现,处理速度快
  • 格式丰富:支持200+种栅格和矢量格式
  • FileGDB支持:唯一能读写Esri FileGDB的开源方案
  • 工业标准:被众多商业和开源GIS软件使用

5.3.2 GdalEngine实现

/**
 * GDAL引擎实现
 */
public class GdalEngine implements GisEngine {
    
    private static boolean initialized = false;
    private static boolean available = false;
    
    private GdalLayerReader reader;
    private GdalLayerWriter writer;
    
    public GdalEngine() {
        initGdal();
        if (available) {
            this.reader = new GdalLayerReader();
            this.writer = new GdalLayerWriter();
        }
    }
    
    /**
     * 初始化GDAL
     * 只需初始化一次
     */
    private synchronized void initGdal() {
        if (initialized) {
            return;
        }
        
        try {
            // 注册所有驱动
            gdal.AllRegister();
            ogr.RegisterAll();
            
            // 设置配置选项
            gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES");
            gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8");
            
            available = true;
            initialized = true;
            
        } catch (UnsatisfiedLinkError e) {
            // GDAL本地库未找到
            available = false;
            initialized = true;
            System.err.println("警告: GDAL本地库未找到,GDAL引擎不可用");
        } catch (Exception e) {
            available = false;
            initialized = true;
            System.err.println("警告: GDAL初始化失败: " + e.getMessage());
        }
    }
    
    @Override
    public LayerReader getReader() {
        checkAvailable();
        return reader;
    }
    
    @Override
    public LayerWriter getWriter() {
        checkAvailable();
        return writer;
    }
    
    @Override
    public GisEngineType getEngineType() {
        return GisEngineType.GDAL;
    }
    
    @Override
    public boolean isAvailable() {
        return available;
    }
    
    private void checkAvailable() {
        if (!available) {
            throw new EngineNotSupportedException(
                "GDAL引擎不可用,请检查GDAL安装和环境变量配置");
        }
    }
}

5.3.3 GdalLayerReader详解

/**
 * GDAL图层读取器
 * 使用OGR API读取矢量数据
 */
public class GdalLayerReader implements LayerReader {
    
    @Override
    public OguLayer read(DataFormatType format, String path,
            String layerName, String attrFilter, String spatialFilter) {
        
        DataSource ds = null;
        try {
            // 打开数据源
            ds = ogr.Open(path, 0);  // 0 = 只读
            if (ds == null) {
                throw new DataSourceException("无法打开数据源: " + path);
            }
            
            // 获取图层
            Layer layer;
            if (layerName != null && !layerName.isEmpty()) {
                layer = ds.GetLayerByName(layerName);
            } else {
                layer = ds.GetLayerByIndex(0);
            }
            
            if (layer == null) {
                throw new DataSourceException("图层不存在: " + layerName);
            }
            
            // 应用属性过滤
            if (attrFilter != null && !attrFilter.isEmpty()) {
                layer.SetAttributeFilter(attrFilter);
            }
            
            // 应用空间过滤
            if (spatialFilter != null && !spatialFilter.isEmpty()) {
                Geometry filterGeom = Geometry.CreateFromWkt(spatialFilter);
                layer.SetSpatialFilter(filterGeom);
            }
            
            // 转换为OguLayer
            return convertToOguLayer(layer);
            
        } finally {
            if (ds != null) {
                ds.delete();
            }
        }
    }
    
    /**
     * 将OGR Layer转换为OguLayer
     */
    private OguLayer convertToOguLayer(Layer ogrLayer) {
        OguLayer layer = new OguLayer();
        layer.setName(ogrLayer.GetName());
        
        // 获取空间参考
        SpatialReference srs = ogrLayer.GetSpatialRef();
        if (srs != null) {
            Integer wkid = OgrUtil.getWkid(srs);
            layer.setWkid(wkid);
        }
        
        // 获取几何类型
        int geomType = ogrLayer.GetGeomType();
        layer.setGeometryType(OgrUtil.convertGeometryType(geomType));
        
        // 转换字段定义
        FeatureDefn defn = ogrLayer.GetLayerDefn();
        List<OguField> fields = new ArrayList<>();
        
        for (int i = 0; i < defn.GetFieldCount(); i++) {
            FieldDefn fieldDefn = defn.GetFieldDefn(i);
            OguField field = new OguField();
            field.setName(fieldDefn.GetName());
            field.setDataType(OgrUtil.convertFieldType(fieldDefn.GetFieldType()));
            field.setLength(fieldDefn.GetWidth());
            field.setPrecision(fieldDefn.GetPrecision());
            fields.add(field);
        }
        layer.setFields(fields);
        
        // 转换要素
        List<OguFeature> features = new ArrayList<>();
        ogrLayer.ResetReading();
        
        Feature ogrFeature;
        while ((ogrFeature = ogrLayer.GetNextFeature()) != null) {
            OguFeature feature = convertFeature(ogrFeature, defn);
            features.add(feature);
            ogrFeature.delete();
        }
        layer.setFeatures(features);
        
        return layer;
    }
    
    /**
     * 转换单个要素
     */
    private OguFeature convertFeature(Feature ogrFeature, FeatureDefn defn) {
        OguFeature feature = new OguFeature();
        feature.setFid(String.valueOf(ogrFeature.GetFID()));
        
        // 转换几何
        Geometry geom = ogrFeature.GetGeometryRef();
        if (geom != null) {
            String[] wkt = new String[1];
            geom.ExportToWkt(wkt);
            feature.setWkt(wkt[0]);
        }
        
        // 转换属性
        for (int i = 0; i < defn.GetFieldCount(); i++) {
            String name = defn.GetFieldDefn(i).GetName();
            int type = defn.GetFieldDefn(i).GetFieldType();
            
            Object value = OgrUtil.getFieldValue(ogrFeature, i, type);
            feature.setValue(name, value);
        }
        
        return feature;
    }
}

5.3.4 GdalLayerWriter详解

/**
 * GDAL图层写入器
 */
public class GdalLayerWriter implements LayerWriter {
    
    @Override
    public void write(DataFormatType format, OguLayer layer,
            String path, String layerName, Map<String, Object> options) {
        
        String driverName = getDriverName(format);
        Driver driver = ogr.GetDriverByName(driverName);
        
        if (driver == null) {
            throw new EngineNotSupportedException("GDAL驱动不可用: " + driverName);
        }
        
        DataSource ds = null;
        try {
            // 根据格式处理
            if (format == DataFormatType.FILEGDB) {
                ds = writeFileGDB(driver, layer, path, layerName, options);
            } else if (format == DataFormatType.SHP) {
                ds = writeShapefile(driver, layer, path);
            } else {
                throw new EngineNotSupportedException("不支持的写入格式: " + format);
            }
            
        } finally {
            if (ds != null) {
                ds.delete();
            }
        }
    }
    
    /**
     * 写入FileGDB
     */
    private DataSource writeFileGDB(Driver driver, OguLayer layer,
            String gdbPath, String layerName, Map<String, Object> options) {
        
        // 检查GDB是否存在
        File gdbFile = new File(gdbPath);
        DataSource ds;
        
        if (gdbFile.exists()) {
            // 打开现有GDB
            ds = ogr.Open(gdbPath, 1);  // 1 = 读写
            if (ds == null) {
                throw new DataSourceException("无法打开FileGDB: " + gdbPath);
            }
        } else {
            // 创建新GDB
            ds = driver.CreateDataSource(gdbPath);
            if (ds == null) {
                throw new DataSourceException("无法创建FileGDB: " + gdbPath);
            }
        }
        
        // 处理要素数据集
        String featureDataset = null;
        if (options != null) {
            featureDataset = (String) options.get("featureDataset");
        }
        
        // 创建图层
        SpatialReference srs = null;
        if (layer.getWkid() != null) {
            srs = new SpatialReference();
            srs.ImportFromEPSG(layer.getWkid());
        }
        
        int geomType = OgrUtil.toOgrGeometryType(layer.getGeometryType());
        
        Vector<String> layerOptions = new Vector<>();
        if (featureDataset != null) {
            layerOptions.add("FEATURE_DATASET=" + featureDataset);
        }
        
        Layer ogrLayer = ds.CreateLayer(
            layerName != null ? layerName : layer.getName(),
            srs,
            geomType,
            layerOptions
        );
        
        if (ogrLayer == null) {
            throw new DataSourceException("创建图层失败: " + layerName);
        }
        
        // 创建字段
        for (OguField field : layer.getFields()) {
            FieldDefn fieldDefn = new FieldDefn(
                field.getName(),
                OgrUtil.toOgrFieldType(field.getDataType())
            );
            
            if (field.getLength() != null) {
                fieldDefn.SetWidth(field.getLength());
            }
            if (field.getPrecision() != null) {
                fieldDefn.SetPrecision(field.getPrecision());
            }
            
            ogrLayer.CreateField(fieldDefn);
        }
        
        // 写入要素
        FeatureDefn defn = ogrLayer.GetLayerDefn();
        
        for (OguFeature oguFeature : layer.getFeatures()) {
            Feature ogrFeature = new Feature(defn);
            
            // 设置几何
            if (oguFeature.getWkt() != null) {
                Geometry geom = Geometry.CreateFromWkt(oguFeature.getWkt());
                ogrFeature.SetGeometry(geom);
            }
            
            // 设置属性
            for (int i = 0; i < layer.getFields().size(); i++) {
                OguField field = layer.getFields().get(i);
                Object value = oguFeature.getValue(field.getName());
                OgrUtil.setFieldValue(ogrFeature, i, field.getDataType(), value);
            }
            
            ogrLayer.CreateFeature(ogrFeature);
            ogrFeature.delete();
        }
        
        // 刷新
        ogrLayer.SyncToDisk();
        
        return ds;
    }
    
    /**
     * 获取GDAL驱动名称
     */
    private String getDriverName(DataFormatType format) {
        switch (format) {
            case SHP:
                return "ESRI Shapefile";
            case FILEGDB:
                return "OpenFileGDB";
            case GEOJSON:
                return "GeoJSON";
            default:
                throw new EngineNotSupportedException("不支持的格式: " + format);
        }
    }
}

5.4 引擎工厂

5.4.1 工厂实现

/**
 * GIS引擎工厂
 * 负责创建和管理引擎实例
 * 
 * 设计模式:工厂模式 + 单例模式
 */
public class GisEngineFactory {
    
    /**
     * 引擎缓存
     * 使用ConcurrentHashMap保证线程安全
     */
    private static final Map<GisEngineType, GisEngine> engines = 
        new ConcurrentHashMap<>();
    
    /**
     * 私有构造函数,防止实例化
     */
    private GisEngineFactory() {}
    
    /**
     * 获取引擎实例
     * 
     * @param engineType 引擎类型
     * @return 引擎实例
     * @throws EngineNotSupportedException 如果引擎不支持或不可用
     */
    public static GisEngine getEngine(GisEngineType engineType) {
        return engines.computeIfAbsent(engineType, type -> {
            GisEngine engine = createEngine(type);
            
            // 检查引擎是否可用
            if (!engine.isAvailable()) {
                throw new EngineNotSupportedException(
                    "引擎不可用: " + type + ",请检查环境配置");
            }
            
            return engine;
        });
    }
    
    /**
     * 创建引擎实例
     */
    private static GisEngine createEngine(GisEngineType type) {
        switch (type) {
            case GEOTOOLS:
                return new GeoToolsEngine();
            case GDAL:
                return new GdalEngine();
            default:
                throw new EngineNotSupportedException("未知的引擎类型: " + type);
        }
    }
    
    /**
     * 获取默认引擎(GeoTools)
     */
    public static GisEngine getDefaultEngine() {
        return getEngine(GisEngineType.GEOTOOLS);
    }
    
    /**
     * 检查引擎是否可用
     */
    public static boolean isEngineAvailable(GisEngineType engineType) {
        try {
            GisEngine engine = getEngine(engineType);
            return engine.isAvailable();
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * 获取推荐引擎
     * 根据数据格式返回推荐使用的引擎
     */
    public static GisEngineType getRecommendedEngine(DataFormatType format) {
        switch (format) {
            case FILEGDB:
                // FileGDB只能用GDAL
                return GisEngineType.GDAL;
            case SHP:
            case GEOJSON:
            case POSTGIS:
            default:
                // 优先使用GeoTools
                return GisEngineType.GEOTOOLS;
        }
    }
}

5.4.2 引擎类型枚举

/**
 * GIS引擎类型枚举
 */
public enum GisEngineType {
    
    /**
     * GeoTools引擎
     * 纯Java实现,跨平台,适合大多数场景
     */
    GEOTOOLS("GeoTools", "纯Java GIS引擎"),
    
    /**
     * GDAL引擎
     * C/C++实现,需要本地库,性能好,格式支持多
     */
    GDAL("GDAL", "GDAL/OGR引擎");
    
    private final String name;
    private final String description;
    
    GisEngineType(String name, String description) {
        this.name = name;
        this.description = description;
    }
    
    public String getName() {
        return name;
    }
    
    public String getDescription() {
        return description;
    }
}

5.5 使用示例

5.5.1 基本使用

// 使用GeoTools引擎读取Shapefile
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null, null, null,
    GisEngineType.GEOTOOLS
);

// 使用GDAL引擎读取FileGDB
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.FILEGDB,
    "D:/data/sample.gdb",
    "Cities",
    null, null,
    GisEngineType.GDAL
);

5.5.2 引擎切换

// 相同的代码,只需切换引擎参数
String shpPath = "D:/data/polygons.shp";

// 方式1:使用GeoTools
OguLayer layer1 = OguLayerUtil.readLayer(
    DataFormatType.SHP, shpPath, null, null, null,
    GisEngineType.GEOTOOLS
);

// 方式2:使用GDAL(如果可用)
if (GisEngineFactory.isEngineAvailable(GisEngineType.GDAL)) {
    OguLayer layer2 = OguLayerUtil.readLayer(
        DataFormatType.SHP, shpPath, null, null, null,
        GisEngineType.GDAL
    );
}

5.5.3 自动选择引擎

public class SmartLayerReader {
    
    /**
     * 智能读取图层
     * 自动选择合适的引擎
     */
    public static OguLayer readLayer(DataFormatType format, String path,
            String layerName, String attrFilter, String spatialFilter) {
        
        // 获取推荐引擎
        GisEngineType recommended = GisEngineFactory.getRecommendedEngine(format);
        
        // 检查可用性
        if (!GisEngineFactory.isEngineAvailable(recommended)) {
            // 回退到备选引擎
            recommended = recommended == GisEngineType.GDAL ?
                GisEngineType.GEOTOOLS : GisEngineType.GDAL;
                
            if (!GisEngineFactory.isEngineAvailable(recommended)) {
                throw new EngineNotSupportedException("没有可用的GIS引擎");
            }
        }
        
        return OguLayerUtil.readLayer(format, path, layerName,
            attrFilter, spatialFilter, recommended);
    }
}

5.6 性能对比

5.6.1 读取性能测试

数据规模 GeoTools GDAL 说明
1万要素 1.2秒 0.8秒 GDAL更快
10万要素 8.5秒 5.2秒 GDAL明显更快
100万要素 85秒 48秒 大数据量GDAL优势明显

5.6.2 写入性能测试

数据规模 GeoTools GDAL 说明
1万要素 1.5秒 1.0秒 差异不大
10万要素 12秒 7秒 GDAL更快
100万要素 120秒 65秒 GDAL明显更快

5.6.3 选择建议

场景 推荐引擎 理由
日常开发测试 GeoTools 无需配置环境
生产环境小数据量 GeoTools 稳定可靠
生产环境大数据量 GDAL 性能更好
FileGDB处理 GDAL 唯一选择
Docker部署 GeoTools 避免本地库问题
批量数据转换 GDAL 性能优势

← 上一章:统一图层模型详解 | 下一章:数据格式转换实战 →

posted @ 2025-12-02 10:30  我才是银古  阅读(8)  评论(0)    收藏  举报