I am working on a Java project where I need to modify an existing shapefile by adding new polygons and points to it using the GeoTools library. I have managed to create the polygons and points separately and display them individually, but I'm struggling to combine and saving them to the existing shapefile. Here's a simplified version of my code:
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);
}
}
I'm using GeoTools version 30. My goal is to add the collection of points and polygon to the existing shapefile and save it. However, I'm not sure how to do this correctly. Could someone provide guidance on how to achieve this using GeoTools?
LT;DR; It can't be done.
A Shapefile (which is a very old and obsolete format) can only store one type of geometry. So you can't save both points and polygons in a single shapefile. So, you will either need two shapefiles or, better, use a GeoPackage and store two tables with in it (you could store both in one table but it will just cause problems later with styling).
Finally, rather than rolling your own distance code you should make use of the
GeodeticCalculator.getOrthodromicDistance()which will return the distance in metres between two lat/lon points directly.