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(); } // ... 关闭流的代码不变 ... }
关键注意事项
- 坐标系SRID:一定要确认你的shp文件的坐标系,替换代码里的
srid值(比如常用的WGS84是4326,UTM分带的话需要对应EPSG代码)。可以用QGIS打开shp文件查看坐标系信息。 - DBF字段名:确保
record.getString("name")里的字段名和你DBF文件里的实际字段名一致,大小写敏感。 - 大形状处理:如果有超大的Polygon/MultiPolygon,可能需要调整
setMaxNumberOfPointsPerShape的值,避免抛出异常。
内容的提问来源于stack exchange,提问作者ShaiNe Ram
相关产品推荐
相关产品推荐

