view artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQAdapted.java @ 9277:2323d005f9a5

compile error fix
author gernotbelger
date Fri, 20 Jul 2018 10:39:02 +0200
parents 5e38e2924c07
children
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.artifacts.states;

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

import org.apache.log4j.Logger;
import org.dive4elements.artifactdatabase.ProtocolUtils;
import org.dive4elements.artifactdatabase.data.StateData;
import org.dive4elements.artifacts.Artifact;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.artifacts.common.utils.XMLUtils;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.access.RangeAccess;
import org.dive4elements.river.artifacts.model.RangeWithValues;
import org.dive4elements.river.artifacts.model.WstFactory;
import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.Range;
import org.dive4elements.river.model.River;
import org.dive4elements.river.model.Wst;
import org.dive4elements.river.utils.RiverUtils;
import org.w3c.dom.Element;

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

    /** The log used in this state. */
    private static Logger log = 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 final int order;

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

        @Override
        public int compare(final Gauge a, final Gauge b) {
            return this.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 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(final XMLUtils.ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
        log.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 {
            log.warn("Unknown data object: " + name);
            return null;
        }
    }

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

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

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

    /** Create the items for input to the ranges per mode. */
    protected Element[] createValueItems(final XMLUtils.ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
        log.debug("WQAdapted.createValueItems");

        final D4EArtifact flysArtifact = (D4EArtifact) artifact;

        final RangeAccess rangeAccess = new RangeAccess(flysArtifact);
        final double[] dist = rangeAccess.getKmRange();
        // TODO use Access to get River and gauges.
        final River river = RiverUtils.getRiver(flysArtifact);
        final Wst wst = WstFactory.getWst(river);
        final List<Gauge> gauges = RiverUtils.getGauges(flysArtifact);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /** In obj: 0 is label, 1 is value. */
    protected Element createItem(final XMLUtils.ElementCreator cr, final Object obj, final double[] q, final double[] w) {
        final Element item = ProtocolUtils.createArtNode(cr, "item", null, null);
        final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
        final Element value = ProtocolUtils.createArtNode(cr, "value", null, null);

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

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

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

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

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

        return item;
    }

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

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

        final 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(final Gauge gauge, final Wst wst) {
        log.debug("WQAdapted.determineMinMaxQ");

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

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

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

    /** Indicate client which input elements to use. */
    @Override
    protected String getUIProvider() {
        return "wq_panel_adapted";
    }

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

        final D4EArtifact flys = (D4EArtifact) artifact;
        final StateData data = getData(flys, FIELD_WQ_MODE);

        final String mode = data != null ? (String) data.getValue() : null;
        final 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(final Artifact artifact) throws IllegalArgumentException {
        log.debug("WQAdapted.validateW");
        final D4EArtifact flys = (D4EArtifact) artifact;

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

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

        final List<Gauge> gauges = RiverUtils.getGauges((D4EArtifact) artifact);

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

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

        return true;
    }

    protected boolean validateQ(final Artifact artifact) throws IllegalArgumentException {
        log.debug("WQAdapted.validateQ");
        final D4EArtifact flys = (D4EArtifact) artifact;

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

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

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

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

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

        return true;
    }

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

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

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

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

        return true;
    }

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

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

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

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

        return true;
    }

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

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

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

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

            final double lower = Double.parseDouble(parts[0]);
            final double upper = Double.parseDouble(parts[1]);

            final String[] values = parts[3].split(",");

            final int num = values.length;
            final double[] res = new double[num];

            for (int i = 0; i < num; i++) {
                try {
                    res[i] = Double.parseDouble(values[i]);
                }
                catch (final NumberFormatException nfe) {
                    log.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