如何解析GeoJSON并将坐标转换为PostGIS兼容的几何格式?
将GeoJSON转换为PostGIS兼容WKT格式的高效实现方法
需求说明
需要将FeatureCollection类型的GeoJSON数据转换为PostGIS可识别的格式,提取每个Feature的几何类型(geometry.type)和坐标(geometry.coordinates),生成类似ST_GeomFromText('POLYGON((0 1, 2 5, 2 7, 0 7, 0 1))')的WKT语句。
原始GeoJSON数据如下:
{ "type": "FeatureCollection", "name": "Some Boundary", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, "features": [ { "type": "Feature", "properties": { "State_Name": "ABC", "State_Code": "01" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 76.767318, 33.228753 ], [ 76.767318, 33.228753 ] ] ] } }, { "type": "Feature", "properties": { "State_Name": "DEF", "State_Code": "02" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 79.008814, 31.115041 ], [ 79.008814, 31.115041 ] ] ] } }, { "type": "Feature", "properties": { "State_Name": "GHI", "State_Code": "03" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 76.942524, 30.383268 ], [ 76.850815, 30.717589 ], [ 76.852673, 30.718222 ], [ 76.853471, 30.718813 ] ] ] } } ] }
现有问题
自行编写的Java代码仅处理了简单Polygon的场景,遇到多环Polygon、MultiPolygon或其他几何类型时会失效,代码如下:
JSONObject jsonObject = (JSONObject) obj; JSONArray features = (JSONArray) jsonObject.get("features"); JSONArray coordinates = (JSONArray) geometryObject.get("coordinates"); JSONArray coordinate = (JSONArray) coordinates.get(0); String geoString = "'"+str+"'(("; for(int j=0; j<coordinate.size(); j++){ JSONArray tempArr = (JSONArray) coordinate.get(i); geoString = geoString + tempArr.get(0).toString() +" "; geoString = geoString + tempArr.get(1).toString(); int len = coordinate.size() - 1; if(i < len){ geoString = geoString +","; } } geoString = geoString + "))'";
解决方案
方案1:使用JTS GIS库(推荐)
JTS是专门处理空间数据的成熟库,能完美解析所有OGC标准的几何类型,避免手动解析的漏洞,同时支持WKT格式转换。
步骤1:引入依赖(Maven)
<dependency> <groupId>org.locationtech.jts</groupId> <artifactId>jts-core</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>org.locationtech.jts.io</groupId> <artifactId>jts-io-common</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20230227</version> </dependency>
步骤2:实现代码
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.geojson.GeoJsonReader; import org.json.JSONArray; import org.json.JSONObject; public class GeoJsonToPostgis { public static void main(String[] args) { // 替换为你的FeatureCollection JSON字符串 String geoJsonStr = "{...}"; JSONObject featureCollection = new JSONObject(geoJsonStr); JSONArray features = featureCollection.getJSONArray("features"); GeoJsonReader geoJsonReader = new GeoJsonReader(); for (int i = 0; i < features.length(); i++) { JSONObject feature = features.getJSONObject(i); JSONObject geometry = feature.getJSONObject("geometry"); JSONObject properties = feature.getJSONObject("properties"); try { // 解析GeoJSON几何为JTS对象 Geometry geom = geoJsonReader.read(geometry.toString()); // 转换为WKT格式 String wkt = geom.toText(); // 生成PostGIS兼容的语句(指定SRID=4326,对应GeoJSON的CRS84) String postgisGeomExpr = String.format("ST_GeomFromText('%s', 4326)", wkt); // 结合属性生成完整插入语句 String stateName = properties.getString("State_Name"); String stateCode = properties.getString("State_Code"); System.out.printf("INSERT INTO boundary_table (state_name, state_code, geom) VALUES ('%s', '%s', %s);%n", stateName, stateCode, postgisGeomExpr); } catch (Exception e) { System.err.println("处理第" + i + "个Feature失败:" + e.getMessage()); e.printStackTrace(); } } } }
优势
- 支持所有几何类型(Point、LineString、Polygon、MultiPolygon等)
- 自动处理坐标嵌套、多环结构
- 内置空间数据校验,避免无效几何
- 可直接指定SRID,确保空间参考一致性
方案2:修复手动解析代码(不依赖第三方库)
如果无法引入外部库,可通过分层处理坐标嵌套、针对不同几何类型分支解析来修复代码:
import org.json.JSONArray; import org.json.JSONObject; public class GeoJsonToWkt { public static void main(String[] args) { String geoJsonStr = "{...}"; JSONObject featureCollection = new JSONObject(geoJsonStr); JSONArray features = featureCollection.getJSONArray("features"); for (int i = 0; i < features.length(); i++) { JSONObject feature = features.getJSONObject(i); JSONObject geometry = feature.getJSONObject("geometry"); String geomType = geometry.getString("type"); JSONArray coordinates = geometry.getJSONArray("coordinates"); // 构建WKT坐标部分 String coordSegment = buildCoordinateSegment(coordinates, geomType); String wkt = String.format("%s(%s)", geomType.toUpperCase(), coordSegment); String postgisExpr = String.format("ST_GeomFromText('%s', 4326)", wkt); // 输出插入语句 String stateName = feature.getJSONObject("properties").getString("State_Name"); System.out.printf("INSERT INTO boundary_table (state_name, geom) VALUES ('%s', %s);%n", stateName, postgisExpr); } } private static String buildCoordinateSegment(JSONArray coordinates, String geomType) { StringBuilder sb = new StringBuilder(); switch (geomType) { case "Point": sb.append(coordinates.getDouble(0)).append(" ").append(coordinates.getDouble(1)); break; case "LineString": sb.append("("); appendCoordPairs(coordinates, sb); sb.append(")"); break; case "Polygon": sb.append("("); for (int i = 0; i < coordinates.length(); i++) { if (i > 0) sb.append(","); sb.append("("); appendCoordPairs(coordinates.getJSONArray(i), sb); sb.append(")"); } sb.append(")"); break; case "MultiPolygon": sb.append("("); for (int i = 0; i < coordinates.length(); i++) { if (i > 0) sb.append(","); sb.append("("); JSONArray polygonRings = coordinates.getJSONArray(i); for (int j = 0; j < polygonRings.length(); j++) { if (j > 0) sb.append(","); sb.append("("); appendCoordPairs(polygonRings.getJSONArray(j), sb); sb.append(")"); } sb.append(")"); } sb.append(")"); break; // 可扩展支持MultiPoint、MultiLineString等类型 } return sb.toString(); } private static void appendCoordPairs(JSONArray coordPairs, StringBuilder sb) { for (int i = 0; i < coordPairs.length(); i++) { if (i > 0) sb.append(","); JSONArray pair = coordPairs.getJSONArray(i); sb.append(pair.getDouble(0)).append(" ").append(pair.getDouble(1)); } } }
改进点
- 针对不同几何类型做分支处理,覆盖常见场景
- 分层处理坐标嵌套,支持多环Polygon和MultiPolygon
- 使用StringBuilder提升字符串拼接效率
- 修复了原代码中循环变量混用的错误
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

