Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java @ 1905:d025009fec73
Recommend fixations for computed discharge curves.
flys-artifacts/trunk@3265 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Wed, 16 Nov 2011 09:43:58 +0000 |
parents | feef1121e800 |
children | cdeb31ef20a1 |
line wrap: on
line source
package de.intevation.flys.artifacts; import java.awt.geom.Point2D; 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; import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; 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.Calculation; import de.intevation.flys.artifacts.model.CalculationResult; import de.intevation.flys.artifacts.model.CrossSectionFactory; 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.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.Gauge; import de.intevation.flys.model.River; import de.intevation.flys.model.CrossSection; import de.intevation.flys.model.CrossSectionLine; import de.intevation.flys.utils.DoubleUtil; import de.intevation.flys.utils.FLYSUtils; import gnu.trove.TDoubleArrayList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; 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; import de.intevation.flys.artifacts.model.CalculationMessage; /** * The default WINFO artifact. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class WINFOArtifact extends FLYSArtifact implements FacetTypes { /** 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(); qs = getQsForWs(ws); if (qs == null) { return error(new WQKms[0], "conversion ws to qs failed."); } } WstValueTable wst = WstValueTableFactory.getTable(river); if (wst == null) { return error(new WQKms[0], "No Wst found for selected river."); } double [] range = FLYSUtils.getKmRange(this); if (range == null) { return error(new WQKms[0], "No range found"); } double refKm; if (isFreeQ()) { 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 " + range[0]); } 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, "Cannot determine river."); } Gauge g = getGauge(); if (g == null) { return error(null, "Cannot determine gauge."); } double[] locations = FLYSUtils.getLocations(this); if (locations == null) { return error(null, "Cannot determine location."); } WstValueTable wst = WstValueTableFactory.getTable(r); if (wst == null) { return error(null, "No Wst found for selected 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 found"); } 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 gauges found"); } 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], "Cannot determine river."); } double[] locations = FLYSUtils.getLocations(this); if (locations == null) { return error(new WQKms[0], "Cannot determine location."); } WstValueTable wst = WstValueTableFactory.getTable(r); if (wst == null) { return error(new WQKms[0], "No Wst found for selected 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 found for selected 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], "Cannot figure out range."); } Calculation4 calc4 = new Calculation4(segments, river, isQ()); return calc4.calculate(table, range[0], range[1], range[2]); } 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 List of all cross-sections for current river. * * @return List of CrossSections for current river, null in case of error. */ protected List<CrossSection> getCrossSections() { River river = FLYSUtils.getRiver(this); if (river == null) { logger.warn("No river in WINFO found"); return null; } return CrossSectionFactory.getCrossSections(river); } /** * Get CrossSectionLine spatially closest to what is specified in the data * "cross_section.km" (and from "cross_section.index"). * * @return CrossSectionLine closest to "cross_section.km". */ protected CrossSectionLine searchCrossSectionKmLine() { double wishKM = 0.0f; try { wishKM = Double.parseDouble(getDataAsString("cross_section.km")); } catch (Exception e) { logger.warn("Exception when parsing cross_section.km", e); } int crossSectionIdx = 0; List<CrossSection> sections = getCrossSections(); if (sections.size() < crossSectionIdx) { logger.error("Cross-section not found: " + crossSectionIdx); return null; } if (sections.size() == 0 || sections.get(crossSectionIdx).getLines().size() == 0) { return null; } // Get the cross section closest to requested km. // Naive, linear approach. List<CrossSectionLine> crossSectionLines = sections.get(crossSectionIdx).getLines(); CrossSectionLine oldLine = crossSectionLines.get(0); double oldDiff = Math.abs(wishKM - oldLine.getKm().doubleValue()); for (CrossSectionLine line: crossSectionLines) { double diff = Math.abs(wishKM - line.getKm().doubleValue()); if (diff > oldDiff) { break; } oldDiff = diff; oldLine = line; } return oldLine; } /** * Get km for which a CrossSection is actually available (this may vary * from the user picked "cross_section.km" data). * * @return km for which cross section is calculated. */ public double getCrossSectionSnapKm() { // Note that this is this triggers a linear search. CrossSectionLine line = searchCrossSectionKmLine(); if (line == null) { logger.warn("No Cross Section for given km found."); return 0.0f; } else { return line.getKm().doubleValue(); } } /** * Get points of Profile of cross section. * * @return an array holding coordinates of points of profile ( * in the form {{x1, x2} {y1, y2}} ). */ public double [][] getCrossSectionData() { logger.info("getCrossSectionData() for cross_section.km " + getDataAsString("cross_section.km")); CrossSectionLine line = searchCrossSectionKmLine(); return line != null ? line.fetchCrossSectionProfile() : null; } /** * 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) { logger.debug("getWaterLines()"); CrossSectionLine csl = searchCrossSectionKmLine(); List<Point2D> points = csl.fetchCrossSectionLinesPoints(); // 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 = 0.0f; int old_idx = 0; try { wishKM = Double.parseDouble(getDataAsString("cross_section.km")); } catch (Exception e) { logger.warn("Exception when trying to get cross_section.km", e); } 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); } /** * Get name of cross sections. * @return list of names of cross-sections. */ public List<String> getCrossSectionNames() { List<String> names = new ArrayList<String>(); for (CrossSection section: getCrossSections()) { names.add(section.getDescription()); } return names; } /** * 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) { 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 (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() + "'"); } DischargeTables dt = new DischargeTables(r.getName(), g.getName()); Map<String, double [][]> tmp = dt.getValues(); double[][] values = tmp.get(g.getName()); double[] qs = new double[ws.length]; for (int i = 0; i < ws.length; i++) { qs[i] = dt.getQForW(values, ws[i]); if (debug) { logger.debug("w: " + ws[i] + " -> q: " + qs[i]); } } return qs; } /** * 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() { River river = FLYSUtils.getRiver(this); if (river == null) { logger.debug("no river found"); return null; } double[] dist = FLYSUtils.getKmRange(this); if (dist == null) { logger.debug("no range found"); return null; } if (logger.isDebugEnabled()) { logger.debug("Determine gauge for:"); logger.debug("... river: " + river.getName()); logger.debug("... distance: " + dist[0] + " - " + dist[1]); } Gauge gauge = river.determineGauge(dist[0], dist[1]); String name = gauge != null ? gauge.getName() : "'n/a"; logger.debug("Found gauge: " + name); return gauge; } /** * 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_mode"); StateData dSelection = getData("wq_selection"); String mode = dMode != null ? (String) dMode.getValue() : ""; String sel = dSelection != null ? (String)dSelection.getValue() : null; if (mode.equals("Q")) { if (sel != null && sel.equals("single")) { 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_mode"); return mode != null && mode.getValue().equals("Q"); } /** * 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() { StateData mode = getData("wq_free"); String value = (mode != null) ? (String) mode.getValue() : null; logger.debug("isFreeQ: " + value); if (value == null) { return false; } return 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_mode"); StateData dValues = getData("wq_values"); String mode = (dMode != null) ? (String) dMode.getValue() : ""; if (mode.equals("Q")) { 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) { StateData dMode = getData("wq_mode"); StateData dValues = getData("wq_values"); String mode = (dMode != null) ? (String) dMode.getValue() : ""; if (mode.equals("W")) { 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 dMode = getData("wq_mode"); StateData dSingle = getData("wq_single"); String mode = (dMode != null) ? (String) dMode.getValue() : ""; if (mode.equals("W")) { if (dSingle != null) { return getSingleWQValues(); } else { return getWQTriple(); } } else { logger.warn("You try to get Qs, but W 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 :