Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 3318:dbe2f85bf160
merged flys-artifacts/2.8
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:35 +0200 |
parents | 31168ac9c7e7 |
children | f903f1e5aed6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java Fri Sep 28 12:14:35 2012 +0200 @@ -0,0 +1,1206 @@ +package de.intevation.flys.artifacts; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.flys.artifacts.geom.Lines; + +import de.intevation.flys.artifacts.model.Calculation1; +import de.intevation.flys.artifacts.model.Calculation2; +import de.intevation.flys.artifacts.model.Calculation3; +import de.intevation.flys.artifacts.model.Calculation4; +import de.intevation.flys.artifacts.model.Calculation5; +import de.intevation.flys.artifacts.model.Calculation6; +import de.intevation.flys.artifacts.model.Calculation; +import de.intevation.flys.artifacts.model.CalculationResult; +import de.intevation.flys.artifacts.model.DischargeTables; +import de.intevation.flys.artifacts.model.FacetTypes; +import de.intevation.flys.artifacts.model.MainValuesFactory; +import de.intevation.flys.artifacts.model.Segment; +import de.intevation.flys.artifacts.model.WQCKms; +import de.intevation.flys.artifacts.model.WQKms; +import de.intevation.flys.artifacts.model.WW; +import de.intevation.flys.artifacts.model.WstValueTable; +import de.intevation.flys.artifacts.model.WstValueTableFactory; + +import de.intevation.flys.artifacts.states.LocationDistanceSelect; + +import de.intevation.flys.model.DischargeTable; +import de.intevation.flys.model.FastCrossSectionLine; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; + +import de.intevation.flys.utils.DoubleUtil; +import de.intevation.flys.utils.FLYSUtils; + +import gnu.trove.TDoubleArrayList; + +import java.awt.geom.Point2D; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + + +/** + * The default WINFO artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class WINFOArtifact +extends FLYSArtifact +implements FacetTypes, WaterLineArtifact { + + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(WINFOArtifact.class); + + /** The name of the artifact. */ + public static final String ARTIFACT_NAME = "winfo"; + + /** XPath */ + public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static"; + + /** The default number of steps between the start end end of a selected Q + * range. */ + public static final int DEFAULT_Q_STEPS = 30; + + /** The default step width between the start end end kilometer. */ + public static final double DEFAULT_KM_STEPS = 0.1; + + + /** + * The default constructor. + */ + public WINFOArtifact() { + } + + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + @Override + public String getName() { + return ARTIFACT_NAME; + } + + protected static boolean reportGeneratedWs( + Calculation report, + double [] ws + ) { + if (ws == null || ws.length < 2) { + return false; + } + + double lastW = ws[0]; + boolean alreadyReported = false; + + for (int i = 1; i < ws.length; ++i) { + if (Math.abs(lastW - ws[i]) < 1e-5) { + if (!alreadyReported) { + alreadyReported = true; + report.addProblem("more.than.one.q.for.w", ws[i]); + } + } + else { + alreadyReported = false; + } + lastW = ws[i]; + } + + return true; + } + + + // + // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES + // + + /** + * Returns the data that is computed by a waterlevel computation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public CalculationResult getWaterlevelData() + { + logger.debug("WINFOArtifact.getWaterlevelData"); + + if (getDataAsString("calculation_mode").equals("calc.discharge.longitudinal.section")) { + return getDischargeLongitudinalSectionData(); + } + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return error(new WQKms[0], "no.river.selected"); + } + + double[] kms = getKms(); + if (kms == null) { + return error(new WQKms[0], "no.kms.selected"); + } + + double[] qs = getQs(); + double[] ws = null; + boolean qSel = true; + + Calculation report = new Calculation(); + + if (qs == null) { + logger.debug("Determine Q values based on a set of W values."); + qSel = false; + ws = getWs(); + double [][] qws = getQsForWs(ws); + if (qws == null || qws.length == 0) { + return error(new WQKms[0], "converting.ws.to.qs.failed"); + } + qs = qws[0]; + + if (reportGeneratedWs(report, qws[1])) { + ws = qws[1]; + } + } + + WstValueTable wst = WstValueTableFactory.getTable(river); + if (wst == null) { + return error(new WQKms[0], "no.wst.for.selected.river"); + } + + + double [] range = FLYSUtils.getKmRange(this); + if (range == null) { + return error(new WQKms[0], "no.range.found"); + } + + double refKm; + + if (isFreeQ() || isFreeW()) { + refKm = range[0]; + logger.debug("'free' calculation (km " + refKm + ")"); + } + else { + Gauge gauge = river.determineGaugeByPosition(range[0]); + if (gauge == null) { + return error( + new WQKms[0], "no.gauge.found.for.km"); + } + + refKm = gauge.getStation().doubleValue(); + + logger.debug( + "reference gauge: " + gauge.getName() + " (km " + refKm + ")"); + } + + return computeWaterlevelData(kms, qs, ws, wst, refKm, report); + } + + + /** + * Computes the data of a waterlevel computation based on the interpolation + * in WstValueTable. + * + * @param kms The kilometer values. + * @param qs The discharge values. + * @param wst The WstValueTable used for the interpolation. + * + * @return an array of data triples that consist of W, Q and Kms. + */ + public static CalculationResult computeWaterlevelData( + double [] kms, + double [] qs, + double [] ws, + WstValueTable wst, + double refKm, + Calculation report + ) { + logger.info("WINFOArtifact.computeWaterlevelData"); + + Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm); + + if (report != null) { + calc1.addProblems(report); + } + + return calc1.calculate(wst); + } + + + /** + * Returns the data that is computed by a duration curve computation. + * + * @return the data computed by a duration curve computation. + */ + public CalculationResult getDurationCurveData() { + logger.debug("WINFOArtifact.getDurationCurveData"); + + River r = FLYSUtils.getRiver(this); + + if (r == null) { + return error(null, "no.river.selected"); + } + + Gauge g = getGauge(); + + if (g == null) { + return error(null, "no.gauge.selected"); + } + + double[] locations = FLYSUtils.getLocations(this); + + if (locations == null) { + return error(null, "no.locations.selected"); + } + + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + return error(null, "no.wst.for.river"); + } + + return computeDurationCurveData(g, wst, locations[0]); + } + + + /** + * Computes the data used to create duration curves. + * + * @param gauge The selected gauge. + * @param location The selected location. + * + * @return the computed data. + */ + public static CalculationResult computeDurationCurveData( + Gauge gauge, + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDurationCurveData"); + + Object[] obj = MainValuesFactory.getDurationCurveData(gauge); + + int[] days = (int[]) obj[0]; + double[] qs = (double[]) obj[1]; + + Calculation3 calculation = new Calculation3(location, days, qs); + + return calculation.calculate(wst); + } + + + /** + * Returns the data that is computed by a discharge curve computation. + * + * @return the data computed by a discharge curve computation. + */ + public CalculationResult getComputedDischargeCurveData() + throws NullPointerException + { + logger.debug("WINFOArtifact.getComputedDischargeCurveData"); + + River r = FLYSUtils.getRiver(this); + + if (r == null) { + return error(new WQKms[0], "no.river.selected"); + } + + double[] locations = FLYSUtils.getLocations(this); + + if (locations == null) { + return error(new WQKms[0], "no.locations.selected"); + } + + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + return error(new WQKms[0], "no.wst.for.river"); + } + + return computeDischargeCurveData(wst, locations[0]); + } + + + /** + * Computes the data used to create computed discharge curves. + * + * @param wst The WstValueTable that is used for the interpolation (river- + * bound). + * @param location The location where the computation should be based on. + * + * @return an object that contains tuples of W/Q values at the specified + * location. + */ + public static CalculationResult computeDischargeCurveData( + WstValueTable wst, + double location) + { + logger.info("WINFOArtifact.computeDischargeCurveData"); + + Calculation2 calculation = new Calculation2(location); + + return calculation.calculate(wst); + } + + + /** Create CalculationResult with data and message. */ + protected static final CalculationResult error(Object data, String msg) { + return new CalculationResult(data, new Calculation(msg)); + } + + + /** + * Returns the data computed by the discharge longitudinal section + * computation. + * + * @return an array of WQKms object - one object for each given Q value. + */ + public CalculationResult getDischargeLongitudinalSectionData() { + + logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData"); + + River river = FLYSUtils.getRiver(this); + if (river == null) { + logger.debug("No river selected."); + return error(new WQKms[0], "no.river.selected"); + } + + WstValueTable table = WstValueTableFactory.getTable(river); + if (table == null) { + logger.debug("No wst found for selected river."); + return error(new WQKms[0], "no.wst.for.river"); + } + + List<Segment> segments = getSegments(); + + if (segments == null) { + logger.debug("Cannot create segments."); + return error(new WQKms[0], "cannot.create.segments"); + } + + double [] range = getFromToStep(); + + if (range == null) { + logger.debug("Cannot figure out range."); + return error(new WQKms[0], "no.range.found"); + } + + Calculation4 calc4 = new Calculation4(segments, river, isQ()); + + return calc4.calculate(table, range[0], range[1], range[2]); + } + + + /** + * Returns the data that is computed by a reference curve computation. + * + * @return the data computed by a reference curve computation. + */ + public CalculationResult getReferenceCurveData(CallContext context) { + + Double startKm = getReferenceStartKm(); + + if (startKm == null) { + return error(new WW[0], "no.reference.start.km"); + } + + double [] endKms = getReferenceEndKms(); + + if (endKms == null || endKms.length == 0) { + return error(new WW[0], "no.reference.end.kms"); + } + + Calculation5 calc5 = new Calculation5(startKm, endKms); + + River r = FLYSUtils.getRiver(this); + if (r == null) { + return error(new WW[0], "no.river.found"); + } + + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + return error(new WW[0], "no.wst.for.river"); + } + + Map<Double, Double> kms2gaugeDatums = r.queryGaugeDatumsKMs(); + + return calc5.calculate(wst, kms2gaugeDatums, context); + } + + + /** Get reference (start) km. */ + public Double getReferenceStartKm() { + StateData sd = getData("reference_startpoint"); + + if (sd == null) { + logger.warn("no reference start given."); + return null; + } + + logger.debug("Reference start km given: " + sd.getValue()); + + String input = (String) sd.getValue(); + + if (input == null || (input = input.trim()).length() == 0) { + logger.warn("reference start string is empty."); + return null; + } + + try { + return Double.valueOf(input); + } + catch (NumberFormatException nfe) { + logger.warn("reference start string is not numeric."); + } + + return null; + } + + + /** + * Get end kms for reference curve (null if none). + */ + public double [] getReferenceEndKms() { + StateData sd = getData("reference_endpoint"); + + if (sd == null) { + logger.warn("no reference end given."); + return null; + } + else { + logger.debug("Reference end km : " + sd.getValue()); + } + + String input = (String) sd.getValue(); + + if (input == null || (input = input.trim()).length() == 0) { + logger.warn("reference end string is empty."); + return null; + } + + TDoubleArrayList endKms = new TDoubleArrayList(); + + for (String part: input.split("\\s+")) { + try { + double km = Double.parseDouble(part); + if (!endKms.contains(km)) { + endKms.add(km); + } + } + catch (NumberFormatException nfe) { + logger.warn("reference end string is not numeric."); + } + } + + return endKms.toNativeArray(); + } + + + public CalculationResult getHistoricalDischargeData() { + Gauge gauge = FLYSUtils.getReferenceGauge(this); + String rawTimerange = getDataAsString("year_range"); + String rawValues = getDataAsString("historical_values"); + int mode = getDataAsInteger("historical_mode"); + + long[] timerange = FLYSUtils.longArrayFromString(rawTimerange); + double[] values = FLYSUtils.doubleArrayFromString(rawValues); + + Calendar start = new GregorianCalendar(); + start.setTimeInMillis(timerange[0]); + Calendar end = new GregorianCalendar(); + end.setTimeInMillis(timerange[1]); + + Calculation6 calc = new Calculation6( + mode, + new long[] { start.getTimeInMillis(), end.getTimeInMillis() }, + values); + + return calc.calculate(gauge); + } + + + public List<Segment> getSegments() { + StateData wqValues = getData("wq_values"); + if (wqValues == null) { + logger.warn("no wq_values given"); + return Collections.emptyList(); + } + String input = (String)wqValues.getValue(); + if (input == null || (input = input.trim()).length() == 0) { + logger.warn("wq_values are empty"); + return Collections.emptyList(); + } + return Segment.parseSegments(input); + } + + + /** + * Get corrected waterline against surface/profile. + */ + public Lines.LineData waterLineC(int idx, FastCrossSectionLine csl) { + List<Point2D> points = csl.getPoints(); + + WQKms[] wqckms = (WQKms[]) + getDischargeLongitudinalSectionData().getData(); + + // Find index of km. + double wishKM = csl.getKm(); + + // Find W/C at km, linear naive approach. + WQCKms triple = (WQCKms) wqckms[idx-1]; + + int old_idx = 0; + + if (triple.size() == 0) { + logger.warn("Calculation of c/waterline is empty."); + return Lines.createWaterLines(points, 0.0f); + } + + // Linear seach in WQKms for closest km. + double old_dist_wish = Math.abs(wishKM - triple.getKm(0)); + double last_c = triple.getC(0); + + for (int i = 0, T = triple.size(); i < T; i++) { + double diff = Math.abs(wishKM - triple.getKm(i)); + if (diff > old_dist_wish) { + break; + } + last_c = triple.getC(i); + old_dist_wish = diff; + } + + return Lines.createWaterLines(points, last_c); + } + + + /** + * Get points of line describing the surface of water at cross section. + * + * @param idx Index for getWaterlevelData. + * @param csl The profile/surface to fill with water. + * @param nextIgnored Ignored in this implementation of WaterLineArtifact. + * @param prevIgnored Ignored in this implementation of WaterLineArtifact. + * + * @return an array holding coordinates of points of surface of water ( + * in the form {{x1, x2} {y1, y2}} ). + */ + @Override + public Lines.LineData getWaterLines(int idx, FastCrossSectionLine csl, + double nextIgnored, double prevIgnored) { + logger.debug("getWaterLines(" + idx + ")"); + + List<Point2D> points = csl.getPoints(); + + // Need W at km + WQKms [] wqkms = (WQKms[]) getWaterlevelData().getData(); + if (wqkms.length == 0) { + logger.error("No WQKms found."); + return Lines.createWaterLines(points, 0.0f); + } + + if (wqkms.length <= idx) { + logger.error("getWaterLines() requested index (" + + idx + " not found."); + return waterLineC(idx, csl); + } + + // Find index of km. + double wishKM = csl.getKm(); + + // Find W at km, linear naive approach. + WQKms triple = wqkms[idx]; + + int old_idx = 0; + + if (triple.size() == 0) { + logger.warn("Calculation of waterline is empty."); + return Lines.createWaterLines(points, 0.0f); + } + + // Linear seach in WQKms for closest km. + double old_dist_wish = Math.abs(wishKM - triple.getKm(0)); + double last_w = triple.getW(0); + + for (int i = 0, T = triple.size(); i < T; i++) { + double diff = Math.abs(wishKM - triple.getKm(i)); + if (diff > old_dist_wish) { + break; + } + last_w = triple.getW(i); + old_dist_wish = diff; + } + + return Lines.createWaterLines(points, last_w); + } + + + /** + * Returns the Qs for a number of Ws. This method makes use of + * DischargeTables.getQForW(). + * + * @param ws An array of W values. + * + * @return an array of Q values. + */ + public double [][] getQsForWs(double[] ws) { + + if (ws == null) { + logger.error("getQsForWs: ws == null"); + return null; + } + + boolean debug = logger.isDebugEnabled(); + + if (debug) { + logger.debug("FLYSArtifact.getQsForWs"); + } + + River r = FLYSUtils.getRiver(this); + if (r == null) { + logger.warn("no river found"); + return null; + } + + double [] range = FLYSUtils.getKmRange(this); + if (range == null) { + logger.warn("no ranges found"); + return null; + } + + if (isFreeW()) { + logger.debug("Bezugslinienverfahren I: W auf freier Strecke"); + // The simple case of the "Bezugslinienverfahren" + // "W auf freier Strecke". + WstValueTable wst = WstValueTableFactory.getTable(r); + if (wst == null) { + logger.warn("no wst value table found"); + return null; + } + double km = range[0]; + + TDoubleArrayList outQs = new TDoubleArrayList(ws.length); + TDoubleArrayList outWs = new TDoubleArrayList(ws.length); + + boolean generatedWs = false; + + for (int i = 0; i < ws.length; ++i) { + double w = ws[i]; + if (debug) { + logger.debug("getQsForWs: lookup Q for W: " + w); + } + // There could bemore than one Q per W. + double [] qs = wst.findQsForW(km, w); + for (int j = 0; j < qs.length; ++j) { + outWs.add(ws[i]); + outQs.add(qs[j]); + } + generatedWs |= qs.length != 1; + } + + if (debug) { + logger.debug("getQsForWs: number of Qs: " + outQs.size()); + } + + return new double [][] { + outQs.toNativeArray(), + generatedWs ? outWs.toNativeArray() : null }; + } + + if (debug) { + logger.debug("range: " + Arrays.toString(range)); + } + + Gauge g = r.determineGaugeByPosition(range[0]); + if (g == null) { + logger.warn("no gauge found for km: " + range[0]); + return null; + } + + if (debug) { + logger.debug("convert w->q with gauge '" + g.getName() + "'"); + } + + DischargeTable dt = g.fetchMasterDischargeTable(); + + if (dt == null) { + logger.warn("No master discharge table found for gauge '" + + g.getName() + "'"); + return null; + } + + double [][] values = DischargeTables.loadDischargeTableValues(dt, 1); + + TDoubleArrayList wsOut = new TDoubleArrayList(ws.length); + TDoubleArrayList qsOut = new TDoubleArrayList(ws.length); + + boolean generatedWs = false; + + for (int i = 0; i < ws.length; i++) { + if (Double.isNaN(ws[i])) { + logger.warn("W is NaN: ignored"); + continue; + } + double w = ws[i] / 100d; + double [] qs = DischargeTables.getQsForW(values, w); + + if (qs.length == 0) { + logger.warn("No Qs found for W = " + ws[i]); + } + else { + for (double q: qs) { + wsOut.add(ws[i]); + qsOut.add(q * 100d); + } + } + generatedWs |= qs.length != 1; + } + + return new double [][] { + qsOut.toNativeArray(), + generatedWs ? wsOut.toNativeArray() : null + }; + } + + + /** + * Determines the selected mode of distance/range input. + * + * @return true, if the range mode is selected otherwise false. + */ + public boolean isRange() { + StateData mode = getData("ld_mode"); + + if (mode == null) { + logger.warn("No mode location/range chosen. Defaults to range."); + return true; + } + + String value = (String) mode.getValue(); + + return value.equals("distance"); + } + + + /** + * Returns the selected distance based on a given range (from, to). + * + * @param dFrom The StateData that contains the lower value. + * @param dTo The StateData that contains the upper value. + * + * @return the selected distance. + */ + protected double[] getDistanceByRange(StateData dFrom, StateData dTo) { + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + return new double[] { from, to }; + } + + + /** + * Returns the selected Kms. + * + * @param distance An 2dim array with [lower, upper] values. + * + * @return the selected Kms. + */ + public double[] getKms(double[] distance) { + StateData dStep = getData("ld_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Kms."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // transform step from 'm' into 'km' + step = step / 1000; + + if (step == 0d) { + step = DEFAULT_KM_STEPS; + } + + return DoubleUtil.explode(distance[0], distance[1], step); + } + + + /** + * Returns the selected Kms. + * + * @return the selected kms. + */ + public double[] getKms() { + if (isRange()) { + double[] distance = FLYSUtils.getKmRange(this); + return getKms(distance); + + } + else { + return LocationDistanceSelect.getLocations(this); + } + } + + + public double [] getFromToStep() { + if (!isRange()) { + return null; + } + double [] fromTo = FLYSUtils.getKmRange(this); + + if (fromTo == null) { + return null; + } + + StateData dStep = getData("ld_step"); + if (dStep == null) { + return null; + } + + double [] result = new double[3]; + result[0] = fromTo[0]; + result[1] = fromTo[1]; + + try { + String step = (String)dStep.getValue(); + result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d); + } + catch (NumberFormatException nfe) { + return null; + } + + return result; + } + + + /** + * Returns the gauge based on the current distance and river. + * + * @return the gauge. + */ + public Gauge getGauge() { + return FLYSUtils.getGauge(this); + } + + + /** + * Returns the gauges that match the selected kilometer range. + * + * @return the gauges based on the selected kilometer range. + */ + public List<Gauge> getGauges() { + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return null; + } + + double [] dist = FLYSUtils.getKmRange(this); + if (dist == null) { + return null; + } + + return river.determineGauges(dist[0], dist[1]); + } + + + /** + * This method returns the Q values. + * + * @return the selected Q values or null, if no Q values are selected. + */ + public double[] getQs() { + StateData dMode = getData("wq_isq"); + StateData dSelection = getData("wq_isrange"); + + boolean isRange = dSelection != null + ? Boolean.valueOf((String)dSelection.getValue()) + : false; + + if (isQ()) { + if (!isRange) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Qs, but W has been inserted."); + return null; + } + } + + + public boolean isQ() { + StateData mode = getData("wq_isq"); + String value = (mode != null) ? (String) mode.getValue() : null; + return value != null ? Boolean.valueOf(value) : false; + } + + public boolean isW() { + StateData mode = getData("wq_isq"); + String value = (mode != null) ? (String) mode.getValue() : null; + return value != null ? !Boolean.valueOf(value) : false; + } + + public boolean isFreeW() { + if(!isW()) { + return false; + } + StateData mode = getData("wq_isfree"); + String value = (mode != null) ? (String) mode.getValue() : null; + + return value != null ? Boolean.valueOf(value) : false; + } + + + /** + * Returns true, if the parameter is set to compute data on a free range. + * Otherwise it returns false, which tells the calculation that it is bound + * to a gauge. + * + * @return true, if the calculation should compute on a free range otherwise + * false and the calculation is bound to a gauge. + */ + public boolean isFreeQ() { + if(!isQ()) { + return false; + } + StateData mode = getData("wq_isfree"); + String value = (mode != null) ? (String) mode.getValue() : null; + + logger.debug("isFreeQ: " + value); + + return value != null && Boolean.valueOf(value); + } + + + /** + * Returns the Q values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of Q values. + */ + public double[] getQs(double[] range) { + StateData dMode = getData("wq_isq"); + + if (isQ()) { + return getWQForDist(range); + } + + logger.warn("You try to get Qs, but Ws has been inserted."); + return null; + } + + + /** + * Returns the W values based on a specified kilometer range. + * + * @param range A 2dim array with lower and upper kilometer range. + * + * @return an array of W values. + */ + public double[] getWs(double[] range) { + if (isW()) { + return getWQForDist(range); + } + + logger.warn("You try to get Ws, but Qs has been inserted."); + return null; + } + + + /** + * This method returns the W values. + * + * @return the selected W values or null, if no W values are selected. + */ + public double[] getWs() { + StateData dSingle = getData("wq_single"); + + if (isW()) { + if (dSingle != null) { + return getSingleWQValues(); + } + else { + return getWQTriple(); + } + } + else { + logger.warn("You try to get Ws, but Q has been inserted."); + return null; + } + } + + /** + * This method returns the given W or Q values for a specific range + * (inserted in the WQ input panel for discharge longitudinal sections). + * + * @param dist A 2dim array with lower und upper kilometer values. + * + * @return an array of W or Q values. + */ + protected double[] getWQForDist(double[] dist) { + logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]); + StateData data = getData("wq_values"); + + if (data == null) { + logger.warn("Missing wq values!"); + return null; + } + + String dataString = (String) data.getValue(); + String[] ranges = dataString.split(":"); + + for (String range: ranges) { + String[] parts = range.split(";"); + + double lower = Double.parseDouble(parts[0]); + double upper = Double.parseDouble(parts[1]); + + if (lower <= dist[0] && upper >= dist[1]) { + String[] values = parts[2].split(","); + + int num = values.length; + double[] res = new double[num]; + + for (int i = 0; i < num; i++) { + try { + res[i] = Double.parseDouble(values[i]); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + return res; + } + } + + logger.warn("Specified range for WQ not found!"); + + return null; + } + + + /** + * This method returns an array of inserted WQ triples that consist of from, + * to and the step width. + * + * @return an array of from, to and step width. + */ + protected double[] getWQTriple() { + StateData dFrom = getData("wq_from"); + StateData dTo = getData("wq_to"); + + if (dFrom == null || dTo == null) { + logger.warn("Missing start or end value for range."); + return null; + } + + double from = Double.parseDouble((String) dFrom.getValue()); + double to = Double.parseDouble((String) dTo.getValue()); + + StateData dStep = getData("wq_step"); + + if (dStep == null) { + logger.warn("No step width given. Cannot compute Qs."); + return null; + } + + double step = Double.parseDouble((String) dStep.getValue()); + + // if no width is given, the DEFAULT_Q_STEPS is used to compute the step + // width. Maybe, we should round the value to a number of digits. + if (step == 0d) { + double diff = to - from; + step = diff / DEFAULT_Q_STEPS; + } + + return DoubleUtil.explode(from, to, step); + } + + + /** + * Returns an array of inserted WQ double values stored as whitespace + * separated list. + * + * @return an array of W or Q values. + */ + protected double[] getSingleWQValues() { + StateData dSingle = getData("wq_single"); + + if (dSingle == null) { + logger.warn("Cannot determine single WQ values. No data given."); + return null; + } + + String tmp = (String) dSingle.getValue(); + String[] strValues = tmp.split(" "); + + TDoubleArrayList values = new TDoubleArrayList(); + + for (String strValue: strValues) { + try { + values.add(Double.parseDouble(strValue)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + values.sort(); + + return values.toNativeArray(); + } + + + /** + * Determines Facets initial disposition regarding activity (think of + * selection in Client ThemeList GUI). This will be checked one time + * when the facet enters a collections describe document. + * + * @param facetName name of the facet. + * @param index index of the facet. + * @return 0 if not active + */ + @Override + public int getInitialFacetActivity(String outputName, String facetName, int index) { + + logger.debug("WINFOArtifact.active?: " + + outputName + + "/" + + facetName); + + if (facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_Q) || + facetName.equals(COMPUTED_DISCHARGE_MAINVALUES_W) + && outputName.equals("computed_discharge_curve")) + { + return 0; + } + + return StringUtils.contains(facetName, INACTIVES) ? 0 : 1; + } + + private static final String [] INACTIVES = new String[] { + LONGITUDINAL_Q, + DURATION_Q + }; + + + /** + * Returns the WstValueTable of current river. + */ + public WstValueTable getWstValueTable() { + River r = FLYSUtils.getRiver(this); + return WstValueTableFactory.getTable(r); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :