sascha@465: package de.intevation.gnv.utils;
sascha@465: 
sascha@465: import java.io.File;
sascha@465: import java.io.IOException;
sascha@498: import java.io.Serializable;
sascha@465: import java.net.MalformedURLException;
ingo@760: import java.text.NumberFormat;
tim@649: import java.util.Collection;
sascha@498: import java.util.Date;
sascha@498: import java.util.HashMap;
sascha@498: import java.util.List;
sascha@498: import java.util.Map;
sascha@465: 
sascha@498: import org.apache.log4j.Logger;
sascha@498: import org.geotools.data.DataStoreFactorySpi;
sascha@465: import org.geotools.data.DataUtilities;
sascha@465: import org.geotools.data.DefaultTransaction;
sascha@498: import org.geotools.data.FeatureStore;
sascha@498: import org.geotools.data.Transaction;
sascha@465: import org.geotools.data.shapefile.ShapefileDataStore;
sascha@465: import org.geotools.data.shapefile.ShapefileDataStoreFactory;
sascha@498: import org.geotools.feature.FeatureCollection;
sascha@498: import org.geotools.feature.FeatureCollections;
sascha@498: import org.geotools.feature.SchemaException;
sascha@498: import org.geotools.feature.simple.SimpleFeatureBuilder;
sascha@498: import org.geotools.referencing.crs.DefaultGeographicCRS;
sascha@498: import org.opengis.feature.simple.SimpleFeature;
sascha@498: import org.opengis.feature.simple.SimpleFeatureType;
sascha@465: 
tim@799: import com.vividsolutions.jts.geom.Geometry;
tim@799: import com.vividsolutions.jts.geom.MultiLineString;
tim@799: import com.vividsolutions.jts.geom.MultiPolygon;
tim@799: import com.vividsolutions.jts.io.ParseException;
tim@799: import com.vividsolutions.jts.io.WKTReader;
tim@799: 
tim@799: import de.intevation.gnv.geobackend.base.Result;
tim@799: import de.intevation.gnv.geobackend.base.ResultDescriptor;
tim@799: 
sascha@465: /**
sascha@780:  * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
sascha@465:  */
sascha@465: public final class ShapeFileWriter
sascha@465: {
sascha@465:     private static Logger log = Logger.getLogger(
sascha@465:         ShapeFileWriter.class);
sascha@465: 
ingo@760:     private static NumberFormat format = NumberFormat.getInstance();
ingo@760: 
ingo@806:     /**
ingo@806:      * Precision used to format double values.
ingo@806:      */
ingo@760:     public static final int DOUBLE_PRECISION = 3;
ingo@760: 
ingo@761:     static {
ingo@761:         format.setMaximumFractionDigits(DOUBLE_PRECISION);
ingo@761:     }
ingo@761: 
sascha@465:     private ShapeFileWriter() {
sascha@465:     }
sascha@465: 
ingo@806: 
ingo@806:     /**
ingo@806:      * Write multilinestrings to shapefile.
ingo@806:      *
ingo@806:      * @param shapeFile Shapefile.
ingo@806:      * @param parameterId The parameter id.
ingo@806:      * @param layer The layer.
ingo@806:      * @param date The date.
ingo@806:      * @param multiLineStrings The multilinestring.
ingo@806:      * @return true, if shapefile writing was successful - otherwise false.
ingo@806:      */
sascha@465:     public static boolean writeMultiLineStringsToFile(
sascha@465:         File                                shapeFile,
sascha@498:         Integer                             parameterId,
sascha@498:         Integer                             layer,
sascha@498:         Date                                date,
sascha@465:         List<Pair<Object, MultiLineString>> multiLineStrings
sascha@465:     ) {
sascha@498:         return writeMultiLineStringsToFile(
sascha@498:             shapeFile,
sascha@498:             parameterId,
sascha@498:             layer,
sascha@498:             date,
sascha@778:             multiLineStrings,
sascha@498:             "isolines");
sascha@465:     }
sascha@465: 
ingo@806: 
ingo@806:     /**
ingo@806:      * Write multilinestrings to shapefile.
ingo@806:      *
ingo@806:      * @param shapeFile Shapefile.
ingo@806:      * @param parameterId The parameter id.
ingo@806:      * @param layer The layer.
ingo@806:      * @param date The date.
ingo@806:      * @param multiLineStrings The multilinestring.
ingo@806:      * @param name A name.
ingo@806:      * @return true, if shapefile writing was successful - otherwise false.
ingo@806:      */
sascha@465:     public static boolean writeMultiLineStringsToFile(
sascha@465:         File                                shapeFile,
sascha@498:         Integer                             parameterId,
sascha@498:         Integer                             layer,
sascha@498:         Date                                date,
sascha@465:         List<Pair<Object, MultiLineString>> multiLineStrings,
sascha@498:         String                              name
sascha@465:     ) {
sascha@498:         Map<String, Serializable> params = new HashMap<String, Serializable>();
sascha@465: 
sascha@465:         try {
sascha@498:             params.put("url", shapeFile.toURI().toURL());
sascha@465:         }
sascha@465:         catch (MalformedURLException mue) {
sascha@465:             log.error(mue.getLocalizedMessage(), mue);
sascha@465:             return false;
sascha@465:         }
sascha@465: 
sascha@498:         params.put("create spatial index", Boolean.TRUE);
sascha@465: 
sascha@465: 
sascha@498:         if (name == null) {
sascha@498:             name = shapeFile.getName();
sascha@498:         }
sascha@465: 
sascha@498:         SimpleFeatureType TYPE;
sascha@778: 
sascha@778:         try {
sascha@498:             TYPE = DataUtilities.createType(
sascha@498:                 name,
sascha@498:                 "geom:MultiLineString:srid=4326," +
sascha@778:                 "PARAMETER:Integer," +
sascha@498:                 "LAYER:Integer,"     +
sascha@498:                 "DATE:Date,"         +
ingo@760:                 "VALUE:Double,"       +
ingo@760:                 "DESC:String");
sascha@465:         }
sascha@465:         catch (SchemaException se) {
sascha@465:             log.error(se.getLocalizedMessage(), se);
sascha@498:             return false;
sascha@465:         }
sascha@498: 
sascha@498:         SimpleFeatureBuilder featureBuilder =
sascha@498:             new SimpleFeatureBuilder(TYPE);
sascha@498: 
sascha@778:         FeatureCollection<SimpleFeatureType, SimpleFeature> collection =
sascha@498:             FeatureCollections.newCollection();
sascha@498: 
sascha@498:         for (Pair<Object, MultiLineString> pair: multiLineStrings) {
sascha@498:             featureBuilder.add(pair.getB());
sascha@498:             featureBuilder.add(parameterId);
sascha@498:             featureBuilder.add(layer);
sascha@498:             featureBuilder.add(date);
sascha@498:             featureBuilder.add(pair.getA());
ingo@760:             featureBuilder.add(value2description(asDouble(pair.getA())));
sascha@498:             SimpleFeature feature = featureBuilder.buildFeature(null);
sascha@498:             collection.add(feature);
sascha@498:         }
sascha@498: 
sascha@498:         DataStoreFactorySpi dataStoreFactory = new ShapefileDataStoreFactory();
sascha@498: 
sascha@498:         Transaction transaction = null;
sascha@498: 
sascha@498:         boolean success = false;
sascha@498:         try {
sascha@498:             ShapefileDataStore newDataStore =
sascha@498:                 (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
sascha@498: 
sascha@498:             newDataStore.createSchema(TYPE);
sascha@498:             newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
sascha@498: 
sascha@498:             transaction = new DefaultTransaction("create");
sascha@498: 
sascha@498:             String typeName = newDataStore.getTypeNames()[0];
sascha@498: 
sascha@498:             FeatureStore<SimpleFeatureType, SimpleFeature> featureStore =
sascha@498:                 (FeatureStore<SimpleFeatureType, SimpleFeature>)
sascha@498:                     newDataStore.getFeatureSource(typeName);
sascha@498: 
sascha@498:             featureStore.setTransaction(transaction);
sascha@498: 
sascha@498:             featureStore.addFeatures(collection);
sascha@498:             transaction.commit();
sascha@498:             success = true;
sascha@778:         }
sascha@465:         catch (IOException ioe) {
sascha@465:             log.error(ioe.getLocalizedMessage(), ioe);
sascha@465:         }
sascha@465:         finally {
sascha@498:             if (transaction != null) {
sascha@498:                 if (!success) {
sascha@498:                     try { transaction.rollback(); }
sascha@498:                     catch (IOException ioe) {}
sascha@498:                 }
sascha@498:                 try { transaction.close(); }
sascha@465:                 catch (IOException ioe) {}
sascha@465:             }
sascha@465:         }
sascha@465: 
sascha@465:         return success;
sascha@465:     }
sascha@465: 
ingo@806: 
ingo@806:     /**
ingo@806:      * Write multipolygon to file.
ingo@806:      *
ingo@806:      * @param shapeFile The shapefile.
ingo@806:      * @param parameterId The parameter id.
ingo@806:      * @param layer The layer.
ingo@806:      * @param date The date.
ingo@806:      * @param multiPolygons Multipolygons.
ingo@806:      * @return true, if shapefile writing was successful - otherwise false.
ingo@806:      */
sascha@465:     public static boolean writeMultiPolygonsToFile(
sascha@465:         File                       shapeFile,
sascha@498:         Integer                    parameterId,
sascha@498:         Integer                    layer,
sascha@498:         Date                       date,
sascha@465:         Map<Integer, MultiPolygon> multiPolygons
sascha@465:     ) {
sascha@498:         return writeMultiPolygonsToFile(
sascha@778:             shapeFile,
sascha@498:             parameterId,
sascha@498:             layer,
sascha@498:             date,
sascha@778:             multiPolygons,
sascha@498:             "polygons");
sascha@465:     }
sascha@465: 
sascha@778: 
ingo@806:     /**
ingo@806:      * Write data to shapefile.
ingo@806:      *
ingo@806:      * @param shapeFile The shapefile.
ingo@806:      * @param name The name.
ingo@806:      * @param data The data.
ingo@806:      * @param geometryType The geometry type.
ingo@806:      * @return true, if shapefile writing was successful - otherwise false.
ingo@806:      */
tim@799:     public static boolean writeDataToFile(File shapeFile,
tim@649:                                           String name,
tim@799:                                           Collection<Result> data,
tim@799:                                           String geometryType){
sascha@778: 
tim@649:         WKTReader wktReader = new WKTReader();
sascha@778: 
tim@649:         Map<String, Serializable> params = new HashMap<String, Serializable>();
tim@649: 
tim@649:         try {
tim@649:             params.put("url", shapeFile.toURI().toURL());
tim@649:         }
tim@649:         catch (MalformedURLException mue) {
tim@649:             log.error(mue.getLocalizedMessage(), mue);
tim@799:             return false;
tim@649:         }
tim@649: 
tim@649:         params.put("create spatial index", Boolean.TRUE);
tim@649: 
tim@649: 
tim@649:         if (name == null) {
tim@649:             name = shapeFile.getName();
tim@649:         }
tim@649: 
tim@649:         SimpleFeatureType type = null;
tim@649:         SimpleFeatureBuilder featureBuilder = null;
sascha@778:         FeatureCollection<SimpleFeatureType, SimpleFeature> collection =
tim@649:             FeatureCollections.newCollection();
tim@727:         int j = 0;
tim@649:         for (Result result: data) {
tim@727:             j++;
tim@649:             try {
tim@649:                 Geometry g = wktReader.read(result.getString(0));
tim@727:                 ResultDescriptor rd = result.getResultDescriptor();
tim@727:                 int columns = rd.getColumnCount();
tim@649:                 if (type == null){
sascha@778:                     try {
tim@799:                         String schema = "geom:"+geometryType+":srid=4326";
tim@727:                         for (int i = 1; i < columns; i++){
tim@727:                             schema+=","+rd.getColumnName(i)+
tim@727:                                     ":"+rd.getColumnClassName(i);
tim@727:                         }
tim@727:                         type = DataUtilities.createType(name, schema);
tim@649:                     }
tim@649:                     catch (SchemaException se) {
tim@649:                         log.error(se.getLocalizedMessage(), se);
tim@799:                         return false;
tim@649:                     }
tim@649:                     featureBuilder = new SimpleFeatureBuilder(type);
tim@649:                 }
tim@649:                 featureBuilder.add(g);
tim@727:                 for (int i = 1; i < columns; i++){
tim@727:                     featureBuilder.add(result.getObject(i));
tim@727:                 }
tim@649:                 SimpleFeature feature = featureBuilder.buildFeature(null);
tim@649:                 collection.add(feature);
tim@649:             } catch (ParseException e) {
tim@727:                 log.error("cannot create geometry "+j+" for "+result.getString(0));
tim@649:             } catch (java.lang.IllegalArgumentException e){
tim@649:                 log.error("cannot create geometry for "+result.getString(0));
tim@649:             }
tim@649:         }
tim@649: 
tim@649:         DataStoreFactorySpi dataStoreFactory = new ShapefileDataStoreFactory();
tim@649: 
tim@649:         Transaction transaction = null;
tim@649: 
tim@649:         boolean success = false;
tim@649:         try {
tim@649:             ShapefileDataStore newDataStore =
tim@649:                 (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
tim@649: 
tim@649:             newDataStore.createSchema(type);
tim@649:             newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
tim@649: 
tim@649:             transaction = new DefaultTransaction("create");
tim@649: 
tim@649:             String typeName = newDataStore.getTypeNames()[0];
tim@649: 
tim@649:             FeatureStore<SimpleFeatureType, SimpleFeature> featureStore =
tim@649:                 (FeatureStore<SimpleFeatureType, SimpleFeature>)
tim@649:                     newDataStore.getFeatureSource(typeName);
tim@649: 
tim@649:             featureStore.setTransaction(transaction);
tim@649: 
tim@649:             featureStore.addFeatures(collection);
tim@649:             transaction.commit();
tim@649:             success = true;
sascha@778:         }
tim@649:         catch (IOException ioe) {
tim@649:             log.error(ioe.getLocalizedMessage(), ioe);
tim@649:         }
tim@649:         finally {
tim@649:             if (transaction != null) {
tim@649:                 if (!success) {
tim@649:                     try { transaction.rollback(); }
tim@649:                     catch (IOException ioe) {}
tim@649:                 }
tim@649:                 try { transaction.close(); }
tim@649:                 catch (IOException ioe) {}
tim@649:             }
tim@649:         }
tim@649: 
tim@799:         return true;
tim@649:     }
sascha@778: 
ingo@806: 
ingo@806:     /**
ingo@806:      * Write multipolygons to file.
ingo@806:      *
ingo@806:      * @param shapeFile The shapefile.
ingo@806:      * @param parameterId The parameter id.
ingo@806:      * @param layer The layer.
ingo@806:      * @param date The date.
ingo@806:      * @param multiPolygons Multipolygons.
ingo@806:      * @param name A name.
ingo@806:      * @return true, if shapefile writing was successful - otherwise false.
ingo@806:      */
sascha@465:     public static boolean writeMultiPolygonsToFile(
sascha@465:         File                       shapeFile,
sascha@498:         Integer                    parameterId,
sascha@498:         Integer                    layer,
sascha@498:         Date                       date,
sascha@465:         Map<Integer, MultiPolygon> multiPolygons,
sascha@498:         String                     name
sascha@465:     ) {
sascha@498:         Map<String, Serializable> params = new HashMap<String, Serializable>();
sascha@465: 
sascha@465:         try {
sascha@498:             params.put("url", shapeFile.toURI().toURL());
sascha@465:         }
sascha@465:         catch (MalformedURLException mue) {
sascha@465:             log.error(mue.getLocalizedMessage(), mue);
sascha@465:             return false;
sascha@465:         }
sascha@465: 
sascha@498:         params.put("create spatial index", Boolean.TRUE);
sascha@465: 
sascha@465: 
sascha@498:         if (name == null) {
sascha@498:             name = shapeFile.getName();
sascha@498:         }
sascha@465: 
sascha@498:         SimpleFeatureType TYPE;
sascha@778: 
sascha@778:         try {
sascha@498:             TYPE = DataUtilities.createType(
sascha@498:                 name,
sascha@498:                 "geom:MultiPolygon:srid=4326," +
sascha@778:                 "PARAMETER:Integer," +
sascha@498:                 "LAYER:Integer,"     +
sascha@498:                 "DATE:Date,"         +
sascha@498:                 "CLASS:Integer");
sascha@465:         }
sascha@465:         catch (SchemaException se) {
sascha@465:             log.error(se.getLocalizedMessage(), se);
sascha@498:             return false;
sascha@465:         }
sascha@498: 
sascha@498:         SimpleFeatureBuilder featureBuilder =
sascha@498:             new SimpleFeatureBuilder(TYPE);
sascha@498: 
sascha@778:         FeatureCollection<SimpleFeatureType, SimpleFeature> collection =
sascha@498:             FeatureCollections.newCollection();
sascha@498: 
sascha@498:         for (Map.Entry<Integer, MultiPolygon> entry:
sascha@498:             multiPolygons.entrySet()
sascha@498:         ) {
sascha@498:             featureBuilder.add(entry.getValue());
sascha@498:             featureBuilder.add(parameterId);
sascha@498:             featureBuilder.add(layer);
sascha@498:             featureBuilder.add(date);
sascha@498:             featureBuilder.add(entry.getKey());
sascha@498:             SimpleFeature feature = featureBuilder.buildFeature(null);
sascha@498:             collection.add(feature);
sascha@498:         }
sascha@498: 
sascha@498:         DataStoreFactorySpi dataStoreFactory = new ShapefileDataStoreFactory();
sascha@498: 
sascha@498:         Transaction transaction = null;
sascha@498: 
sascha@498:         boolean success = false;
sascha@498:         try {
sascha@498:             ShapefileDataStore newDataStore =
sascha@498:                 (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
sascha@498: 
sascha@498:             newDataStore.createSchema(TYPE);
sascha@498:             newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);
sascha@498: 
sascha@498:             transaction = new DefaultTransaction("create");
sascha@498: 
sascha@498:             String typeName = newDataStore.getTypeNames()[0];
sascha@498: 
sascha@498:             FeatureStore<SimpleFeatureType, SimpleFeature> featureStore =
sascha@498:                 (FeatureStore<SimpleFeatureType, SimpleFeature>)
sascha@498:                     newDataStore.getFeatureSource(typeName);
sascha@498: 
sascha@498:             featureStore.setTransaction(transaction);
sascha@498: 
sascha@498:             featureStore.addFeatures(collection);
sascha@498:             transaction.commit();
sascha@498:             success = true;
sascha@778:         }
sascha@465:         catch (IOException ioe) {
sascha@465:             log.error(ioe.getLocalizedMessage(), ioe);
sascha@465:         }
sascha@465:         finally {
sascha@498:             if (transaction != null) {
sascha@498:                 if (!success) {
sascha@498:                     try { transaction.rollback(); }
sascha@498:                     catch (IOException ioe) {}
sascha@498:                 }
sascha@498:                 try { transaction.close(); }
sascha@465:                 catch (IOException ioe) {}
sascha@465:             }
sascha@465:         }
sascha@465: 
sascha@465:         return success;
sascha@465:     }
sascha@465: 
ingo@806:     /**
ingo@806:      * Returns an object as Double.
ingo@806:      *
ingo@806:      * @param a An object.
ingo@806:      * @return Object <i>a</i> as Double. If <i>a</i> is not a double and no
ingo@806:      * number, 0d is returned.
ingo@806:      */
sascha@465:     private static final Double asDouble(Object a) {
sascha@498:         if (a instanceof Double) {
sascha@465:             return (Double)a;
sascha@498:         }
sascha@498:         if (a instanceof Number) {
sascha@465:             return Double.valueOf(((Number)a).doubleValue());
sascha@498:         }
sascha@465:         return 0d;
sascha@465:     }
ingo@760: 
ingo@806:     /**
ingo@806:      * Turns a double value into a string representation taking account of the
ingo@806:      * locale.
ingo@806:      *
ingo@806:      * @param value A double value.
ingo@806:      * @return The double value formatted as string.
ingo@806:      */
ingo@760:     private static final String value2description(Double value) {
ingo@760:         return format.format(value);
ingo@760:     }
sascha@465: }
sascha@465: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :