Write M value into shapefile with geotools

I'm using the Java 1.8 library geotools (version "23-SNAPSHOT") to generate a shapefile.

Opening the resulting .shp file with QGIS was successfull and I noticed that the gemeotry was apparently automatically set to "PointZM", which is exactly what I want. Every point surprisingly also lists an "M" value (but they're all 0).

How do I set the M value (additional to x, y and z!) using the library? Does the library now automatically default to using "PointZM" (it didn't with an older version)?

The code I more or less copied from here:

final SimpleFeatureType TYPE = DataUtilities.createType("Location","the_geom:Point:srid=4326,name:String,number:Integer");
List<SimpleFeature> features = new ArrayList<SimpleFeature>();
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);

//Create features/points
for(int i=0; i<maxpoints; i++) {
Point p = geometryFactory.createPoint(new Coordinate(i*10,i*5,i));
SimpleFeature feature = featureBuilder.buildFeature(null);
}

//Generate shapefile
ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
Map<String, Serializable> params = new HashMap<String,Serializable>();
params.put("url", fileout.toURI().toURL());
params.put("create spatial index", Boolean.TRUE);
ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
newDataStore.createSchema(TYPE);

//Write file
Transaction transaction = new DefaultTransaction("create");
String typeName = newDataStore.getTypeNames()[0];
SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);
SimpleFeatureType SHAPE_TYPE = featureSource.getSchema();

if (featureSource instanceof SimpleFeatureStore) {
SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
SimpleFeatureCollection collection = new ListFeatureCollection(TYPE, features);
featureStore.setTransaction(transaction);

try {
transaction.commit();
} catch (Exception e) {
e.printStackTrace();
transaction.rollback();
} finally {
transaction.close();
}
} else {
System.out.println(typeName + " does not support read/write access");
System.exit(1);
}


There's a setM function for Coordinate, so I tried this:

Coordinate c = new Coordinate(i*10,i*5,i);
c.setM(i*20);
Point p = geometryFactory.createPoint(c);


But this only throws an exception:

Exception in thread "main" java.lang.IllegalArgumentException: Invalid ordinate index: 3

• According to the shapefile specification the format does not actually support XYZ geometries because with Z comes automatically also M downloads.esri.com/support/whitepapers/mo_/shapefile.pdf. – user30184 Sep 4 at 10:45
• @user30184 Thanks for the link. "PointM" only supports XYM, yes but check on page 15: A PointZ consists of a triplet of double-precision coordinates in the order X, Y, Z plus a measure.. That's exactly what I need but how do I set it up? – Neph Sep 4 at 10:51
• That's what I tried to say. If you have XYZ shapefile there is automatically place for storing M values as well. Unfortunately I do not know how that is done with GeoTools. – user30184 Sep 4 at 10:58

Using a Coordinate object throws an exception if you're trying to set its M value:

Coordinate c = new Coordinate(i*10,i*5,i);
c.setM(i*20);
Point p = geometryFactory.createPoint(c);


Exception in thread "main" java.lang.IllegalArgumentException: Invalid ordinate index: 3

This is because the normal Coordinate hasn't got an actual "m" value (so it would be the 4th of 3 available spots) and doesn't like having it set either:

public void setM(double m) {
throw new IllegalArgumentException("Invalid ordinate index: " + M);
}


Instead use CoordinateXYZM, which extends Coordinate:

CoordinateXYZM c = new CoordinateXYZM(i*10,i*5,i,i*20);
Point p = geometryFactory.createPoint(c);