Mercurial > dive4elements > gnv-client
diff gnv-artifacts/src/main/java/de/intevation/gnv/utils/WKTUtils.java @ 420:c6a287398379
Outsourcing of some methods for preparing results for chart creation.
gnv-artifacts/trunk@468 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Ingo Weinzierl <ingo.weinzierl@intevation.de> |
---|---|
date | Mon, 21 Dec 2009 15:57:04 +0000 |
parents | |
children | 2402173a1490 |
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 Mon Dec 21 15:57:04 2009 +0000 @@ -0,0 +1,396 @@ +package de.intevation.gnv.utils; + +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Point; +import com.vividsolutions.jts.geom.LineString; +import com.vividsolutions.jts.io.ParseException; +import com.vividsolutions.jts.io.WKTReader; + +import de.intevation.gnv.geobackend.base.DefaultResult; +import de.intevation.gnv.geobackend.base.DefaultResultDescriptor; +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.geobackend.base.ResultDescriptor; +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.Interpolation2D; +import de.intevation.gnv.math.LinearFunction; +import de.intevation.gnv.math.LinearMetrics; +import de.intevation.gnv.math.Point2d; +import de.intevation.gnv.state.InputData; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +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); + + private static final String [] DIFF_COLUMS = { + "GROUP1", + "GROUP2", + "GROUP3" + }; + + private static final String [] COLUMN_BLACKLIST = { + "MEDIAN.MESHPOINT.JPOSITION", + "MEDIAN.MESHPOINT.IPOSITION" + }; + + public static final double NAUTICAL_MILE = 1852.216d; + public static final double KILOMETER = 1000d; + + public static final double EPSILON = 1e-5d; + public static final int INTERPOLATION_STEPS = + Integer.getInteger("interpolation.steps", 500).intValue(); + + + public static final class SectionHandler + implements Interpolation2D.Consumer + { + private ArrayList<Point2d> points; + private List<Coordinate> path; + private Collection<Result> output; + private Result prototyp; + private ResultDescriptor descriptor; + private boolean lastWasSuccess; + + public SectionHandler() { + } + + public SectionHandler( + List<Coordinate> path, + Collection<Result> output, + ResultDescriptor descriptor + ) { + this.path = path; + this.output = output; + this.descriptor = descriptor; + points = new ArrayList<Point2d>(); + lastWasSuccess = true; + } + + public void finish() { + if (!points.isEmpty()) { + double distance = toKM( + DistanceCalculator.calculateDistance(path)); + + if (distance > EPSILON) { + + Interpolation2D.interpolate( + path, + points, + 0d, + distance, + INTERPOLATION_STEPS, + LinearMetrics.INSTANCE, + this); + } + + points.clear(); + } + lastWasSuccess = true; + } + + public void setPrototyp(Result prototyp) { + this.prototyp = prototyp; + } + + public void handle(Result result) { + Coordinate coordinate = + toCoordinate(result.getString("SHAPE")); + double value = result.getDouble("YORDINATE"); + int iPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION"); + int jPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION"); + Point2d p = new Point2d( + coordinate.x, + coordinate.y, + value, + iPos, jPos); + points.add(p); + } + + public void interpolated(Coordinate coordinate, boolean success) { + DefaultResult result = new DefaultResult(descriptor); + ResultDescriptor pd = prototyp.getResultDescriptor(); + + int pcolums = pd.getColumnCount(); + for (int i = 0, j = 0; i < pcolums; ++i) { + String colname = pd.getColumnName(i); + if (blacklisted(colname)) { + continue; + } + if (colname.equals("SHAPE")) { + result.addColumnValue(j, OutputUtils.toWKT(coordinate)); + } + else if (colname.equals("YORDINATE")) { + if (success) { + result.addColumnValue(j, Double.valueOf(coordinate.z)); + } + else if (lastWasSuccess) { + // only insert null if last was valid. + // This prevents flooding the result set with nulls + // if interpolating over a large gap. + result.addColumnValue(j, null); + } + } + else { + result.addColumnValue(j, prototyp.getObject(i)); + } + ++j; + } + output.add(result); + lastWasSuccess = success; + } + } + + + private static final boolean blacklisted(String column) { + for (int i = 0; i < COLUMN_BLACKLIST.length; ++i) { + if (COLUMN_BLACKLIST.equals(column)) { + return true; + } + } + return false; + } + + private 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)) { + if (log.isDebugEnabled()) { + log.debug("+++++++++++++++ differs ++++++++++++++"); + log.debug(" " + oa + " != " + ob); + } + return true; + } + } + return false; + } + + public static Collection<Result> process( + List<Coordinate> path, + Collection<Result> input + ) { + log.debug("------ number of points before processing: " + input.size()); + ArrayList<Result> output = new ArrayList<Result>(); + + Result last = null; + + int [] diffColums = null; + + SectionHandler sectionHandler = null; + + for (Result result: input) { + + if (sectionHandler == null) { + + ResultDescriptor rd = result.getResultDescriptor(); + diffColums = rd.getColumnIndices(DIFF_COLUMS); + int columns = rd.getColumnCount(); + + DefaultResultDescriptor resultDescriptor = + new DefaultResultDescriptor(); + + for (int j = 0; j < columns; ++j) { + String columnName = rd.getColumnName(j); + if (!blacklisted(columnName)) { + resultDescriptor.addColumn( + columnName, + rd.getColumnClassName(j)); + } + } + + sectionHandler = new SectionHandler( + path, + output, + resultDescriptor); + + sectionHandler.setPrototyp(result); + } + + if (last != null && different(last, result, diffColums)) { + sectionHandler.finish(); + sectionHandler.setPrototyp(result); + } + + sectionHandler.handle(result); + + last = result; + } + + if (sectionHandler != null) { + sectionHandler.finish(); + } + + log.debug("------ number of points after processing: " + output.size()); + + return output; + } + + + public static Coordinate toCoordinate(String shape) { + try { + return ((Point)(new WKTReader().read(shape))).getCoordinate(); + } + catch (ParseException pe) { + log.error(pe); + } + 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 Collection<Result> worldCoordinatesToIndex( + Collection<Result> result, + Map<String, InputData> inputData, + String ijkQueryID, + String queryID, + String[] filterValues + ) throws ParseException, QueryException + { + // 1. IJK Anfragen für Stuetzpunkte im Netz ausführen. + LineString ls = (LineString)new WKTReader().read( + inputData.get("mesh_linestring").getValue()); + + Coordinate[] coords = ls.getCoordinates(); + + List<java.awt.Point> points = new ArrayList<java.awt.Point>(coords.length); + + String meshid = inputData.get("meshid").getValue(); + QueryExecutor queryExecutor = QueryExecutorFactory + .getInstance() + .getQueryExecutor(); + + ArrayList missingPoints = new ArrayList(); + + String additionWhere = "FEATUREID=FEATUREID"; + + for (int i = 0; i < coords.length; i++) { + + String wkt = toWKT(coords[i]); + + 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); + log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt); + points.add(i, new java.awt.Point(iPos,jPos)); + }else{ + 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 + } + } + + if (missingPoints.size() == coords.length) { + log.debug("cannot create index buffer"); + } + else { // generate index filter + 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); + } + } + + if (!remainsMissingPoints) { + // TODO: Make Tablenames and Columns Configurable + IndexBuffer ib = new IndexBuffer( + points, + "MEDIAN.MESHPOINT.IPOSITION", + "MEDIAN.MESHPOINT.JPOSITION" ); + additionWhere = ib.toWhereClause(); + log.debug("Additional Where Clause = "+additionWhere); + // 2. Aus diesen Stuetzpunkten den Resultset generieren. + } + } // if generate index filter + + String[] addedFilterValues = new String[filterValues.length+1]; + System.arraycopy(filterValues, 0, addedFilterValues, 0, filterValues.length); + addedFilterValues[filterValues.length] = additionWhere; + + result = process( + Arrays.asList(coords), + queryExecutor.executeQuery( + queryID, + addedFilterValues)); + + return result; + } +}