view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/NaviChartOutputTab.java @ 9263:abf14917be32

Moved stepping behaviour of NaviOutputChart into an exchangeable strategy. Allows for distinct values stepping of sinfo flood duration.
author gernotbelger
date Tue, 17 Jul 2018 19:48:18 +0200
parents 850ce16034e9
children 05405292a7ca
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.chart;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.ui.CollectionView;
import org.dive4elements.river.client.shared.model.AbstractFixBunduArtifact;
import org.dive4elements.river.client.shared.model.Artifact;
import org.dive4elements.river.client.shared.model.Collection;
import org.dive4elements.river.client.shared.model.CollectionItem;
import org.dive4elements.river.client.shared.model.FixFilter;
import org.dive4elements.river.client.shared.model.OutputMode;
import org.dive4elements.river.client.shared.model.SINFOArtifact;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.NumberFormat;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Button;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.form.fields.events.KeyPressEvent;
import com.smartgwt.client.widgets.form.fields.events.KeyPressHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;

/**
 * Tab representing and showing one Chart-output with a "navi" thing.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class NaviChartOutputTab extends ChartOutputTab implements TabSelectedHandler {
    private TextItem currentkm;

    private final NumberFormat kmFormat = NumberFormat.getDecimalFormat();

    private INaviChartStepper stepper;

    public NaviChartOutputTab(final String title, final Collection collection, final OutputMode mode, final CollectionView collectionView) {
        super(title, collection, mode, collectionView);

        this.stepper = new NilNaviChartStepper();

        this.right.removeChild(this.chart);
        this.right.addChild(createNaviChart());
        collectionView.registerTabHandler(this);
    }

    protected Canvas createNaviChart() {
        final Artifact art = this.collectionView.getArtifact();
        final VLayout root = new VLayout();
        root.setWidth100();
        root.setHeight100();

        final HLayout layout = new HLayout();
        layout.setAlign(Alignment.CENTER);

        final DynamicForm form = new DynamicForm();
        final Button lower = new Button("<<");
        lower.setWidth(30);
        final Button upper = new Button(">>");
        upper.setWidth(30);
        this.currentkm = new TextItem();
        this.currentkm.setWidth(60);
        this.currentkm.setShowTitle(false);

        form.setFields(this.currentkm);
        form.setWidth(60);

        this.stepper = createStepper(art);

        // Always jump to the from km when initialized.
        final double currentKm = this.stepper.getCurrentKm();
        this.collectionView.setCurrentKm(currentKm);
        this.currentkm.setValue(this.kmFormat.format(currentKm));

        lower.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final ClickEvent ce) {
                updateChartDown();
            }
        });

        upper.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final ClickEvent ce) {
                updateChartUp();
            }
        });

        this.currentkm.addKeyPressHandler(new KeyPressHandler() {
            @Override
            public void onKeyPress(final KeyPressEvent kpe) {

                if (!kpe.getKeyName().equals("Enter"))
                    return;

                if (kpe.getItem().getValue() != null) {
                    final String kmText = kpe.getItem().getValue().toString();
                    updateChartKm(kmText);
                }
            }
        });
        layout.addMember(lower);
        layout.addMember(form);
        layout.addMember(upper);

        root.addMember(this.chart);
        root.addMember(layout);
        return root;
    }

    private INaviChartStepper createStepper(final Artifact art) {

        if (art instanceof AbstractFixBunduArtifact) {
            final AbstractFixBunduArtifact fix = (AbstractFixBunduArtifact) art;
            final FixFilter fixFilter = fix.getFilter();

            final double fromKm = fixFilter.getLowerKm();
            final double toKm = fixFilter.getUpperKm();

            final String s = fix.getArtifactDescription().getDataValueAsString("ld_step");
            try {
                final double ds = Double.parseDouble(s);
                return new MinMaxStepNaviChartStepper(fromKm, toKm, ds);
            }
            catch (final NumberFormatException nfe) {
                return new MinMaxStepNaviChartStepper(fromKm, toKm, 100d);
            }
        } else if (art instanceof SINFOArtifact) {
            /* special case for SINFO-Flood-Duration */
            final SINFOArtifact sinfo = (SINFOArtifact) art;

            final CollectionItem item = this.collection.getItem(sinfo.getUuid());

            final Set<Double> validKms = sinfo.getValidDurationChartKms(item);
            return new DistinctValuesNaviChartStepper(validKms);
        } else {
            // Probably WINFOArtifact kind of artifact.

            double fromKm;
            double toKm;

            final double[] kmRange = art.getArtifactDescription().getKMRange();
            if (kmRange != null && kmRange.length == 2) {
                fromKm = kmRange[0];
                toKm = kmRange[1];
            } else {
                GWT.log("No KM range in description found.");
                return new NilNaviChartStepper();
            }

            final String ld_step = art.getArtifactDescription().getDataValueAsString("ld_step");
            try {
                final Double step = Double.valueOf(ld_step);
                return new MinMaxStepNaviChartStepper(fromKm, toKm, step);
            }
            catch (final Exception e) {
                GWT.log("No ld_steps data or not parsable.", e);
                return new MinMaxStepNaviChartStepper(fromKm, toKm, 100d);
            }
        }
    }

    protected void updateChartKm(final String kmText) {

        NaviChartOutputTab.this.tbarPanel.deselectControls();

        try {
            final double d = this.kmFormat.parse(kmText);

            final double validCurrentKm = this.stepper.setValidCurrentKm(d);
            updateCurrentKm(validCurrentKm);
        }
        catch (final NumberFormatException e) {
            SC.warn("Invalid value: " + kmText);
            // do nothing, but an error message would be nice
        }
    }

    /**
     * Callback when km-up-button is clicked.
     * Increases collectionViews KM and refreshes view.
     */
    protected void updateChartUp() {

        this.tbarPanel.deselectControls();

        final double nextKm = this.stepper.stepForward();
        updateCurrentKm(nextKm);
    }

    /**
     * Callback when km-down-button is clicked.
     * Decreases collectionViews KM and refreshes view.
     */
    protected void updateChartDown() {

        this.tbarPanel.deselectControls();

        final double prevKm = this.stepper.stepBackward();
        updateCurrentKm(prevKm);
    }

    private void updateCurrentKm(final double currentKm) {

        this.collectionView.setCurrentKm(currentKm);

        this.tbarPanel.updateLinks();

        updateChartPanel();
        updateChartInfo();

        this.currentkm.setValue(this.kmFormat.format(currentKm));
        this.tbarPanel.onZoom(null);

    }

    /**
     * Returns the existing chart panel.
     *
     * @return the existing chart panel.
     */
    @Override
    public Canvas getChartPanel() {
        return this.chart;
    }

    /**
     * Builds the URL that points to the chart image.
     *
     * @param width
     *            The width of the requested chart.
     * @param height
     *            The height of the requested chart.
     * @param xr
     *            Optional x range (used for zooming).
     * @param yr
     *            Optional y range (used for zooming).
     *
     * @return the URL to the chart image.
     */
    @Override
    protected String getImgUrl(final int width, final int height) {
        final Config config = Config.getInstance();

        String imgUrl = GWT.getModuleBaseURL();
        imgUrl += "chart";
        imgUrl += "?uuid=" + this.collection.identifier();
        imgUrl += "&type=" + this.mode.getName();
        imgUrl += "&locale=" + config.getLocale();
        imgUrl += "&timestamp=" + new Date().getTime();
        imgUrl += "&width=" + Integer.toString(width);
        imgUrl += "&height=" + Integer.toString(height - 40);

        final Number[] zoom = getZoomValues();

        if (zoom != null) {
            if (zoom[0].intValue() != 0 || zoom[1].intValue() != 1) {
                // a zoom range of 0-1 means displaying the whole range. In such
                // case we don't need to zoom.
                imgUrl += "&minx=" + zoom[0];
                imgUrl += "&maxx=" + zoom[1];
            }

            if (zoom[2].intValue() != 0 || zoom[3].intValue() != 1) {
                // a zoom range of 0-1 means displaying the whole range. In such
                // case we don't need to zoom.
                imgUrl += "&miny=" + zoom[2];
                imgUrl += "&maxy=" + zoom[3];
            }
        }

        if (this.collectionView.getCurrentKm() == -1) {
            // REMARK: this happens, because we get called from the constructor of our super class

            if (this.collectionView.getArtifact() instanceof AbstractFixBunduArtifact) {
                final AbstractFixBunduArtifact fix = (AbstractFixBunduArtifact) this.collectionView.getArtifact();
                this.collectionView.setCurrentKm(fix.getFilter().getLowerKm());
            }
            else
                this.collectionView.setCurrentKm(this.collectionView.getArtifact().getArtifactDescription().getKMRange()[0]);
        }

        if (this.collectionView.getCurrentKm() != -1) {
            imgUrl += "&currentKm=" + this.collectionView.getCurrentKm();
        }

        return imgUrl;
    }

    @Override
    public void onTabSelected(final TabSelectedEvent tse) {
        if (this.equals(tse.getTab())) {

            final double currentKm = this.collectionView.getCurrentKm();

            final double validCurrentKm = this.stepper.setValidCurrentKm(currentKm);
            updateCurrentKm(validCurrentKm);
        }
    }

    @Override
    public Map<String, String> getChartAttributes() {
        Map<String, String> attr = new HashMap<String, String>();

        attr = super.getChartAttributes();
        attr.put("km", String.valueOf(this.collectionView.getCurrentKm()));

        return attr;
    }

    /**
     * In contrast to supers implementation, include the currently selected
     * km in the url.
     */
    @Override
    public String getExportUrl(final int width, final int height, final String format) {
        String url = super.getExportUrl(width, height, format);
        if (this.collectionView.getCurrentKm() != -1) {
            url += "&currentKm=" + this.collectionView.getCurrentKm();
        }
        return url;
    }
}

http://dive4elements.wald.intevation.org