tim@352: /** tim@352: * tim@352: */ tim@352: package de.intevation.gnv.state.profile.horizontal; tim@352: tim@352: import java.util.ArrayList; tim@352: import java.util.Arrays; tim@352: import java.util.Collection; tim@352: import java.util.List; tim@352: tim@352: import org.apache.log4j.Logger; tim@352: import org.w3c.dom.Node; tim@352: tim@352: import com.vividsolutions.jts.geom.Coordinate; sascha@362: import com.vividsolutions.jts.geom.Point; tim@352: import com.vividsolutions.jts.geom.LineString; sascha@362: tim@352: import com.vividsolutions.jts.io.ParseException; tim@352: import com.vividsolutions.jts.io.WKTReader; tim@352: tim@352: import de.intevation.artifactdatabase.Config; sascha@357: tim@352: import de.intevation.gnv.artifacts.cache.CacheFactory; sascha@357: sascha@362: import de.intevation.gnv.geobackend.base.DefaultResultDescriptor; sascha@362: import de.intevation.gnv.geobackend.base.ResultDescriptor; sascha@362: import de.intevation.gnv.geobackend.base.DefaultResult; tim@352: import de.intevation.gnv.geobackend.base.Result; sascha@362: sascha@362: import de.intevation.gnv.math.Point2d; sascha@362: import de.intevation.gnv.math.Interpolation2D; sascha@362: tim@352: import de.intevation.gnv.geobackend.base.query.QueryExecutor; tim@352: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; tim@352: import de.intevation.gnv.geobackend.base.query.exception.QueryException; tim@352: import de.intevation.gnv.geobackend.sde.datasources.ResultSet; sascha@357: sascha@362: tim@352: import de.intevation.gnv.utils.IndexBuffer; sascha@362: import de.intevation.gnv.utils.DistanceCalculator; tim@352: sascha@357: import de.intevation.gnv.math.LinearFunction; sascha@362: import de.intevation.gnv.math.LinearMetrics; sascha@357: sascha@357: import org.apache.commons.math.optimization.fitting.CurveFitter; sascha@357: sascha@357: import org.apache.commons.math.optimization.general.GaussNewtonOptimizer; sascha@357: import org.apache.commons.math.optimization.OptimizationException; sascha@357: sascha@357: import org.apache.commons.math.FunctionEvaluationException; sascha@357: tim@352: /** tim@352: * @author Tim Englich tim@352: * tim@352: */ tim@352: public class HorizontalProfileMeshCrossOutputState extends tim@352: HorizontalProfileOutputState { tim@352: tim@352: /** tim@352: * tim@352: */ tim@352: private static final long serialVersionUID = 2205958041745637263L; tim@352: tim@352: /** tim@352: * the logger, used to log exceptions and additonaly information tim@352: */ tim@352: private static Logger log = tim@352: Logger.getLogger(HorizontalProfileMeshCrossOutputState.class); tim@352: tim@352: tim@352: private String ijkQueryID = null; tim@352: tim@352: /** tim@352: * Constructor tim@352: */ tim@352: public HorizontalProfileMeshCrossOutputState() { tim@352: super(); tim@352: } tim@352: tim@352: /** tim@352: * @see de.intevation.gnv.state.timeseries.TimeSeriesOutputState#setup(org.w3c.dom.Node) tim@352: */ tim@352: @Override tim@352: public void setup(Node configuration) { tim@352: super.setup(configuration); tim@352: this.ijkQueryID = Config.getStringXPath(configuration,"queryID-ijk"); tim@352: tim@352: } tim@352: tim@352: @Override tim@352: protected Collection getChartResult(String uuid) { tim@352: log.debug("OutputStateBase.getChartResult"); tim@352: Collection result = null; tim@352: if (CacheFactory.getInstance().isInitialized()) { tim@352: String key = uuid + super.getID(); tim@352: log.debug("Hash for Queryelements: " + key); tim@352: net.sf.ehcache.Element value = CacheFactory.getInstance().getCache().get(key); tim@352: if (value != null) { tim@352: result = (Collection) (value.getObjectValue()); tim@352: }else{ tim@352: tim@352: if (this.inputData.containsKey("mesh_linestring")){ tim@352: tim@352: try { sascha@357: // 1. IJK Anfragen für Stuetzpunkte im Netz ausführen. tim@352: LineString ls = (LineString)new WKTReader().read(this.inputData tim@352: .get("mesh_linestring") tim@352: .getValue()); tim@352: Coordinate[] coords = ls.getCoordinates(); tim@352: tim@352: List points = new ArrayList(coords.length); tim@352: tim@352: String meshid = this.inputData.get("meshid").getValue(); tim@352: QueryExecutor queryExecutor = QueryExecutorFactory tim@352: .getInstance() tim@352: .getQueryExecutor(); sascha@357: sascha@357: ArrayList missingPoints = new ArrayList(); sascha@357: sascha@357: String additionWhere = "TRUE"; sascha@357: sascha@357: for (int i = 0; i < coords.length; i++) { sascha@357: tim@352: String wkt = "POINT( "+coords[i].x+" "+coords[i].y+" )"; tim@352: tim@352: result = queryExecutor.executeQuery(this.ijkQueryID, tim@352: new String[]{meshid,wkt}); tim@352: if (!result.isEmpty()){ tim@352: Result resultValue = result.iterator().next(); tim@352: int iPos = resultValue.getInteger(1); tim@352: int jPos = resultValue.getInteger(0); tim@352: log.debug("Found Pos "+iPos+"/"+jPos +" for "+wkt); tim@352: points.add(i, new java.awt.Point(iPos,jPos)); tim@352: }else{ tim@352: log.debug("No i/j Pos found for "+wkt); sascha@357: missingPoints.add(new Object [] { Integer.valueOf(i), coords[i] }); tim@352: points.add(i, null); tim@352: // Special Case no i,j found for Coordinate tim@352: } tim@352: } sascha@357: sascha@357: if (missingPoints.size() == coords.length) { sascha@357: log.debug("cannot create index buffer"); sascha@357: } sascha@357: else { // generate index filter sascha@357: boolean remainsMissingPoints = !missingPoints.isEmpty(); sascha@357: sascha@357: if (remainsMissingPoints) { sascha@357: // try to guess the missing (i, j) sascha@357: CurveFitter iFitter = new CurveFitter(new GaussNewtonOptimizer(true)); sascha@357: CurveFitter jFitter = new CurveFitter(new GaussNewtonOptimizer(true)); sascha@357: sascha@357: for (int i = 0, N = points.size(); i < N; ++i) { sascha@357: java.awt.Point p = (java.awt.Point)points.get(i); sascha@357: if (p != null) { sascha@357: Coordinate coord = coords[i]; sascha@357: iFitter.addObservedPoint(coord.x, p.x); sascha@357: jFitter.addObservedPoint(coord.y, p.y); sascha@357: } sascha@357: } sascha@357: try { sascha@357: // XXX: Assumption: (i, j) are created by componentwise linear function. sascha@357: // This is surely not correct because (x, y) are in a ellipsoid projection. sascha@357: // TODO: use ellipsoid functions and fit with Levenberg Marquardt. sascha@357: double [] iParams = iFitter.fit( sascha@357: LinearFunction.INSTANCE, new double [] { 1d, 1d }); sascha@357: sascha@357: double [] jParams = jFitter.fit( sascha@357: LinearFunction.INSTANCE, new double [] { 1d, 1d }); sascha@357: sascha@357: for (int i = missingPoints.size()-1; i >= 0; --i) { sascha@357: Object [] a = (Object [])missingPoints.get(i); sascha@357: Coordinate coord = (Coordinate)a[1]; sascha@357: int pi = (int)Math.round(iParams[0]*coord.x + iParams[1]); sascha@357: int pj = (int)Math.round(jParams[0]*coord.y + jParams[1]); sascha@357: points.set( sascha@357: ((Integer)a[0]).intValue(), sascha@357: new java.awt.Point(pi, pj)); sascha@357: } sascha@357: sascha@357: remainsMissingPoints = false; // we filled the gaps sascha@357: } sascha@357: catch (FunctionEvaluationException fee) { sascha@357: log.error(fee); sascha@357: } sascha@357: catch (OptimizationException oe) { sascha@357: log.error(oe); sascha@357: } sascha@357: } sascha@357: sascha@357: if (!remainsMissingPoints) { sascha@357: // TODO: Make Tablenames and Columns Configurable sascha@357: IndexBuffer ib = new IndexBuffer( sascha@357: points, sascha@357: "MEDIAN.MESHPOINT.IPOSITION", sascha@357: "MEDIAN.MESHPOINT.JPOSITION" ); sascha@357: additionWhere = ib.toWhereClause(); sascha@357: log.debug("Additional Where Clause = "+additionWhere); sascha@357: // 2. Aus diesen Stuetzpunkten den Resultset generieren. sascha@357: } sascha@357: } // if generate index filter tim@352: tim@352: String[] filterValues = this.generateFilterValuesFromInputData(); tim@352: String[] addedFilterValues = new String[filterValues.length+1]; tim@352: System.arraycopy(filterValues, 0, addedFilterValues, 0, filterValues.length); tim@352: addedFilterValues[filterValues.length] = additionWhere; tim@352: sascha@361: result = process( sascha@362: Arrays.asList(coords), sascha@361: queryExecutor.executeQuery( sascha@361: this.queryID, sascha@361: addedFilterValues)); tim@352: tim@352: } catch (ParseException e) { tim@352: log.error(e,e); tim@352: }catch (QueryException e) { tim@352: log.error(e,e); tim@352: } tim@352: }else{ tim@352: // TODO: definieren was passiert wenn kein linestring vorhanden ist. tim@352: } tim@352: tim@352: if (CacheFactory.getInstance().isInitialized()) { tim@352: CacheFactory.getInstance().getCache().put(new net.sf.ehcache.Element(key, result)); tim@352: } tim@352: tim@352: } tim@352: } tim@352: return result; tim@352: } tim@352: sascha@362: private static final String [] COLUMN_BLACKLIST = { sascha@362: "MEDIAN.MESHPOINT.JPOSITION", sascha@362: "MEDIAN.MESHPOINT.IPOSITION" sascha@362: }; sascha@361: sascha@362: private static final boolean blacklisted(String column) { sascha@362: for (int i = 0; i < COLUMN_BLACKLIST.length; ++i) { sascha@362: if (COLUMN_BLACKLIST.equals(column)) { sascha@362: return true; sascha@362: } sascha@362: } sascha@362: return false; sascha@362: } sascha@361: sascha@362: private static boolean different(Result a, Result b, int [] indices) { sascha@362: for (int i = 0; i < indices.length; ++i) { sascha@362: Object oa = a.getObject(indices[i]); sascha@362: Object ob = b.getObject(indices[i]); sascha@362: if ((oa == null && ob != null) sascha@362: || (oa != null && ob == null) sascha@362: || (oa != null && !oa.equals(ob))) { sascha@362: return true; sascha@362: } sascha@362: } sascha@362: return false; sascha@362: } sascha@362: sascha@362: private static final String [] DIFF_COLUMS = { sascha@362: "GROUP1", sascha@362: "GROUP2", sascha@362: "GROUP3" sascha@362: }; sascha@362: sascha@362: public static final class SectionHandler sascha@362: implements Interpolation2D.Consumer sascha@362: { sascha@362: private ArrayList points; sascha@362: private List path; sascha@362: private Collection output; sascha@362: private Result prototyp; sascha@362: private ResultDescriptor descriptor; sascha@362: sascha@362: public SectionHandler() { sascha@362: } sascha@362: sascha@362: public SectionHandler( sascha@362: List path, sascha@362: Collection output, sascha@362: ResultDescriptor descriptor sascha@362: ) { sascha@362: this.path = path; sascha@362: this.output = output; sascha@362: this.descriptor = descriptor; sascha@362: } sascha@362: sascha@362: public void finish() { sascha@362: if (!points.isEmpty()) { sascha@362: double distance = toKM( sascha@362: DistanceCalculator.calculateDistance(path)); sascha@362: sascha@362: Interpolation2D.interpolate( sascha@362: path, sascha@362: points, sascha@362: 0d, sascha@362: distance, sascha@362: steps(distance), sascha@362: LinearMetrics.INSTANCE, // XXX: This wrong!!! sascha@362: this); sascha@362: sascha@362: points.clear(); sascha@362: } sascha@362: } sascha@362: sascha@362: public void setPrototyp(Result prototyp) { sascha@362: this.prototyp = prototyp; sascha@362: } sascha@362: sascha@362: public void handle(Result result) { sascha@362: Coordinate coordinate = sascha@362: toCoordinate(result.getString("SHAPE")); sascha@362: double value = result.getDouble("YORDINATE"); sascha@362: int iPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION"); sascha@362: int jPos = result.getInteger("MEDIAN.MESHPOINT.JPOSITION"); sascha@362: Point2d p = new Point2d( sascha@362: coordinate.x, sascha@362: coordinate.y, sascha@362: value, sascha@362: iPos, jPos); sascha@362: points.add(p); sascha@362: } sascha@362: sascha@362: public void interpolated(Coordinate coordinate) { sascha@362: DefaultResult result = new DefaultResult(descriptor); sascha@362: ResultDescriptor pd = prototyp.getResultDescriptor(); sascha@362: sascha@362: int pcolums = pd.getColumnCount(); sascha@362: for (int i = 0, j = 0; i < pcolums; ++i) { sascha@362: String colname = pd.getColumnName(i); sascha@362: if (blacklisted(colname)) { sascha@362: continue; sascha@362: } sascha@362: if (colname.equals("SHAPE")) { sascha@362: result.addColumnValue(j, toWKT(coordinate)); sascha@362: } sascha@362: else if (colname.equals("YORDINATE")) { sascha@362: result.addColumnValue(j, Double.valueOf(coordinate.z)); sascha@362: } sascha@362: ++j; sascha@362: } sascha@362: output.add(result); sascha@362: } sascha@362: } // class SectionHandler sascha@362: sascha@362: public static final double NAUTICAL_MILE = 1852.216d; sascha@362: public static final double KILOMETER = 1000d; sascha@362: sascha@362: public static final double toKM(double distance) { sascha@362: return (distance * NAUTICAL_MILE) / KILOMETER; sascha@362: } sascha@362: sascha@362: public static final double INTERPOLATION_STEP_WIDTH = sascha@362: Double.parseDouble(System.getProperty( sascha@362: "interpolation.step.width", "100")); sascha@362: sascha@362: public static int steps(double km) { sascha@362: return (int)Math.ceil( sascha@362: Math.max(1d, (km * KILOMETER)/INTERPOLATION_STEP_WIDTH)); sascha@362: } sascha@362: sascha@362: public static Coordinate toCoordinate(String shape) { sascha@362: try { sascha@362: return ((Point)(new WKTReader().read(shape))).getCoordinate(); sascha@362: } sascha@362: catch (ParseException pe) { sascha@362: log.error(pe); sascha@362: } sascha@362: return null; sascha@362: } sascha@362: sascha@362: public static String toWKT(Coordinate coordinate) { sascha@362: StringBuilder sb = new StringBuilder("POINT("); sascha@362: sb.append(coordinate.x) sascha@362: .append(' ') sascha@362: .append(coordinate.y) sascha@362: .append(')'); sascha@362: return sb.toString(); sascha@362: } sascha@362: sascha@362: protected Collection process( sascha@362: List path, sascha@362: Collection input sascha@362: ) { sascha@362: ArrayList output = new ArrayList(); sascha@362: sascha@362: sascha@362: Result last = null; sascha@362: sascha@362: int [] diffColums = null; sascha@362: sascha@362: SectionHandler sectionHandler = null; sascha@362: sascha@362: for (Result result: input) { sascha@362: sascha@362: if (sectionHandler == null) { sascha@362: sascha@362: ResultDescriptor rd = result.getResultDescriptor(); sascha@362: diffColums = rd.getColumnIndices(DIFF_COLUMS); sascha@362: int columns = rd.getColumnCount(); sascha@362: sascha@362: DefaultResultDescriptor resultDescriptor = sascha@362: new DefaultResultDescriptor(); sascha@362: sascha@362: for (int j = 0; j < columns; ++j) { sascha@362: String columnName = rd.getColumnName(j); sascha@362: if (!blacklisted(columnName)) { sascha@362: resultDescriptor.addColumn( sascha@362: columnName, sascha@362: rd.getColumnClassName(j)); sascha@362: } sascha@362: } sascha@362: sascha@362: sectionHandler = new SectionHandler( sascha@362: path, sascha@362: output, sascha@362: resultDescriptor); sascha@362: sascha@362: sectionHandler.setPrototyp(result); sascha@362: } sascha@362: sascha@362: if (last != null && different(last, result, diffColums)) { sascha@362: sectionHandler.finish(); sascha@362: sectionHandler.setPrototyp(result); sascha@362: } sascha@362: sascha@362: sectionHandler.handle(result); sascha@362: sascha@362: last = result; sascha@362: } sascha@362: sascha@362: if (sectionHandler != null) { sascha@362: sectionHandler.finish(); sascha@362: } sascha@362: sascha@362: return output; sascha@361: } tim@352: }