view flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java @ 4174:eaf83d4ae6b1

Sorted gauges for reference gauge selection in historical discharge calculation based on their name. Now, Gauge implements the Java Comparable interface and takes its name into account.
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Thu, 18 Oct 2012 13:12:24 +0200
parents b220287a171e
children 96ea983b5c45
line wrap: on
line source
package de.intevation.flys.artifacts.states;

import java.util.ArrayList;
import java.util.List;
import java.util.Comparator;
import java.util.Collections;

import org.apache.log4j.Logger;

import org.w3c.dom.Element;

import de.intevation.artifacts.Artifact;
import de.intevation.artifacts.CallContext;

import de.intevation.artifactdatabase.ProtocolUtils;
import de.intevation.artifactdatabase.data.StateData;

import de.intevation.artifacts.common.utils.XMLUtils;

import de.intevation.flys.model.Gauge;
import de.intevation.flys.model.Range;
import de.intevation.flys.model.River;
import de.intevation.flys.model.Wst;

import de.intevation.flys.artifacts.FLYSArtifact;

import de.intevation.flys.artifacts.model.RangeWithValues;
import de.intevation.flys.artifacts.model.WstFactory;
import de.intevation.flys.utils.FLYSUtils;


/**
 * State to input W/Q data.
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class WQAdapted extends DefaultState {

    /** The logger used in this state.*/
    private static Logger logger = Logger.getLogger(WQAdapted.class);

    public static final String FIELD_WQ_MODE = "wq_isq";

    public static final String FIELD_WQ_VALUES = "wq_values";

    public static final class GaugeOrder implements Comparator<Gauge> {
        private int order;

        public GaugeOrder(boolean up) {
            order = up ? 1 : -1;
        }

        public int compare(Gauge a, Gauge b) {
            return order * a.getRange().getA().compareTo(b.getRange().getA());
        }
    } // class GaugeOrder

    public static final GaugeOrder GAUGE_UP   = new GaugeOrder(true);
    public static final GaugeOrder GAUGE_DOWN = new GaugeOrder(false);

    /** Trivial, empty constructor. */
    public WQAdapted() {
    }


    /**
     * This method creates one element for each gauge of the selected river that
     * is intersected by the given kilometer range. Each element is a tuple of
     * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower
     * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper
     * kilometer range.
     *
     * @param cr The ElementCreator.
     * @param artifact The FLYS artifact.
     * @param name The name of the data item.
     * @param context The CallContext.
     *
     * @return a list of elements that consist of tuples of the intersected
     * gauges of the selected river.
     */
    @Override
    protected Element[] createItems(
        XMLUtils.ElementCreator cr,
        Artifact    artifact,
        String      name,
        CallContext context)
    {
        logger.debug("WQAdapted.createItems");

        if (name != null && name.equals(FIELD_WQ_MODE)) {
            return createModeItems(cr, artifact, name, context);
        }
        else if (name != null && name.equals(FIELD_WQ_VALUES)) {
            return createValueItems(cr, artifact, name, context);
        }
        else {
            logger.warn("Unknown data object: " + name);
            return null;
        }
    }


    /** Creates "Q" and "W" items. */
    protected Element[] createModeItems(
        XMLUtils.ElementCreator cr,
        Artifact    artifact,
        String      name,
        CallContext context)
    {
        logger.debug("WQAdapted.createModeItems");

        Element w = createItem(cr, new String[] { "w", "W" });
        Element q = createItem(cr, new String[] { "q", "Q" });

        return new Element[] { w, q };
    }


    protected Element[] createValueItems(
        XMLUtils.ElementCreator cr,
        Artifact    artifact,
        String      name,
        CallContext context)
    {
        logger.debug("WQAdapted.createValueItems");

        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;

        double[]    dist   = FLYSUtils.getKmRange(flysArtifact);
        River       river  = FLYSUtils.getRiver(flysArtifact);
        Wst         wst    = WstFactory.getWst(river);
        List<Gauge> gauges = FLYSUtils.getGauges(flysArtifact);

        int num = gauges != null ? gauges.size() : 0;

        if (num == 0) {
            logger.warn("Selected distance matches no gauges.");
            return null;
        }

        List<Element> elements = new ArrayList<Element>();

        double rangeFrom = dist[0];
        double rangeTo   = dist[1];

        if (rangeFrom < rangeTo) {
            Collections.sort(gauges, GAUGE_UP);
            for (Gauge gauge: gauges) {
                Range range = gauge.getRange();
                double lower = range.getA().doubleValue();
                double upper = range.getB().doubleValue();

                // If gauge out of range, skip it.
                if (upper <= rangeFrom || lower >= rangeTo) {
                    continue;
                }

                double from = lower < rangeFrom ? rangeFrom : lower;
                double to   = upper > rangeTo   ? rangeTo   : upper;

                double[] mmQ = determineMinMaxQ(gauge, wst);
                double[] mmW = gauge.determineMinMaxW();

                elements.add(createItem(
                    cr, new String[] { from + ";" + to, ""}, mmQ, mmW));
            }
        }
        else {
            Collections.sort(gauges, GAUGE_DOWN);
            rangeFrom = dist[1];
            rangeTo   = dist[0];
            for (Gauge gauge: gauges) {
                Range range = gauge.getRange();
                double lower = range.getA().doubleValue();
                double upper = range.getB().doubleValue();

                double from = lower < rangeFrom ? rangeFrom : lower;
                double to   = upper > rangeTo   ? rangeTo   : upper;

                // TODO probably need to continue out if oof range (see above).

                double[] mmQ = determineMinMaxQ(gauge, wst);
                double[] mmW = gauge.determineMinMaxW();

                elements.add(createItem(
                    cr, new String[] { to + ";" + from, ""}, mmQ, mmW));
            }
        }

        Element[] els = new Element[elements.size()];
        return elements.toArray(els);
    }


    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
        return createItem(cr, obj, null, null);
    }


    protected Element createItem(
        XMLUtils.ElementCreator cr,
        Object   obj,
        double[] q,
        double[] w)
    {
        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);

        String[] arr = (String[]) obj;

        label.setTextContent(arr[0]);
        value.setTextContent(arr[1]);

        item.appendChild(label);
        item.appendChild(value);

        if (q != null) {
            Element qRange = createRangeElement(cr, q, "Q");
            item.appendChild(qRange);
        }

        if (w != null) {
            Element wRange = createRangeElement(cr, w, "W");
            item.appendChild(wRange);
        }

        return item;
    }


    protected Element createRangeElement(
        XMLUtils.ElementCreator cr,
        double[] mm,
        String   type)
    {
        Element range = ProtocolUtils.createArtNode(
            cr, "range",
            new String[] {"type"},
            new String[] {type});

        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
        min.setTextContent(String.valueOf(mm[0]));

        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
        max.setTextContent(String.valueOf(mm[1]));

        range.appendChild(min);
        range.appendChild(max);

        return range;
    }


    /**
     * Determines the min and max Q value for the given gauge. If no min and
     * max values could be determined, this method will return
     * [Double.MIN_VALUE, Double.MAX_VALUE].
     *
     * @param gauge
     * @param wst
     *
     * @return the min and max Q values for the given gauge.
     */
    protected double[] determineMinMaxQ(Gauge gauge, Wst wst) {
        logger.debug("WQAdapted.determineMinMaxQ");

        double[] minmaxQ = gauge != null
            ? wst.determineMinMaxQ(gauge.getRange())
            : null;

        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;

        return new double[] { minQ, maxQ };
    }


    @Override
    protected String getUIProvider() {
        return "wq_panel_adapted";
    }


    @Override
    public boolean validate(Artifact artifact)
    throws IllegalArgumentException
    {
        logger.debug("WQAdapted.validate");

        FLYSArtifact flys = (FLYSArtifact) artifact;
        StateData    data = getData(flys, FIELD_WQ_MODE);

        String mode = data != null ? (String) data.getValue() : null;
        boolean isQ = mode != null
            ? Boolean.valueOf(mode)
            : false;

        if (!isQ) {
            return validateW(artifact);
        }
        else if (isQ) {
            return validateQ(artifact);
        }
        else {
            throw new IllegalArgumentException("error_feed_no_wq_mode_selected");
        }
    }


    protected boolean validateW(Artifact artifact)
    throws IllegalArgumentException
    {
        logger.debug("WQAdapted.validateW");
        FLYSArtifact flys = (FLYSArtifact) artifact;

        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));

        if (rwvs == null) {
            throw new IllegalArgumentException("error_missing_wq_data");
        }

        List<Gauge> gauges = FLYSUtils.getGauges((FLYSArtifact) artifact);

        for (Gauge gauge: gauges) {
            Range range  = gauge.getRange();
            double lower = range.getA().doubleValue();
            double upper = range.getB().doubleValue();

            for (RangeWithValues rwv: rwvs) {
                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
                    compareWsWithGauge(gauge, rwv.getValues());
                }
            }
        }

        return true;
    }


    protected boolean validateQ(Artifact artifact)
    throws IllegalArgumentException
    {
        logger.debug("WQAdapted.validateQ");
        FLYSArtifact flys = (FLYSArtifact) artifact;

        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));

        if (rwvs == null) {
            throw new IllegalArgumentException("error_missing_wq_data");
        }

        List<Gauge> gauges = FLYSUtils.getGauges(flys);
        River        river = FLYSUtils.getRiver(flys);
        Wst            wst = WstFactory.getWst(river);

        for (Gauge gauge: gauges) {
            Range range  = gauge.getRange();
            double lower = range.getA().doubleValue();
            double upper = range.getB().doubleValue();

            for (RangeWithValues rwv: rwvs) {
                if (lower <= rwv.getStart() && upper >= rwv.getEnd()) {
                    compareQsWithGauge(wst, gauge, rwv.getValues());
                }
            }
        }

        return true;
    }


    protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs)
    throws IllegalArgumentException
    {
        double[] minmax = gauge != null
            ? wst.determineMinMaxQ(gauge.getRange())
            : null;

        if (minmax == null) {
            logger.warn("Could not determine min/max Q of gauge.");
            return true;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Validate Qs with:");
            logger.debug("-- Gauge: " + gauge.getName());
            logger.debug("-- Gauge min: " + minmax[0]);
            logger.debug("-- Gauge max: " + minmax[1]);
        }

        for (double q: qs) {
            if (q < minmax[0] || q > minmax[1]) {
                throw new IllegalArgumentException(
                    "error_feed_q_values_invalid");
            }
        }

        return true;
    }


    protected boolean compareWsWithGauge(Gauge gauge, double[] ws)
    throws IllegalArgumentException
    {
        double[] minmax = gauge != null
            ? gauge.determineMinMaxW()
            : null;

        if (minmax == null) {
            logger.warn("Could not determine min/max W of gauge.");
            return true;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Validate Ws with:");
            logger.debug("-- Gauge: " + gauge.getName());
            logger.debug("-- Gauge min: " + minmax[0]);
            logger.debug("-- Gauge max: " + minmax[1]);
        }

        for (double w: ws) {
            if (w < minmax[0] || w > minmax[1]) {
                throw new IllegalArgumentException(
                    "error_feed_w_values_invalid");
            }
        }

        return true;
    }


    protected RangeWithValues[] extractInput(StateData data) {
        if (data == null) {
            return null;
        }

        String dataString = (String) data.getValue();
        String[]   ranges = dataString.split(":");

        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();

        for (String range: ranges) {
            String[] parts = range.split(";");

            double lower = Double.parseDouble(parts[0]);
            double upper = Double.parseDouble(parts[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);
                }
            }

            rwv.add(new RangeWithValues(lower, upper, res));
        }

        return rwv.toArray(new RangeWithValues[rwv.size()]);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org