view artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATWriter.java @ 7597:fca46ce8e4f5

(issue1225) Implement Magic labels. There is now a new value in the chartsettings "Suggested Label" which is hidden in the property editor. A suggested label is the label that combines the label's of all processors that wrote data to an axis. This suggested label is set as the label when the user has not overwritten the label.
author Andre Heinecke <aheinecke@intevation.de>
date Mon, 25 Nov 2013 14:58:14 +0100
parents 65a5deaee769
children f4b3861712a4
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.exports.fixings;

import org.dive4elements.artifacts.CallMeta;

import org.dive4elements.river.artifacts.math.fitting.Function;

import org.dive4elements.river.artifacts.model.Parameters;

import org.dive4elements.river.artifacts.resources.Resources;

import org.dive4elements.river.exports.ATWriter;

import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.River;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;

import java.util.Locale;

import org.apache.log4j.Logger;

/** Export Fixation Analysis Results to AT. */
public class FixATWriter
{
    /** Private logger. */
    private static Logger log = Logger.getLogger(FixATWriter.class);

    public static final String I18N_HEADER_KEY =
        "fix.export.at.header";

    public static final String I18N_GAUGE_HEADER_KEY =
        "fix.export.at.gauge.header";

    public static final String I18N_HEADER_DEFAULT =
        "Exported fixings discharge curve for {0} {0}-km: {1}";

    public static final String I18N_GAUGE_HEADER_DEFAULT =
        "Exported fixings discharge curve for {0}, gauge: {1} datum[{3}] = {2}";

    public static final String [] Q_MAX_COLUMN = new String [] { "max_q" };

    private static final int    MAX_ITERATIONS = 10000;
    private static final double EPSILON        = 1e-8;
    private static final double MIN_Q          = 1e-4;

    protected Function   function;
    protected Parameters parameters;

    public FixATWriter() {
    }

    public FixATWriter(Function function, Parameters parameters) {
        this.function   = function;
        this.parameters = parameters;
    }

    public void write(
        Writer   writer,
        CallMeta meta,
        River    river,
        double   km
    )
    throws IOException {
        PrintWriter out = new PrintWriter(writer);

        int subtractPNP = 0;
        // Special case handling for at's at gauges
        Gauge gauge = river.determineGaugeByPosition(km);
        if (Math.abs(km - gauge.getStation().doubleValue()) < 1e-4) {
            printGaugeHeader(out, meta, river, gauge);
            subtractPNP = (int)Math.round(gauge.getDatum().doubleValue() * 100);
        } else {
            printHeader(out, meta, river.getName(), km);
        }

        double [] coeffs = parameters.interpolate(
            "km", km, function.getParameterNames());

        double [] qMax = parameters.interpolate(
            "km", km, Q_MAX_COLUMN);

        if (coeffs == null || qMax == null) {
            log.debug("No data found at km " + km + ".");
            return;
        }

        org.dive4elements.river.artifacts.math.Function funcInst =
            function.instantiate(coeffs);

        // Increase Q max about 5%.
        qMax[0] += Math.abs(qMax[0])*0.05;

        double wMax = funcInst.value(qMax[0]);

        if (Double.isNaN(wMax) || wMax < 0d) {
            log.debug("function '" + function.getName() +
                "' eval failed at " + wMax);
            return;
        }

        Function inverse = function.getInverse();

        org.dive4elements.river.artifacts.math.Function invInst =
            inverse.instantiate(coeffs);

        double wMin = minW(invInst, wMax, qMax[0]);

        double wMinCM = wMin * 100d - subtractPNP;
        double wMaxCM = wMax * 100d;

        int wRow = ((int)wMinCM / 10) * 10;

        if ((wMinCM - (int)wMinCM) > 0d) {
            wMinCM = (int)wMinCM + 1d;
        }

        double w = (wMinCM + subtractPNP) / 100.0;

        int wcm = ((int)wMinCM) % 10;

        if (log.isDebugEnabled()) {
            log.debug("wMinCM: " + wMinCM);
            log.debug("wMaxCM: " + wMaxCM);
            log.debug("wcm: " + wcm);
            log.debug("subtractPNP: " + subtractPNP);
        }

        out.printf(Locale.US, "%8d", wRow);

        for (int i = 0; i < wcm; i++) {
            out.print(ATWriter.EMPTY);
        }

        for (;;) {
            while (wcm++ < 10) {
                if (w > wMax) {
                    break;
                }
                double q = invInst.value(w);
                if (Double.isNaN(w)) {
                    out.print(ATWriter.EMPTY);
                }
                else {
                    ATWriter.printQ(out, q);
                }
                w += 0.01d;
            }
            out.println();
            if (w > wMax) {
                break;
            }
            out.printf(Locale.US, "%8d", (wRow += 10));
            wcm = 0;
        }

        out.flush();
    }

    protected void printHeader(
        PrintWriter out,
        CallMeta    meta,
        String      river,
        double      km
    ) {
        out.println("*" + Resources.format(
            meta,
            I18N_HEADER_KEY,
            I18N_HEADER_DEFAULT,
            river, km));
    }

    protected void printGaugeHeader(
        PrintWriter out,
        CallMeta    meta,
        River       river,
        Gauge       gauge
    ) {
        out.println("*" + Resources.format(
            meta,
            I18N_GAUGE_HEADER_KEY,
            I18N_GAUGE_HEADER_DEFAULT,
            new Object[] { river.getName(), gauge.getName(),
                gauge.getDatum(), river.getWstUnit().getName() }));
    }

    private static double minW(
        org.dive4elements.river.artifacts.math.Function function,
        double maxW,
        double maxQ
    ) {
        double stepWidth = 10d;

        double lastW = maxW;
        double lastQ = maxQ;

        for (int i = 0; i < MAX_ITERATIONS; ++i) {
            double w = lastW - stepWidth;
            double q = function.value(w);

            if (Double.isNaN(q) || q > lastQ || q < MIN_Q) {
                if (stepWidth < EPSILON) {
                    break;
                }
                stepWidth *= 0.5d;
                continue;
            }

            lastW = w;
            lastQ = q;
        }

        return lastW;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org