Search code examples
javageotoolsjts

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();


}

Solution

  • 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)