view artifacts/src/main/java/org/dive4elements/river/artifacts/states/WQSelect.java @ 9425:3f49835a00c3

Extended CrossSectionFacet so it may fetch different data from within the artifact result. Also allows to have acces to the potentially already computed artifact result via its normal computation cache.
author gernotbelger
date Fri, 17 Aug 2018 15:31:02 +0200
parents 2323d005f9a5
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.text.NumberFormat;

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.artifacts.common.utils.XMLUtils.ElementCreator;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.access.ComputationRangeAccess;
import org.dive4elements.river.artifacts.access.RangeAccess;
import org.dive4elements.river.artifacts.access.RangeAccess.KM_MODE;
import org.dive4elements.river.artifacts.model.WstFactory;
import org.dive4elements.river.artifacts.model.WstValueTable;
import org.dive4elements.river.artifacts.model.WstValueTableFactory;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.River;
import org.dive4elements.river.model.Wst;
import org.dive4elements.river.utils.RiverUtils;
import org.w3c.dom.Element;

import gnu.trove.TDoubleArrayList;

/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class WQSelect extends DefaultState {

    /** The log used in this class. */
    private static Logger log = Logger.getLogger(WQSelect.class);

    /** The default step width for Qs. */
    public static final String DEFAULT_STEP_Q = "50";

    /** The default step width for Qs. */
    public static final String DEFAULT_STEP_W = "30";

    /** The max number of steps for Qs and Ws. */
    public static final int MAX_STEPS = 30;

    /** The name of the 'mode' field. */
    public static final String WQ_MODE = "wq_isq";

    /** Them name fo the 'free' field. */
    public static final String WQ_FREE = "wq_isfree";

    /** The name of the 'selection' field. */
    public static final String WQ_SELECTION = "wq_isrange";

    /** The name of the 'from' field. */
    public static final String WQ_FROM = "wq_from";

    /** The name of the 'to' field. */
    public static final String WQ_TO = "wq_to";

    /** The name of the 'step' field. */
    public static final String WQ_STEP = "wq_step";

    /** The name of the 'single' field. */
    public static final String WQ_SINGLE = "wq_single";

    /**
     * The default constructor that initializes an empty State object.
     */
    public WQSelect() {
    }

    @Override
    protected Element createStaticData(final D4EArtifact flys, final ElementCreator creator, final CallContext cc, final String name, final String value,
            final String type) {
        if (!name.equals(WQ_SINGLE)) {
            return super.createStaticData(flys, creator, cc, name, value, type);
        }

        final Boolean isQ = flys.getDataAsBoolean(WQ_MODE);
        final Boolean isFree = flys.getDataAsBoolean(WQ_FREE);

        final Element dataElement = creator.create("data");
        creator.addAttr(dataElement, "name", name, true);
        creator.addAttr(dataElement, "type", type, true);

        final Element itemElement = creator.create("item");
        creator.addAttr(itemElement, "value", value, true);

        String label;

        if (!isQ || isFree) {
            label = getLabel(flys, cc, value);
        } else {
            label = getSpecialLabel(flys, cc, value);
        }

        creator.addAttr(itemElement, "label", label, true);

        dataElement.appendChild(itemElement);

        return dataElement;
    }

    protected static String getLabel(final D4EArtifact winfo, final CallContext cc, final String raw) {
        final String[] values = raw.split(" ");

        if (values.length < 1) {
            return null;
        }

        final StringBuilder label = new StringBuilder();

        final NumberFormat nf = NumberFormat.getInstance(Resources.getLocale(cc.getMeta()));

        for (final String value : values) {
            try {
                final double v = Double.parseDouble(value.trim());

                final String formatted = nf.format(v);

                if (label.length() > 0) {
                    label.append(';');
                }
                label.append(formatted);
            }
            catch (final NumberFormatException nfe) {
                // do nothing here
            }
        }

        return label.toString();
    }

    protected static String getSpecialLabel(final D4EArtifact winfo, final CallContext cc, final String raw) {
        final String[] values = raw.split(" ");

        if (values.length < 1) {
            return null;
        }

        final NumberFormat nf = NumberFormat.getInstance(Resources.getLocale(cc.getMeta()));

        final RangeAccess rangeAccess = new RangeAccess(winfo);
        final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());

        final StringBuilder label = new StringBuilder();

        for (final String value : values) {
            try {
                final double v = Double.parseDouble(value.trim());

                String tmp = nf.format(v);
                final String mv = RiverUtils.getNamedMainValue(gauge, v);

                if (mv != null && mv.length() > 0) {
                    tmp = mv + ": " + tmp;
                    log.debug("Add main value: '" + mv + "'");
                }
                if (label.length() > 0) {
                    label.append(';');
                }
                label.append(tmp);
            }
            catch (final NumberFormatException nfe) {
                // do nothing here
            }
        }

        return label.toString();
    }

    @Override
    protected Element createData(final XMLUtils.ElementCreator cr, final Artifact artifact, final StateData data, final CallContext context) {
        final Element select = ProtocolUtils.createArtNode(cr, "select", null, null);

        cr.addAttr(select, "name", data.getName(), true);

        final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);

        // XXX: DEAD CODE
        /*
         * Element choices = ProtocolUtils.createArtNode(
         * cr, "choices", null, null);
         */

        label.setTextContent(Resources.getMsg(context.getMeta(), data.getName(), data.getName()));

        select.appendChild(label);

        return select;
    }

    @Override
    protected Element[] createItems(final XMLUtils.ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
        final D4EArtifact flys = (D4EArtifact) artifact;

        final double[] minmaxW = determineMinMaxW(flys);
        final double[] minmaxWFree = determineMinMaxWFree(flys);
        final double[] minmaxQ = determineMinMaxQAtGauge(flys);
        final double[] minmaxQFree = determineMinMaxQ(flys);

        if (name.equals("wq_from")) {
            final Element minW = createItem(cr, new String[] { "minW", String.valueOf(minmaxW[0]) });

            final Element minQ = createItem(cr, new String[] { "minQ", String.valueOf(minmaxQ[0]) });

            final Element minQFree = createItem(cr, new String[] { "minQFree", String.valueOf(minmaxQFree[0]) });

            final Element minWFree = createItem(cr, new String[] { "minWFree", String.valueOf(minmaxWFree[0]) });

            return new Element[] { minW, minQ, minQFree, minWFree };
        } else if (name.equals("wq_to")) {
            final Element maxW = createItem(cr, new String[] { "maxW", String.valueOf(minmaxW[1]) });

            final Element maxQ = createItem(cr, new String[] { "maxQ", String.valueOf(minmaxQ[1]) });

            final Element maxQFree = createItem(cr, new String[] { "maxQFree", String.valueOf(minmaxQFree[1]) });

            final Element maxWFree = createItem(cr, new String[] { "maxWFree", String.valueOf(minmaxWFree[1]) });

            return new Element[] { maxW, maxQ, maxQFree, maxWFree };
        } else {
            final Element stepW = createItem(cr, new String[] { "stepW", String.valueOf(getStepsW(minmaxW[0], minmaxW[1])) });
            final Element stepQ = createItem(cr, new String[] { "stepQ", String.valueOf(getStepsQ(minmaxQ[0], minmaxQ[1])) });
            final Element stepQFree = createItem(cr, new String[] { "stepQFree", String.valueOf(getStepsQ(minmaxQFree[0], minmaxQFree[1])) });
            final Element stepWFree = createItem(cr, new String[] { "stepWFree", String.valueOf(getStepsW(minmaxWFree[0], minmaxWFree[1])) });

            return new Element[] { stepW, stepQ, stepQFree, stepWFree };
        }
    }

    protected static double getStepsW(final double min, final double max) {
        final double diff = min < max ? max - min : min - max;
        final double step = diff / MAX_STEPS;

        if (step < 10) {
            return getSteps(step, 1);
        } else if (step < 100) {
            return getSteps(step, 10);
        } else if (step < 1000) {
            return getSteps(step, 100);
        } else {
            return step;
        }
    }

    protected static double getStepsQ(final double min, final double max) {
        final double diff = min < max ? max - min : min - max;
        final double step = diff / MAX_STEPS;

        if (step < 10) {
            return getSteps(step, 1);
        } else if (step < 100) {
            return getSteps(step, 10);
        } else if (step < 1000) {
            return getSteps(step, 100);
        } else {
            return step;
        }
    }

    protected static double getSteps(final double steps, final double factor) {
        final int fac = (int) (steps / factor);
        final double diff = steps - fac * factor;

        if (diff == 0) {
            return steps;
        }

        return factor * (fac + 1);
    }

    // ist mit super identisch
    // protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
    // 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);
    //
    // return item;
    // }

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

    /**
     * Determines the min and max W value for the current gauge. If no min and
     * max values could be determined, this method will return
     * [Double.MIN_VALUE, Double.MAX_VALUE].
     *
     * @param artifact
     *            The D4EArtifact.
     *
     * @return the min and max W values for the current gauge.
     */
    protected double[] determineMinMaxW(final D4EArtifact winfo) {
        log.debug("WQSelect.determineCurrentGauge");

        final RangeAccess rangeAccess = new RangeAccess(winfo);
        final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());

        final double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null;

        final double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE;
        final double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE;

        return new double[] { minW, maxW };
    }

    /**
     * Determines the min and max W value. If no min and
     * max values could be determined, this method will return
     * [Double.MIN_VALUE, Double.MAX_VALUE].
     *
     * @param artifact
     *            The D4EArtifact.
     *
     * @return the min and max W values.
     */
    protected double[] determineMinMaxWFree(final D4EArtifact winfo) {
        log.debug("WQSelect.determineMinMaxWFree");

        final WstValueTable valueTable = WstValueTableFactory.getTable(RiverUtils.getRiver(winfo));

        final ComputationRangeAccess access = new ComputationRangeAccess(winfo);

        double[] minmaxW = null;
        if (valueTable != null) {

            final double startKm = access.getStartKm();
            // Use the start km to determine the min max values.
            minmaxW = valueTable.getMinMaxW(startKm);
        }
        return minmaxW != null ? minmaxW : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
    }

    /**
     * Determines the min and max Q value for the current gauge. If no min and
     * max values could be determined, this method will return
     * [Double.MIN_VALUE, Double.MAX_VALUE].
     *
     * @param artifact
     *            The D4EArtifact.
     *
     * @return the min and max Q values for the current gauge.
     */
    protected double[] determineMinMaxQAtGauge(final D4EArtifact winfo) {
        log.debug("WQSelect.determineMinMaxQAtGauge");

        final RangeAccess rangeAccess = new RangeAccess(winfo);
        final River river = rangeAccess.getRiver();
        final Gauge gauge = river.determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());

        final Wst wst = WstFactory.getWst(river);

        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 };
    }

    /**
     * Determines the min and max Q value for the current kilometer range. If no
     * min and max values could be determined, this method will return
     *
     * @param artifact
     *            The D4EArtifact.
     *
     * @return the min and max Q values for the current kilometer range.
     */
    protected double[] determineMinMaxQ(final D4EArtifact winfo) {
        log.debug("WQSelect.determineMinMaxQ");

        final WstValueTable valueTable = WstValueTableFactory.getTable(RiverUtils.getRiver(winfo));

        final ComputationRangeAccess access = new ComputationRangeAccess(winfo);

        double[] minmaxQ = null;
        if (valueTable != null) {

            final KM_MODE mode = access.getKmRangeMode();
            switch (mode) {
            case RANGE: {
                final double[] km = access.getFromToStep();
                minmaxQ = valueTable.getMinMaxQ(km[0], km[1], km[2]);
                break;
            }

            case DISTANCE_ONLY: {
                minmaxQ = valueTable.getMinMaxQ(access.getFrom(), access.getTo(), 0.1);
                break;
            }

            default: {
                final double[] km = access.getKms();
                minmaxQ = valueTable.getMinMaxQ(km[0]);
                for (int i = 1; i < km.length; i++) {
                    final double[] tmp = valueTable.getMinMaxQ(km[i]);
                    if (tmp[0] < minmaxQ[0]) {
                        minmaxQ[0] = tmp[0];
                    }
                    if (tmp[1] > minmaxQ[1]) {
                        minmaxQ[1] = tmp[1];
                    }
                }
                break;
            }
            }
        }
        return minmaxQ != null ? minmaxQ : new double[] { Double.MIN_VALUE, Double.MAX_VALUE };
    }

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

        final D4EArtifact flys = (D4EArtifact) artifact;

        final StateData data = getData(flys, WQ_SELECTION);
        final boolean isRange = data != null ? Boolean.valueOf((String) data.getValue()) : false;

        if (!isRange) {
            return validateSingle(flys);
        } else {
            return validateRange(flys);
        }
    }

    protected boolean validateBounds(final double fromValid, final double toValid, final double from, final double to, final double step)
            throws IllegalArgumentException {
        log.debug("RangeState.validateRange");

        if (from < fromValid) {
            log.error("Invalid 'from'. " + from + " is smaller than " + fromValid);
            throw new IllegalArgumentException("error_feed_from_out_of_range");
        } else if (to > toValid) {
            log.error("Invalid 'to'. " + to + " is bigger than " + toValid);
            throw new IllegalArgumentException("error_feed_to_out_of_range");
        }

        return true;
    }

    protected boolean validateSingle(final D4EArtifact artifact) throws IllegalArgumentException {
        log.debug("WQSelect.validateSingle");

        final StateData data = getData(artifact, WQ_SINGLE);

        final String tmp = data != null ? (String) data.getValue() : null;

        if (tmp == null || tmp.length() == 0) {
            throw new IllegalArgumentException("error_empty_state");
        }

        final String[] strValues = tmp.split(" ");
        final TDoubleArrayList all = new TDoubleArrayList();

        for (final String strValue : strValues) {
            try {
                all.add(Double.parseDouble(strValue));
            }
            catch (final NumberFormatException nfe) {
                log.warn(nfe, nfe);
            }
        }

        all.sort();

        final RiverUtils.WQ_MODE mode = RiverUtils.getWQMode(artifact);

        log.debug("WQ Mode: " + mode);

        double[] minmax = null;

        if (mode == RiverUtils.WQ_MODE.WGAUGE) {
            minmax = determineMinMaxW(artifact);
        } else if (mode == RiverUtils.WQ_MODE.QGAUGE) {
            minmax = determineMinMaxQAtGauge(artifact);
        } else if (mode == RiverUtils.WQ_MODE.QFREE) {
            minmax = determineMinMaxQ(artifact);
        } else {
            minmax = determineMinMaxWFree(artifact);
        }

        final double min = all.get(0);
        final double max = all.get(all.size() - 1);

        log.debug("Inserted min value = " + min);
        log.debug("Inserted max value = " + max);

        return validateBounds(minmax[0], minmax[1], min, max, 0d);
    }

    protected boolean validateRange(final D4EArtifact artifact) throws IllegalArgumentException {
        log.debug("WQSelect.validateRange");

        final RiverUtils.WQ_MODE mode = RiverUtils.getWQMode(artifact);

        if (mode == null) {
            throw new IllegalArgumentException("error_feed_invalid_wq_mode");
        }

        final StateData dFrom = artifact.getData(WQ_FROM);
        final StateData dTo = artifact.getData(WQ_TO);
        final StateData dStep = artifact.getData(WQ_STEP);

        final String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
        final String toStr = dTo != null ? (String) dTo.getValue() : null;
        final String stepStr = dStep != null ? (String) dStep.getValue() : null;

        if (fromStr == null || toStr == null || stepStr == null) {
            throw new IllegalArgumentException("error_empty_state");
        }

        try {
            final double from = Double.parseDouble(fromStr);
            final double to = Double.parseDouble(toStr);
            final double step = Double.parseDouble(stepStr);

            if (mode == RiverUtils.WQ_MODE.WGAUGE) {
                return validateGaugeW(artifact, from, to, step);
            } else if (mode == RiverUtils.WQ_MODE.QGAUGE) {
                return validateGaugeQ(artifact, from, to, step);
            } else if (mode == RiverUtils.WQ_MODE.QFREE) {
                return validateFreeQ(artifact, from, to, step);
            } else if (mode == RiverUtils.WQ_MODE.WFREE) {
                return validateFreeW(artifact, from, to, step);
            } else {
                throw new IllegalArgumentException("error_feed_invalid_wq_mode");
            }
        }
        catch (final NumberFormatException nfe) {
            throw new IllegalArgumentException("error_feed_number_format");
        }
    }

    /**
     * Validates the inserted W values.
     *
     * @param artifact
     *            The owner artifact.
     * @param from
     *            The lower value of the W range.
     * @param to
     *            The upper value of the W range.
     * @param step
     *            The step width.
     *
     * @return true, if everything was fine, otherwise an exception is thrown.
     */
    protected boolean validateGaugeW(final D4EArtifact artifact, final double from, final double to, final double step) throws IllegalArgumentException {
        log.debug("WQSelect.validateGaugeW");

        final double[] minmaxW = determineMinMaxW(artifact);

        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
    }

    /**
     * Validates the inserted Q values based on the Q range for the current
     * gauge.
     *
     * @param artifact
     *            The owner artifact.
     * @param from
     *            The lower value of the Q range.
     * @param to
     *            The upper value of the Q range.
     * @param step
     *            The step width.
     *
     * @return true, if everything was fine, otherwise an exception is thrown.
     */
    protected boolean validateGaugeQ(final D4EArtifact artifact, final double from, final double to, final double step) throws IllegalArgumentException {
        log.debug("WQSelect.validateGaugeQ");

        final double[] minmaxQ = determineMinMaxQAtGauge(artifact);

        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
    }

    /**
     * Validates the inserted Q values based on the Q range for the current
     * kilometer range.
     *
     * @param artifact
     *            The owner artifact.
     * @param from
     *            The lower value of the Q range.
     * @param to
     *            The upper value of the Q range.
     * @param step
     *            The step width.
     *
     * @return true, if everything was fine, otherwise an exception is thrown.
     */
    protected boolean validateFreeQ(final D4EArtifact artifact, final double from, final double to, final double step) throws IllegalArgumentException {
        log.debug("WQSelect.validateFreeQ");

        final double[] minmaxQ = determineMinMaxQ(artifact);

        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
    }

    /**
     * Validates the inserted W values based on the W range for the current
     * kilometer range.
     *
     * @param artifact
     *            The owner artifact.
     * @param from
     *            The lower value of the W range.
     * @param to
     *            The upper value of the W range.
     * @param step
     *            The step width.
     *
     * @return true, if everything was fine, otherwise an exception is thrown.
     */
    protected boolean validateFreeW(final D4EArtifact artifact, final double from, final double to, final double step) throws IllegalArgumentException {
        log.debug("WQSelect.validateFreeW");

        final double[] minmaxW = determineMinMaxWFree(artifact);

        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
    }

}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org