view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WQAdaptedInputPanel.java @ 9416:05405292a7ca

Navigationtheme panel now shows themes of dWt and WQ charts grayed out, if the current station is outside the valid range of the theme.
author gernotbelger
date Thu, 16 Aug 2018 16:28:03 +0200
parents 43c975f8ab92
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.client.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.services.WQInfoService;
import org.dive4elements.river.client.client.services.WQInfoServiceAsync;
import org.dive4elements.river.client.client.ui.wq.QDTable;
import org.dive4elements.river.client.client.ui.wq.WTable;
import org.dive4elements.river.client.shared.model.ArtifactDescription;
import org.dive4elements.river.client.shared.model.Data;
import org.dive4elements.river.client.shared.model.DataItem;
import org.dive4elements.river.client.shared.model.DataList;
import org.dive4elements.river.client.shared.model.DefaultData;
import org.dive4elements.river.client.shared.model.DefaultDataItem;
import org.dive4elements.river.client.shared.model.WQDataItem;
import org.dive4elements.river.client.shared.model.WQInfoObject;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.TitleOrientation;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
import com.smartgwt.client.widgets.form.fields.events.FocusEvent;
import com.smartgwt.client.widgets.form.fields.events.FocusHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.Tab;

/**
 * This UIProvider creates a widget to enter W or Q data for discharge
 * longitudinal section computations.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class WQAdaptedInputPanel extends AbstractWQAdaptedInputPanel implements FocusHandler {
    private static final long serialVersionUID = -3218827566805476423L;
    private static final String FIELD_WQ_MODE = "wq_isq";
    private static final String FIELD_WQ_W = "W";
    private static final String FIELD_WQ_Q = "Q";

    /** Stores the min/max values for each w range (gauge). */
    private final Map<String, double[]> wranges = new HashMap<String, double[]>();

    /** Service to fetch W/Q MainValues. */
    private final WQInfoServiceAsync wqInfoService = GWT.create(WQInfoService.class);

    /** The currently focussed Input element. */
    private DoubleArrayPanel itemWithFocus;

    private void enableWTabs() {
        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
            this.tabs.disableTab(2 * i);
            this.tabs.enableTab(2 * i + 1);
        }
    }

    private void enableQTabs() {
        for (int i = 0; i < this.doubleArrayPanels.size(); i++) {
            this.tabs.enableTab(2 * i);
            this.tabs.disableTab(2 * i + 1);
        }
    }

    /**
     * Create radio button for switching w and q input.
     * Radiobutton-change also triggers helper panel tab selection.
     */

    private final Canvas createMode(final DataList dataList) {
        final RadioGroupItem wq = new RadioGroupItem(FIELD_WQ_MODE);
        wq.setShowTitle(false);
        wq.setVertical(false);
        wq.setWidth(200);

        final LinkedHashMap<String, String> wqValues = new LinkedHashMap<String, String>();
        wqValues.put(FIELD_WQ_W, this.MSG.wqW());
        wqValues.put(WQAdaptedInputPanel.FIELD_WQ_Q, this.MSG.wqQatGauge());

        wq.setValueMap(wqValues);

        this.modes = new DynamicForm();
        this.modes.setFields(wq);
        this.modes.setWidth(200);
        wq.addChangeHandler(new ChangeHandler() {
            @Override
            public void onChange(final ChangeEvent e) {
                final DynamicForm form = e.getForm();

                if (form.getValueAsString(FIELD_WQ_MODE).contains("Q")) {
                    WQAdaptedInputPanel.this.tabs.selectTab(0);
                    enableQTabs();
                } else {
                    WQAdaptedInputPanel.this.tabs.selectTab(1);
                    enableWTabs();
                }
            }
        });

        final LinkedHashMap<String, String> initial = new LinkedHashMap<String, String>();
        initial.put(FIELD_WQ_MODE, this.FIELD_WQ_Q);
        this.modes.setValues(initial);
        this.tabs.selectTab(1);
        return this.modes;
    }

    @Override
    protected void createTabs() {
        for (int i = 0; i < this.gaugeRanges.size(); i++) {
            // Later the tabs title will get adjusted to include gauges name.
            // TODO the tabs title becomes rather long through that (i18n).

            final Tab wTab = new Tab(this.MSG.wq_table_w());
            final WTable wTable = new WTable();
            this.wTables.add(wTable);
            wTable.showSelect();
            wTab.setPane(wTable);

            final Tab qTab = new Tab(this.MSG.wq_table_q());
            final QDTable qdTable = new QDTable();
            this.qdTables.add(qdTable);
            qTab.setPane(qdTable);
            qdTable.showSelect();

            this.tabs.addTab(wTab, i * 2 + 0);
            this.tabs.addTab(qTab, i * 2 + 1);

        }

        // Defaults at "Q", first input field.
        this.tabs.selectTab(0);
        enableQTabs();
    }

    @Override
    protected void beforeCreate(final DataList data) {
    }

    @Override
    protected void afterCreate() {
        // We actually want the first Q tab to be selected and all
        // Q tabs to be enabled. I sense a bug in TabSet here, as
        // the code in the W/Q radiogroup-changehandler behaves
        // exactly vice versa (enabling Q, selecting tab 0).
        enableWTabs();
        this.tabs.selectTab(1);
    }

    @Override
    protected Canvas createWidget(final DataList dataList) {
        final VLayout layout = new VLayout();
        final Canvas mode = createMode(dataList);
        final Canvas list = createList(dataList);

        final List<WQDataItem> items = getWQItems(dataList);
        final int listHeight = ROW_HEIGHT * items.size();

        mode.setHeight(25);
        mode.setWidth(200);

        layout.addMember(mode);
        layout.addMember(list);

        layout.setHeight(25 + listHeight);
        layout.setWidth(350);

        initUserDefaults(dataList);

        return layout;
    }

    private void initUserDefaults(final DataList dataList) {
        initUserWQValues(dataList);
        initUserWQMode(dataList);
    }

    private void initUserWQMode(final DataList dataList) {
        final List<Data> allData = dataList.getAll();

        final Data dDef = getData(allData, "wq_mode");
        final DataItem def = dDef != null ? dDef.getDefault() : null;
        final String value = def != null ? def.getStringValue() : null;

        if (value != null && value.equals(FIELD_WQ_W)) {
            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_W);
        } else {
            this.modes.setValue(FIELD_WQ_MODE, FIELD_WQ_Q);
        }
    }

    private Canvas createList(final DataList dataList) {
        final VLayout layout = new VLayout();

        final List<WQDataItem> items = getWQItems(dataList);

        int i = 0;

        for (final DataItem item : items) {
            if (item instanceof WQDataItem) {
                final String title = item.getLabel(); // of form: 70.5;112.0
                final String label = item.getStringValue();

                // Rename W and Q tab to include gauges name.
                this.tabs.getTab(i * 2).setTitle(this.tabs.getTab(i * 2).getTitle() + " (" + label + ")");
                this.tabs.getTab(i * 2 + 1).setTitle(this.tabs.getTab(i * 2 + 1).getTitle() + " (" + label + ")");

                final DoubleArrayPanel dap = new DoubleArrayPanel(label, null, this, this, TitleOrientation.LEFT);

                this.wqranges.put(title, dap);
                this.doubleArrayPanels.add(dap);

                if (item instanceof WQDataItem) {
                    final WQDataItem wq = (WQDataItem) item;
                    final double[] mmQ = wq.getQRange();
                    final double[] mmW = wq.getWRange();

                    this.qranges.put(title, mmQ);
                    this.wranges.put(title, mmW);
                }

                layout.addMember(dap);
                i++;
            }

        }

        layout.setHeight(items.size() * ROW_HEIGHT);

        return layout;
    }

    @Override
    public Canvas createOld(final DataList dataList) { // TODO: OVERRIDE
        final List<Data> all = dataList.getAll();
        final Data wqData = getData(all, "wq_values");
        final Data wqMode = getData(all, "wq_isq");
        final boolean isQ = wqMode.getItems()[0].getStringValue().equals("true");
        final Canvas back = getBackButton(dataList.getState());

        final HLayout valLayout = new HLayout();
        final HLayout modeLayout = new HLayout();
        final VLayout vlayout = new VLayout();

        final Label wqLabel = new Label(dataList.getLabel());
        final Label modeLabel = new Label("");

        wqLabel.setValign(VerticalAlignment.TOP);

        wqLabel.setWidth(200);
        wqLabel.setHeight(25);
        modeLabel.setHeight(25);
        modeLabel.setWidth(200);

        valLayout.addMember(wqLabel);
        valLayout.addMember(createOldWQValues(wqData, isQ));

        valLayout.addMember(back);
        modeLayout.addMember(modeLabel);

        vlayout.addMember(valLayout);
        vlayout.addMember(modeLayout);

        return vlayout;
    }

    @Override
    public List<String> validate() {
        if (isWMode()) {
            return validateW();
        } else {
            return validateQ();
        }
    }

    private boolean isWMode() {
        final String mode = (String) this.modes.getValue(FIELD_WQ_MODE);

        return FIELD_WQ_W.equals(mode);
    }

    protected List<String> validateRange(final Map<String, double[]> ranges) {
        final List<String> errors = new ArrayList<String>();
        final NumberFormat nf = NumberFormat.getDecimalFormat();

        for (final Map.Entry<String, DoubleArrayPanel> entry : this.wqranges.entrySet()) {

            final String key = entry.getKey();
            final DoubleArrayPanel dap = entry.getValue();

            if (!dap.validateForm()) {
                errors.add(this.MSG.error_invalid_double_value());
                return errors;
            }

            final double[] mm = ranges.get(key);
            if (mm == null) {
                SC.warn(this.MSG.error_read_minmax_values());
                continue;
            }

            final double[] values = dap.getInputValues();
            final double[] good = new double[values.length];

            int idx = 0;

            final List<String> tmpErrors = new ArrayList<String>();
            for (final double value : values) {
                if (value < mm[0] || value > mm[1]) {
                    String tmp = this.MSG.error_validate_range();
                    tmp = tmp.replace("$1", nf.format(value));
                    tmp = tmp.replace("$2", nf.format(mm[0]));
                    tmp = tmp.replace("$3", nf.format(mm[1]));
                    tmpErrors.add(tmp);
                } else {
                    good[idx++] = value;
                }
            }

            final double[] justGood = new double[idx];
            for (int i = 0; i < justGood.length; i++) {
                justGood[i] = good[i];
            }

            if (!tmpErrors.isEmpty()) {
                dap.setValues(justGood);

                errors.addAll(tmpErrors);
            }
        }

        return errors;
    }

    private Data getWQMode() {
        final String wqMode = this.modes.getValueAsString(FIELD_WQ_MODE);
        String value = "false";
        if (wqMode.equals("Q")) {
            value = "true";
        }
        final DataItem item = new DefaultDataItem("wq_isq", "wq_isq", value);
        final Data mode = new DefaultData("wq_isq", null, null, new DataItem[] { item });

        return mode;
    }

    @Override
    public Data[] getData() {
        final Data mode = getWQMode();
        final Data values = getWQValues("wq_values");

        return new Data[] { mode, values };
    }

    private List<String> validateW() {
        return validateRange(this.wranges);
    }

    private List<String> validateQ() {
        return validateRange(this.qranges);
    }

    @Override
    public void onFocus(final FocusEvent event) {
        this.itemWithFocus = (DoubleArrayPanel) event.getForm();
        // Switch to respective tab.
        // TODO which makes a focus loss
        final int inputIndex = this.doubleArrayPanels.indexOf(this.itemWithFocus);
        this.tabs.selectTab(inputIndex * 2 + (isWMode() ? 0 : 1));
    }

    protected void callMainValuesService(final String locale, final String river, final double start, final double end,
            final AsyncCallback<WQInfoObject[]> cb) {
        this.wqInfoService.getWQInfo(locale, river, start, end, cb);
    }

    @Override
    protected void fetchWQData() {
        {
            final Config config = Config.getInstance();
            final String locale = config.getLocale();

            final ArtifactDescription adescr = this.artifact.getArtifactDescription();
            final DataList[] data = adescr.getOldData();

            final String river = getRiverName(data);

            int i = 0;

            // Get Data for respective gauge.
            for (final Double[] range : this.gaugeRanges) {
                // Gauge ranges overlap, move start and end a bit closer
                // to each other.
                final double rDiff = (range[1] - range[0]) / 10d;
                final int fi = i;
                // TODO: HIER MÜSSTE EIN SERVICE HIN, der den größten gemeinsamen zeitlichen Bereich der daily_discharge_values aller
                // Pegel ermittelt

                final AsyncCallback<WQInfoObject[]> cb = new AsyncCallback<WQInfoObject[]>() {
                    @Override
                    public void onFailure(final Throwable caught) {
                        GWT.log("Could not recieve wq informations.");
                        SC.warn(caught.getMessage());
                    }

                    @Override
                    public void onSuccess(final WQInfoObject[] wqi) {
                        final int num = wqi != null ? wqi.length : 0;
                        GWT.log("Received " + num + " wq informations (" + fi + ".");

                        if (num == 0) {
                            return;
                        }

                        addWQInfo(wqi, fi, null);
                    }
                };

                callMainValuesService(locale, river, range[0] + rDiff, range[1] - rDiff, cb);

                i++;
            }
        }

    }

    @Override
    protected String getWQInfoMsg() {
        return null;
    }
}

http://dive4elements.wald.intevation.org