将 CSV 转换为 Shp 数据

前言

在GIS开发中,经常需要进行数据的转换处理。在之前的文章中讲了如何使用GeoTools读取Shapefile数据,并且展示了将Shapefile数据导入PostGIS空间数据库的多种方式。但是还缺少Shapefile数据转换来源的操作。

本篇教程在之前文章的基础上讲解如何将CSV文件转换为我们熟悉的Shapefile数据。

开发环境

本文使用开发环境如下,仅供参考。

时间:2025年

GeoTools:34-SNAPSHOT

IDE:IDEA2025.1.2

JDK:17

1. 准备CSV文件

CSV(Comma-Separated Values)文件是一种纯文本格式,用于存储表格数据(如电子表格或数据库)。它以结构简单、兼容性广泛而著称,是数据交换中最常用的格式之一。

CSV文本结构:

复制代码
Name,Age,Occupation
Alice,28,Engineer
Bob,32,Designer
Charlie,45,Manager

CSV表格结构:

2. 安装依赖

在之前开发的基础上增加gt-epsg-hsql依赖包。

xml 复制代码
<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-shapefile</artifactId>
    <version>${geotools.version}</version>
</dependency>
<dependency>
    <groupId>org.geotools</groupId>
    <artifactId>gt-epsg-hsql</artifactId>
    <version>${geotools.version}</version>
</dependency>

3. 读取CSV文件

使用showOpenFile方法打开文件选择框,然后使用createType构造要素结构,第一个参数"location"为要素类型,第二个参数为要素属性。the_geom字段表明数据几何类型为Pointsrid表明数据坐标系为4326以及后面的字段名称和对应字段类型。

java 复制代码
// 设置外观
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

// 选择文件
File file = JFileDataStoreChooser.showOpenFile("csv",null);
if(file == null ){
    return;
}

// 创建要素类型
final SimpleFeatureType TYPE = DataUtilities.createType(
        "location",
        "the_geom:Point:srid=4326,"+
                "name:String,"+
                "number:Integer"
);

现在可以读取CSV数据并构造Features,使用GeometryFactory来创建几何属性。

ini 复制代码
// 创建要素
List<SimpleFeature> features = new ArrayList<>();
// GeometryFactory 用来为要素创建几何属性
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

try(BufferedReader reader = new BufferedReader(new FileReader(file))){
    // 读取第一行头部数据
    String line = reader.readLine();
    for(line = reader.readLine(); line != null; line = reader.readLine()){
        if(line.trim().length()>0){
            String[] tokens =  line.split(",");
            double latitude = Double.parseDouble(tokens[0]);
            double longitude = Double.parseDouble(tokens[1]);

            String name = tokens[2].trim();
            int number = Integer.parseInt(tokens[3].trim());

            // 构造点
            Point point = geometryFactory.createPoint(new Coordinate(longitude,latitude));
            featureBuilder.add(point);
            featureBuilder.add(name);
            featureBuilder.add(number);
            SimpleFeature feature = featureBuilder.buildFeature(null);
            features.add(feature);
        }
    }
}

4. 创建Shapefile

ShapefileDataStoreFactory创建Shp工厂,在createDataStore参数中将属性"create spatial index"设置为true标明为Shp数据创建空间索引。

ini 复制代码
// 从要素集创建Shapefile
File newFile = getNewShapeFile(file);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

Map<String, Serializable> params = new HashMap<>();
params.put("url",newFile.toURI().toURL());
params.put("create spatial index",Boolean.TRUE);

ShapefileDataStore dataStore =  (ShapefileDataStore) dataStoreFactory.createDataStore(params);

// TYPE 用作描述文件内容的模板
dataStore.createSchema(TYPE);

通过确认FeatureSource对象实现了FeatureStore方法来检查是否具有读写权限,使用ListFeatureCollection包装FeatureCollection对象。最后使用transaction.comit()一次性安全地写出所有数据。

ini 复制代码
// 输出要素数据到Shapefile
Transaction transaction = new DefaultTransaction("create");
String typeName = dataStore.getTypeNames()[0];

SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
SimpleFeatureType featureType = featureSource.getSchema();

if(featureSource instanceof SimpleFeatureStore){
    SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
    SimpleFeatureCollection featureCollection = new ListFeatureCollection(featureType,features);
    featureStore.setTransaction(transaction);

    try {
        featureStore.addFeatures(featureCollection);
        transaction.commit();
    }catch (Exception e){
        e.printStackTrace();
        transaction.rollback();
    }finally {
        transaction.close();
    }
    System.exit(0);
}else {
    System.out.println(typeName + "缺少读|写权限!!");
    System.exit(1);
}

5. Shapefile输出位置

使用getNewShapeFile方法选择Shp输出位置。

ini 复制代码
// 提示输出Shapefile
private static File getNewShapeFile(File csvFile){
    String path = csvFile.getAbsolutePath();
    String newPath = path.substring(0,path.length()-4)+".shp";

    JFileDataStoreChooser chooser = new JFileDataStoreChooser(".shp");
    chooser.setDialogTitle("保存 ShapeFile");
    chooser.setSelectedFile(new File(newPath));

    int returnVal = chooser.showSaveDialog(null);

    if(returnVal != JFileDataStoreChooser.APPROVE_OPTION){
        System.exit(0);
    }

    File newFile = chooser.getSelectedFile();
    if(newFile.equals(csvFile)){
        System.out.println("Error:不能替换" + csvFile);
        System.exit(0);
    }
    return newFile;
}
相关推荐
benbenAItalk5 小时前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_962885725 小时前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎12346 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
陈随易8 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周8 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师9 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing989 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
EatFan9 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡10 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒10 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端