第05章 - 双引擎架构设计
第05章 - 双引擎架构设计
5.1 双引擎架构概述
5.1.1 为什么需要双引擎
OGU4J支持两种GIS引擎:GeoTools和GDAL。这种设计源于实际开发中的需求:
| 需求场景 | 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 | 性能优势 |

浙公网安备 33010602011771号