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:
tom@9726: import org.apache.logging.log4j.Logger;
tom@9726: import org.apache.logging.log4j.LogManager;
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:
tom@9726: private static final Logger log = LogManager.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 {
tom@8747: RiverAxis axis = RiverAxis.getRiverAxis(rivername);
tom@8747: if (axis != null) {
tom@8747: return axis.getGeom().getEnvelopeInternal();
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: /**
tom@8856: * Returns 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).
tom@8856: * @param attrs Optional. 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:
tom@8856: SimpleFeatureIterator iterator =
tom@8856: (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 :