view flys-artifacts/src/main/java/de/intevation/flys/exports/fixings/FixATWriter.java @ 3279:5a7b4f890d53

Delta W(t) chart subtitle fixed flys-artifacts/trunk@4930 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Christian Lins <christian.lins@intevation.de>
date Wed, 11 Jul 2012 10:53:38 +0000
parents 0c8a6145098b
children 1dc904370a64
line wrap: on
line source
package de.intevation.flys.exports.fixings;

import de.intevation.artifacts.CallMeta;

import de.intevation.flys.artifacts.math.fitting.Function;

import de.intevation.flys.artifacts.model.Parameters;

import de.intevation.flys.artifacts.resources.Resources;

import de.intevation.flys.exports.ATWriter;

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

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Locale;

import org.apache.log4j.Logger;

public class FixATWriter
{
    private static Logger log = Logger.getLogger(FixATWriter.class);

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

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

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

    public static class WQ {

        protected double w;
        protected double q;

        public WQ() {
        }

        public WQ(double w, double q) {
            this.w = w;
            this.q = q;
        }
    } // class WQ

    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,
        String   river,
        double   km
    )
    throws IOException {
        PrintWriter out = new PrintWriter(writer);
        printHeader(out, meta, river, 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;
        }

        de.intevation.flys.artifacts.math.Function funcInst =
            function.instantiate(coeffs);

        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();

        de.intevation.flys.artifacts.math.Function invInst =
            inverse.instantiate(coeffs);

        /* TODO: Do some fancy root finding stuff with the
         * first derivative of the inverse function to find
         * the minimum of function and directly export
         * the longest strict monotonous ascending run of
         * ws of the inverse in range [0, wMax].
         *
         * To simplify the situation we iterate in steps
         * of 10cm over the range and export the longest
         * run.
         */

        ArrayDeque<WQ> longest = new ArrayDeque<WQ>();
        ArrayDeque<WQ> current = new ArrayDeque<WQ>();

        for (double w = 0d; w <= wMax; w += 0.1d) {
            double q = invInst.value(w);

            if (Double.isNaN(q)) {
                log.debug("Eval of inverse for " + w + " failed.");
                continue;
            }

            WQ wq = new WQ(w, q);

            if (current.isEmpty() || current.getLast().q < q) {
                current.add(wq);
            }
            else {
                if (current.size() >= longest.size()) {
                    longest = current;
                }
                current = new ArrayDeque<WQ>();
                current.add(wq);
            }
        }

        if (current.size() >= longest.size()) {
            longest = current;
        }

        printWQs(out, longest);
        out.flush();
    }

    protected void printWQs(PrintWriter out, Collection<WQ> wqs) {
        int lastColumn = 10;
        for (WQ wq: wqs) {
            int column = (int)(wq.w * 10d) % 10;

            if (lastColumn > column) {
                for (; lastColumn < 10; ++lastColumn) {
                    out.print(ATWriter.EMPTY);
                }
                out.println();
                out.printf(Locale.US, "%8d", (int)Math.round(wq.w*100.0));
                lastColumn = 0;
            }
            for (;lastColumn < column-1; ++lastColumn) {
                out.print(ATWriter.EMPTY);
            }
            ATWriter.printQ(out, wq.q);

            lastColumn = column;
        }
        out.println();
    }

    protected void printHeader(
        PrintWriter out,
        CallMeta    meta,
        String      river,
        double      km
    ) {
        out.println(Resources.format(
            meta,
            I18N_HEADER_KEY,
            I18N_HEADER_DEFAULT,
            river, km));
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org