You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java读取SHP文件提取坐标并入库PostgreSQL的技术求助

解决ShapeFile读取MultiPolygon、提取坐标并入库PostgreSQL的问题

看起来你遇到了两个核心问题:一是无法识别MultiPolygon类型,二是没法提取坐标并完成PostgreSQL入库。我来一步步帮你搞定:

问题根源

你当前的代码只用了AbstractShape来接收所有形状,但Polygon和MultiPolygon对应不同的子类(PolygonShape和MultiPolygonShape),AbstractShape本身没有暴露坐标获取的方法,必须向下转型到具体子类才能处理对应的结构。


步骤1:正确识别Shape类型并提取坐标

首先,你需要导入对应的形状子类,然后在循环里判断类型并转型,再提取坐标转换为PostGIS支持的WKT格式(这是入库最方便的方式)。

1.1 导入必要的类

import org.nocrala.tools.gis.data.esri.shapefile.shape.PolygonShape;
import org.nocrala.tools.gis.data.esri.shapefile.shape.MultiPolygonShape;
import org.nocrala.tools.gis.data.esri.shapefile.shape.PolygonPart;
import org.nocrala.tools.gis.data.esri.shapefile.dbf.DbfReader;
import org.nocrala.tools.gis.data.esri.shapefile.dbf.DbfRecord;

1.2 修改读取逻辑,处理两种Shape类型

public class BigFileExample {
    public static void main(String[] args) throws IOException, InvalidShapeFileException {
        FileInputStream shpIs = new FileInputStream("D:\\Test\\Filename.shp");
        // 同时读取DBF文件(属性数据存在这里)
        FileInputStream dbfIs = new FileInputStream("D:\\Test\\Filename.dbf");

        ValidationPreferences prefs = new ValidationPreferences();
        prefs.setMaxNumberOfPointsPerShape(16650);
        ShapeFileReader shpReader = new ShapeFileReader(shpIs, prefs);
        DbfReader dbfReader = new DbfReader(dbfIs);

        ShapeFileHeader h = shpReader.getHeader();
        System.out.println("The shape type of this file is " + h.getShapeType());

        int total = 0;
        AbstractShape shape;
        DbfRecord record;

        // 同时遍历Shape和DBF记录(一一对应)
        while ((shape = shpReader.next()) != null && (record = dbfReader.nextRecord()) != null) {
            String name = record.getString("name"); // 替换成你DBF里实际的name字段名
            ShapeType shapeType = shape.getShapeType();
            System.out.println("Processing shape " + total + ", type: " + shapeType);

            String wkt = "";
            // 处理Polygon
            if (shapeType == ShapeType.POLYGON) {
                PolygonShape polygon = (PolygonShape) shape;
                wkt = convertPolygonToWKT(polygon.getPoints());
            }
            // 处理MultiPolygon
            else if (shapeType == ShapeType.MULTIPOLYGON) {
                MultiPolygonShape multiPolygon = (MultiPolygonShape) shape;
                wkt = convertMultiPolygonToWKT(multiPolygon);
            }

            // 入库(后面会实现这个方法)
            insertIntoPostgreSQL(name, wkt);
            total++;
        }

        System.out.println("Total shapes processed: " + total);
        shpIs.close();
        dbfIs.close();
    }

    // 把Polygon的坐标数组转成WKT
    private static String convertPolygonToWKT(double[][] points) {
        StringBuilder wkt = new StringBuilder("POLYGON ((");
        for (int i = 0; i < points.length; i++) {
            if (i > 0) wkt.append(", ");
            wkt.append(points[i][0]).append(" ").append(points[i][1]);
        }
        wkt.append("))");
        return wkt.toString();
    }

    // 把MultiPolygon转成WKT
    private static String convertMultiPolygonToWKT(MultiPolygonShape multiPolygon) {
        StringBuilder wkt = new StringBuilder("MULTIPOLYGON (");
        for (int i = 0; i < multiPolygon.getPartsCount(); i++) {
            PolygonPart part = multiPolygon.getPart(i);
            String partWkt = convertPolygonToWKT(part.getPoints());
            // 去掉POLYGON前缀,保留内部的坐标组
            partWkt = partWkt.substring("POLYGON (".length(), partWkt.length() - 1);
            if (i > 0) wkt.append(", ");
            wkt.append("(").append(partWkt).append(")");
        }
        wkt.append(")");
        return wkt.toString();
    }
}

