第06章 - 数据格式转换实战

第06章 - 数据格式转换实战

6.1 格式转换概述

6.1.1 支持的格式

OGU4J支持以下主流GIS数据格式:

格式 扩展名 读取 写入 推荐引擎 说明
Shapefile .shp GeoTools/GDAL 最通用的矢量格式
GeoJSON .geojson/.json GeoTools Web友好的JSON格式
FileGDB .gdb GDAL ESRI地理数据库
PostGIS - GeoTools PostgreSQL空间扩展
国土TXT .txt 内置 自然资源部门格式

6.1.2 格式类型枚举

/**
 * 数据格式类型枚举
 */
public enum DataFormatType {
    
    /**
     * Shapefile格式
     * 包含.shp、.dbf、.shx、.prj等文件
     */
    SHP("Shapefile", ".shp"),
    
    /**
     * GeoJSON格式
     * JSON格式的地理数据
     */
    GEOJSON("GeoJSON", ".geojson"),
    
    /**
     * Esri FileGDB格式
     * ESRI的文件地理数据库
     */
    FILEGDB("FileGDB", ".gdb"),
    
    /**
     * PostGIS格式
     * PostgreSQL数据库中的空间表
     */
    POSTGIS("PostGIS", null);
    
    private final String name;
    private final String extension;
    
    // 构造函数和getter方法
}

6.1.3 转换流程

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   源数据格式     │ --> │    OguLayer     │ --> │   目标数据格式   │
│  (SHP/GeoJSON)  │     │   (统一模型)     │     │  (GDB/PostGIS)  │
└─────────────────┘     └─────────────────┘     └─────────────────┘
        │                       │                       │
        │                       │                       │
        ▼                       ▼                       ▼
   OguLayerUtil            数据处理              OguLayerUtil
   .readLayer()          (过滤/转换)            .writeLayer()

6.2 Shapefile处理

6.2.1 读取Shapefile

基础读取

import com.znlgis.ogu4j.datasource.OguLayerUtil;
import com.znlgis.ogu4j.engine.model.layer.OguLayer;
import com.znlgis.ogu4j.engine.enums.DataFormatType;
import com.znlgis.ogu4j.engine.enums.GisEngineType;

// 简单读取
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,                      // layerName(Shapefile可为null)
    null,                      // 属性过滤
    null,                      // 空间过滤
    GisEngineType.GEOTOOLS
);

System.out.println("图层名称: " + layer.getName());
System.out.println("要素数量: " + layer.getFeatureCount());
System.out.println("字段数量: " + layer.getFields().size());

带属性过滤的读取

// 使用CQL表达式过滤
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,
    "POPULATION > 1000000",    // 人口大于100万的城市
    null,
    GisEngineType.GEOTOOLS
);

// 复杂过滤条件
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,
    "LEVEL = 1 AND PROVINCE = '广东'",  // 一级城市且在广东省
    null,
    GisEngineType.GEOTOOLS
);

// 模糊匹配
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,
    "NAME LIKE '%市'",         // 名称以"市"结尾
    null,
    GisEngineType.GEOTOOLS
);

带空间过滤的读取

// 使用WKT定义过滤范围
String filterWkt = "POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))";

OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,
    null,
    filterWkt,                 // 只读取与此范围相交的要素
    GisEngineType.GEOTOOLS
);

// 同时使用属性和空间过滤
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.SHP,
    "D:/data/cities.shp",
    null,
    "LEVEL = 1",               // 一级城市
    filterWkt,                 // 且在指定范围内
    GisEngineType.GEOTOOLS
);

6.2.2 写入Shapefile

基础写入

// 假设已有OguLayer对象
OguLayer layer = createSampleLayer();

// 写入Shapefile
OguLayerUtil.writeLayer(
    DataFormatType.SHP,
    layer,
    "D:/output/result.shp",
    null,                      // layerName
    null,                      // options
    GisEngineType.GEOTOOLS
);

完整示例:创建并写入Shapefile

public class ShapefileExample {
    
    public static void main(String[] args) {
        // 1. 创建图层
        OguLayer layer = new OguLayer();
        layer.setName("中国城市");
        layer.setWkid(4490);  // CGCS2000
        layer.setGeometryType(GeometryType.POINT);
        
        // 2. 定义字段
        List<OguField> fields = Arrays.asList(
            createField("NAME", FieldDataType.STRING, 50),
            createField("LEVEL", FieldDataType.INTEGER, null),
            createField("POP", FieldDataType.DOUBLE, null)
        );
        layer.setFields(fields);
        
        // 3. 添加要素
        List<OguFeature> features = Arrays.asList(
            createFeature("1", "北京", 1, 2154.2, "POINT(116.407 39.904)"),
            createFeature("2", "上海", 1, 2428.1, "POINT(121.473 31.230)"),
            createFeature("3", "广州", 2, 1881.0, "POINT(113.264 23.129)")
        );
        layer.setFeatures(features);
        
        // 4. 写入Shapefile
        OguLayerUtil.writeLayer(
            DataFormatType.SHP,
            layer,
            "D:/output/cities.shp",
            null, null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("Shapefile创建成功!");
    }
    
    private static OguField createField(String name, FieldDataType type, Integer length) {
        OguField field = new OguField();
        field.setName(name);
        field.setDataType(type);
        field.setLength(length);
        return field;
    }
    
    private static OguFeature createFeature(String fid, String name, 
            int level, double pop, String wkt) {
        OguFeature feature = new OguFeature();
        feature.setFid(fid);
        feature.setWkt(wkt);
        feature.setValue("NAME", name);
        feature.setValue("LEVEL", level);
        feature.setValue("POP", pop);
        return feature;
    }
}

6.2.3 Shapefile注意事项

字段名长度限制

Shapefile的DBF文件限制字段名最长10个字符:

// 错误示例:字段名超过10字符
OguField field = new OguField();
field.setName("POPULATION_COUNT");  // 16字符,会被截断!

// 正确做法:使用短字段名
field.setName("POP_CNT");           // 7字符
field.setAlias("人口数量");          // 别名可以是中文

编码处理

// OGU4J自动处理中文编码
// 读取时会检测DBF编码
// 写入时默认使用UTF-8

// 如果遇到编码问题,检查cpg文件
// cities.cpg 应包含: UTF-8

坐标精度

// Shapefile坐标精度有限
// 对于高精度坐标,建议使用其他格式

// 检查坐标精度
OguFeature feature = layer.getFeatures().get(0);
String wkt = feature.getWkt();
System.out.println("坐标: " + wkt);
// 输出可能是: POINT(116.4070 39.9040)  // 4位小数

6.3 GeoJSON处理

6.3.1 读取GeoJSON

// 读取GeoJSON文件
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.GEOJSON,
    "D:/data/cities.geojson",
    null, null, null,
    GisEngineType.GEOTOOLS
);

// 遍历要素
for (OguFeature feature : layer.getFeatures()) {
    System.out.printf("ID: %s, 名称: %s%n",
        feature.getFid(),
        feature.getValue("name"));
}

6.3.2 写入GeoJSON

// 将图层保存为GeoJSON
OguLayerUtil.writeLayer(
    DataFormatType.GEOJSON,
    layer,
    "D:/output/result.geojson",
    null, null,
    GisEngineType.GEOTOOLS
);

6.3.3 GeoJSON与JSON字符串转换

// OguLayer转换为JSON字符串(非GeoJSON格式)
String json = layer.toJSON();

// 从JSON字符串恢复OguLayer
OguLayer restored = OguLayer.fromJSON(json);

// 如果需要标准GeoJSON格式,使用写入方法
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 写入到输出流...
String geojsonString = baos.toString("UTF-8");

6.3.4 GeoJSON特点

优点 缺点
人类可读 文件较大
Web友好 不支持索引
无需多个文件 大数据量性能差
标准化格式 不适合频繁更新

6.4 FileGDB处理

6.4.1 FileGDB概述

FileGDB是ESRI的文件地理数据库格式,特点:

  • 支持多个图层(要素类)
  • 支持要素数据集(Feature Dataset)
  • 支持域、关系类等高级功能
  • 性能优于Shapefile
  • 只能使用GDAL引擎处理

6.4.2 读取FileGDB

读取单个图层

// 确保GDAL可用
if (!GisEngineFactory.isEngineAvailable(GisEngineType.GDAL)) {
    throw new RuntimeException("请先安装并配置GDAL");
}

// 读取指定图层
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.FILEGDB,
    "D:/data/sample.gdb",
    "Cities",                  // 图层名称
    null, null,
    GisEngineType.GDAL
);

带过滤条件读取

// 属性过滤
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.FILEGDB,
    "D:/data/sample.gdb",
    "Cities",
    "POPULATION > 500000",
    null,
    GisEngineType.GDAL
);

