Abhishek Dey Das Abhishek Dey Das - 5 months ago 17
Java Question

Need help converting an array of JTS Geometry objects to a shape file

I have an array of JTS Geometry objects that I need to put in a shape file. I also have some other attributes that needs to go in a DBase file. I need to index the spatial objects as well and if required create the projection file. Is there a way to do this using JTS/GeoTools. I tried ShapeFileWriter but that doesn't seem enough (no dbf support for instance).

public Shape(String shpFileName, String shxFileName) throws FileNotFoundException {
RandomAccessFile shpFile = new RandomAccessFile(shpFileName, "rw");
this.shpChannel = shpFile.getChannel();
RandomAccessFile shxFile = new RandomAccessFile(shxFileName, "rw");
this.shxChannel = shxFile.getChannel();
}

public void createShapeFile(GeometryCollection geometries, ShapeType shapeType) throws IOException {
ShapefileWriter writer = new ShapefileWriter(this.shpChannel, this.shxChannel);
writer.write(geometries, shapeType);
writer.close();


}

Answer

You can't write out a set of Geometries and get a DBF file (as there are no attributes to put in it). You need to create a FeatureCollection and then pass that to a ShapeFileDatastore.

You will need something like:

public boolean writeFeatures(
        FeatureCollection<SimpleFeatureType, SimpleFeature> features) {

    if (shpDataStore == null) {
        throw new IllegalStateException(
                "Datastore can not be null when writing");
    }
    SimpleFeatureType schema = features.getSchema();
    GeometryDescriptor geom = schema
            .getGeometryDescriptor();

    try {

        /*
         * Write the features to the shapefile
         */
        Transaction transaction = new DefaultTransaction(
                "create");

        String typeName = shpDataStore.getTypeNames()[0];
        SimpleFeatureSource featureSource = shpDataStore
                .getFeatureSource(typeName);

        /*
         * The Shapefile format has a couple limitations: - "the_geom" is always
         * first, and used for the geometry attribute name - "the_geom" must be of
         * type Point, MultiPoint, MuiltiLineString, MultiPolygon - Attribute
         * names are limited in length - Not all data types are supported (example
         * Timestamp represented as Date)
         *
         * Because of this we have to rename the geometry element and then rebuild
         * the features to make sure that it is the first attribute.
         */

        List<AttributeDescriptor> attributes = schema
                .getAttributeDescriptors();
        GeometryType geomType = null;
        List<AttributeDescriptor> attribs = new ArrayList<AttributeDescriptor>();
        for (AttributeDescriptor attrib : attributes) {
            AttributeType type = attrib.getType();
            if (type instanceof GeometryType) {
                geomType = (GeometryType) type;

            } else {
                attribs.add(attrib);
            }
        }

        GeometryTypeImpl gt = new GeometryTypeImpl(
                new NameImpl("the_geom"), geomType.getBinding(),
                geomType.getCoordinateReferenceSystem(),
                geomType.isIdentified(), geomType.isAbstract(),
                geomType.getRestrictions(), geomType.getSuper(),
                geomType.getDescription());

        GeometryDescriptor geomDesc = new GeometryDescriptorImpl(
                gt, new NameImpl("the_geom"),
                geom.getMinOccurs(), geom.getMaxOccurs(),
                geom.isNillable(), geom.getDefaultValue());

        attribs.add(0, geomDesc);

        SimpleFeatureType shpType = new SimpleFeatureTypeImpl(
                schema.getName(), attribs, geomDesc,
                schema.isAbstract(), schema.getRestrictions(),
                schema.getSuper(), schema.getDescription());


        shpDataStore.createSchema(shpType);

        if (featureSource instanceof SimpleFeatureStore) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

            List<SimpleFeature> feats = new ArrayList<SimpleFeature>();

            FeatureIterator<SimpleFeature> features2 = features
                    .features();
            while (features2.hasNext()) {
                SimpleFeature f = features2.next();
                SimpleFeature reType = SimpleFeatureBuilder
                        .build(shpType, f.getAttributes(), "");

                feats.add(reType);
            }
            features2.close();
            SimpleFeatureCollection collection = new ListFeatureCollection(
                    shpType, feats);

            featureStore.setTransaction(transaction);
            try {
                List<FeatureId> ids = featureStore
                        .addFeatures(collection);
                transaction.commit();
            } catch (Exception problem) {
                problem.printStackTrace();
                transaction.rollback();
            } finally {
                transaction.close();
            }
            shpDataStore.dispose();
            return true;
        } else {
            shpDataStore.dispose();
            System.err.println("ShapefileStore not writable");
            return false;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

There is a full working example at https://github.com/ianturton/geotools-cookbook/tree/master/modules/output/src/main/java/org/ianturton/cookbook/output (including the pom file to sort out the dependencies)