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;
ingo@420: import com.vividsolutions.jts.geom.LineString;
ingo@420: import com.vividsolutions.jts.io.ParseException;
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: 
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:                 if (log.isDebugEnabled()) {
ingo@420:                     log.debug("+++++++++++++++ differs ++++++++++++++");
ingo@420:                     log.debug("   " + oa + " != " + ob);
ingo@420:                 }
ingo@420:                 return true;
ingo@420:             }
ingo@420:         }
ingo@420:         return false;
ingo@420:     }
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:         }
ingo@420:         return null;
ingo@420:     }
ingo@420: 
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: 
ingo@420: 
ingo@423:     public static String worldCoordinatesToIndex(
sascha@440: 		Coordinate []       coords,
sascha@440:         Collection<Result>  result,
sascha@440: 		String              meshid,
sascha@440:         String              ijkQueryID
sascha@440:     )
sascha@440: 	throws QueryException
ingo@420:     {
ingo@420:         // 1. IJK Anfragen für Stuetzpunkte im Netz ausführen.
ingo@420: 
ingo@420:         List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length);
ingo@420: 
ingo@420:         QueryExecutor queryExecutor = QueryExecutorFactory
ingo@420:                                         .getInstance()
ingo@420:                                         .getQueryExecutor();
ingo@420: 
ingo@420:         ArrayList missingPoints = new ArrayList();
ingo@420: 
ingo@420:         String additionWhere = "FEATUREID=FEATUREID";
ingo@420: 
ingo@420:         for (int i = 0; i < coords.length; i++) {
ingo@420: 
ingo@420:             String wkt = toWKT(coords[i]);
ingo@420: 
ingo@420:             result = queryExecutor.executeQuery(ijkQueryID,
ingo@420:                                                new String[]{meshid,wkt});
ingo@420:             if (!result.isEmpty()){
ingo@420:                 Result resultValue = result.iterator().next();
ingo@420:                 int iPos = resultValue.getInteger(1);
ingo@420:                 int jPos = resultValue.getInteger(0);
ingo@420:                 log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt);
ingo@420:                 points.add(i, new java.awt.Point(iPos,jPos));
ingo@420:             }else{
ingo@420:                 log.debug("No i/j Pos found for "+wkt);
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: 
ingo@420:         if (missingPoints.size() == coords.length) {
ingo@420:             log.debug("cannot create index buffer");
ingo@420:         }
ingo@420:         else { // generate index filter
ingo@420:             boolean remainsMissingPoints = !missingPoints.isEmpty();
ingo@420: 
ingo@420:             if (remainsMissingPoints) {
ingo@420:                 // try to guess the missing (i, j)
ingo@420:                 CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true));
ingo@420:                 CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true));
ingo@420: 
ingo@420:                 for (int i = 0, N = points.size(); i < N; ++i) {
ingo@420:                     java.awt.Point p = (java.awt.Point)points.get(i);
ingo@420:                     if (p != null) {
ingo@420:                         Coordinate coord = coords[i];
ingo@420:                         iFitter.addObservedPoint(coord.x, p.x);
ingo@420:                         jFitter.addObservedPoint(coord.y, p.y);
ingo@420:                     }
ingo@420:                 }
ingo@420:                 try {
ingo@420:                     // XXX: Assumption: (i, j) are created by componentwise linear function.
ingo@420:                     // This is surely not correct because (x, y) are in a ellipsoid projection.
ingo@420:                     // TODO: use ellipsoid functions and fit with Levenberg Marquardt.
ingo@420:                     double [] iParams = iFitter.fit(
ingo@420:                         LinearFunction.INSTANCE, new double [] { 1d, 1d });
ingo@420: 
ingo@420:                     double [] jParams = jFitter.fit(
ingo@420:                         LinearFunction.INSTANCE, new double [] { 1d, 1d });
ingo@420: 
ingo@420:                     for (int i = missingPoints.size()-1; i >= 0; --i) {
ingo@420:                         Object [] a = (Object [])missingPoints.get(i);
ingo@420:                         Coordinate coord = (Coordinate)a[1];
ingo@420:                         int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]);
ingo@420:                         int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]);
ingo@420:                         points.set(
ingo@420:                             ((Integer)a[0]).intValue(),
ingo@420:                             new java.awt.Point(pi, pj));
ingo@420:                     }
ingo@420: 
ingo@420:                     remainsMissingPoints = false; // we filled the gaps
ingo@420:                 }
ingo@420:                 catch (FunctionEvaluationException fee) {
ingo@420:                     log.error(fee);
ingo@420:                 }
ingo@420:                 catch (OptimizationException oe) {
ingo@420:                     log.error(oe);
ingo@420:                 }
ingo@420:             }
ingo@420: 
ingo@420:             if (!remainsMissingPoints) {
ingo@420:                 // TODO: Make Tablenames and Columns Configurable
ingo@420:                 IndexBuffer ib = new IndexBuffer(
ingo@420:                     points, 
ingo@420:                     "MEDIAN.MESHPOINT.IPOSITION", 
ingo@420:                     "MEDIAN.MESHPOINT.JPOSITION" );
ingo@420:                 additionWhere = ib.toWhereClause();
ingo@420:                 log.debug("Additional Where Clause = "+additionWhere);
ingo@420:                 // 2. Aus diesen Stuetzpunkten den Resultset generieren.
ingo@420:             }
ingo@420:         } // if generate index filter
ingo@420: 
ingo@423:         return additionWhere;
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: }