view gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java @ 478:0e0c64c821dc

Added support to step back to the point for choosing a product. gnv-artifacts/trunk@547 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Fri, 15 Jan 2010 18:21:49 +0000
parents 06887e2e3f7a
children 211cad2fb5ba
line wrap: on
line source
package de.intevation.gnv.utils;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.io.ParseException;

import com.vividsolutions.jts.io.WKTReader;

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.util.ArrayList;
import java.util.Collection;
import java.util.List;

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.commons.math.FunctionEvaluationException;

import org.apache.log4j.Logger;

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 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;
    }

    public static Coordinate toCoordinate(String shape) {
        try {
            return ((Point)(new WKTReader().read(shape))).getCoordinate();
        }
        catch (ParseException pe) {
            log.error(pe);
        }
		catch (ClassCastException cce) {
			log.error("cannot read WKT point", cce);
		}
        return null;
    }

    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;
    }

    public static final double toKM(double distance) {
        return (distance * NAUTICAL_MILE) / KILOMETER;
    }


    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,
         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 = 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;
	}
}

http://dive4elements.wald.intevation.org