diff gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java @ 1119:7c4f81f74c47

merged gnv-artifacts
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:00 +0200
parents f953c9a559d8
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java	Fri Sep 28 12:14:00 2012 +0200
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 2010 by Intevation GmbH
+ *
+ * This program is free software under the LGPL (>=v2.1)
+ * Read the file LGPL.txt coming with the software for details
+ * or visit http://www.gnu.org/licenses/ if it does not exist.
+ */
+
+package de.intevation.gnv.utils;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+
+import com.vividsolutions.jts.io.ParseException;
+import com.vividsolutions.jts.io.WKTReader;
+
+import de.intevation.gnv.artifacts.ressource.RessourceFactory;
+
+import de.intevation.gnv.geobackend.base.Result;
+
+import de.intevation.gnv.geobackend.base.query.QueryExecutor;
+import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
+
+import de.intevation.gnv.geobackend.base.query.exception.QueryException;
+
+import de.intevation.gnv.math.LinearFunction;
+
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import org.apache.commons.math.optimization.OptimizationException;
+
+import org.apache.commons.math.optimization.fitting.CurveFitter;
+
+import org.apache.commons.math.optimization.general.GaussNewtonOptimizer;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A helper class which supports some useful methods to work with wkt strings.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class WKTUtils {
+
+    private static Logger log = Logger.getLogger(WKTUtils.class);
+
+    public static final double NAUTICAL_MILE = 1852.216d;
+    public static final double KILOMETER     = 1000d;
+
+    public static final String I_NAME = "MEDIAN.MESHPOINT.IPOSITION";
+    public static final String J_NAME = "MEDIAN.MESHPOINT.JPOSITION";
+
+    public static final String TRUE_EXPRESSION = "FEATUREID=FEATUREID";
+
+    public static final String[] COORDINATE_OUT_FORMAT = {
+        "coordinate.template.northeast",
+        "coordinate.template.southeast",
+        "coordinate.template.northwest",
+        "coordinate.template.southwest"
+    };
+
+    public static final String DEFAULT_COORDINATE_TEMPLATE =
+        "{0}\u00b0N {1}'' {2}\u00b0E {3}''";
+
+    /**
+     * Checks the difference of two <code>Result</code>s.
+     *
+     * @param a A Result.
+     * @param b Another Result.
+     * @param indices Indices to be checked.
+     * @return true, if <i>a</i> and <i>b</i> are different - otherwise false.
+     */
+    public static boolean different(Result a, Result b, int [] indices) {
+        for (int i = 0; i < indices.length; ++i) {
+            String oa = a.getString(indices[i]);
+            String ob = b.getString(indices[i]);
+
+            if (oa == null && ob == null)  {
+                continue;
+            }
+
+            if (oa == null || ob == null) {
+                return true;
+            }
+
+            if (!oa.equals(ob)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Turns a wkt string into a coordinate.
+     *
+     * @param shape A wkt string.
+     * @return wkt string as coordinate.
+     */
+    public static Coordinate toCoordinate(String shape) {
+        try {
+            return shape != null
+                ? ((Point)(new WKTReader().read(shape))).getCoordinate()
+                : null;
+        }
+        catch (ParseException pe) {
+            log.error(pe);
+        }
+		catch (ClassCastException cce) {
+			log.error("cannot read WKT point", cce);
+		}
+        return null;
+    }
+
+    /**
+     * Turns a wkt string into a polygon.
+     *
+     * @param shape A wkt string.
+     * @return wkt string as polygon.
+     */
+    public static Polygon toPolygon(String shape) {
+        try {
+            return (Polygon)new WKTReader().read(shape);
+        }
+        catch (ParseException pe) {
+            log.error(pe);
+        }
+		catch (ClassCastException cce) {
+			log.error("cannot read WKT polygon", cce);
+		}
+        return null;
+    }
+
+    /**
+     * Returns a distance in km.
+     *
+     * @param distance A distance.
+     * @return distance in km.
+     */
+    public static final double toKM(double distance) {
+        return (distance * NAUTICAL_MILE) / KILOMETER;
+    }
+
+
+    /**
+     * Turns a coordinate into a wkt string.
+     *
+     * @param coordinate A coordinate.
+     * @return the coordinate as wkt string.
+     */
+    public static String toWKT(Coordinate coordinate) {
+        StringBuilder sb = new StringBuilder("POINT(");
+        sb.append(coordinate.x)
+          .append(' ')
+          .append(coordinate.y)
+          .append(')');
+        return sb.toString();
+    }
+
+
+    public static final String indexBox(
+        java.awt.Point a,
+        java.awt.Point b,
+        String         iName,
+        String         jName
+    ) {
+        int minI = Math.min(a.x, b.x) - 1;
+        int maxI = Math.max(a.x, b.x) + 1;
+        int minJ = Math.min(a.y, b.y) - 1;
+        int maxJ = Math.max(a.y, b.y) + 1;
+        StringBuilder sb = new StringBuilder("(")
+                          .append(iName).append(" >= ").append(minI)
+          .append(" AND ").append(iName).append(" <= ").append(maxI)
+          .append(" AND ").append(jName).append(" >= ").append(minJ)
+          .append(" AND ").append(jName).append(" <= ").append(maxJ)
+          .append(')');
+        return sb.toString();
+    }
+
+    public static final String diagonalBox(List<java.awt.Point> points) {
+
+        if (points.get(0) != null && points.get(2) != null) {
+            return indexBox(
+                points.get(0), points.get(2),
+                I_NAME,
+                J_NAME);
+        }
+
+        if (points.get(1) != null && points.get(3) != null) {
+            return indexBox(
+                points.get(1), points.get(3),
+                I_NAME,
+                J_NAME);
+        }
+
+        return null;
+    }
+
+    public static String worldEnvelopeCoordinatesToIndex(
+         Coordinate []       coords,
+         String              meshid,
+         String              ijkQueryID
+    )
+    throws QueryException
+    {
+        List<java.awt.Point> points =
+            new ArrayList<java.awt.Point>(coords.length);
+
+        ArrayList<Object []> missingPoints =
+            new ArrayList<Object []>();
+
+        createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints);
+
+        String additionWhere = null;
+
+        if (missingPoints.size() == coords.length) {
+            log.debug("cannot create index buffer");
+        }
+        else {
+            additionWhere = diagonalBox(points);
+
+            if (additionWhere == null) {
+                // 3 Points are missing or are on one side of the envelope
+                boolean remainsMissingPoints = calculateIJ4MissingPoints(
+                    coords, points, missingPoints);
+
+                if (!remainsMissingPoints) {
+                    additionWhere = diagonalBox(points);
+                }
+            }
+        }
+
+        return additionWhere != null
+            ? additionWhere
+            : TRUE_EXPRESSION;
+    }
+
+    public static String worldCoordinatesToIndex(
+		Coordinate []       coords,
+        Collection<Result>  result,
+		String              meshid,
+        String              ijkQueryID
+    )
+	throws QueryException
+    {
+        List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length);
+
+        ArrayList<Object []> missingPoints = new ArrayList<Object []>();
+
+        createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints);
+
+        String additionWhere = TRUE_EXPRESSION;
+
+        if (missingPoints.size() == coords.length) {
+            log.debug("cannot create index buffer");
+        }
+        else { // generate index filter
+            boolean remainsMissingPoints = calculateIJ4MissingPoints(
+                coords, points, missingPoints);
+
+            if (!remainsMissingPoints) {
+                // TODO: Make Tablenames and Columns Configurable
+                IndexBuffer ib = new IndexBuffer(
+                    points,
+                    I_NAME,
+                    J_NAME );
+                additionWhere = ib.toWhereClause();
+                log.debug("Additional Where Clause = "+additionWhere);
+            }
+        } // if generate index filter
+
+        return additionWhere;
+    }
+
+
+    /**
+     * @param coords
+     * @param points
+     * @param missingPoints
+     * @return
+     */
+    private static boolean calculateIJ4MissingPoints(
+        Coordinate []        coords,
+        List<java.awt.Point> points,
+        ArrayList<Object[]>  missingPoints
+    ) {
+        boolean remainsMissingPoints = !missingPoints.isEmpty();
+
+        if (remainsMissingPoints) {
+            // try to guess the missing (i, j)
+            CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true));
+            CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true));
+
+            for (int i = 0, N = points.size(); i < N; ++i) {
+                java.awt.Point p = (java.awt.Point)points.get(i);
+                if (p != null) {
+                    Coordinate coord = coords[i];
+                    iFitter.addObservedPoint(coord.x, p.x);
+                    jFitter.addObservedPoint(coord.y, p.y);
+                }
+            }
+            try {
+                // XXX: Assumption: (i, j) are created by componentwise linear function.
+                // This is surely not correct because (x, y) are in a ellipsoid projection.
+                // TODO: use ellipsoid functions and fit with Levenberg Marquardt.
+                double [] iParams = iFitter.fit(
+                    LinearFunction.INSTANCE, new double [] { 1d, 1d });
+
+                double [] jParams = jFitter.fit(
+                    LinearFunction.INSTANCE, new double [] { 1d, 1d });
+
+                for (int i = missingPoints.size()-1; i >= 0; --i) {
+                    Object [] a = (Object [])missingPoints.get(i);
+                    Coordinate coord = (Coordinate)a[1];
+                    int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]);
+                    int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]);
+                    points.set(
+                        ((Integer)a[0]).intValue(),
+                        new java.awt.Point(pi, pj));
+                }
+
+                remainsMissingPoints = false; // we filled the gaps
+            }
+            catch (FunctionEvaluationException fee) {
+                log.error(fee);
+            }
+            catch (OptimizationException oe) {
+                log.error(oe);
+            }
+        }
+        return remainsMissingPoints;
+    }
+
+
+    /**
+     * @param coords
+     * @param meshid
+     * @param ijkQueryID
+     * @param points
+     * @param missingPoints
+     * @throws QueryException
+     */
+    private static void createIJKPoints(
+        Coordinate[]         coords,
+        String               meshid,
+        String               ijkQueryID,
+        List<java.awt.Point> points,
+        ArrayList<Object []> missingPoints
+    )
+    throws QueryException
+    {
+        boolean debug = log.isDebugEnabled();
+
+        QueryExecutor queryExecutor = QueryExecutorFactory
+            .getInstance()
+            .getQueryExecutor();
+
+        for (int i = 0; i < coords.length; i++) {
+
+            String wkt = toWKT(coords[i]);
+
+            Collection<Result> result = queryExecutor.executeQuery(
+                ijkQueryID,
+                new String [] {meshid, wkt});
+
+            if (!result.isEmpty()) {
+                Result resultValue = result.iterator().next();
+                int iPos = resultValue.getInteger(1);
+                int jPos = resultValue.getInteger(0);
+                if (debug) {
+                    log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt);
+                }
+                points.add(i, new java.awt.Point(iPos,jPos));
+            }
+            else {
+                if (debug) {
+                    log.debug("No i/j Pos found for "+wkt);
+                }
+                missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] });
+                points.add(i, null);
+                // Special Case no i,j found for Coordinate
+            }
+        }
+    }
+
+	public static Coordinate [] toCoordinates(String wkt) {
+		try {
+			LineString ls = (LineString)new WKTReader().read(wkt);
+			return ls.getCoordinates();
+		}
+		catch (ParseException pe) {
+			log.error("cannot read WKT line string", pe);
+		}
+		catch (ClassCastException cce) {
+			log.error("cannot read WKT line string", cce);
+		}
+		return null;
+	}
+
+    /**
+     * Turns a wkt coordinate into a string format using the default locale of
+     * the virtual machine.
+     *
+     * @param wkt A wkt coordinate.
+     * @return A formatted coordinate.
+     */
+    public static String toText(String wkt) {
+        return toText(Locale.getDefault(), wkt);
+    }
+
+    /**
+     * Turns a point into a string format using the default locale of the
+     * virtual machine.
+     *
+     * @param p A point.
+     * @return A point formatted as string.
+     */
+    public static String toText(Point p) {
+        return toText(Locale.getDefault(), toWKT(p.getCoordinate()));
+    }
+
+    /**
+     * Turns a point into a string format using a given locale.
+     *
+     * @param locale A locale.
+     * @param p A point.
+     * @return a point formatted as string.
+     */
+    public static String toText(Locale locale, Point p) {
+        return toText(locale, toWKT(p.getCoordinate()));
+    }
+
+    /**
+     * Turns a wkt coordiante into a formatted text using a given locale.
+     *
+     * @param locale A locale.
+     * @param wkt A wkt coordinate.
+     * @return a formatted coordinate.
+     */
+    public static String toText(Locale locale, String wkt) {
+        String formattedCoordinate = null;
+        try {
+            Point p = (Point)new WKTReader().read(wkt);
+            double lat = p.getY();
+            double lon =p.getX();
+
+            int choice = 0;
+
+            if (lat <0 ) {
+                choice += 1;
+                lat=-lat;
+            }
+
+            if (lon <0 ) {
+                choice += 2;
+                lon=-lon;
+            }
+
+            RessourceFactory factory = RessourceFactory.getInstance();
+            String template = factory.getRessource(
+                locale,
+                COORDINATE_OUT_FORMAT[choice],
+                DEFAULT_COORDINATE_TEMPLATE
+            );
+
+            NumberFormat minFormatter = NumberFormat.getInstance(locale);
+            minFormatter.setMaximumFractionDigits(3);
+            minFormatter.setMinimumFractionDigits(3);
+
+            String minLat = minFormatter.format(60.*(lat-((int)lat)));
+            String minLon = minFormatter.format(60.*(lon-((int)lon)));
+
+            NumberFormat degFormatter = NumberFormat.getInstance(locale);
+            degFormatter.setMinimumIntegerDigits(2);
+
+            String formLat = degFormatter.format((int)lat);
+            String formLon = degFormatter.format((int)lon);
+
+            MessageFormat formatter = new MessageFormat(template);
+
+            Object[] args = {
+                formLat, minLat,
+                formLon, minLon
+            };
+
+            return formatter.format(args);
+
+        }
+        catch (ParseException e) {
+            log.warn(e,e);
+        }
+
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org