如何使用GeoTools在Java中向已有Shapefile添加多边形和点
问题描述
开发Java项目时,使用GeoTools 30版本修改已有Shapefile,需要向其中添加新的多边形和点要素。目前已成功单独创建并展示多边形和点,但无法将它们合并保存到已有Shapefile中,请求指导正确实现方式。
现有代码
public class CRSLab { public static void main(String[] args) throws Exception { CRSLab lab = new CRSLab(); lab.displayShapefile(); } private void displayShapefile() throws Exception { List<PolygonPoint> locations = new ArrayList<>(); initData(locations); SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.setName("polygonFeature"); builder.add("the_geom", Polygon.class); SimpleFeatureType POLYGON = builder.buildFeatureType(); GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); SimpleFeature simpleFeature = toFeature(locations, POLYGON, geometryFactory); SimpleFeatureTypeBuilder pointBuilder = new SimpleFeatureTypeBuilder(); builder.setName("pointFeature"); builder.add("the_geom", Point.class); SimpleFeatureType pointFeatureType = builder.buildFeatureType(); SimpleFeature simpleFeaturePoint1 = toFeaturePoint(46.45d, -72.15d, pointFeatureType, geometryFactory); SimpleFeature simpleFeaturePoint2 = toFeaturePoint(47.15d, -72.15d, pointFeatureType, geometryFactory); SimpleFeature simpleFeaturePoint3 = toFeaturePoint(48.25d, -72.15d, pointFeatureType, geometryFactory); SimpleFeature simpleFeaturePoint4 = toFeaturePoint(49.35d, -72.15d, pointFeatureType, geometryFactory); DefaultFeatureCollection collection = new DefaultFeatureCollection(); collection.add(simpleFeature); collection.add(simpleFeaturePoint1); collection.add(simpleFeaturePoint2); collection.add(simpleFeaturePoint3); collection.add(simpleFeaturePoint4); File shapeFile = new File(new File("src/main/resources/").getAbsolutePath() + "example_shapefile_1.shp"); Map<String, Serializable> params = new HashMap<>(); params.put("url", shapeFile.toURI().toURL()); ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params); dataStore.createSchema(POLYGON); Transaction polygonTransaction = new DefaultTransaction("create"); String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); if (featureSource instanceof SimpleFeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; featureStore.setTransaction(polygonTransaction); try { featureStore.addFeatures(collection); polygonTransaction.commit(); } catch (Exception problem) { polygonTransaction.rollback(); } } // Create a map content and add our shapefile to it MapContent map = new MapContent(); map.setTitle("Quickstart"); Style style = SLD.createSimpleStyle(featureSource.getSchema(), Color.RED); Layer layer = new FeatureLayer(featureSource, style); map.addLayer(layer); // Now display the map JMapFrame.showMap(map); } private static SimpleFeature toFeature(List<PolygonPoint> locations, SimpleFeatureType POLYGON, GeometryFactory geometryFactory) { Coordinate[] coords = new Coordinate[locations.size()]; int i = 0; for (PolygonPoint location : locations) { Coordinate coord = new Coordinate(location.x, location.y, 0); coords[i] = (coord); i++; } Polygon polygon = geometryFactory.createPolygon(coords); System.out.println(polygon.toString()); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(POLYGON); featureBuilder.add(polygon); return featureBuilder.buildFeature(null); } private static SimpleFeature toFeaturePoint(Double latitude, Double longitude, SimpleFeatureType pointFeatureType, GeometryFactory geometryFactory) { Coordinate coord = new Coordinate(latitude, longitude, 0); Point point = geometryFactory.createPoint(coord); SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(pointFeatureType); featureBuilder.add(point); return featureBuilder.buildFeature(null); } static class PolygonPoint { public double x; public double y; public PolygonPoint(double x, double y) { this.x = x; this.y = y; } } private static void initData(List<PolygonPoint> locations) { locations.add(new PolygonPoint(40.0, -75.0)); locations.add(new PolygonPoint(42.5, -70.0)); locations.add(new PolygonPoint(45.0, -75.0)); locations.add(new PolygonPoint(42.5, -80.0)); locations.add(new PolygonPoint(40.0, -75.0)); } public static double calculateDistanceInMeters(double x1, double y1, double x2, double y2) { double deltaX = x2 - x1; double deltaY = y2 - y1; double distanceSquared = (deltaX * deltaX) + (deltaY * deltaY); return Math.sqrt(distanceSquared); } public static double distance(double lat1, double lon1, double lat2, double lon2) { // The math module contains a function // named toRadians which converts from // degrees to radians. lon1 = Math.toRadians(lon1); lon2 = Math.toRadians(lon2); lat1 = Math.toRadians(lat1); lat2 = Math.toRadians(lat2); // Haversine formula double dlon = lon2 - lon1; double dlat = lat2 - lat1; double a = Math.pow(Math.sin(dlat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon / 2), 2); double c = 2 * Math.asin(Math.sqrt(a)); // Radius of earth in kilometers. Use 3956 // for miles double r = 6371; // calculate the result return (c * r); } }
问题分析与解决方案
核心问题
- 点要素类型创建错误:代码中创建点要素类型时,误用了之前创建多边形的
builder对象,而非新声明的pointBuilder,导致点要素的几何类型仍然是Polygon,与实际创建的Point要素不匹配。 - Shapefile格式限制:Shapefile不支持同一图层包含多种几何类型(Polygon和Point不能共存于同一个Shapefile),这是Shapefile的固有格式约束。
- 操作逻辑错误:当前代码使用
createNewDataStore创建新的Shapefile,而非打开已有文件进行修改,不符合“修改已有Shapefile”的需求。
修正步骤
1. 修复点要素类型创建代码
将点要素类型的创建逻辑改为使用pointBuilder:
SimpleFeatureTypeBuilder pointBuilder = new SimpleFeatureTypeBuilder(); pointBuilder.setName("pointFeature"); pointBuilder.add("the_geom", Point.class); SimpleFeatureType pointFeatureType = pointBuilder.buildFeatureType();
2. 处理不同几何类型的存储
由于Shapefile不支持混合几何类型,有两种可行方案:
方案A:分开存储为不同的Shapefile
将多边形和点分别保存到两个独立的Shapefile中:
// 保存多边形到已有或新的多边形Shapefile File polygonFile = new File("src/main/resources/polygons.shp"); Map<String, Serializable> polygonParams = new HashMap<>(); polygonParams.put("url", polygonFile.toURI().toURL()); ShapefileDataStore polygonStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(polygonParams); polygonStore.createSchema(POLYGON); // 添加多边形要素并提交... // 保存点到独立的点Shapefile File pointFile = new File("src/main/resources/points.shp"); Map<String, Serializable> pointParams = new HashMap<>(); pointParams.put("url", pointFile.toURI().toURL()); ShapefileDataStore pointStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(pointParams); pointStore.createSchema(pointFeatureType); // 添加点要素并提交...
方案B:统一几何类型(转为MultiGeometry)
如果需要将点和多边形放在同一图层,可以将所有要素转为MultiPolygon和MultiPoint,或者使用GeometryCollection(注意:部分GIS软件对GeometryCollection支持有限):
// 修改多边形要素类型为MultiPolygon builder.add("the_geom", MultiPolygon.class); // 创建多边形时转为MultiPolygon MultiPolygon multiPolygon = geometryFactory.createMultiPolygon(new Polygon[]{polygon}); // 修改点要素类型为MultiPoint pointBuilder.add("the_geom", MultiPoint.class); // 创建点时转为MultiPoint MultiPoint multiPoint = geometryFactory.createMultiPoint(new Point[]{point});
3. 修改已有Shapefile的正确方式
如果要向已有Shapefile添加要素,需使用getDataStore打开已有文件,并确保新要素的类型与已有图层的类型一致:
File existingFile = new File("src/main/resources/example_shapefile_1.shp"); Map<String, Serializable> params = new HashMap<>(); params.put("url", existingFile.toURI().toURL()); // 打开已有数据存储 ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createDataStore(params); // 获取已有图层的要素类型 String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureType existingType = featureSource.getSchema(); // 创建与已有类型匹配的要素 // ...(确保新要素的几何类型和属性与existingType一致) if (featureSource instanceof SimpleFeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; Transaction transaction = new DefaultTransaction("addFeatures"); featureStore.setTransaction(transaction); try { featureStore.addFeatures(yourFeatureCollection); transaction.commit(); } catch (Exception e) { transaction.rollback(); e.printStackTrace(); } finally { transaction.close(); } }
完整修正示例(方案A:分开存储+修改已有文件)
假设已有一个多边形Shapefile,向其中添加新多边形,同时创建新的点Shapefile保存点要素:
private void modifyAndSaveFeatures() throws Exception { List<PolygonPoint> locations = new ArrayList<>(); initData(locations); GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); // 1. 处理多边形:添加到已有Shapefile File existingPolygonFile = new File("src/main/resources/example_shapefile_1.shp"); Map<String, Serializable> polygonParams = new HashMap<>(); polygonParams.put("url", existingPolygonFile.toURI().toURL()); ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); ShapefileDataStore polygonStore = (ShapefileDataStore) dataStoreFactory.createDataStore(polygonParams); String polygonTypeName = polygonStore.getTypeNames()[0]; SimpleFeatureSource polygonSource = polygonStore.getFeatureSource(polygonTypeName); SimpleFeatureType existingPolygonType = polygonSource.getSchema(); // 创建匹配类型的多边形要素 SimpleFeature polygonFeature = createMatchingPolygonFeature(locations, existingPolygonType, geometryFactory); DefaultFeatureCollection polygonCollection = new DefaultFeatureCollection(); polygonCollection.add(polygonFeature); if (polygonSource instanceof SimpleFeatureStore) { SimpleFeatureStore polygonFeatureStore = (SimpleFeatureStore) polygonSource; Transaction polygonTx = new DefaultTransaction("addPolygon"); polygonFeatureStore.setTransaction(polygonTx); try { polygonFeatureStore.addFeatures(polygonCollection); polygonTx.commit(); } catch (Exception e) { polygonTx.rollback(); e.printStackTrace(); } finally { polygonTx.close(); } } // 2. 处理点:创建新的点Shapefile File newPointFile = new File("src/main/resources/new_points.shp"); Map<String, Serializable> pointParams = new HashMap<>(); pointParams.put("url", newPointFile.toURI().toURL()); ShapefileDataStore pointStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(pointParams); SimpleFeatureTypeBuilder pointBuilder = new SimpleFeatureTypeBuilder(); pointBuilder.setName("pointFeature"); pointBuilder.add("the_geom", Point.class); // 可添加其他属性,如name等 pointBuilder.add("name", String.class); SimpleFeatureType pointType = pointBuilder.buildFeatureType(); pointStore.createSchema(pointType); // 创建点要素 SimpleFeature point1 = createPointFeature(46.45d, -72.15d, "Point 1", pointType, geometryFactory); SimpleFeature point2 = createPointFeature(47.15d, -72.15d, "Point 2", pointType, geometryFactory); DefaultFeatureCollection pointCollection = new DefaultFeatureCollection(); pointCollection.add(point1); pointCollection.add(point2); String pointTypeName = pointStore.getTypeNames()[0]; SimpleFeatureSource pointSource = pointStore.getFeatureSource(pointTypeName); if (pointSource instanceof SimpleFeatureStore) { SimpleFeatureStore pointFeatureStore = (SimpleFeatureStore) pointSource; Transaction pointTx = new DefaultTransaction("createPoints"); pointFeatureStore.setTransaction(pointTx); try { pointFeatureStore.addFeatures(pointCollection); pointTx.commit(); } catch (Exception e) { pointTx.rollback(); e.printStackTrace(); } finally { pointTx.close(); } } // 展示结果 displayMap(polygonSource, pointSource); } private SimpleFeature createMatchingPolygonFeature(List<PolygonPoint> locations, SimpleFeatureType type, GeometryFactory factory) { Coordinate[] coords = new Coordinate[locations.size()]; int i = 0; for (PolygonPoint loc : locations) { coords[i++] = new Coordinate(loc.x, loc.y); } Polygon polygon = factory.createPolygon(coords); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); builder.add(polygon); // 如果已有类型有其他属性,需对应设置 return builder.buildFeature(null); } private SimpleFeature createPointFeature(double lat, double lon, String name, SimpleFeatureType type, GeometryFactory factory) { Point point = factory.createPoint(new Coordinate(lat, lon)); SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); builder.add(point); builder.add(name); return builder.buildFeature(null); } private void displayMap(SimpleFeatureSource polygonSource, SimpleFeatureSource pointSource) throws IOException { MapContent map = new MapContent(); map.setTitle("Modified Shapefiles"); // 添加多边形图层 Style polygonStyle = SLD.createSimpleStyle(polygonSource.getSchema(), Color.BLUE); Layer polygonLayer = new FeatureLayer(polygonSource, polygonStyle); map.addLayer(polygonLayer); // 添加点图层 Style pointStyle = SLD.createSimpleStyle(pointSource.getSchema(), Color.RED); Layer pointLayer = new FeatureLayer(pointSource, pointStyle); map.addLayer(pointLayer); JMapFrame.showMap(map); }
内容的提问来源于stack exchange,提问作者Siva
相关产品推荐
相关产品推荐