步骤2:实现PostgreSQL(PostGIS)入库逻辑

要入库空间数据,你需要依赖PostGIS的JDBC驱动,然后用ST_GeomFromText函数把WKT转成PostGIS的geometry类型。

2.1 添加Maven依赖(如果用Maven)

<dependencies>
    <!-- PostgreSQL JDBC驱动 -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.6.0</version>
    </dependency>
    <!-- PostGIS JDBC支持 -->
    <dependency>
        <groupId>org.postgis</groupId>
        <artifactId>postgis-jdbc</artifactId>
        <version>2.5.0</version>
    </dependency>
</dependencies>

2.2 实现入库方法

private static void insertIntoPostgreSQL(String name, String wkt) {
    // 替换成你的数据库信息
    String dbUrl = "jdbc:postgresql://localhost:5432/your_database_name?currentSchema=public";
    String dbUser = "your_username";
    String dbPassword = "your_password";
    // 替换成你的坐标系SRID!比如UTM坐标系可能是EPSG:32648,需要确认shp文件的坐标系
    int srid = 4326; 

    String sql = "INSERT INTO geometries (name, geom) VALUES (?, ST_SetSRID(ST_GeomFromText(?), ?))";

    // 使用try-with-resources自动关闭连接
    try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
         PreparedStatement pstmt = conn.prepareStatement(sql)) {

        pstmt.setString(1, name);
        pstmt.setString(2, wkt);
        pstmt.setInt(3, srid);
        pstmt.executeUpdate();

    } catch (SQLException e) {
        System.err.println("入库失败,name: " + name);
        e.printStackTrace();
    }
}

优化建议(处理4万+数据)

因为你的数据有46823条,单条插入效率很低,建议用批量插入提高速度:

// 修改main方法里的入库逻辑,改为批量处理
public static void main(String[] args) throws IOException, InvalidShapeFileException {
    // ... 前面的初始化代码不变 ...

    String dbUrl = "jdbc:postgresql://localhost:5432/your_database_name?currentSchema=public";
    String dbUser = "your_username";
    String dbPassword = "your_password";
    int srid = 4326;
    int batchSize = 1000; // 每1000条提交一次
    int count = 0;

    try (Connection conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
         PreparedStatement pstmt = conn.prepareStatement(
             "INSERT INTO geometries (name, geom) VALUES (?, ST_SetSRID(ST_GeomFromText(?), ?))")) {

        conn.setAutoCommit(false); // 关闭自动提交,手动批量提交

        while ((shape = shpReader.next()) != null && (record = dbfReader.nextRecord()) != null) {
            // ... 前面的形状处理、获取name和wkt的代码不变 ...

            pstmt.setString(1, name);
            pstmt.setString(2, wkt);
            pstmt.setInt(3, srid);
            pstmt.addBatch(); // 添加到批处理

            count++;
            if (count % batchSize == 0) {
                pstmt.executeBatch(); // 执行批处理
                conn.commit(); // 提交事务
                System.out.println("已提交 " + count + " 条数据");
            }
            total++;
        }

        // 提交剩余的不足批量的数据
        pstmt.executeBatch();
        conn.commit();
        System.out.println("所有数据提交完成");

    } catch (SQLException e) {
        e.printStackTrace();
    }

    // ... 关闭流的代码不变 ...
}

关键注意事项

  1. 坐标系SRID:一定要确认你的shp文件的坐标系,替换代码里的srid值(比如常用的WGS84是4326,UTM分带的话需要对应EPSG代码)。可以用QGIS打开shp文件查看坐标系信息。
  2. DBF字段名:确保record.getString("name")里的字段名和你DBF文件里的实际字段名一致,大小写敏感。
  3. 大形状处理:如果有超大的Polygon/MultiPolygon,可能需要调整setMaxNumberOfPointsPerShape的值,避免抛出异常。

内容的提问来源于stack exchange,提问作者ShaiNe Ram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:55