[GIS] Geotools create/save shapefile has no geometry information

geotoolsjavashapefile

When I create and save a shapefile in GeoTools, then open it in ArcGIS or QGIS, it doesn't show up on the map, even though the features are there in the attribute table (and all attributes I added show up).

When I used ArcGIS to convert to JSON, the x,y coordinates were NaN. Using GeoTools to reload the shapefile I created – when loading the geometry attribute I get a null value (that is, in a list of attributes, the first one, which should be a geometry type, is null). So I stepped it through, and it may be an issue with creating attributes but I don't know why it's happening.

I stripped my code down and gave it random coordinates as an example so you can see an exact code I used that failed with minimum fluff:

// Create random points around 28 N, -90 E
int numFeatures = 10;
double[] center = {-90, 28};
double[][] coords = new double[numFeatures][2];
for(double[] coord : coords) {
    coord[0] = center[0] + (Math.random()-0.5);
    coord[1] = center[1] + (Math.random()-0.5);
}

// Create simple feature type 
SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
ReferencingObjectFactory refFactory = new ReferencingObjectFactory();
// use WGS84 datum (uses custom classes of mine here - don't worry about it)
CoordinateReferenceSystem convertedCRS = refFactory.createFromWKT(
    CoordinateSystem.getCoordinateSystem(Presets.Datum.WGS84).getWkt()
);
typeBuilder.setName("testing");
typeBuilder.setCRS(convertedCRS);
typeBuilder.add("geom", Point.class);
SimpleFeatureType featureType = typeBuilder.buildFeatureType();

Now right here, the featureType has 2 HashMaps contained, one is null => 0 and the other is geom => 0, which I assume is attribute type to index (because in version with lots of attribute it has other columns to unique numbers). I'm not sure where I'm getting the null attribute type from. I'm not sure if this was something that happened before.

(Edit) Here I added the line AttributeDescriptor attr0 = featureType.getDescriptor(0); just to check. It looks correct. The type is GeometryTypeImpl with the CRS and binding to class com.vividsolutions.jts.geom.Point. So maybe I'm barking up the wrong tree here and the null => 0 is fine. (/Edit)

Anyways here's the rest of the code:

// build feature collection
List<SimpleFeature> features = new ArrayList<>();
Hints theHint = new Hints(Hints.CRS, convertedCRS);
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(theHint);
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
for(int i = 0; i < numFeatures; i++) {
    Coordinate jtsCoords = new Coordinate(coords[i][0], coords[i][1], 0);
    featureBuilder.add(geometryFactory.createPoint(jtsCoords));
    features.add(featureBuilder.buildFeature(null));
}
SimpleFeatureCollection collection = new ListFeatureCollection(featureType, features);

// create empty file and prep file-writing stuff
String saveFilepath = "C:\\temp\\shapefiletest.shp";
File theFile = new File(saveFilepath);
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<>();
params.put("url", theFile.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(featureType);
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
// write features to file
try {
    if(!SimpleFeatureStore.class.isInstance(featureSource)) {
        throw new Exception(typeName + " does not support read/write access");
    } else {
        SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
        Transaction transaction = new DefaultTransaction("create");
        featureStore.setTransaction(transaction);
        try {
            featureStore.addFeatures(collection);
            transaction.commit();
            transaction.close();
        } catch(Exception e) {
            transaction.rollback();
            transaction.close();
            throw e;
        }
    }
} catch(Exception e) {
    throw e;
} finally {
    newDataStore.dispose();
}

Yes, I've breakpointed it and check variables and everything. So far it looks fine, the feature collection looks as it should, every feature has a Coordinates object with valid coordinates. Then somehow when it actually gets written, nothing. (Again I think it goes back to a null attribute type, but why is it there?)

The funny part is the code worked fine before. I'm not sure when it started erroring so I'm not sure what change caused it. I've tested it on GeoTools 11.0 and 12.0.1. It worked back when I used GeoTools 11.0, the error happened w/o switching to 12.0.1, I just decided to try it and see if it helped.

Best Answer

Fixed it.

typeBuilder.add("geom", Point.class);

should be:

typeBuilder.add("the_geom", Point.class);

Not sure why it worked before then. Either I don't remember changing that line (not sure why I would though), or perhaps some earlier version of GeoTools just "geom" worked. Looks like I may have picked it up from part of the javadoc that did not get updated.