ingo@1052: package de.intevation.flys.utils;
ingo@1052: 
christian@3286: import com.vividsolutions.jts.geom.Coordinate;
christian@3286: import com.vividsolutions.jts.geom.Envelope;
christian@3286: import com.vividsolutions.jts.geom.Geometry;
christian@3286: 
christian@3286: import de.intevation.flys.model.RiverAxis;
christian@3286: 
christian@3286: import java.io.File;
ingo@1106: import java.io.IOException;
ingo@1106: import java.io.Serializable;
ingo@1106: import java.net.MalformedURLException;
ingo@1106: import java.util.ArrayList;
ingo@1106: import java.util.HashMap;
ingo@1106: import java.util.List;
ingo@1106: import java.util.Map;
ingo@1106: 
ingo@1121: import org.apache.log4j.Logger;
christian@3286: import org.geotools.data.DataStoreFactorySpi;
christian@3286: import org.geotools.data.DefaultTransaction;
christian@3286: import org.geotools.data.FeatureWriter;
christian@3286: import org.geotools.data.Transaction;
christian@3286: import org.geotools.data.shapefile.ShapefileDataStore;
christian@3286: import org.geotools.data.shapefile.ShapefileDataStoreFactory;
christian@3286: import org.geotools.data.simple.SimpleFeatureIterator;
christian@3286: import org.geotools.feature.FeatureCollection;
christian@3286: import org.geotools.feature.FeatureIterator;
christian@3286: import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
christian@3286: import org.geotools.geojson.feature.FeatureJSON;
christian@3286: import org.geotools.geometry.jts.JTS;
ingo@3301: import org.geotools.geometry.jts.ReferencedEnvelope;
christian@3286: import org.geotools.referencing.CRS;
ingo@1106: import org.opengis.feature.simple.SimpleFeature;
ingo@1106: import org.opengis.feature.simple.SimpleFeatureType;
ingo@1108: import org.opengis.referencing.FactoryException;
ingo@1108: import org.opengis.referencing.NoSuchAuthorityCodeException;
ingo@1107: import org.opengis.referencing.crs.CoordinateReferenceSystem;
ingo@2762: import org.opengis.referencing.operation.MathTransform;
ingo@2762: import org.opengis.referencing.operation.TransformException;
ingo@1106: 
ingo@1052: public class GeometryUtils {
ingo@1052: 
ingo@1121:     private static final Logger logger = Logger.getLogger(GeometryUtils.class);
ingo@1121: 
ingo@3301:     public static final String PREFIX_EPSG  = "EPSG:";
ingo@3301: 
ingo@2762:     public static final String DEFAULT_EPSG = "EPSG:31467";
ingo@2762: 
ingo@1052:     private GeometryUtils() {
ingo@1052:     }
ingo@1052: 
ingo@1774:     public static Envelope getRiverBoundary(String rivername) {
ingo@2078:         List<RiverAxis> axes = RiverAxis.getRiverAxis(rivername);
ingo@2078:         if (axes != null && axes.size() > 0) {
ingo@2078:             Envelope max = null;
ingo@2078: 
ingo@2078:             for (RiverAxis axis: axes) {
ingo@2078:                 // TODO Take the correct EPSG into account. Maybe, we need to
ingo@2078:                 // reproject the geometry.
ingo@2078:                 Envelope env = axis.getGeom().getEnvelopeInternal();
ingo@2078: 
ingo@2078:                 if (max == null) {
ingo@2078:                     max = env;
ingo@2078:                 }
ingo@2078:                 else {
ingo@2078:                     max.expandToInclude(env);
ingo@2078:                 }
ingo@2078:             }
ingo@2078: 
ingo@2078:             return max;
ingo@1774:         }
ingo@1774: 
ingo@1774:         return null;
ingo@1774:     }
ingo@1774: 
ingo@1774:     public static String getRiverBounds(String rivername) {
ingo@1774:         Envelope env = getRiverBoundary(rivername);
ingo@1774: 
christian@3303:         if (env != null) {
ingo@1774:             return jtsBoundsToOLBounds(env);
ingo@1144:         }
ingo@1144: 
ingo@1144:         return null;
ingo@1144:     }
ingo@1144: 
ingo@1052:     /**
ingo@3301:      * Returns the boundary of Envelope <i>env</i> in OpenLayers representation.
ingo@1052:      *
ingo@1774:      * @param env The envelope of a geometry.
ingo@1052:      *
ingo@1774:      * @return the OpenLayers boundary of <i>env</i>.
ingo@1052:      */
ingo@1774:     public static String jtsBoundsToOLBounds(Envelope env) {
christian@3303:         StringBuilder buf = new StringBuilder();
christian@3303:         buf.append(env.getMinX()); buf.append(' ');
christian@3303:         buf.append(env.getMinY()); buf.append(' ');
christian@3303:         buf.append(env.getMaxX()); buf.append(' ');
christian@3303:         buf.append(env.getMaxY());
christian@3303:         return buf.toString();
ingo@1052:     }
ingo@1106: 
ingo@1641:     public static String createOLBounds(Geometry a, Geometry b) {
ingo@1641:         Coordinate[] ca = a.getCoordinates();
ingo@1641:         Coordinate[] cb = b.getCoordinates();
ingo@1641: 
ingo@1641:         double lowerX = Double.MAX_VALUE;
ingo@1641:         double lowerY = Double.MAX_VALUE;
ingo@1641:         double upperX = -Double.MAX_VALUE;
ingo@1641:         double upperY = -Double.MAX_VALUE;
ingo@1641: 
ingo@1641:         for (Coordinate c: ca) {
ingo@1641:             lowerX = lowerX < c.x ? lowerX : c.x;
ingo@1641:             lowerY = lowerY < c.y ? lowerY : c.y;
ingo@1641: 
ingo@1641:             upperX = upperX > c.x ? upperX : c.x;
ingo@1641:             upperY = upperY > c.y ? upperY : c.y;
ingo@1641:         }
ingo@1641: 
ingo@1641:         for (Coordinate c: cb) {
ingo@1641:             lowerX = lowerX < c.x ? lowerX : c.x;
ingo@1641:             lowerY = lowerY < c.y ? lowerY : c.y;
ingo@1641: 
ingo@1641:             upperX = upperX > c.x ? upperX : c.x;
ingo@1641:             upperY = upperY > c.y ? upperY : c.y;
ingo@1641:         }
ingo@1641: 
ingo@1641:         return "" + lowerX + " " + lowerY + " " + upperX + " " + upperY;
ingo@1641:     }
ingo@1641: 
ingo@1107:     public static SimpleFeatureType buildFeatureType(
christian@3286:         String name, String srs, Class<?> geometryType)
ingo@1107:     {
ingo@1119:         return buildFeatureType(name, srs, geometryType, null);
ingo@1119:     }
ingo@1119: 
ingo@1119:     /**
ingo@1119:      * Creates a new SimpleFeatureType using a SimpleFeatureTypeBuilder.
ingo@1119:      *
ingo@1119:      * @param name The name of the FeatureType.
ingo@1119:      * @param srs The SRS (e.g. "EPSG:31466").
ingo@1119:      * @param geometryType The geometry type's class (e.g. Polygon.class).
ingo@3301:      * @param attrs Optional. An object with attribute-name/attribute-class pairs
ingo@3301:      * where index 0 specifies the name as string and index 1 the
ingo@3301:      * ype as class.
ingo@1119:      *
ingo@1119:      * @return a new SimpleFeatureType.
ingo@1119:      */
ingo@3301:     public static SimpleFeatureType buildFeatureType(String name, String srs,
ingo@3301:         Class<?> geometryType, Object[][] attrs) {
ingo@1108:         try {
ingo@1108:             SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
ingo@1108:             CoordinateReferenceSystem crs    = CRS.decode(srs);
ingo@1106: 
ingo@1108:             builder.setName("flys");
ingo@1108:             builder.setNamespaceURI("http://www.intevation.de/");
ingo@1108:             builder.setCRS(crs);
ingo@1108:             builder.setSRS(srs);
ingo@1106: 
ingo@1108:             builder.add("geometry", geometryType, crs);
ingo@1106: 
ingo@1119:             if (attrs != null) {
ingo@1119:                 for (Object[] attr: attrs) {
christian@3286:                     builder.add((String) attr[0], (Class<?>) attr[1]);
ingo@1119:                 }
ingo@1119:             }
ingo@1119: 
ingo@1108:             return builder.buildFeatureType();
ingo@1108:         }
ingo@1108:         catch (NoSuchAuthorityCodeException nsae) {
ingo@1108:         }
ingo@1108:         catch (FactoryException fe) {
ingo@1108:         }
ingo@1108: 
ingo@1108:         return null;
ingo@1106:     }
ingo@1106: 
ingo@1106:     public static List<SimpleFeature> parseGeoJSON(
ingo@1106:         String geojson, SimpleFeatureType ft
ingo@1106:     ) {
ingo@1106:         List<SimpleFeature> collection = new ArrayList<SimpleFeature>();
ingo@1106: 
ingo@1106:         try {
ingo@1106:             FeatureJSON fjson = new FeatureJSON();
ingo@1106:             fjson.setFeatureType(ft);
ingo@1106: 
ingo@1106:             FeatureIterator<SimpleFeature> iterator =
ingo@1106:                 fjson.streamFeatureCollection(geojson);
ingo@1106: 
ingo@1106:             while (iterator.hasNext()) {
ingo@1106:                 collection.add(iterator.next());
ingo@1106:             }
ingo@1106:         }
ingo@1106:         catch (IOException ioe) {
ingo@1106:             // TODO handle exception
ingo@1106:         }
ingo@1106: 
ingo@1106:         return collection;
ingo@1106:     }
ingo@1106: 
ingo@1106: 
ingo@3301:     /**
ingo@3301:      * This method returns the {@link CoordinateReferenceSystem} by the
ingo@3301:      * {@link String} <i>epsg</i>.
ingo@3301:      *
ingo@3301:      * @param epsg An EPSG code like <b>EPSG:31466</b>
ingo@3301:      *
ingo@3301:      * @return the {@link CoordinateReferenceSystem} specified by <i>epsg</i>.
ingo@3301:      */
ingo@3301:     public static CoordinateReferenceSystem getCoordinateReferenceSystem(
ingo@3301:         String epsg
ingo@1121:     ) {
ingo@3301:         if (epsg == null) {
ingo@3301:             logger.warn("cannot create CoordinateReferenceSystem with null");
ingo@3301:             return null;
ingo@3301:         }
ingo@3301: 
ingo@3301:         if (!epsg.startsWith(PREFIX_EPSG)) {
ingo@3301:             epsg = PREFIX_EPSG + epsg;
ingo@3301:         }
ingo@3301: 
ingo@3301:         try {
ingo@3301:             return CRS.decode(epsg);
ingo@3301:         }
ingo@3301:         catch (FactoryException fe) {
ingo@3301:             logger.error(
ingo@3301:                 "unable to get CoordinateReferenceSystem for: " + epsg,
ingo@3301:                 fe);
ingo@3301:         }
ingo@3301: 
ingo@3301:         return null;
ingo@2762:     }
ingo@2762: 
ingo@2762: 
ingo@3301:     public static Envelope transform(Envelope orig, String targetSrs) {
ingo@3301:         return transform(orig, targetSrs, DEFAULT_EPSG);
ingo@3301:     }
ingo@3301: 
ingo@3301: 
ingo@3301:     public static Envelope transform(
ingo@3301:         Envelope orig,
ingo@3301:         String   targetSrs,
ingo@3301:         String   origSrs
ingo@2762:     ) {
ingo@3301:         if (targetSrs == null || orig == null || origSrs == null) {
ingo@3301:             logger.warn("unable to transform envelope: empty parameters");
ingo@3301:             return orig;
ingo@3301:         }
ingo@3301: 
ingo@3301:         logger.debug("Transform envlope to '" + targetSrs + "'");
ingo@3301:         try {
ingo@3301:             CoordinateReferenceSystem sourceCRS =
ingo@3301:                 getCoordinateReferenceSystem(origSrs);
ingo@3301: 
ingo@3301:             CoordinateReferenceSystem targetCRS =
ingo@3301:                 getCoordinateReferenceSystem(targetSrs);
ingo@3301: 
ingo@3301:             if (sourceCRS != null && targetCRS != null) {
ingo@3301:                 ReferencedEnvelope tmpEnv =
ingo@3301:                     new ReferencedEnvelope(orig, CRS.decode(origSrs));
ingo@3301: 
ingo@3301:                 Envelope target = tmpEnv.transform(targetCRS, false);
ingo@3301: 
ingo@3301:                 if (logger.isDebugEnabled()) {
ingo@3301:                     logger.debug("   orig envelope       : " + orig);
ingo@3301:                     logger.debug("   transformed envelope: " + target);
ingo@3301:                 }
ingo@3301: 
ingo@3301:                 return target;
ingo@3301:             }
ingo@3301:         }
ingo@3301:         catch (NoSuchAuthorityCodeException nsae) {
ingo@3301:             logger.error("Cannot get CoordinateReferenceSystem!", nsae);
ingo@3301:         }
ingo@3301:         catch (FactoryException fe) {
ingo@3301:             logger.error("Cannot get CoordinateReferenceSystem!", fe);
ingo@3301:         }
ingo@3301:         catch (TransformException te) {
ingo@3301:             logger.error("Cannot transform envelope from source "
ingo@3301:                 + origSrs + " to target srs " + targetSrs);
ingo@3301:         }
ingo@3301: 
ingo@3301:         return null;
ingo@3301:     }
ingo@3301: 
ingo@3301: 
ingo@3301:     public static boolean writeShapefile(File shape,
christian@3306:         SimpleFeatureType featureType, FeatureCollection<?, ?> collection) {
ingo@3301:         return writeShapefile(shape, featureType, collection,
ingo@3301:             featureType.getCoordinateReferenceSystem());
ingo@3301:     }
ingo@3301: 
ingo@3301: 
ingo@3301:     public static boolean writeShapefile(File shape,
christian@3306:         SimpleFeatureType featureType, FeatureCollection<?, ?> collection,
ingo@3301:         CoordinateReferenceSystem crs) {
ingo@1164:         if (collection.isEmpty()) {
ingo@1164:             logger.warn("Shapefile is not written - no features given!");
ingo@1164:             return false;
ingo@1164:         }
ingo@1164: 
ingo@2762:         Transaction   transaction = null;
ingo@1106: 
ingo@1121:         try {
ingo@2762:             MathTransform transform = CRS.findMathTransform(
ingo@2762:                 CRS.decode(DEFAULT_EPSG), crs);
ingo@2762: 
ingo@1121:             Map<String, Serializable> params =
ingo@1121:                 new HashMap<String, Serializable>();
ingo@1106: 
ingo@1121:             params.put("url", shape.toURI().toURL());
ingo@1121:             params.put("create spatial index", Boolean.TRUE);
ingo@1106: 
ingo@1121:             DataStoreFactorySpi dataStoreFactory =
ingo@1121:                 new ShapefileDataStoreFactory();
ingo@1106: 
ingo@1121:             ShapefileDataStore newDataStore =
ingo@1121:                 (ShapefileDataStore)dataStoreFactory.createNewDataStore(params);
ingo@1121:             newDataStore.createSchema(featureType);
ingo@1106: 
ingo@1121:             transaction = new DefaultTransaction("create");
ingo@1121: 
ingo@1121:             String typeName = newDataStore.getTypeNames()[0];
ingo@1121: 
ingo@2762:             FeatureWriter<SimpleFeatureType, SimpleFeature> writer =
ingo@2762:                 newDataStore.getFeatureWriter(typeName, transaction);
ingo@1121: 
ingo@2762:             SimpleFeatureIterator iterator = (SimpleFeatureIterator) collection.features();
ingo@1121: 
ingo@2762:             while (iterator.hasNext()){
ingo@2762:                 SimpleFeature feature = iterator.next();
ingo@2762:                 SimpleFeature copy    = writer.next();
ingo@2762: 
ingo@2762:                 copy.setAttributes(feature.getAttributes());
ingo@2762: 
ingo@2762:                 Geometry orig        = (Geometry) feature.getDefaultGeometry();
ingo@2762:                 Geometry reprojected = JTS.transform(orig, transform);
ingo@2762: 
ingo@2762:                 copy.setDefaultGeometry(reprojected);
ingo@2762:                 writer.write();
ingo@2762:             }
ingo@2762: 
ingo@1121:             transaction.commit();
ingo@1121: 
ingo@1121:             return true;
ingo@1121:         }
ingo@1121:         catch (MalformedURLException mue) {
ingo@1121:             logger.error("Unable to prepare shapefile: " + mue.getMessage());
ingo@1121:         }
ingo@1121:         catch (IOException ioe) {
ingo@1121:             logger.error("Unable to write shapefile: " + ioe.getMessage());
ingo@1121:         }
ingo@2762:         catch (NoSuchAuthorityCodeException nsae) {
ingo@2762:             logger.error("Cannot get CoordinateReferenceSystem for '"
ingo@2762:                 + DEFAULT_EPSG + "'");
ingo@2762:         }
ingo@2762:         catch (FactoryException fe) {
ingo@2762:             logger.error("Cannot get CoordinateReferenceSystem for '"
ingo@2762:                 + DEFAULT_EPSG + "'");
ingo@2762:         }
ingo@2762:         catch (TransformException te) {
ingo@2762:             logger.error("Was not able to transform geometry!", te);
ingo@2762:         }
ingo@1121:         finally {
ingo@1121:             if (transaction != null) {
ingo@1121:                 try {
ingo@1121:                     transaction.close();
ingo@1121:                 }
ingo@1121:                 catch (IOException ioe) { /* do nothing */ }
ingo@1121:             }
ingo@1121:         }
ingo@1121: 
ingo@1121:         return false;
ingo@1106:     }
ingo@1052: }
ingo@1052: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :