Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 2424:092e519ff461
merged flys-artifacts/2.6.1
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:26 +0200 |
parents | ac528b883b47 |
children | 3f1cc396d253 |
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:26 2012 +0200 @@ -0,0 +1,1398 @@ +package de.intevation.flys.artifacts; + +import de.intevation.artifactdatabase.ProtocolUtils; + +import de.intevation.artifactdatabase.data.StateData; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; + +import de.intevation.artifactdatabase.transition.TransitionEngine; + +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.Message; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.flys.artifacts.context.FLYSContext; + +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.CalculationMessage; +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.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.DefaultState; +import de.intevation.flys.artifacts.states.LocationDistanceSelect; + +import de.intevation.flys.geom.Lines; + +import de.intevation.flys.model.FastCrossSectionLine; +import de.intevation.flys.model.Gauge; +import de.intevation.flys.model.River; +import de.intevation.flys.model.DischargeTable; + +import de.intevation.flys.utils.DoubleUtil; +import de.intevation.flys.utils.FLYSUtils; + +import gnu.trove.TDoubleArrayList; + +import java.awt.geom.Point2D; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * 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() { + } + + + /** + * This method returns a description of this artifact. + * + * @param data Some data. + * @param context The CallContext. + * + * @return the description of this artifact. + */ + public Document describe(Document data, CallContext context) { + logger.debug("Describe: the current state is: " + getCurrentStateId()); + + if (logger.isDebugEnabled()) { + dumpArtifact(); + } + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + + StateEngine stateEngine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + TransitionEngine transitionEngine = (TransitionEngine) flysContext.get( + FLYSContext.TRANSITION_ENGINE_KEY); + + List<State> reachable = transitionEngine.getReachableStates( + this, getCurrentState(context), stateEngine); + + Document description = XMLUtils.newDocument(); + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + description, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ProtocolUtils.createRootNode(creator); + description.appendChild(root); + + State current = getCurrentState(context); + + ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash()); + ProtocolUtils.appendState(creator, root, current); + ProtocolUtils.appendReachableStates(creator, root, reachable); + + appendBackgroundActivity(creator, root, context); + + Element name = ProtocolUtils.createArtNode( + creator, "name", + new String[] { "value" }, + new String[] { getName() }); + + Element ui = ProtocolUtils.createArtNode( + creator, "ui", null, null); + + Element staticUI = ProtocolUtils.createArtNode( + creator, "static", null, null); + + Element outs = ProtocolUtils.createArtNode( + creator, "outputmodes", null, null); + appendOutputModes(description, outs, context, identifier()); + + appendStaticUI(description, staticUI, context, identifier()); + + Element dynamic = current.describe( + this, + description, + root, + context, + identifier()); + + if (dynamic != null) { + ui.appendChild(dynamic); + } + + ui.appendChild(staticUI); + + root.appendChild(name); + root.appendChild(ui); + root.appendChild(outs); + + return description; + } + + + /** + * Returns the name of the concrete artifact. + * + * @return the name of the concrete artifact. + */ + public String getName() { + return ARTIFACT_NAME; + } + + + protected static void appendBackgroundActivity( + ElementCreator cr, + Element root, + CallContext context + ) { + Element inBackground = cr.create("background-processing"); + root.appendChild(inBackground); + + cr.addAttr( + inBackground, + "value", + String.valueOf(context.isInBackground()), + true); + + LinkedList<Message> messages = context.getBackgroundMessages(); + + if (messages == null) { + return; + } + + CalculationMessage message = (CalculationMessage) messages.getLast(); + cr.addAttr( + inBackground, + "steps", + String.valueOf(message.getSteps()), + true); + + cr.addAttr( + inBackground, + "currentStep", + String.valueOf(message.getCurrentStep()), + true); + + inBackground.setTextContent(message.getMessage()); + } + + + /** + * Append output mode nodes to a document. + */ + protected void appendOutputModes( + Document doc, + Element outs, + CallContext context, + String uuid) + { + List<String> stateIds = getPreviousStateIds(); + + XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append output modes for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + + List<Output> list = state.getOutputs(); + if (list == null || list.size() == 0) { + logger.debug("-> No output modes for this state."); + continue; + } + + List<Facet> fs = facets.get(stateId); + + if (fs == null || fs.size() == 0) { + logger.debug("No facets for previous state found."); + continue; + } + + logger.debug("Found " + fs.size() + " facets in previous states."); + + List<Output> generated = generateOutputs(list, fs); + + ProtocolUtils.appendOutputModes(doc, outs, generated); + } + + try { + DefaultState cur = (DefaultState) getCurrentState(context); + if (cur.validate(this)) { + List<Output> list = cur.getOutputs(); + if (list != null && list.size() > 0) { + logger.debug( + "Append output modes for current state: " + cur.getID()); + + List<Facet> fs = facets.get(cur.getID()); + + if (fs != null && fs.size() > 0) { + List<Output> generated = generateOutputs(list, fs); + + logger.debug("Found " + fs.size() + " current facets."); + if (!generated.isEmpty()) { + ProtocolUtils.appendOutputModes( + doc, outs, generated); + } + } + else { + logger.debug("No facets found for the current state."); + } + } + } + } + catch (IllegalArgumentException iae) { + // state is not valid, so we do not append its outputs. + } + } + + + /** + * This method appends the static data - that has already been inserted by + * the user - to the static node of the DESCRIBE document. + * + * @param doc The document. + * @param ui The root node. + * @param context The CallContext. + * @param uuid The identifier of the artifact. + */ + protected void appendStaticUI( + Document doc, + Node ui, + CallContext context, + String uuid) + { + List<String> stateIds = getPreviousStateIds(); + + FLYSContext flysContext = FLYSUtils.getFlysContext(context); + StateEngine engine = (StateEngine) flysContext.get( + FLYSContext.STATE_ENGINE_KEY); + + for (String stateId: stateIds) { + logger.debug("Append static data for state: " + stateId); + DefaultState state = (DefaultState) engine.getState(stateId); + + ui.appendChild(state.describeStatic(this, doc, ui, context, uuid)); + } + } + + + // + // 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"); + + 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; + + 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 (qws[1] != null) { // If new ws where generated. + // TODO: Inform user! + 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); + } + + + /** + * Computes the data of a waterlevel computation based on the interpolation + * in WstValueTable. + * + * @param kms The kilometer values. + * @param qa 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 + ) { + logger.info("WINFOArtifact.computeWaterlevelData"); + + Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm); + + 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 used to create discharge curves. + * + */ + public CalculationResult getDischargeCurveData() { + + River river = FLYSUtils.getRiver(this); + if (river == null) { + return error(new WQKms[0], "no.river.selected"); + } + + double [] distance = FLYSUtils.getKmRange(this); + + if (distance == null) { + return error(new WQKms[0], "no.range.found"); + } + + List<Gauge> gauges = river.determineGauges(distance[0], distance[1]); + + if (gauges.isEmpty()) { + return error(new WQKms[0], "no.gauge.selected"); + } + + String [] names = new String[gauges.size()]; + + for (int i = 0; i < names.length; ++i) { + names[i] = gauges.get(i).getName(); + } + + DischargeTables dt = new DischargeTables(river.getName(), names); + + Map<String, double [][]> map = dt.getValues(100d); + + ArrayList<WQKms> res = new ArrayList<WQKms>(); + + for (Gauge gauge: gauges) { + String name = gauge.getName(); + double [][] values = map.get(name); + if (values == null) { + continue; + } + double [] kms = new double[values[0].length]; + Arrays.fill(kms, gauge.getStation().doubleValue()); + res.add(new WQKms(kms, values[0], values[1], name)); + } + + return new CalculationResult( + res.toArray(new WQKms[res.size()]), + new Calculation()); + } + + + /** + * 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. + * @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); + } + + 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. */ + protected 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; + } + + + protected 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"); + + int[] timerange = FLYSUtils.intArrayFromString(rawTimerange); + double[] values = FLYSUtils.doubleArrayFromString(rawValues); + + Calendar start = new GregorianCalendar(timerange[0], 0, 1); + Calendar end = new GregorianCalendar(timerange[1], 0, 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 points of line describing the surface of water at cross section. + * + * @return an array holding coordinates of points of surface of water ( + * in the form {{x1, x2} {y1, y2}} ). + */ + public double [][] getWaterLines(int idx, FastCrossSectionLine csl) { + 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."); + } + + // Find W at km, linear naive approach. + WQKms triple = wqkms[idx]; + + // Find index of km. + double wishKM = csl.getKm(); + 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); + } + } + 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) { + String [] inactives = new String[] { + LONGITUDINAL_Q, + DURATION_Q + }; + + 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 Arrays.asList(inactives).contains(facetName) + ? 0 + : 1; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :