Mercurial > dive4elements > gnv-client
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 :