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

如何使用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);
    }
}
问题分析与解决方案

核心问题

  1. 点要素类型创建错误:代码中创建点要素类型时,误用了之前创建多边形的builder对象,而非新声明的pointBuilder,导致点要素的几何类型仍然是Polygon,与实际创建的Point要素不匹配。
  2. Shapefile格式限制:Shapefile不支持同一图层包含多种几何类型(Polygon和Point不能共存于同一个Shapefile),这是Shapefile的固有格式约束。
  3. 操作逻辑错误:当前代码使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:12:01