// 空间过滤
String bbox = "POLYGON((116 39, 116 40, 117 40, 117 39, 116 39))";
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.FILEGDB,
    "D:/data/sample.gdb",
    "Districts",
    null,
    bbox,
    GisEngineType.GDAL
);

6.4.3 写入FileGDB

创建新GDB并写入

// 写入新的FileGDB
OguLayerUtil.writeLayer(
    DataFormatType.FILEGDB,
    layer,
    "D:/output/new.gdb",       // GDB路径(会创建)
    "Cities",                   // 图层名称
    null,
    GisEngineType.GDAL
);

写入到现有GDB

// 写入现有GDB中的新图层
OguLayerUtil.writeLayer(
    DataFormatType.FILEGDB,
    layer,
    "D:/data/existing.gdb",    // 现有GDB
    "NewLayer",                 // 新图层名
    null,
    GisEngineType.GDAL
);

写入到要素数据集

// 使用options指定要素数据集
Map<String, Object> options = new HashMap<>();
options.put("featureDataset", "Boundaries");  // 要素数据集名称

OguLayerUtil.writeLayer(
    DataFormatType.FILEGDB,
    layer,
    "D:/data/sample.gdb",
    "Districts",
    options,                   // 传入options
    GisEngineType.GDAL
);

6.4.4 获取GDB结构

import com.znlgis.ogu4j.engine.util.GdalCmdUtil;
import com.znlgis.ogu4j.engine.model.GdbGroupModel;

// 获取GDB的完整结构
GdbGroupModel structure = GdalCmdUtil.getGdbDataStructure("D:/data/sample.gdb");

// 遍历要素数据集
for (String dataset : structure.getFeatureDatasets()) {
    System.out.println("要素数据集: " + dataset);
}

// 遍历图层
for (String layer : structure.getLayers()) {
    System.out.println("图层: " + layer);
}

6.5 PostGIS处理

6.5.1 PostGIS连接字符串

// 标准连接字符串格式
String connStr = "PG: host=localhost port=5432 dbname=gisdb " +
                 "user=postgres password=yourpassword active_schema=public";

// 各参数说明:
// host: 数据库主机地址
// port: 端口号,默认5432
// dbname: 数据库名称
// user: 用户名
// password: 密码
// active_schema: 活动模式,默认public

6.5.2 从PostGIS读取

String connStr = "PG: host=localhost port=5432 dbname=gisdb " +
                 "user=postgres password=123456 active_schema=public";

// 读取图层
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.POSTGIS,
    connStr,
    "cities",                  // 表名
    null, null,
    GisEngineType.GEOTOOLS
);

// 带过滤条件
OguLayer layer = OguLayerUtil.readLayer(
    DataFormatType.POSTGIS,
    connStr,
    "cities",
    "population > 1000000",    // SQL WHERE条件
    null,
    GisEngineType.GEOTOOLS
);

6.5.3 写入PostGIS

String connStr = "PG: host=localhost port=5432 dbname=gisdb " +
                 "user=postgres password=123456 active_schema=public";

// 写入新表
OguLayerUtil.writeLayer(
    DataFormatType.POSTGIS,
    layer,
    connStr,
    "new_cities",              // 表名(会创建)
    null,
    GisEngineType.GEOTOOLS
);

6.5.4 PostGIS工具类

import com.znlgis.ogu4j.engine.util.PostgisUtil;

// 解析连接字符串
DbConnBaseModel conn = PostgisUtil.parseConnectionString(connStr);
System.out.println("Host: " + conn.getHost());
System.out.println("Database: " + conn.getDatabase());

// 构建连接字符串
String connStr = PostgisUtil.buildConnectionString(
    "localhost", 5432, "gisdb", "postgres", "123456", "public"
);

6.6 国土TXT格式

6.6.1 格式说明

国土TXT是自然资源部门使用的坐标文本格式:

[属性描述]
要素名称=土地1
土地代码=123456
坐标系=2000国家大地坐标系
...

[地块坐标]
J1,1,500000.123,4000000.456
J2,1,500100.123,4000000.456
J3,1,500100.123,4000100.456
J4,1,500000.123,4000100.456
J1,1,500000.123,4000000.456

6.6.2 读取国土TXT

import com.znlgis.ogu4j.datasource.GtTxtUtil;

// 读取TXT文件
OguLayer layer = GtTxtUtil.loadTxt("D:/data/land.txt", null);

