ingo@105: package de.intevation.flys.artifacts; ingo@105: ingo@109: import java.util.List; sascha@643: import java.util.Set; sascha@643: import java.util.HashSet; ingo@134: import java.util.Vector; sascha@449: import java.util.ArrayList; ingo@109: ingo@105: import org.w3c.dom.Document; ingo@110: import org.w3c.dom.Element; ingo@124: import org.w3c.dom.Node; ingo@105: ingo@105: import org.apache.log4j.Logger; ingo@105: ingo@110: import de.intevation.artifacts.ArtifactNamespaceContext; ingo@105: import de.intevation.artifacts.CallContext; ingo@105: ingo@110: import de.intevation.artifactdatabase.ProtocolUtils; ingo@144: import de.intevation.artifactdatabase.state.Output; ingo@110: import de.intevation.artifactdatabase.state.State; ingo@110: import de.intevation.artifactdatabase.state.StateEngine; ingo@112: import de.intevation.artifactdatabase.transition.TransitionEngine; ingo@110: ingo@109: import de.intevation.artifacts.common.utils.XMLUtils; ingo@109: ingo@385: import de.intevation.flys.model.Gauge; ingo@362: import de.intevation.flys.model.River; ingo@362: ingo@134: import de.intevation.flys.artifacts.states.DefaultState; ingo@109: import de.intevation.flys.artifacts.context.FLYSContext; ingo@402: import de.intevation.flys.artifacts.math.BackJumpCorrector; ingo@385: import de.intevation.flys.artifacts.model.MainValuesFactory; ingo@402: import de.intevation.flys.artifacts.model.WQCKms; ingo@385: import de.intevation.flys.artifacts.model.WQDay; ingo@362: import de.intevation.flys.artifacts.model.WQKms; ingo@362: import de.intevation.flys.artifacts.model.WstValueTable; sascha@451: import de.intevation.flys.artifacts.model.WstValueTable.QPosition; sascha@443: import de.intevation.flys.artifacts.model.WstValueTableFactory; ingo@105: sascha@451: import de.intevation.flys.artifacts.math.LinearRemap; sascha@451: sascha@451: import gnu.trove.TDoubleArrayList; ingo@105: ingo@105: /** ingo@105: * The default WINFO artifact. ingo@105: * ingo@105: * @author Ingo Weinzierl ingo@105: */ ingo@119: public class WINFOArtifact extends FLYSArtifact { ingo@105: ingo@105: /** The logger for this class */ ingo@105: private static Logger logger = Logger.getLogger(WINFOArtifact.class); ingo@105: ingo@105: ingo@121: /** The name of the artifact.*/ ingo@121: public static final String ARTIFACT_NAME = "winfo"; ingo@121: ingo@124: /** XPath */ ingo@124: public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static"; ingo@124: ingo@121: ingo@105: /** ingo@105: * The default constructor. ingo@105: */ ingo@105: public WINFOArtifact() { ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@105: * This method returns a description of this artifact. ingo@105: * ingo@105: * @param data Some data. ingo@128: * @param context The CallContext. ingo@105: * ingo@105: * @return the description of this artifact. ingo@105: */ ingo@105: public Document describe(Document data, CallContext context) { ingo@119: logger.debug("Describe: the current state is: " + getCurrentStateId()); ingo@105: ingo@624: if (logger.isDebugEnabled()) { ingo@624: dumpArtifact(); ingo@624: } ingo@624: ingo@112: FLYSContext flysContext = null; ingo@112: if (context instanceof FLYSContext) { ingo@112: flysContext = (FLYSContext) context; ingo@112: } ingo@112: else { ingo@112: flysContext = (FLYSContext) context.globalContext(); ingo@112: } ingo@110: ingo@112: StateEngine stateEngine = (StateEngine) flysContext.get( ingo@112: FLYSContext.STATE_ENGINE_KEY); ingo@112: ingo@112: TransitionEngine transitionEngine = (TransitionEngine) flysContext.get( ingo@112: FLYSContext.TRANSITION_ENGINE_KEY); ingo@112: ingo@112: List reachable = transitionEngine.getReachableStates( ingo@355: this, getCurrentState(context), stateEngine); ingo@112: ingo@112: Document description = XMLUtils.newDocument(); ingo@110: XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( ingo@110: description, ingo@110: ArtifactNamespaceContext.NAMESPACE_URI, ingo@110: ArtifactNamespaceContext.NAMESPACE_PREFIX); ingo@110: ingo@110: Element root = ProtocolUtils.createRootNode(creator); ingo@115: description.appendChild(root); ingo@115: ingo@119: State current = getCurrentState(context); ingo@119: ingo@110: ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); ingo@119: ProtocolUtils.appendState(creator, root, current); ingo@112: ProtocolUtils.appendReachableStates(creator, root, reachable); ingo@110: ingo@303: Element name = ProtocolUtils.createArtNode( ingo@303: creator, "name", ingo@303: new String[] { "value" }, ingo@303: new String[] { getName() }); ingo@303: ingo@127: Element ui = ProtocolUtils.createArtNode( ingo@127: creator, "ui", null, null); ingo@110: ingo@127: Element staticUI = ProtocolUtils.createArtNode( ingo@127: creator, "static", null, null); ingo@127: ingo@144: Element outs = ProtocolUtils.createArtNode( ingo@144: creator, "outputmodes", null, null); ingo@144: appendOutputModes(description, outs, context, identifier()); ingo@144: ingo@134: appendStaticUI(description, staticUI, context, identifier()); ingo@128: ingo@127: Element dynamic = current.describe( ingo@306: this, ingo@124: description, ingo@127: root, ingo@127: context, ingo@127: identifier()); ingo@127: ingo@144: if (dynamic != null) { ingo@144: ui.appendChild(dynamic); ingo@144: } ingo@144: ingo@128: ui.appendChild(staticUI); ingo@144: ingo@303: root.appendChild(name); ingo@127: root.appendChild(ui); ingo@144: root.appendChild(outs); ingo@124: ingo@110: return description; ingo@105: } ingo@121: ingo@121: ingo@121: /** ingo@121: * Returns the name of the concrete artifact. ingo@121: * ingo@121: * @return the name of the concrete artifact. ingo@121: */ ingo@121: public String getName() { ingo@121: return ARTIFACT_NAME; ingo@121: } ingo@124: ingo@124: ingo@144: protected void appendOutputModes( ingo@144: Document doc, ingo@144: Element outs, ingo@144: CallContext context, ingo@144: String uuid) ingo@144: { ingo@144: Vector stateIds = getPreviousStateIds(); ingo@144: ingo@144: XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( ingo@144: doc, ingo@144: ArtifactNamespaceContext.NAMESPACE_URI, ingo@144: ArtifactNamespaceContext.NAMESPACE_PREFIX); ingo@144: ingo@144: FLYSContext flysContext = getFlysContext(context); ingo@144: StateEngine engine = (StateEngine) flysContext.get( ingo@144: FLYSContext.STATE_ENGINE_KEY); ingo@144: ingo@144: for (String stateId: stateIds) { ingo@144: logger.debug("Append output modes for state: " + stateId); ingo@144: State state = engine.getState(stateId); ingo@144: ingo@144: List list = state.getOutputs(); ingo@144: if (list == null || list.size() == 0) { ingo@624: logger.debug("-> No output modes for this state."); ingo@144: continue; ingo@144: } ingo@144: ingo@144: ProtocolUtils.appendOutputModes(creator, outs, list); ingo@144: } ingo@144: ingo@355: try { ingo@355: DefaultState cur = (DefaultState) getCurrentState(context); ingo@355: if (cur.validate(this, context)) { ingo@355: List list = cur.getOutputs(); ingo@355: if (list != null && list.size() > 0) { ingo@355: logger.debug( ingo@355: "Append output modes for state: " + cur.getID()); ingo@355: ingo@355: ProtocolUtils.appendOutputModes(creator, outs, list); ingo@355: } ingo@355: } ingo@355: } ingo@355: catch (IllegalArgumentException iae) { ingo@355: // state is not valid, so we do not append its outputs. ingo@355: } ingo@144: } ingo@144: ingo@144: ingo@124: /** ingo@124: * This method appends the static data - that has already been inserted by ingo@124: * the user - to the static node of the DESCRIBE document. ingo@124: * ingo@134: * @param doc The document. ingo@134: * @param ui The root node. ingo@134: * @param context The CallContext. ingo@134: * @param uuid The identifier of the artifact. ingo@124: */ ingo@128: protected void appendStaticUI( ingo@134: Document doc, ingo@134: Node ui, ingo@134: CallContext context, ingo@134: String uuid) ingo@128: { ingo@134: Vector stateIds = getPreviousStateIds(); ingo@124: ingo@134: FLYSContext flysContext = getFlysContext(context); ingo@134: StateEngine engine = (StateEngine) flysContext.get( ingo@134: FLYSContext.STATE_ENGINE_KEY); ingo@124: ingo@134: for (String stateId: stateIds) { ingo@134: logger.debug("Append static data for state: " + stateId); ingo@134: DefaultState state = (DefaultState) engine.getState(stateId); ingo@302: ingo@624: ui.appendChild(state.describeStatic(this, doc, ui, context, uuid)); ingo@134: } ingo@362: } ingo@124: ingo@362: ingo@362: // ingo@362: // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES ingo@362: // ingo@362: ingo@362: /** ingo@362: * Returns the data that is computed by a waterlevel computation. ingo@362: * ingo@362: * @return an array of data triples that consist of W, Q and Kms. ingo@362: */ ingo@362: public WQKms[] getWaterlevelData() ingo@362: throws NullPointerException ingo@362: { ingo@362: logger.debug("WINFOArtifact.getWaterlevelData"); ingo@362: ingo@362: River river = getRiver(); ingo@362: if (river == null) { ingo@362: throw new NullPointerException("No river selected."); ingo@362: } ingo@362: ingo@362: double[] kms = getKms(); ingo@362: if (kms == null) { ingo@362: throw new NullPointerException("No Kms selected."); ingo@362: } ingo@362: ingo@447: double[] qs = getQs(); ingo@447: double[] ws = null; ingo@447: boolean qSel = true; ingo@447: ingo@362: if (qs == null) { ingo@377: logger.debug("Determine Q values based on a set of W values."); ingo@447: qSel = false; ingo@447: ws = getWs(); ingo@447: qs = getQsForWs(ws); ingo@362: } ingo@362: sascha@443: WstValueTable wst = WstValueTableFactory.getTable(river); ingo@362: if (wst == null) { ingo@362: throw new NullPointerException("No Wst found for selected river."); ingo@362: } ingo@362: sascha@643: HashSet failed = new HashSet(); sascha@643: sascha@643: WQKms[] results = computeWaterlevelData( sascha@643: kms, qs, wst, river.getKmUp(), failed); ingo@447: ingo@362: // TODO Introduce a caching mechanism here! ingo@362: sascha@643: setWaterlevelNames( sascha@643: results, qSel ? qs : ws, qSel ? "Q" : "W", failed); ingo@447: ingo@447: return results; ingo@447: } ingo@447: ingo@447: ingo@447: /** ingo@447: * Sets the name for waterlevels where each WQKms in r represents a ingo@447: * column. ingo@447: * ingo@447: * @param r The waterlevel columns. ingo@447: * @param v The input values of the computations. ingo@447: * @param wq The WQ mode - can be one of "W" or "Q". ingo@447: */ sascha@643: public static void setWaterlevelNames( sascha@643: WQKms[] r, sascha@643: double[] v, sascha@643: String wq, sascha@643: Set failed sascha@643: ) { sascha@643: int pos = 0; ingo@447: for (int i = 0; i < v.length; i++) { sascha@643: if (!failed.contains(i)) { sascha@643: r[pos++].setName(wq + "=" + v[i]); sascha@643: } ingo@447: } ingo@362: } ingo@362: ingo@362: ingo@362: /** ingo@362: * Computes the data of a waterlevel computation based on the interpolation ingo@362: * in WstValueTable. ingo@362: * ingo@362: * @param kms The kilometer values. ingo@362: * @param qa The discharge values. ingo@362: * @param wst The WstValueTable used for the interpolation. ingo@362: * ingo@362: * @return an array of data triples that consist of W, Q and Kms. ingo@362: */ ingo@362: public static WQKms[] computeWaterlevelData( ingo@362: double[] kms, ingo@362: double[] qs, sascha@635: WstValueTable wst, sascha@643: boolean up, sascha@643: Set failed sascha@635: ) { ingo@362: logger.info("WINFOArtifact.computeWaterlevelData"); ingo@362: ingo@362: WQKms[] wqkms = new WQKms[qs.length]; sascha@380: sascha@449: ArrayList results = new ArrayList(); ingo@362: sascha@636: int referenceIndex = up ? 0 : kms.length-1; sascha@636: sascha@449: for (int i = 0; i < qs.length; i++) { sascha@449: double [] oqs = new double[kms.length]; sascha@449: double [] ows = new double[kms.length]; sascha@449: WstValueTable.QPosition qPosition = sascha@645: wst.interpolate(qs[i], kms[referenceIndex], kms, ows, oqs); sascha@449: if (qPosition != null) { sascha@449: results.add(new WQKms(kms, oqs, ows)); sascha@449: } sascha@449: else { sascha@449: logger.warn("interpolation failed for q = " + qs[i]); sascha@643: failed.add(i); ingo@362: } ingo@362: } ingo@362: sascha@449: return results.toArray(new WQKms[results.size()]); ingo@124: } ingo@385: ingo@385: ingo@385: /** ingo@385: * Returns the data that is computed by a duration curve computation. ingo@385: * ingo@385: * @return the data computed by a duration curve computation. ingo@385: */ ingo@385: public WQDay getDurationCurveData() ingo@385: throws NullPointerException ingo@385: { ingo@385: logger.debug("WINFOArtifact.getDurationCurveData"); ingo@385: ingo@385: River r = getRiver(); ingo@385: ingo@385: if (r == null) { ingo@385: throw new NullPointerException("Cannot determine river."); ingo@385: } ingo@385: ingo@385: Gauge g = getGauge(); ingo@385: ingo@385: if (g == null) { ingo@385: throw new NullPointerException("Cannot determine gauge."); ingo@385: } ingo@385: ingo@385: double[] locations = getLocations(); ingo@385: ingo@385: if (locations == null) { ingo@385: throw new NullPointerException("Cannot determine location."); ingo@385: } ingo@385: sascha@443: WstValueTable wst = WstValueTableFactory.getTable(r); ingo@385: if (wst == null) { ingo@385: throw new NullPointerException("No Wst found for selected river."); ingo@385: } ingo@385: ingo@385: // TODO Introduce a caching mechanism here! ingo@385: ingo@385: return computeDurationCurveData(g, wst, locations[0]); ingo@385: } ingo@385: ingo@385: ingo@385: /** ingo@385: * Computes the data used to create duration curves. ingo@385: * ingo@385: * @param gauge The selected gauge. ingo@385: * @param location The selected location. ingo@385: * ingo@385: * @return the computed data. ingo@385: */ ingo@385: public static WQDay computeDurationCurveData( ingo@385: Gauge gauge, ingo@385: WstValueTable wst, ingo@385: double location) ingo@385: { ingo@385: logger.info("WINFOArtifact.computeDurationCurveData"); ingo@385: ingo@385: Object[] obj = MainValuesFactory.getDurationCurveData(gauge); ingo@385: ingo@385: int[] days = (int[]) obj[0]; ingo@385: double[] qs = (double[]) obj[1]; ingo@385: ingo@385: double[] interpolatedW = new double[qs.length]; ingo@385: interpolatedW = wst.interpolateW(location, qs, interpolatedW); ingo@385: ingo@385: WQDay wqday = new WQDay(qs.length); ingo@385: ingo@385: for (int i = 0; i < days.length; i++) { ingo@385: wqday.add(days[i], interpolatedW[i], qs[i]); ingo@385: } ingo@385: ingo@385: return wqday; ingo@385: } ingo@393: ingo@393: ingo@393: /** ingo@393: * Returns the data that is computed by a discharge curve computation. ingo@393: * ingo@393: * @return the data computed by a discharge curve computation. ingo@393: */ ingo@393: public WQKms getComputedDischargeCurveData() ingo@393: throws NullPointerException ingo@393: { ingo@393: logger.debug("WINFOArtifact.getComputedDischargeCurveData"); ingo@393: ingo@393: River r = getRiver(); ingo@393: ingo@393: if (r == null) { ingo@393: throw new NullPointerException("Cannot determine river."); ingo@393: } ingo@393: ingo@393: double[] locations = getLocations(); ingo@393: ingo@393: if (locations == null) { ingo@393: throw new NullPointerException("Cannot determine location."); ingo@393: } ingo@393: sascha@443: WstValueTable wst = WstValueTableFactory.getTable(r); ingo@393: if (wst == null) { ingo@393: throw new NullPointerException("No Wst found for selected river."); ingo@393: } ingo@393: ingo@456: WQKms wqkms = computeDischargeCurveData(wst, locations[0]); ingo@456: ingo@393: // TODO Introduce a caching mechanism here! ingo@393: ingo@456: setComputedDischargeCurveNames(wqkms, locations[0]); ingo@456: ingo@456: return wqkms; ingo@456: } ingo@456: ingo@456: ingo@456: /** ingo@456: * Sets the name of the computed discharge curve data. ingo@456: * ingo@456: * @param wqkms The computed WQKms object. ingo@456: * @param l The location used for the computation. ingo@456: */ ingo@456: public static void setComputedDischargeCurveNames(WQKms wqkms, double l) { ingo@456: wqkms.setName(Double.toString(l)); ingo@393: } ingo@393: ingo@393: ingo@393: /** ingo@393: * Computes the data used to create computed discharge curves. ingo@393: * ingo@393: * @param wst The WstValueTable that is used for the interpolation. ingo@393: * @param location The location where the computation should be based on. ingo@393: * ingo@393: * @return an object that contains tuples of W/Q values at the specified ingo@393: * location. ingo@393: */ ingo@393: public static WQKms computeDischargeCurveData( ingo@393: WstValueTable wst, ingo@393: double location) ingo@393: { ingo@393: logger.info("WINFOArtifact.computeDischargeCurveData"); ingo@393: ingo@393: double[][] wqs = wst.interpolateWQ(location); ingo@393: ingo@393: if (wqs == null) { ingo@393: logger.error("Cannot compute discharge curve data."); ingo@393: return null; ingo@393: } ingo@393: ingo@393: double[] ws = wqs[0]; ingo@393: double[] qs = wqs[1]; ingo@393: ingo@393: WQKms wqkms = new WQKms(ws.length); ingo@393: ingo@393: for (int i = 0; i < ws.length; i++) { ingo@393: wqkms.add(ws[i], qs[i], location); ingo@393: } ingo@393: ingo@393: return wqkms; ingo@393: } ingo@402: sascha@642: private static final double [] getBounds(double [][] segments) { sascha@642: boolean down = true; sascha@642: double min = Double.MAX_VALUE; sascha@642: double max = -Double.MAX_VALUE; sascha@642: sascha@642: for (double [] segment: segments) { sascha@642: if (down = segment[0] > segment[1]) { sascha@642: if (segment[1] < min) min = segment[1]; sascha@642: if (segment[0] > max) max = segment[0]; sascha@642: } sascha@642: else { sascha@642: if (segment[0] < min) min = segment[0]; sascha@642: if (segment[1] > max) max = segment[1]; sascha@642: } sascha@642: } sascha@642: sascha@642: return down sascha@642: ? new double [] { max, min } sascha@642: : new double [] { min, max }; sascha@642: sascha@642: } sascha@642: ingo@402: /** ingo@402: * Returns the data computed by the discharge longitudinal section ingo@402: * computation. ingo@402: * ingo@402: * @return an array of WQKms object - one object for each given Q value. ingo@402: */ sascha@451: public WQKms [] getDischargeLongitudinalSectionData() { sascha@451: ingo@402: logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData"); ingo@402: ingo@402: River river = getRiver(); ingo@402: if (river == null) { ingo@402: logger.error("No river selected."); sascha@451: return new WQKms[0]; ingo@402: } ingo@402: sascha@635: boolean kmUp = river.getKmUp(); sascha@635: sascha@443: WstValueTable wst = WstValueTableFactory.getTable(river); ingo@402: if (wst == null) { sascha@451: logger.error("No wst found for selected river."); sascha@451: return new WQKms[0]; ingo@402: } ingo@402: sascha@637: double [][] segments = getRanges(); sascha@637: sascha@637: if (logger.isDebugEnabled()) { sascha@637: logger.debug("segments ----------------- enter"); sascha@637: for (double [] segment: segments) { sascha@637: logger.debug(" " + joinDoubles(segment)); sascha@637: } sascha@637: logger.debug("segments ----------------- leave"); sascha@637: } ingo@402: sascha@451: if (segments.length < 1) { sascha@451: logger.warn("no segments given"); sascha@451: return new WQKms[0]; sascha@451: } ingo@450: sascha@451: if (segments.length == 1) { sascha@451: // fall back to normal "Wasserstand/Wasserspiegellage" calculation sascha@451: double [] qs = toQs(segments[0]); sascha@451: if (qs == null) { sascha@451: logger.warn("no qs given"); sascha@451: return new WQKms[0]; sascha@451: } sascha@451: if (qs.length == 1) { sascha@451: double [] kms = getKms(segments[0]); sascha@643: HashSet failed = new HashSet(); sascha@643: WQKms [] results = computeWaterlevelData sascha@643: (kms, qs, wst, kmUp, failed); sascha@643: setWaterlevelNames( sascha@643: results, qs, isQ() ? "Q" : "W", failed); sascha@643: return results; sascha@451: } sascha@451: } ingo@402: sascha@451: // more than one segment sascha@451: sascha@451: double [] boundKms; sascha@451: sascha@451: if (segments.length == 2) { sascha@642: boundKms = getBounds(segments); sascha@451: } sascha@451: else { sascha@451: TDoubleArrayList bounds = new TDoubleArrayList(); sascha@451: sascha@642: bounds.add(Math.min(segments[0][0], segments[0][1])); sascha@451: sascha@451: for (int i = 1; i < segments.length-1; ++i) { sascha@451: double [] segment = segments[i]; sascha@451: sascha@451: Gauge gauge = river.determineGauge(segment[0], segment[1]); sascha@451: sascha@451: if (gauge == null) { sascha@451: logger.warn("no gauge found between " + sascha@451: segment[0] + " and " + segment[1]); sascha@451: bounds.add(0.5*(segment[0] + segment[1])); sascha@451: } sascha@451: else { sascha@451: bounds.add(gauge.getStation().doubleValue()); sascha@451: } sascha@451: } sascha@451: sascha@642: bounds.add(Math.max( sascha@642: segments[segments.length-1][0], sascha@642: segments[segments.length-1][1])); sascha@642: sascha@451: boundKms = bounds.toNativeArray(); sascha@451: } sascha@451: sascha@451: if (logger.isDebugEnabled()) { sascha@451: logger.debug("bound kms: " + joinDoubles(boundKms)); sascha@451: } sascha@451: sascha@451: double [][] iqs = null; sascha@451: sascha@451: for (int i = 0; i < segments.length; ++i) { sascha@451: double [] iqsi = toQs(segments[i]); sascha@451: if (iqsi == null) { sascha@451: logger.warn("iqsi == null"); sascha@451: return new WQKms[0]; sascha@451: } sascha@451: sascha@451: if (iqs == null) { sascha@451: iqs = new double[iqsi.length][boundKms.length]; sascha@451: } sascha@451: else if (iqs.length != iqsi.length) { sascha@451: logger.warn("iqsi.logger != iqs.length: " sascha@451: + iqsi.length + " " + iqsi.length); sascha@451: return new WQKms[0]; sascha@451: } sascha@451: sascha@451: if (logger.isDebugEnabled()) { sascha@451: logger.debug("segments qs[ " + i + "]: " + joinDoubles(iqsi)); sascha@451: } sascha@451: sascha@451: for (int j = 0; j < iqs.length; ++j) { sascha@451: iqs[j][i] = iqsi[j]; sascha@451: } sascha@451: } sascha@451: sascha@451: if (logger.isDebugEnabled()) { sascha@451: for (int i = 0; i < iqs.length; ++i) { sascha@451: logger.debug("iqs[" + i + "]: " + joinDoubles(iqs[i])); sascha@451: } sascha@451: } sascha@451: sascha@451: double [] boundWs = new double[boundKms.length]; sascha@451: double [] boundQs = new double[boundKms.length]; sascha@451: sascha@452: double [] okms = getKms(new double [] { sascha@452: boundKms[0], boundKms[boundKms.length-1] }); sascha@451: sascha@451: ArrayList results = new ArrayList(); sascha@451: sascha@637: int referenceIndex = kmUp ? 0 : boundKms.length-1; sascha@637: sascha@643: HashSet failed = new HashSet(); sascha@643: sascha@451: for (int i = 0; i < iqs.length; ++i) { sascha@451: double [] iqsi = iqs[i]; sascha@451: sascha@451: QPosition qPosition = wst.interpolate( sascha@635: iqsi[0], sascha@645: boundKms[referenceIndex], sascha@635: boundKms, boundWs, boundQs); sascha@451: sascha@451: if (qPosition == null) { sascha@451: logger.warn("interpolation failed for " + iqsi[i]); sascha@643: failed.add(i); ingo@402: continue; ingo@402: } ingo@402: sascha@451: LinearRemap remap = new LinearRemap(); ingo@450: sascha@451: for (int j = 1; j < boundKms.length; ++j) { sascha@451: remap.add( sascha@451: boundKms[j-1], boundKms[j], sascha@451: boundQs[j-1], iqsi[j-1], sascha@451: boundQs[j], iqsi[j]); ingo@402: } ingo@402: sascha@451: double [] oqs = new double[okms.length]; sascha@451: double [] ows = new double[okms.length]; ingo@450: sascha@451: wst.interpolate(okms, ows, oqs, qPosition, remap); sascha@451: sascha@451: BackJumpCorrector bjc = new BackJumpCorrector(); sascha@451: if (bjc.doCorrection(okms, ows)) { sascha@451: logger.debug("Discharge longitudinal section has backjumps."); sascha@451: results.add(new WQCKms(okms, oqs, ows, bjc.getCorrected())); sascha@451: } sascha@451: else { sascha@451: results.add(new WQKms(okms, oqs, ows)); sascha@642: } ingo@402: } ingo@402: sascha@455: WQKms [] wqkms = results.toArray(new WQKms[results.size()]); sascha@455: sascha@643: setDischargeLongitudinalSectionNames( sascha@643: wqkms, iqs, isQ() ? "Q" : "W", failed); sascha@455: sascha@455: return wqkms; sascha@451: } ingo@402: sascha@451: protected static String joinDoubles(double [] x) { sascha@451: if (x == null) { sascha@452: return ""; sascha@451: } sascha@451: StringBuilder sb = new StringBuilder(); sascha@451: for (int i = 0; i < x.length; ++i) { sascha@451: if (i > 0) sb.append(", "); sascha@451: sb.append(x[i]); sascha@451: } sascha@451: return sb.toString(); sascha@451: } sascha@451: sascha@451: protected double [] toQs(double [] range) { sascha@451: double [] qs = getQs(range); sascha@451: if (qs == null) { sascha@451: logger.debug("Determine Q values based on a set of W values."); sascha@451: double [] ws = getWs(range); sascha@451: qs = getQsForWs(ws); sascha@451: } sascha@451: return qs; ingo@402: } ingo@402: ingo@402: ingo@402: /** ingo@450: * Sets the name for discharge longitudinal section curves where each WQKms ingo@450: * in r represents a column. ingo@450: */ ingo@450: public static void setDischargeLongitudinalSectionNames( sascha@643: WQKms [] wqkms, sascha@643: double [][] iqs, sascha@643: String wq, sascha@643: Set failed sascha@455: ) { ingo@450: logger.debug("WINFOArtifact.setDischargeLongitudinalSectionNames"); ingo@450: sascha@455: // TODO: I18N ingo@450: sascha@643: int pos = 0; sascha@643: sascha@643: for (int j = 0; j < iqs.length; ++j) { sascha@643: if (failed.contains(j)) { sascha@643: continue; sascha@643: } sascha@455: StringBuilder sb = new StringBuilder(wq) sascha@455: .append(" benutzerdefiniert ("); ingo@450: sascha@455: double [] iqsi = iqs[j]; sascha@455: for (int i = 0; i < iqsi.length; i++) { sascha@455: if (i > 0) { sascha@455: sb.append("; "); sascha@455: } sascha@455: sb.append(iqsi[i]); sascha@455: } sascha@455: sb.append(")"); ingo@450: sascha@643: wqkms[pos++].setName(sb.toString()); sascha@455: } ingo@450: } ingo@105: } ingo@105: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :