ingo@420: package de.intevation.gnv.utils; ingo@420: ingo@420: import com.vividsolutions.jts.geom.Coordinate; ingo@420: import com.vividsolutions.jts.geom.Point; sascha@471: import com.vividsolutions.jts.geom.Polygon; ingo@420: import com.vividsolutions.jts.geom.LineString; ingo@420: import com.vividsolutions.jts.io.ParseException; sascha@471: ingo@420: import com.vividsolutions.jts.io.WKTReader; ingo@420: ingo@420: import de.intevation.gnv.geobackend.base.Result; ingo@420: import de.intevation.gnv.geobackend.base.query.QueryExecutor; ingo@420: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; ingo@420: import de.intevation.gnv.geobackend.base.query.exception.QueryException; sascha@440: ingo@420: import de.intevation.gnv.math.LinearFunction; ingo@420: ingo@420: import java.util.ArrayList; ingo@420: import java.util.Collection; ingo@420: import java.util.List; ingo@420: ingo@420: import org.apache.commons.math.optimization.OptimizationException; ingo@420: import org.apache.commons.math.optimization.fitting.CurveFitter; ingo@420: import org.apache.commons.math.optimization.general.GaussNewtonOptimizer; ingo@420: import org.apache.commons.math.FunctionEvaluationException; ingo@420: ingo@420: import org.apache.log4j.Logger; ingo@420: ingo@420: public abstract class WKTUtils { ingo@420: ingo@420: private static Logger log = Logger.getLogger(WKTUtils.class); ingo@420: ingo@420: public static final double NAUTICAL_MILE = 1852.216d; ingo@420: public static final double KILOMETER = 1000d; ingo@420: sascha@471: public static final String I_NAME = "MEDIAN.MESHPOINT.IPOSITION"; sascha@471: public static final String J_NAME = "MEDIAN.MESHPOINT.JPOSITION"; sascha@471: sascha@471: public static final String TRUE_EXPRESSION = "FEATUREID=FEATUREID"; sascha@471: ingo@423: public static boolean different(Result a, Result b, int [] indices) { ingo@420: for (int i = 0; i < indices.length; ++i) { ingo@420: String oa = a.getString(indices[i]); ingo@420: String ob = b.getString(indices[i]); ingo@420: ingo@420: if (oa == null && ob == null) { ingo@420: continue; ingo@420: } ingo@420: ingo@420: if (oa == null || ob == null) { ingo@420: return true; ingo@420: } ingo@420: ingo@420: if (!oa.equals(ob)) { ingo@420: return true; ingo@420: } ingo@420: } ingo@420: return false; ingo@420: } ingo@420: ingo@420: public static Coordinate toCoordinate(String shape) { ingo@420: try { ingo@420: return ((Point)(new WKTReader().read(shape))).getCoordinate(); ingo@420: } ingo@420: catch (ParseException pe) { ingo@420: log.error(pe); ingo@420: } sascha@471: catch (ClassCastException cce) { sascha@471: log.error("cannot read WKT point", cce); sascha@471: } ingo@420: return null; ingo@420: } ingo@420: sascha@471: public static Polygon toPolygon(String shape) { sascha@471: try { sascha@471: return (Polygon)new WKTReader().read(shape); sascha@471: } sascha@471: catch (ParseException pe) { sascha@471: log.error(pe); sascha@471: } sascha@471: catch (ClassCastException cce) { sascha@471: log.error("cannot read WKT polygon", cce); sascha@471: } sascha@471: return null; sascha@471: } ingo@420: ingo@420: public static final double toKM(double distance) { ingo@420: return (distance * NAUTICAL_MILE) / KILOMETER; ingo@420: } ingo@420: ingo@420: ingo@420: public static String toWKT(Coordinate coordinate) { ingo@420: StringBuilder sb = new StringBuilder("POINT("); ingo@420: sb.append(coordinate.x) ingo@420: .append(' ') ingo@420: .append(coordinate.y) ingo@420: .append(')'); ingo@420: return sb.toString(); ingo@420: } ingo@420: sascha@471: public static final String indexBox( sascha@471: java.awt.Point a, sascha@471: java.awt.Point b, sascha@471: String iName, sascha@471: String jName sascha@471: ) { sascha@471: int minI = Math.min(a.x, b.x) - 1; sascha@471: int maxI = Math.max(a.x, b.x) + 1; sascha@471: int minJ = Math.min(a.y, b.y) - 1; sascha@471: int maxJ = Math.max(a.y, b.y) + 1; sascha@471: StringBuilder sb = new StringBuilder("(") sascha@471: .append(iName).append(" >= ").append(minI) sascha@471: .append(" AND ").append(iName).append(" <= ").append(maxI) sascha@471: .append(" AND ").append(jName).append(" >= ").append(minJ) sascha@471: .append(" AND ").append(jName).append(" <= ").append(maxJ) sascha@471: .append(')'); sascha@471: return sb.toString(); sascha@471: } sascha@471: sascha@471: public static final String diagonalBox(List points) { sascha@471: sascha@471: if (points.get(0) != null && points.get(2) != null) { sascha@471: return indexBox( sascha@471: points.get(0), points.get(2), sascha@471: I_NAME, sascha@471: J_NAME); sascha@471: } sascha@471: sascha@471: if (points.get(1) != null && points.get(3) != null) { sascha@471: return indexBox( sascha@471: points.get(1), points.get(3), sascha@471: I_NAME, sascha@471: J_NAME); sascha@471: } sascha@471: sascha@471: return null; sascha@471: } sascha@471: tim@468: public static String worldEnvelopeCoordinatesToIndex( tim@468: Coordinate [] coords, tim@468: String meshid, tim@468: String ijkQueryID tim@468: ) tim@468: throws QueryException tim@468: { sascha@471: List points = sascha@471: new ArrayList(coords.length); sascha@471: sascha@471: ArrayList missingPoints = sascha@471: new ArrayList(); sascha@471: tim@468: createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints); tim@468: sascha@471: String additionWhere = null; sascha@471: tim@468: if (missingPoints.size() == coords.length) { tim@468: log.debug("cannot create index buffer"); sascha@471: } sascha@471: else { sascha@471: additionWhere = diagonalBox(points); sascha@471: sascha@471: if (additionWhere == null) { sascha@471: // 3 Points are missing or are on one side of the envelope sascha@471: boolean remainsMissingPoints = calculateIJ4MissingPoints( sascha@471: coords, points, missingPoints); sascha@471: sascha@471: if (!remainsMissingPoints) { sascha@471: additionWhere = diagonalBox(points); tim@468: } tim@468: } tim@468: } sascha@471: sascha@471: return additionWhere != null sascha@471: ? additionWhere sascha@471: : TRUE_EXPRESSION; tim@468: } sascha@471: ingo@423: public static String worldCoordinatesToIndex( sascha@440: Coordinate [] coords, sascha@440: Collection result, sascha@440: String meshid, sascha@440: String ijkQueryID sascha@440: ) sascha@440: throws QueryException ingo@420: { ingo@420: List points = new ArrayList(coords.length); ingo@420: tim@468: ArrayList missingPoints = new ArrayList(); ingo@420: tim@468: createIJKPoints(coords, meshid, ijkQueryID, points, missingPoints); tim@468: sascha@471: String additionWhere = TRUE_EXPRESSION; sascha@471: tim@468: if (missingPoints.size() == coords.length) { tim@468: log.debug("cannot create index buffer"); tim@468: } tim@468: else { // generate index filter sascha@471: boolean remainsMissingPoints = calculateIJ4MissingPoints( sascha@471: coords, points, missingPoints); ingo@420: tim@468: if (!remainsMissingPoints) { tim@468: // TODO: Make Tablenames and Columns Configurable tim@468: IndexBuffer ib = new IndexBuffer( tim@468: points, sascha@471: I_NAME, sascha@471: J_NAME ); tim@468: additionWhere = ib.toWhereClause(); tim@468: log.debug("Additional Where Clause = "+additionWhere); tim@468: } tim@468: } // if generate index filter ingo@420: tim@468: return additionWhere; tim@468: } tim@468: tim@468: tim@468: /** tim@468: * @param coords tim@468: * @param points tim@468: * @param missingPoints tim@468: * @return tim@468: */ tim@468: private static boolean calculateIJ4MissingPoints( sascha@471: Coordinate [] coords, sascha@471: List points, sascha@471: ArrayList missingPoints sascha@471: ) { tim@468: boolean remainsMissingPoints = !missingPoints.isEmpty(); tim@468: tim@468: if (remainsMissingPoints) { tim@468: // try to guess the missing (i, j) tim@468: CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true)); tim@468: CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true)); tim@468: tim@468: for (int i = 0, N = points.size(); i < N; ++i) { tim@468: java.awt.Point p = (java.awt.Point)points.get(i); tim@468: if (p != null) { tim@468: Coordinate coord = coords[i]; tim@468: iFitter.addObservedPoint(coord.x, p.x); tim@468: jFitter.addObservedPoint(coord.y, p.y); tim@468: } tim@468: } tim@468: try { tim@468: // XXX: Assumption: (i, j) are created by componentwise linear function. tim@468: // This is surely not correct because (x, y) are in a ellipsoid projection. tim@468: // TODO: use ellipsoid functions and fit with Levenberg Marquardt. tim@468: double [] iParams = iFitter.fit( tim@468: LinearFunction.INSTANCE, new double [] { 1d, 1d }); tim@468: tim@468: double [] jParams = jFitter.fit( tim@468: LinearFunction.INSTANCE, new double [] { 1d, 1d }); tim@468: tim@468: for (int i = missingPoints.size()-1; i >= 0; --i) { tim@468: Object [] a = (Object [])missingPoints.get(i); tim@468: Coordinate coord = (Coordinate)a[1]; tim@468: int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]); tim@468: int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]); tim@468: points.set( tim@468: ((Integer)a[0]).intValue(), tim@468: new java.awt.Point(pi, pj)); tim@468: } tim@468: tim@468: remainsMissingPoints = false; // we filled the gaps tim@468: } tim@468: catch (FunctionEvaluationException fee) { tim@468: log.error(fee); tim@468: } tim@468: catch (OptimizationException oe) { tim@468: log.error(oe); tim@468: } tim@468: } tim@468: return remainsMissingPoints; tim@468: } tim@468: tim@468: tim@468: /** tim@468: * @param coords tim@468: * @param meshid tim@468: * @param ijkQueryID tim@468: * @param points tim@468: * @param missingPoints tim@468: * @throws QueryException tim@468: */ sascha@471: private static void createIJKPoints( sascha@471: Coordinate[] coords, sascha@471: String meshid, sascha@471: String ijkQueryID, sascha@471: List points, sascha@471: ArrayList missingPoints sascha@471: ) sascha@471: throws QueryException sascha@471: { sascha@471: boolean debug = log.isDebugEnabled(); sascha@471: tim@468: QueryExecutor queryExecutor = QueryExecutorFactory tim@468: .getInstance() tim@468: .getQueryExecutor(); tim@468: ingo@420: for (int i = 0; i < coords.length; i++) { ingo@420: ingo@420: String wkt = toWKT(coords[i]); ingo@420: sascha@471: Collection result = queryExecutor.executeQuery( sascha@471: ijkQueryID, sascha@471: new String [] {meshid, wkt}); sascha@471: sascha@471: if (!result.isEmpty()) { ingo@420: Result resultValue = result.iterator().next(); ingo@420: int iPos = resultValue.getInteger(1); ingo@420: int jPos = resultValue.getInteger(0); sascha@471: if (debug) { sascha@471: log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt); sascha@471: } ingo@420: points.add(i, new java.awt.Point(iPos,jPos)); sascha@471: } sascha@471: else { sascha@471: if (debug) { sascha@471: log.debug("No i/j Pos found for "+wkt); sascha@471: } ingo@420: missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] }); ingo@420: points.add(i, null); ingo@420: // Special Case no i,j found for Coordinate ingo@420: } ingo@420: } ingo@420: } sascha@440: sascha@440: public static Coordinate [] toCoordinates(String wkt) { sascha@440: try { sascha@440: LineString ls = (LineString)new WKTReader().read(wkt); sascha@440: return ls.getCoordinates(); sascha@440: } sascha@440: catch (ParseException pe) { sascha@440: log.error("cannot read WKT line string", pe); sascha@440: } sascha@440: catch (ClassCastException cce) { sascha@440: log.error("cannot read WKT line string", cce); sascha@440: } sascha@440: return null; sascha@440: } ingo@420: }