teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.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: teichmann@5831: import org.dive4elements.river.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; rrenkert@5181: import org.hibernate.HibernateException; 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: teichmann@8202: private static final Logger log = 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) { rrenkert@5170: try { rrenkert@5170: List axes = RiverAxis.getRiverAxis(rivername); rrenkert@5170: if (axes != null && axes.size() > 0) { rrenkert@5170: Envelope max = null; ingo@2078: rrenkert@5170: for (RiverAxis axis: axes) { rrenkert@5170: // TODO Take the correct EPSG into account. Maybe, we need to rrenkert@5170: // reproject the geometry. rrenkert@5170: Envelope env = axis.getGeom().getEnvelopeInternal(); rrenkert@5170: rrenkert@5170: if (max == null) { rrenkert@5170: max = env; rrenkert@5170: } rrenkert@5170: else { rrenkert@5170: max.expandToInclude(env); rrenkert@5170: } ingo@2078: } rrenkert@5170: rrenkert@5170: return max; ingo@2078: } rrenkert@5170: } rrenkert@5181: catch(HibernateException iae) { teichmann@8202: log.warn("Exception, no valid river axis found for " + rivername); rrenkert@5170: return null; ingo@1774: } teichmann@8202: log.warn("No valid river axis found for " + rivername); 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 env in OpenLayers representation. ingo@1052: * ingo@1774: * @param env The envelope of a geometry. ingo@1052: * ingo@1774: * @return the OpenLayers boundary of env. 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 parseGeoJSON( ingo@1106: String geojson, SimpleFeatureType ft ingo@1106: ) { ingo@1106: List collection = new ArrayList(); ingo@1106: ingo@1106: try { ingo@1106: FeatureJSON fjson = new FeatureJSON(); ingo@1106: fjson.setFeatureType(ft); ingo@1106: ingo@1106: FeatureIterator 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} epsg. ingo@3301: * ingo@3301: * @param epsg An EPSG code like EPSG:31466 ingo@3301: * ingo@3301: * @return the {@link CoordinateReferenceSystem} specified by epsg. ingo@3301: */ ingo@3301: public static CoordinateReferenceSystem getCoordinateReferenceSystem( ingo@3301: String epsg ingo@1121: ) { ingo@3301: if (epsg == null) { teichmann@8202: log.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) { teichmann@8202: log.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) { teichmann@8202: log.warn("unable to transform envelope: empty parameters"); ingo@3301: return orig; ingo@3301: } ingo@3301: teichmann@8202: log.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: teichmann@8202: if (log.isDebugEnabled()) { teichmann@8202: log.debug(" orig envelope : " + orig); teichmann@8202: log.debug(" transformed envelope: " + target); ingo@3301: } ingo@3301: ingo@3301: return target; ingo@3301: } ingo@3301: } ingo@3301: catch (NoSuchAuthorityCodeException nsae) { teichmann@8202: log.error("Cannot get CoordinateReferenceSystem!", nsae); ingo@3301: } ingo@3301: catch (FactoryException fe) { teichmann@8202: log.error("Cannot get CoordinateReferenceSystem!", fe); ingo@3301: } ingo@3301: catch (TransformException te) { teichmann@8202: log.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()) { teichmann@8202: log.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 params = ingo@1121: new HashMap(); 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 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) { teichmann@8202: log.error("Unable to prepare shapefile: " + mue.getMessage()); ingo@1121: } ingo@1121: catch (IOException ioe) { teichmann@8202: log.error("Unable to write shapefile: " + ioe.getMessage()); ingo@1121: } ingo@2762: catch (NoSuchAuthorityCodeException nsae) { teichmann@8202: log.error("Cannot get CoordinateReferenceSystem for '" ingo@2762: + DEFAULT_EPSG + "'"); ingo@2762: } ingo@2762: catch (FactoryException fe) { teichmann@8202: log.error("Cannot get CoordinateReferenceSystem for '" ingo@2762: + DEFAULT_EPSG + "'"); ingo@2762: } ingo@2762: catch (TransformException te) { teichmann@8202: log.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 :