// 获取元数据
OguLayerMetadata metadata = layer.getMetadata();
System.out.println("坐标系: " + metadata.getCoordinateSystemName());
System.out.println("分带方式: " + metadata.getZoneDivision());

// 获取坐标点
for (OguFeature feature : layer.getFeatures()) {
    List<OguCoordinate> coords = feature.getCoordinates();
    for (OguCoordinate coord : coords) {
        System.out.printf("点号: %s, X: %.3f, Y: %.3f%n",
            coord.getPointNo(), coord.getX(), coord.getY());
    }
}

6.6.3 写入国土TXT

import com.znlgis.ogu4j.datasource.GtTxtUtil;
import com.znlgis.ogu4j.engine.model.layer.OguLayerMetadata;

// 准备元数据
OguLayerMetadata metadata = new OguLayerMetadata();
metadata.setDataSource("自然资源部");
metadata.setCoordinateSystemName("2000国家大地坐标系");
metadata.setZoneDivision("3");                    // 3度分带
metadata.setProjectionType("高斯克吕格");
metadata.setMeasureUnit("米");

// 写入TXT
int zoneNumber = 39;  // 带号
GtTxtUtil.saveTxt(layer, "D:/output/result.txt", metadata, null, zoneNumber);

6.7 格式转换实战

6.7.1 Shapefile转GeoJSON

public class ShpToGeoJson {
    
    public static void convert(String shpPath, String geojsonPath) {
        // 1. 读取Shapefile
        OguLayer layer = OguLayerUtil.readLayer(
            DataFormatType.SHP,
            shpPath,
            null, null, null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("读取完成,要素数量: " + layer.getFeatureCount());
        
        // 2. 写入GeoJSON
        OguLayerUtil.writeLayer(
            DataFormatType.GEOJSON,
            layer,
            geojsonPath,
            null, null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("转换完成: " + geojsonPath);
    }
    
    public static void main(String[] args) {
        convert("D:/data/cities.shp", "D:/output/cities.geojson");
    }
}

6.7.2 Shapefile转FileGDB

public class ShpToGdb {
    
    public static void convert(String shpPath, String gdbPath, String layerName) {
        // 1. 读取Shapefile
        OguLayer layer = OguLayerUtil.readLayer(
            DataFormatType.SHP,
            shpPath,
            null, null, null,
            GisEngineType.GEOTOOLS  // 用GeoTools读取
        );
        
        // 2. 写入FileGDB
        OguLayerUtil.writeLayer(
            DataFormatType.FILEGDB,
            layer,
            gdbPath,
            layerName,
            null,
            GisEngineType.GDAL      // 必须用GDAL写入
        );
        
        System.out.println("转换完成");
    }
    
    public static void main(String[] args) {
        convert("D:/data/cities.shp", "D:/output/sample.gdb", "Cities");
    }
}

6.7.3 FileGDB转PostGIS

public class GdbToPostgis {
    
    public static void convert(String gdbPath, String layerName,
            String postgisConnStr, String tableName) {
        
        // 1. 从FileGDB读取
        OguLayer layer = OguLayerUtil.readLayer(
            DataFormatType.FILEGDB,
            gdbPath,
            layerName,
            null, null,
            GisEngineType.GDAL
        );
        
        // 2. 写入PostGIS
        OguLayerUtil.writeLayer(
            DataFormatType.POSTGIS,
            layer,
            postgisConnStr,
            tableName,
            null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("转换完成,表名: " + tableName);
    }
    
    public static void main(String[] args) {
        String connStr = "PG: host=localhost port=5432 dbname=gisdb " +
                        "user=postgres password=123456 active_schema=public";
        
        convert("D:/data/sample.gdb", "Cities", connStr, "cities_imported");
    }
}

6.7.4 批量格式转换

public class BatchConverter {
    
    /**
     * 批量将Shapefile转换为GeoJSON
     */
    public static void batchConvert(String inputDir, String outputDir) {
        File dir = new File(inputDir);
        File[] shpFiles = dir.listFiles((d, name) -> name.endsWith(".shp"));
        
        if (shpFiles == null || shpFiles.length == 0) {
            System.out.println("未找到Shapefile文件");
            return;
        }
        
        // 创建输出目录
        new File(outputDir).mkdirs();
        
        int success = 0;
        int failed = 0;
        
        for (File shpFile : shpFiles) {
            String baseName = shpFile.getName().replace(".shp", "");
            String outputPath = outputDir + "/" + baseName + ".geojson";
            
            try {
                // 读取
                OguLayer layer = OguLayerUtil.readLayer(
                    DataFormatType.SHP,
                    shpFile.getAbsolutePath(),
                    null, null, null,
                    GisEngineType.GEOTOOLS
                );
                
                // 写入
                OguLayerUtil.writeLayer(
                    DataFormatType.GEOJSON,
                    layer,
                    outputPath,
                    null, null,
                    GisEngineType.GEOTOOLS
                );
                
                System.out.println("✓ " + baseName);
                success++;
                
            } catch (Exception e) {
                System.out.println("✗ " + baseName + ": " + e.getMessage());
                failed++;
            }
        }
        
        System.out.printf("%n转换完成: 成功 %d, 失败 %d%n", success, failed);
    }
    
    public static void main(String[] args) {
        batchConvert("D:/data/shapefiles", "D:/output/geojson");
    }
}

6.7.5 带坐标转换的格式转换

public class ConvertWithReproject {
    
    /**
     * 转换格式并进行坐标系转换
     */
    public static void convertAndReproject(String inputPath, String outputPath,
            int targetWkid) {
        
        // 1. 读取数据
        OguLayer layer = OguLayerUtil.readLayer(
            DataFormatType.SHP,
            inputPath,
            null, null, null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("原坐标系: WKID " + layer.getWkid());
        
        // 2. 坐标转换
        if (layer.getWkid() != null && layer.getWkid() != targetWkid) {
            layer = CrsUtil.reproject(layer, targetWkid);
            System.out.println("已转换到: WKID " + targetWkid);
        }
        
        // 3. 保存
        OguLayerUtil.writeLayer(
            DataFormatType.GEOJSON,
            layer,
            outputPath,
            null, null,
            GisEngineType.GEOTOOLS
        );
        
        System.out.println("保存完成: " + outputPath);
    }
    
    public static void main(String[] args) {
        // 将投影坐标转换为地理坐标
        convertAndReproject(
            "D:/data/projected.shp",
            "D:/output/geographic.geojson",
            4490  // CGCS2000地理坐标系
        );
    }
}

6.8 错误处理

6.8.1 常见异常

try {
    OguLayer layer = OguLayerUtil.readLayer(...);
} catch (DataSourceException e) {
    // 数据源问题:文件不存在、连接失败等
    System.err.println("数据源错误: " + e.getMessage());
} catch (FormatParseException e) {
    // 格式解析问题:CQL语法错误、WKT格式错误等
    System.err.println("格式错误: " + e.getMessage());
} catch (EngineNotSupportedException e) {
    // 引擎问题:GDAL未安装、格式不支持等
    System.err.println("引擎错误: " + e.getMessage());
} catch (OguException e) {
    // 其他OGU4J异常
    System.err.println("处理错误: " + e.getMessage());
}

6.8.2 最佳实践

public class SafeConverter {
    
    public static boolean safeConvert(String input, String output,
            DataFormatType inputFormat, DataFormatType outputFormat) {
        
        // 1. 验证输入
        if (input == null || output == null) {
            System.err.println("路径不能为空");
            return false;
        }
        
        // 2. 检查输入文件
        if (inputFormat != DataFormatType.POSTGIS) {
            File inputFile = new File(input);
            if (!inputFile.exists()) {
                System.err.println("输入文件不存在: " + input);
                return false;
            }
        }
        
        // 3. 选择引擎
        GisEngineType readEngine = GisEngineFactory.getRecommendedEngine(inputFormat);
        GisEngineType writeEngine = GisEngineFactory.getRecommendedEngine(outputFormat);
        
        // 4. 检查引擎可用性
        if (!GisEngineFactory.isEngineAvailable(readEngine)) {
            System.err.println("读取引擎不可用: " + readEngine);
            return false;
        }
        if (!GisEngineFactory.isEngineAvailable(writeEngine)) {
            System.err.println("写入引擎不可用: " + writeEngine);
            return false;
        }
        
        // 5. 执行转换
        try {
            OguLayer layer = OguLayerUtil.readLayer(
                inputFormat, input, null, null, null, readEngine
            );
            
            layer.validate();  // 验证数据
            
            OguLayerUtil.writeLayer(
                outputFormat, layer, output, null, null, writeEngine
            );
            
            return true;
            
        } catch (Exception e) {
            System.err.println("转换失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
}

← 上一章:双引擎架构设计 | 下一章:几何处理与空间分析 →

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