ingo@105: package de.intevation.flys.artifacts;
ingo@105: 
sascha@1055: import de.intevation.artifactdatabase.data.StateData;
ingo@105: 
sascha@3556: import de.intevation.artifactdatabase.state.Facet;
sascha@3556: import de.intevation.artifactdatabase.state.FacetActivity;
sascha@3556: 
sascha@3556: import de.intevation.artifacts.Artifact;
sascha@1055: import de.intevation.artifacts.CallContext;
sascha@1055: 
sascha@3233: import de.intevation.artifacts.common.utils.StringUtils;
sascha@3233: 
sascha@3233: import de.intevation.flys.artifacts.geom.Lines;
sascha@3233: 
sascha@1055: import de.intevation.flys.artifacts.model.Calculation1;
sascha@1055: import de.intevation.flys.artifacts.model.Calculation2;
sascha@1055: import de.intevation.flys.artifacts.model.Calculation3;
sascha@1055: import de.intevation.flys.artifacts.model.Calculation4;
sascha@2194: import de.intevation.flys.artifacts.model.Calculation5;
ingo@2215: import de.intevation.flys.artifacts.model.Calculation6;
sascha@1055: import de.intevation.flys.artifacts.model.Calculation;
sascha@1055: import de.intevation.flys.artifacts.model.CalculationResult;
sascha@1055: import de.intevation.flys.artifacts.model.DischargeTables;
felix@1809: import de.intevation.flys.artifacts.model.FacetTypes;
sascha@1055: import de.intevation.flys.artifacts.model.Segment;
felix@3124: import de.intevation.flys.artifacts.model.WQCKms;
sascha@1055: import de.intevation.flys.artifacts.model.WQKms;
sascha@2194: import de.intevation.flys.artifacts.model.WW;
sascha@1055: import de.intevation.flys.artifacts.model.WstValueTable;
sascha@1055: import de.intevation.flys.artifacts.model.WstValueTableFactory;
sascha@2126: 
sascha@1055: import de.intevation.flys.artifacts.states.LocationDistanceSelect;
sascha@1055: 
sascha@3193: import de.intevation.flys.model.DischargeTable;
sascha@2194: import de.intevation.flys.model.FastCrossSectionLine;
ingo@385: import de.intevation.flys.model.Gauge;
ingo@362: import de.intevation.flys.model.River;
ingo@362: 
sascha@1055: import de.intevation.flys.utils.DoubleUtil;
ingo@1095: import de.intevation.flys.utils.FLYSUtils;
sascha@655: 
sascha@1055: import gnu.trove.TDoubleArrayList;
sascha@451: 
sascha@2194: import java.awt.geom.Point2D;
sascha@2194: 
sascha@1055: import java.util.Arrays;
ingo@2228: import java.util.Calendar;
sascha@1055: import java.util.Collections;
ingo@2228: import java.util.GregorianCalendar;
sascha@1055: import java.util.List;
sascha@1055: import java.util.Map;
sascha@1055: 
sascha@1055: import org.apache.log4j.Logger;
sascha@1055: 
felix@2733: 
ingo@105: /**
ingo@105:  * The default WINFO artifact.
ingo@105:  *
ingo@105:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@105:  */
felix@1809: public class WINFOArtifact
felix@1809: extends      FLYSArtifact
felix@1981: implements   FacetTypes, WaterLineArtifact {
ingo@105: 
felix@1029:     /** The logger for this class. */
ingo@105:     private static Logger logger = Logger.getLogger(WINFOArtifact.class);
ingo@105: 
felix@1029:     /** 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: 
sascha@1055:     /** The default number of steps between the start end end of a selected Q
felix@1115:      * range. */
sascha@1055:     public static final int DEFAULT_Q_STEPS = 30;
sascha@1055: 
felix@1115:     /** The default step width between the start end end kilometer. */
sascha@1055:     public static final double DEFAULT_KM_STEPS = 0.1;
sascha@1055: 
sascha@3556:     private static final String [] INACTIVES = new String[] {
sascha@3556:         LONGITUDINAL_Q,
sascha@3556:         DURATION_Q
sascha@3556:     };
sascha@3556: 
sascha@3556:     static {
sascha@3556:         // TODO: Move to configuration.
sascha@3556:         FacetActivity.Registry.getInstance().register(
sascha@3556:             ARTIFACT_NAME,
sascha@3556:             new FacetActivity() {
sascha@3556:                 @Override
sascha@3558:                 public Boolean isInitialActive(
sascha@3556:                     Artifact artifact,
sascha@3556:                     Facet    facet,
sascha@3556:                     String   outputName
sascha@3556:                 ) {
sascha@3556:                     String fname = facet.getName();
sascha@3556:                     if ((fname.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
sascha@3556:                     ||   fname.equals(COMPUTED_DISCHARGE_MAINVALUES_W))
sascha@3556:                     && outputName.equals("computed_discharge_curve")) {
sascha@3558:                         return Boolean.FALSE;
sascha@3556:                     }
sascha@3556:                     return !StringUtils.contains(fname, INACTIVES);
sascha@3556: 
sascha@3556:                 }
sascha@3556:             });
sascha@3556:     }
ingo@121: 
ingo@105:     /**
ingo@105:      * The default constructor.
ingo@105:      */
ingo@105:     public WINFOArtifact() {
ingo@105:     }
ingo@105: 
ingo@105: 
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:      */
sascha@3193:     @Override
ingo@121:     public String getName() {
ingo@121:         return ARTIFACT_NAME;
ingo@121:     }
ingo@124: 
sascha@2624:     protected static boolean reportGeneratedWs(
sascha@2624:         Calculation report,
sascha@2624:         double []   ws
sascha@2624:     ) {
sascha@2624:         if (ws == null || ws.length < 2) {
sascha@2624:             return false;
sascha@2624:         }
sascha@2624: 
sascha@2624:         double  lastW = ws[0];
sascha@2624:         boolean alreadyReported = false;
sascha@2624: 
sascha@2624:         for (int i = 1; i < ws.length; ++i) {
sascha@2624:             if (Math.abs(lastW - ws[i]) < 1e-5) {
sascha@2624:                 if (!alreadyReported) {
sascha@2624:                     alreadyReported = true;
sascha@2624:                     report.addProblem("more.than.one.q.for.w", ws[i]);
sascha@2624:                 }
sascha@2624:             }
sascha@2624:             else {
sascha@2624:                 alreadyReported = false;
sascha@2624:             }
sascha@2624:             lastW = ws[i];
sascha@2624:         }
sascha@2624: 
sascha@2624:         return true;
sascha@2624:     }
sascha@2624: 
ingo@362: 
ingo@362:     //
ingo@362:     // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES
ingo@362:     //
ingo@362: 
ingo@687:     /**
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:      */
sascha@709:     public CalculationResult getWaterlevelData()
ingo@362:     {
ingo@362:         logger.debug("WINFOArtifact.getWaterlevelData");
ingo@362: 
sascha@3449:         if (getDataAsString("calculation_mode")
sascha@3449:             .equals("calc.discharge.longitudinal.section")
sascha@3449:         ) {
felix@2765:             return getDischargeLongitudinalSectionData();
felix@2765:         }
felix@2765: 
felix@1102:         River river = FLYSUtils.getRiver(this);
ingo@362:         if (river == null) {
sascha@2166:             return error(new WQKms[0], "no.river.selected");
ingo@362:         }
ingo@362: 
ingo@362:         double[] kms = getKms();
ingo@362:         if (kms == null) {
sascha@2166:             return error(new WQKms[0], "no.kms.selected");
ingo@362:         }
ingo@362: 
ingo@447:         double[] qs   = getQs();
ingo@447:         double[] ws   = null;
ingo@447:         boolean  qSel = true;
ingo@447: 
sascha@2624:         Calculation report = new Calculation();
sascha@2624: 
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();
sascha@2165:             double [][] qws = getQsForWs(ws);
sascha@2415:             if (qws == null || qws.length == 0) {
sascha@2166:                 return error(new WQKms[0], "converting.ws.to.qs.failed");
sascha@735:             }
sascha@2165:             qs = qws[0];
sascha@2165: 
sascha@3076:             if (reportGeneratedWs(report, qws[1])) {
sascha@2165:                 ws = qws[1];
sascha@2165:             }
ingo@362:         }
ingo@362: 
sascha@443:         WstValueTable wst = WstValueTableFactory.getTable(river);
ingo@362:         if (wst == null) {
sascha@2166:             return error(new WQKms[0], "no.wst.for.selected.river");
ingo@362:         }
ingo@362: 
sascha@708: 
ingo@1095:         double [] range = FLYSUtils.getKmRange(this);
sascha@738:         if (range == null) {
sascha@2166:             return error(new WQKms[0], "no.range.found");
sascha@738:         }
sascha@736: 
sascha@738:         double refKm;
sascha@738: 
sascha@2164:         if (isFreeQ() || isFreeW()) {
sascha@738:             refKm = range[0];
sascha@738:             logger.debug("'free' calculation (km " + refKm + ")");
sascha@738:         }
sascha@738:         else {
sascha@736:             Gauge gauge = river.determineGaugeByPosition(range[0]);
sascha@708:             if (gauge == null) {
sascha@736:                 return error(
sascha@2166:                     new WQKms[0], "no.gauge.found.for.km");
sascha@708:             }
sascha@736: 
sascha@736:             refKm = gauge.getStation().doubleValue();
sascha@736: 
sascha@736:             logger.debug(
sascha@736:                 "reference gauge: " + gauge.getName() + " (km " + refKm + ")");
sascha@708:         }
sascha@708: 
sascha@2624:         return computeWaterlevelData(kms, qs, ws, wst, refKm, report);
ingo@447:     }
ingo@447: 
felix@1115: 
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.
felix@3272:      * @param qs 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:      */
sascha@709:     public static CalculationResult computeWaterlevelData(
ingo@686:         double []     kms,
ingo@686:         double []     qs,
ingo@686:         double []     ws,
sascha@635:         WstValueTable wst,
sascha@2624:         double        refKm,
sascha@2624:         Calculation   report
sascha@635:     ) {
ingo@362:         logger.info("WINFOArtifact.computeWaterlevelData");
ingo@362: 
sascha@738:         Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm);
sascha@636: 
sascha@2624:         if (report != null) {
sascha@2624:             calc1.addProblems(report);
sascha@2624:         }
sascha@2624: 
sascha@709:         return calc1.calculate(wst);
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:      */
sascha@709:     public CalculationResult getDurationCurveData() {
ingo@385:         logger.debug("WINFOArtifact.getDurationCurveData");
ingo@385: 
felix@1102:         River r = FLYSUtils.getRiver(this);
ingo@385: 
ingo@385:         if (r == null) {
sascha@2166:             return error(null, "no.river.selected");
ingo@385:         }
ingo@385: 
ingo@385:         Gauge g = getGauge();
ingo@385: 
ingo@385:         if (g == null) {
sascha@2166:            return error(null, "no.gauge.selected");
ingo@385:         }
ingo@385: 
ingo@1095:         double[] locations = FLYSUtils.getLocations(this);
ingo@385: 
ingo@385:         if (locations == null) {
sascha@2166:             return error(null, "no.locations.selected");
ingo@385:         }
ingo@385: 
sascha@443:         WstValueTable wst = WstValueTableFactory.getTable(r);
ingo@385:         if (wst == null) {
sascha@2166:             return error(null, "no.wst.for.river");
ingo@385:         }
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:      */
sascha@709:     public static CalculationResult computeDurationCurveData(
felix@1974:         Gauge         gauge,
felix@1974:         WstValueTable wst,
felix@1974:         double        location)
ingo@385:     {
ingo@385:         logger.info("WINFOArtifact.computeDurationCurveData");
ingo@385: 
sascha@3647:         Object[] obj = gauge.fetchDurationCurveData();
ingo@385: 
ingo@385:         int[]    days = (int[]) obj[0];
ingo@385:         double[] qs   = (double[]) obj[1];
ingo@385: 
ingo@686:         Calculation3 calculation = new Calculation3(location, days, qs);
ingo@385: 
ingo@686:         return calculation.calculate(wst);
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:      */
sascha@709:     public CalculationResult getComputedDischargeCurveData()
ingo@393:     throws NullPointerException
ingo@393:     {
ingo@393:         logger.debug("WINFOArtifact.getComputedDischargeCurveData");
ingo@393: 
felix@1102:         River r = FLYSUtils.getRiver(this);
ingo@393: 
ingo@393:         if (r == null) {
sascha@2166:             return error(new WQKms[0], "no.river.selected");
ingo@393:         }
ingo@393: 
ingo@1095:         double[] locations = FLYSUtils.getLocations(this);
ingo@393: 
ingo@393:         if (locations == null) {
sascha@2166:             return error(new WQKms[0], "no.locations.selected");
ingo@393:         }
ingo@393: 
sascha@443:         WstValueTable wst = WstValueTableFactory.getTable(r);
ingo@393:         if (wst == null) {
sascha@2166:             return error(new WQKms[0], "no.wst.for.river");
ingo@393:         }
ingo@393: 
sascha@709:         return computeDischargeCurveData(wst, locations[0]);
ingo@456:     }
ingo@456: 
ingo@456: 
ingo@456:     /**
ingo@393:      * Computes the data used to create computed discharge curves.
ingo@393:      *
felix@3272:      * @param wst The WstValueTable that is used for the interpolation (river-
felix@3272:      *            bound).
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:      */
sascha@709:     public static CalculationResult computeDischargeCurveData(
ingo@393:         WstValueTable wst,
ingo@393:         double location)
ingo@393:     {
ingo@393:         logger.info("WINFOArtifact.computeDischargeCurveData");
ingo@393: 
ingo@686:         Calculation2 calculation = new Calculation2(location);
ingo@393: 
sascha@709:         return calculation.calculate(wst);
sascha@709:     }
ingo@393: 
felix@3272: 
felix@3272:     /** Create CalculationResult with data and message. */
sascha@709:     protected static final CalculationResult error(Object data, String msg) {
sascha@709:         return new CalculationResult(data, new Calculation(msg));
ingo@393:     }
ingo@402: 
felix@1148: 
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@709:     public CalculationResult getDischargeLongitudinalSectionData() {
sascha@451: 
ingo@402:         logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData");
ingo@402: 
felix@1102:         River river = FLYSUtils.getRiver(this);
ingo@402:         if (river == null) {
sascha@709:             logger.debug("No river selected.");
sascha@2166:             return error(new WQKms[0], "no.river.selected");
ingo@402:         }
ingo@402: 
sascha@655:         WstValueTable table = WstValueTableFactory.getTable(river);
sascha@655:         if (table == null) {
sascha@709:             logger.debug("No wst found for selected river.");
sascha@2166:             return error(new WQKms[0], "no.wst.for.river");
ingo@402:         }
ingo@402: 
sascha@655:         List<Segment> segments = getSegments();
sascha@637: 
sascha@655:         if (segments == null) {
sascha@709:             logger.debug("Cannot create segments.");
sascha@2166:             return error(new WQKms[0], "cannot.create.segments");
sascha@451:         }
ingo@450: 
sascha@655:         double [] range = getFromToStep();
sascha@451: 
sascha@655:         if (range == null) {
sascha@709:             logger.debug("Cannot figure out range.");
sascha@2166:             return error(new WQKms[0], "no.range.found");
sascha@451:         }
sascha@451: 
sascha@655:         Calculation4 calc4 = new Calculation4(segments, river, isQ());
sascha@455: 
sascha@709:         return calc4.calculate(table, range[0], range[1], range[2]);
ingo@450:     }
sascha@1055: 
felix@2229: 
sascha@2194:     /**
sascha@2194:      * Returns the data that is computed by a reference curve computation.
sascha@2194:      *
sascha@2194:      * @return the data computed by a reference curve computation.
sascha@2194:      */
sascha@2326:     public CalculationResult getReferenceCurveData(CallContext context) {
sascha@2194: 
sascha@2194:         Double startKm = getReferenceStartKm();
sascha@2194: 
sascha@2194:         if (startKm == null) {
sascha@2194:             return error(new WW[0], "no.reference.start.km");
sascha@2194:         }
sascha@2194: 
sascha@2194:         double [] endKms = getReferenceEndKms();
sascha@2194: 
sascha@2194:         if (endKms == null || endKms.length == 0) {
sascha@2194:             return error(new WW[0], "no.reference.end.kms");
sascha@2194:         }
sascha@2194: 
sascha@2194:         Calculation5 calc5 = new Calculation5(startKm, endKms);
sascha@2194: 
sascha@2194:         River r = FLYSUtils.getRiver(this);
sascha@2194:         if (r == null) {
sascha@2194:             return error(new WW[0], "no.river.found");
sascha@2194:         }
sascha@2194: 
sascha@2194:         WstValueTable wst = WstValueTableFactory.getTable(r);
sascha@2194:         if (wst == null) {
sascha@2194:             return error(new WW[0], "no.wst.for.river");
sascha@2194:         }
sascha@2194: 
felix@2257:         Map<Double, Double> kms2gaugeDatums = r.queryGaugeDatumsKMs();
sascha@2256: 
sascha@2326:         return calc5.calculate(wst, kms2gaugeDatums, context);
sascha@2194:     }
sascha@2194: 
felix@2229: 
felix@2251:     /** Get reference (start) km. */
felix@2754:     public Double getReferenceStartKm() {
felix@2308:         StateData sd = getData("reference_startpoint");
sascha@2194: 
sascha@2194:         if (sd == null) {
sascha@2194:             logger.warn("no reference start given.");
sascha@2194:             return null;
sascha@2194:         }
sascha@2194: 
felix@2251:         logger.debug("Reference start km given: " + sd.getValue());
felix@2251: 
felix@2251:         String input = (String) sd.getValue();
sascha@2194: 
sascha@2726:         if (input == null || (input = input.trim()).length() == 0) {
sascha@2194:             logger.warn("reference start string is empty.");
sascha@2194:             return null;
sascha@2194:         }
sascha@2194: 
sascha@2194:         try {
sascha@2194:             return Double.valueOf(input);
sascha@2194:         }
sascha@2194:         catch (NumberFormatException nfe) {
sascha@2194:             logger.warn("reference start string is not numeric.");
sascha@2194:         }
sascha@2194: 
sascha@2194:         return null;
sascha@2194:     }
sascha@2194: 
felix@2251: 
felix@2754:     /**
felix@2754:      * Get end kms for reference curve (null if none).
felix@2754:      */
felix@2754:     public double [] getReferenceEndKms() {
felix@2308:         StateData sd = getData("reference_endpoint");
sascha@2194: 
sascha@2194:         if (sd == null) {
sascha@2194:             logger.warn("no reference end given.");
sascha@2194:             return null;
sascha@2194:         }
felix@2251:         else {
felix@2251:             logger.debug("Reference end km : " + sd.getValue());
felix@2251:         }
sascha@2194: 
felix@2251:         String input = (String) sd.getValue();
sascha@2194: 
sascha@2415:         if (input == null || (input = input.trim()).length() == 0) {
sascha@2194:             logger.warn("reference end string is empty.");
sascha@2194:             return null;
sascha@2194:         }
sascha@2194: 
sascha@2194:         TDoubleArrayList endKms = new TDoubleArrayList();
sascha@2194: 
sascha@2194:         for (String part: input.split("\\s+")) {
sascha@2194:             try {
sascha@2309:                 double km = Double.parseDouble(part);
sascha@2309:                 if (!endKms.contains(km)) {
sascha@2309:                     endKms.add(km);
sascha@2309:                 }
sascha@2194:             }
sascha@2194:             catch (NumberFormatException nfe) {
sascha@2194:                 logger.warn("reference end string is not numeric.");
sascha@2194:             }
sascha@2194:         }
sascha@2194: 
sascha@2194:         return endKms.toNativeArray();
sascha@2194:     }
felix@1137: 
ingo@2215: 
ingo@2215:     public CalculationResult getHistoricalDischargeData() {
ingo@2219:         Gauge  gauge        = FLYSUtils.getReferenceGauge(this);
ingo@2219:         String rawTimerange = getDataAsString("year_range");
ingo@2219:         String rawValues    = getDataAsString("historical_values");
ingo@2219:         int    mode         = getDataAsInteger("historical_mode");
ingo@2219: 
raimund@2600:         long[]   timerange = FLYSUtils.longArrayFromString(rawTimerange);
ingo@2219:         double[] values    = FLYSUtils.doubleArrayFromString(rawValues);
ingo@2219: 
raimund@2600:         Calendar start = new GregorianCalendar();
raimund@2600:         start.setTimeInMillis(timerange[0]);
raimund@2600:         Calendar end   = new GregorianCalendar();
raimund@2600:         end.setTimeInMillis(timerange[1]);
ingo@2228: 
ingo@2228:         Calculation6 calc = new Calculation6(
ingo@2228:             mode,
ingo@2228:             new long[] { start.getTimeInMillis(), end.getTimeInMillis() },
ingo@2228:             values);
ingo@2228: 
ingo@2228:         return calc.calculate(gauge);
ingo@2215:     }
ingo@2215: 
ingo@2215: 
sascha@1055:     public List<Segment> getSegments() {
sascha@1055:         StateData wqValues = getData("wq_values");
sascha@1055:         if (wqValues == null) {
sascha@1055:             logger.warn("no wq_values given");
sascha@3876:             return Collections.<Segment>emptyList();
sascha@1055:         }
felix@3444:         String input = (String) wqValues.getValue();
sascha@1055:         if (input == null || (input = input.trim()).length() == 0) {
sascha@1055:             logger.warn("wq_values are empty");
sascha@3876:             return Collections.<Segment>emptyList();
sascha@1055:         }
sascha@1055:         return Segment.parseSegments(input);
sascha@1055:     }
sascha@1055: 
felix@1122: 
felix@1122:     /**
felix@3124:      * Get corrected waterline against surface/profile.
felix@3124:      */
felix@3124:     public Lines.LineData waterLineC(int idx, FastCrossSectionLine csl) {
felix@3124:         List<Point2D> points = csl.getPoints();
felix@3124: 
felix@3124:         WQKms[] wqckms = (WQKms[])
felix@3124:             getDischargeLongitudinalSectionData().getData();
felix@3124: 
felix@3124:         // Find index of km.
felix@3124:         double wishKM = csl.getKm();
felix@3124: 
felix@3124:         // Find W/C at km, linear naive approach.
felix@3124:         WQCKms triple = (WQCKms) wqckms[idx-1];
felix@3124: 
felix@3124:         int old_idx = 0;
felix@3124: 
felix@3124:         if (triple.size() == 0) {
felix@3124:             logger.warn("Calculation of c/waterline is empty.");
felix@3124:             return Lines.createWaterLines(points, 0.0f);
felix@3124:         }
felix@3124: 
felix@3124:         // Linear seach in WQKms for closest km.
felix@3124:         double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
felix@3124:         double last_c = triple.getC(0);
felix@3124: 
felix@3124:         for (int i = 0, T = triple.size(); i < T; i++) {
felix@3124:             double diff = Math.abs(wishKM - triple.getKm(i));
felix@3124:             if (diff > old_dist_wish) {
felix@3124:                 break;
felix@3124:             }
felix@3124:             last_c = triple.getC(i);
felix@3124:             old_dist_wish = diff;
felix@3124:         }
felix@3124: 
felix@3124:         return Lines.createWaterLines(points, last_c);
felix@3124:     }
felix@3124: 
felix@3124: 
felix@3124:     /**
felix@1122:      * Get points of line describing the surface of water at cross section.
felix@1122:      *
felix@3123:      * @param idx Index for getWaterlevelData.
felix@3123:      * @param csl The profile/surface to fill with water.
felix@3272:      * @param nextIgnored Ignored in this implementation of WaterLineArtifact.
felix@3272:      * @param prevIgnored Ignored in this implementation of WaterLineArtifact.
felix@3123:      *
felix@1122:      * @return an array holding coordinates of points of surface of water (
felix@1122:      *         in the form {{x1, x2} {y1, y2}} ).
felix@1122:      */
felix@2652:     @Override
felix@3272:     public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl,
felix@3272:         double nextIgnored, double prevIgnored) {
felix@1975:         logger.debug("getWaterLines(" + idx + ")");
felix@1975: 
sascha@2120:         List<Point2D> points = csl.getPoints();
felix@1802: 
felix@1139:         // Need W at km
felix@1139:         WQKms [] wqkms = (WQKms[]) getWaterlevelData().getData();
felix@1139:         if (wqkms.length == 0) {
felix@1139:             logger.error("No WQKms found.");
sascha@1651:             return Lines.createWaterLines(points, 0.0f);
felix@1139:         }
felix@1139: 
felix@3123:         if (wqkms.length <= idx) {
felix@1802:             logger.error("getWaterLines() requested index ("
felix@1802:                          + idx + " not found.");
felix@3124:             return waterLineC(idx, csl);
sascha@1651:         }
felix@1802: 
felix@3124:         // Find index of km.
felix@3124:         double wishKM = csl.getKm();
felix@3124: 
sascha@1651:         // Find W at km, linear naive approach.
felix@1802:         WQKms triple = wqkms[idx];
felix@1802: 
sascha@1651:         if (triple.size() == 0) {
sascha@1651:             logger.warn("Calculation of waterline is empty.");
sascha@1651:             return Lines.createWaterLines(points, 0.0f);
sascha@1651:         }
sascha@1651: 
felix@3892:         // Early abort if we would need to extrapolate.
felix@3892:         int T = triple.size();
felix@3892:         double max_km = triple.getKm(T-1), min_km = triple.getKm(0);
felix@3892:         if (wishKM < min_km || wishKM > max_km) {
felix@3892:             // TODO Does this have to be done in the other WaterlineArtifact
felix@3892:             //      implementations, too?
felix@3892:             logger.warn("Will not extrapolate waterlevels.");
felix@3892:             return Lines.createWaterLines(points, 0.0f);
felix@3892:         }
felix@3892: 
felix@3892:         int old_idx = 0;
felix@3892: 
sascha@1651:         // Linear seach in WQKms for closest km.
sascha@1651:         double old_dist_wish = Math.abs(wishKM - triple.getKm(0));
sascha@1651:         double last_w = triple.getW(0);
sascha@1651: 
felix@3892:         for (int i = 0; i < T; i++) {
sascha@1651:             double diff = Math.abs(wishKM - triple.getKm(i));
sascha@1651:             if (diff > old_dist_wish) {
sascha@1651:                 break;
felix@1139:             }
sascha@1651:             last_w = triple.getW(i);
sascha@1651:             old_dist_wish = diff;
felix@1139:         }
felix@3123: 
sascha@1651:         return Lines.createWaterLines(points, last_w);
felix@1122:     }
felix@1122: 
felix@1122: 
felix@1122:     /**
sascha@1055:      * Returns the Qs for a number of Ws. This method makes use of
sascha@1055:      * DischargeTables.getQForW().
sascha@1055:      *
sascha@1055:      * @param ws An array of W values.
sascha@1055:      *
sascha@1055:      * @return an array of Q values.
sascha@1055:      */
sascha@2165:     public double [][] getQsForWs(double[] ws) {
sascha@1055: 
sascha@2415:         if (ws == null) {
sascha@2415:             logger.error("getQsForWs: ws == null");
sascha@2415:             return null;
sascha@2415:         }
sascha@2415: 
sascha@1055:         boolean debug = logger.isDebugEnabled();
sascha@1055: 
sascha@1055:         if (debug) {
sascha@1055:             logger.debug("FLYSArtifact.getQsForWs");
sascha@1055:         }
sascha@1055: 
felix@1102:         River r = FLYSUtils.getRiver(this);
sascha@1055:         if (r == null) {
sascha@1055:             logger.warn("no river found");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
ingo@1095:         double [] range = FLYSUtils.getKmRange(this);
sascha@1055:         if (range == null) {
sascha@1055:             logger.warn("no ranges found");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@2164:         if (isFreeW()) {
sascha@2415:             logger.debug("Bezugslinienverfahren I: W auf freier Strecke");
sascha@2165:             // The simple case of the "Bezugslinienverfahren"
sascha@2165:             // "W auf freier Strecke".
sascha@2164:             WstValueTable wst = WstValueTableFactory.getTable(r);
sascha@2164:             if (wst == null) {
sascha@2164:                 logger.warn("no wst value table found");
sascha@2164:                 return null;
sascha@2164:             }
sascha@2164:             double km = range[0];
sascha@2164: 
sascha@2165:             TDoubleArrayList outQs = new TDoubleArrayList(ws.length);
sascha@2165:             TDoubleArrayList outWs = new TDoubleArrayList(ws.length);
sascha@2165: 
sascha@2165:             boolean generatedWs = false;
sascha@2164: 
sascha@2164:             for (int i = 0; i < ws.length; ++i) {
sascha@2415:                 double w = ws[i];
sascha@2415:                 if (debug) {
sascha@2415:                     logger.debug("getQsForWs: lookup Q for W: " + w);
sascha@2415:                 }
sascha@2165:                 // There could bemore than one Q per W.
sascha@2415:                 double [] qs = wst.findQsForW(km, w);
sascha@2165:                 for (int j = 0; j < qs.length; ++j) {
sascha@2165:                     outWs.add(ws[i]);
sascha@2165:                     outQs.add(qs[j]);
sascha@2164:                 }
sascha@2165:                 generatedWs |= qs.length != 1;
sascha@2164:             }
sascha@2164: 
sascha@2415:             if (debug) {
sascha@2415:                 logger.debug("getQsForWs: number of Qs: " + outQs.size());
sascha@2415:             }
sascha@2415: 
sascha@3076:             return new double [][] {
sascha@3076:                 outQs.toNativeArray(),
sascha@2165:                 generatedWs ? outWs.toNativeArray() : null };
sascha@2164:         }
sascha@2164: 
sascha@1055:         if (debug) {
sascha@1055:             logger.debug("range: " + Arrays.toString(range));
sascha@1055:         }
sascha@1055: 
sascha@1055:         Gauge g = r.determineGaugeByPosition(range[0]);
sascha@1055:         if (g == null) {
sascha@1055:             logger.warn("no gauge found for km: " + range[0]);
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         if (debug) {
sascha@1055:             logger.debug("convert w->q with gauge '" + g.getName() + "'");
sascha@1055:         }
sascha@1055: 
sascha@2418:         DischargeTable dt = g.fetchMasterDischargeTable();
sascha@1055: 
sascha@2418:         if (dt == null) {
sascha@2418:             logger.warn("No master discharge table found for gauge '"
sascha@2418:                 + g.getName() + "'");
sascha@2418:             return null;
sascha@2418:         }
sascha@2418: 
sascha@2418:         double [][] values = DischargeTables.loadDischargeTableValues(dt, 1);
sascha@1055: 
sascha@2415:         TDoubleArrayList wsOut = new TDoubleArrayList(ws.length);
sascha@2415:         TDoubleArrayList qsOut = new TDoubleArrayList(ws.length);
sascha@2415: 
sascha@2418:         boolean generatedWs = false;
sascha@2418: 
sascha@1055:         for (int i = 0; i < ws.length; i++) {
sascha@2415:             if (Double.isNaN(ws[i])) {
sascha@2415:                 logger.warn("W is NaN: ignored");
sascha@2415:                 continue;
sascha@2415:             }
sascha@2418:             double w = ws[i] / 100d;
sascha@2418:             double [] qs = DischargeTables.getQsForW(values, w);
sascha@2418: 
sascha@2418:             if (qs.length == 0) {
sascha@2418:                 logger.warn("No Qs found for W = " + ws[i]);
sascha@2415:             }
sascha@2418:             else {
sascha@2418:                 for (double q: qs) {
sascha@2418:                     wsOut.add(ws[i]);
sascha@2607:                     qsOut.add(q * 100d);
sascha@2418:                 }
sascha@1055:             }
sascha@2418:             generatedWs |= qs.length != 1;
sascha@1055:         }
sascha@1055: 
sascha@2415:         return new double [][] {
sascha@2415:             qsOut.toNativeArray(),
sascha@2418:             generatedWs ? wsOut.toNativeArray() : null
sascha@2415:         };
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Determines the selected mode of distance/range input.
sascha@1055:      *
sascha@1055:      * @return true, if the range mode is selected otherwise false.
sascha@1055:      */
sascha@1055:     public boolean isRange() {
sascha@1055:         StateData mode = getData("ld_mode");
sascha@1055: 
sascha@1055:         if (mode == null) {
sascha@1055:             logger.warn("No mode location/range chosen. Defaults to range.");
sascha@1055:             return true;
sascha@1055:         }
sascha@1055: 
sascha@1055:         String value = (String) mode.getValue();
sascha@1055: 
sascha@1055:         return value.equals("distance");
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the selected distance based on a given range (from, to).
sascha@1055:      *
sascha@1055:      * @param dFrom The StateData that contains the lower value.
sascha@1055:      * @param dTo The StateData that contains the upper value.
sascha@1055:      *
sascha@1055:      * @return the selected distance.
sascha@1055:      */
sascha@1055:     protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
sascha@1055:         double from = Double.parseDouble((String) dFrom.getValue());
sascha@1055:         double to   = Double.parseDouble((String) dTo.getValue());
sascha@1055: 
sascha@1055:         return new double[] { from, to };
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the selected Kms.
sascha@1055:      *
sascha@1055:      * @param distance An 2dim array with [lower, upper] values.
sascha@1055:      *
sascha@1055:      * @return the selected Kms.
sascha@1055:      */
sascha@1055:     public double[] getKms(double[] distance) {
sascha@1055:         StateData dStep = getData("ld_step");
sascha@1055: 
sascha@1055:         if (dStep == null) {
sascha@1055:             logger.warn("No step width given. Cannot compute Kms.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         double step = Double.parseDouble((String) dStep.getValue());
sascha@1055: 
sascha@1055:         // transform step from 'm' into 'km'
sascha@1055:         step = step / 1000;
sascha@1055: 
sascha@1055:         if (step == 0d) {
sascha@1055:             step = DEFAULT_KM_STEPS;
sascha@1055:         }
sascha@1055: 
sascha@1055:         return DoubleUtil.explode(distance[0], distance[1], step);
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the selected Kms.
sascha@1055:      *
sascha@1055:      * @return the selected kms.
sascha@1055:      */
sascha@1055:     public double[] getKms() {
sascha@1055:         if (isRange()) {
ingo@1095:             double[] distance = FLYSUtils.getKmRange(this);
sascha@1055:             return getKms(distance);
sascha@1055: 
sascha@1055:         }
sascha@1055:         else {
sascha@1055:             return LocationDistanceSelect.getLocations(this);
sascha@1055:         }
sascha@1055:     }
sascha@1055: 
felix@1137: 
sascha@1055:     public double [] getFromToStep() {
sascha@1055:         if (!isRange()) {
sascha@1055:             return null;
sascha@1055:         }
ingo@1095:         double [] fromTo = FLYSUtils.getKmRange(this);
sascha@1055: 
sascha@1055:         if (fromTo == null) {
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         StateData dStep = getData("ld_step");
sascha@1055:         if (dStep == null) {
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         double [] result = new double[3];
sascha@1055:         result[0] = fromTo[0];
sascha@1055:         result[1] = fromTo[1];
sascha@1055: 
sascha@1055:         try {
sascha@1055:             String step = (String)dStep.getValue();
sascha@1055:             result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
sascha@1055:         }
sascha@1055:         catch (NumberFormatException nfe) {
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         return result;
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the gauge based on the current distance and river.
sascha@1055:      *
sascha@1055:      * @return the gauge.
sascha@1055:      */
sascha@1055:     public Gauge getGauge() {
ingo@2045:         return FLYSUtils.getGauge(this);
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * This method returns the Q values.
sascha@1055:      *
sascha@1055:      * @return the selected Q values or null, if no Q values are selected.
sascha@1055:      */
sascha@1055:     public double[] getQs() {
raimund@2556:         StateData dMode      = getData("wq_isq");
raimund@2556:         StateData dSelection = getData("wq_isrange");
sascha@1055: 
raimund@2552:         boolean isRange = dSelection != null
raimund@2552:             ? Boolean.valueOf((String)dSelection.getValue())
raimund@2552:             : false;
sascha@1055: 
raimund@2552:         if (isQ()) {
raimund@2552:             if (!isRange) {
sascha@1055:                 return getSingleWQValues();
sascha@1055:             }
sascha@1055:             else {
sascha@1055:                 return getWQTriple();
sascha@1055:             }
sascha@1055:         }
sascha@1055:         else {
sascha@1055:             logger.warn("You try to get Qs, but W has been inserted.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     public boolean isQ() {
raimund@2556:         StateData mode = getData("wq_isq");
raimund@2552:         String value = (mode != null) ? (String) mode.getValue() : null;
raimund@2552:         return value != null ? Boolean.valueOf(value) : false;
raimund@2552:     }
raimund@2552: 
raimund@2552:     public boolean isW() {
raimund@2556:         StateData mode = getData("wq_isq");
raimund@2552:         String value = (mode != null) ? (String) mode.getValue() : null;
raimund@2552:         return value != null ? !Boolean.valueOf(value) : false;
sascha@1055:     }
sascha@1055: 
sascha@2164:     public boolean isFreeW() {
raimund@2552:         if(!isW()) {
raimund@2552:             return false;
raimund@2552:         }
raimund@2556:         StateData mode = getData("wq_isfree");
raimund@2552:         String value =  (mode != null) ? (String) mode.getValue() : null;
raimund@2552: 
raimund@2552:         return value != null ? Boolean.valueOf(value) : false;
sascha@2164:     }
sascha@2164: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns true, if the parameter is set to compute data on a free range.
sascha@1055:      * Otherwise it returns false, which tells the calculation that it is bound
sascha@1055:      * to a gauge.
sascha@1055:      *
sascha@1055:      * @return true, if the calculation should compute on a free range otherwise
sascha@1055:      * false and the calculation is bound to a gauge.
sascha@1055:      */
sascha@1055:     public boolean isFreeQ() {
raimund@2552:         if(!isQ()) {
raimund@2552:             return false;
raimund@2552:         }
raimund@2556:         StateData mode  = getData("wq_isfree");
felix@1115:         String    value = (mode != null) ? (String) mode.getValue() : null;
sascha@1055: 
sascha@1055:         logger.debug("isFreeQ: " + value);
sascha@1055: 
sascha@2164:         return value != null && Boolean.valueOf(value);
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the Q values based on a specified kilometer range.
sascha@1055:      *
sascha@1055:      * @param range A 2dim array with lower and upper kilometer range.
sascha@1055:      *
sascha@1055:      * @return an array of Q values.
sascha@1055:      */
sascha@1055:     public double[] getQs(double[] range) {
raimund@2556:         StateData dMode   = getData("wq_isq");
sascha@1055: 
raimund@2552:         if (isQ()) {
sascha@1055:             return getWQForDist(range);
sascha@1055:         }
sascha@1055: 
sascha@1055:         logger.warn("You try to get Qs, but Ws has been inserted.");
sascha@1055:         return null;
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns the W values based on a specified kilometer range.
sascha@1055:      *
sascha@1055:      * @param range A 2dim array with lower and upper kilometer range.
sascha@1055:      *
sascha@1055:      * @return an array of W values.
sascha@1055:      */
sascha@1055:     public double[] getWs(double[] range) {
raimund@2552:         if (isW()) {
sascha@1055:             return getWQForDist(range);
sascha@1055:         }
sascha@1055: 
sascha@1055:         logger.warn("You try to get Ws, but Qs has been inserted.");
sascha@1055:         return null;
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * This method returns the W values.
sascha@1055:      *
sascha@1055:      * @return the selected W values or null, if no W values are selected.
sascha@1055:      */
sascha@1055:     public double[] getWs() {
sascha@1055:         StateData dSingle = getData("wq_single");
sascha@1055: 
raimund@2552:         if (isW()) {
sascha@1055:             if (dSingle != null) {
sascha@1055:                 return getSingleWQValues();
sascha@1055:             }
sascha@1055:             else {
sascha@1055:                 return getWQTriple();
sascha@1055:             }
sascha@1055:         }
sascha@1055:         else {
raimund@2552:             logger.warn("You try to get Ws, but Q has been inserted.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055:     }
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * This method returns the given W or Q values for a specific range
sascha@1055:      * (inserted in the WQ input panel for discharge longitudinal sections).
sascha@1055:      *
sascha@1055:      * @param dist A 2dim array with lower und upper kilometer values.
sascha@1055:      *
sascha@1055:      * @return an array of W or Q values.
sascha@1055:      */
sascha@1055:     protected double[] getWQForDist(double[] dist) {
sascha@1055:         logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
sascha@1055:         StateData data = getData("wq_values");
sascha@1055: 
sascha@1055:         if (data == null) {
sascha@1055:             logger.warn("Missing wq values!");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         String dataString = (String) data.getValue();
sascha@1055:         String[]   ranges = dataString.split(":");
sascha@1055: 
sascha@1055:         for (String range: ranges) {
sascha@1055:             String[] parts = range.split(";");
sascha@1055: 
sascha@1055:             double lower = Double.parseDouble(parts[0]);
sascha@1055:             double upper = Double.parseDouble(parts[1]);
sascha@1055: 
sascha@1055:             if (lower <= dist[0] && upper >= dist[1]) {
sascha@1055:                 String[] values = parts[2].split(",");
sascha@1055: 
sascha@1055:                 int      num = values.length;
sascha@1055:                 double[] res = new double[num];
sascha@1055: 
sascha@1055:                 for (int i = 0; i < num; i++) {
sascha@1055:                     try {
sascha@1055:                         res[i] = Double.parseDouble(values[i]);
sascha@1055:                     }
sascha@1055:                     catch (NumberFormatException nfe) {
sascha@1055:                         logger.warn(nfe, nfe);
sascha@1055:                     }
sascha@1055:                 }
sascha@1055: 
sascha@1055:                 return res;
sascha@1055:             }
sascha@1055:         }
sascha@1055: 
sascha@1055:         logger.warn("Specified range for WQ not found!");
sascha@1055: 
sascha@1055:         return null;
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * This method returns an array of inserted WQ triples that consist of from,
sascha@1055:      * to and the step width.
sascha@1055:      *
sascha@1055:      * @return an array of from, to and step width.
sascha@1055:      */
sascha@1055:     protected double[] getWQTriple() {
sascha@1055:         StateData dFrom = getData("wq_from");
sascha@1055:         StateData dTo   = getData("wq_to");
sascha@1055: 
sascha@1055:         if (dFrom == null || dTo == null) {
sascha@1055:             logger.warn("Missing start or end value for range.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         double from = Double.parseDouble((String) dFrom.getValue());
sascha@1055:         double to   = Double.parseDouble((String) dTo.getValue());
sascha@1055: 
sascha@1055:         StateData dStep = getData("wq_step");
sascha@1055: 
sascha@1055:         if (dStep == null) {
sascha@1055:             logger.warn("No step width given. Cannot compute Qs.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         double step  = Double.parseDouble((String) dStep.getValue());
sascha@1055: 
sascha@1055:         // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
sascha@1055:         // width. Maybe, we should round the value to a number of digits.
sascha@1055:         if (step == 0d) {
sascha@1055:             double diff = to - from;
sascha@1055:             step = diff / DEFAULT_Q_STEPS;
sascha@1055:         }
sascha@1055: 
sascha@1055:         return DoubleUtil.explode(from, to, step);
sascha@1055:     }
sascha@1055: 
sascha@1055: 
sascha@1055:     /**
sascha@1055:      * Returns an array of inserted WQ double values stored as whitespace
sascha@1055:      * separated list.
sascha@1055:      *
sascha@1055:      * @return an array of W or Q values.
sascha@1055:      */
sascha@1055:     protected double[] getSingleWQValues() {
sascha@1055:         StateData dSingle = getData("wq_single");
sascha@1055: 
sascha@1055:         if (dSingle == null) {
sascha@1055:             logger.warn("Cannot determine single WQ values. No data given.");
sascha@1055:             return null;
sascha@1055:         }
sascha@1055: 
sascha@1055:         String   tmp       = (String) dSingle.getValue();
sascha@1055:         String[] strValues = tmp.split(" ");
sascha@1055: 
sascha@1055:         TDoubleArrayList values = new TDoubleArrayList();
sascha@1055: 
sascha@1055:         for (String strValue: strValues) {
sascha@1055:             try {
sascha@1055:                 values.add(Double.parseDouble(strValue));
sascha@1055:             }
sascha@1055:             catch (NumberFormatException nfe) {
sascha@1055:                 logger.warn(nfe, nfe);
sascha@1055:             }
sascha@1055:         }
sascha@1055: 
sascha@1055:         values.sort();
sascha@1055: 
sascha@1055:         return values.toNativeArray();
sascha@1055:     }
felix@1762: 
raimund@2617:     /**
felix@3272:      * Returns the WstValueTable of current river.
raimund@2617:      */
raimund@2617:     public WstValueTable getWstValueTable() {
raimund@2617:         River r = FLYSUtils.getRiver(this);
raimund@2617:         return WstValueTableFactory.getTable(r);
raimund@2617:     }
ingo@105: }
ingo@105: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :