view flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java @ 1672:0b6dac664bbb

Moved some generic double array code from BackjumpCorrector to DoubleUtil flys-artifacts/trunk@2885 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 04 Oct 2011 15:29:39 +0000
parents c09c9e05ecfa
children b5209452f6bb
line wrap: on
line source
package de.intevation.flys.artifacts.math;

import java.util.ArrayList;
import java.util.List;

import java.io.Serializable;

import org.apache.commons.math.analysis.interpolation.SplineInterpolator;

import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;

import org.apache.commons.math.ArgumentOutsideDomainException;

import org.apache.commons.math.exception.MathIllegalArgumentException;

import org.apache.log4j.Logger;

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

import de.intevation.flys.utils.DoubleUtil;

public class BackJumpCorrector
implements   Serializable
{
    private static Logger log = Logger.getLogger(BackJumpCorrector.class);

    protected ArrayList<Double> backjumps;

    protected double [] corrected;

    public BackJumpCorrector() {
        backjumps = new ArrayList<Double>();
    }

    public boolean hasBackJumps() {
        return !backjumps.isEmpty();
    }

    public List<Double> getBackJumps() {
        return backjumps;
    }

    public double [] getCorrected() {
        return corrected;
    }

    public boolean doCorrection(
        double []   km,
        double []   ws,
        Calculation errors
    ) {
        boolean wsUp = DoubleUtil.isIncreasing(ws);

        if (wsUp) {
            km = DoubleUtil.swapClone(km);
            ws = DoubleUtil.swapClone(ws);
        }

        boolean kmUp = DoubleUtil.isIncreasing(km);

        if (!kmUp) {
            km = DoubleUtil.sumDiffs(km);
        }

        if (log.isDebugEnabled()) {
            log.debug("BackJumpCorrector.doCorrection ------- enter");
            log.debug("  km increasing: " + DoubleUtil.isIncreasing(km));
            log.debug("  ws increasing: " + DoubleUtil.isIncreasing(ws));
            log.debug("BackJumpCorrector.doCorrection ------- leave");
        }

        boolean hasBackJumps = doCorrectionClean(km, ws, errors);

        if (hasBackJumps && wsUp) {
            // mirror back
            DoubleUtil.swap(corrected);
        }

        return hasBackJumps;
    }

    protected boolean doCorrectionClean(
        double []   km,
        double []   ws,
        Calculation errors
    ) {
        int N = km.length;

        if (N != ws.length) {
            throw new IllegalArgumentException("km.length != ws.length");
        }

        if (N < 2) {
            return false;
        }

        SplineInterpolator interpolator = null;

        for (int i = 1; i < N; ++i) {
            if (ws[i] <= ws[i-1]) {
                // no back jump
                continue;
            }
            backjumps.add(km[i]);

            if (corrected == null) {
                // lazy cloning
                ws = corrected = (double [])ws.clone();
            }

            int    back = i-2;
            double distance = Math.abs(km[i] - km[i-1]);
            double rest = 0.0;
            double ikm  = 0.0;

            while (back > -1) {
                if (ws[back] < ws[i]) { // under water
                    // continue scanning backwards
                    distance += Math.abs(km[back+1] - km[back]);
                    --back;
                    continue;
                }
                if (ws[back] > ws[i]) { // over water
                    // need to calculate intersection
                    log.debug("intersection case");
                    double m = (km[back+1]-km[back])/(ws[back+1]-ws[back]);
                    double b = km[back]-ws[back]*m;
                    ikm = m*ws[i] + b;
                    distance += Math.abs(ikm - km[back+1]);
                    rest = Math.abs(km[back] - ikm);
                }
                else {
                    // equals height at ws[back]
                    log.debug("exact height match");
                    distance += Math.abs(km[back+1] - km[back]);
                    ikm = km[back];
                }
                break;
            }

            if (back <= 0) {
                //log.debug("run over left border");
                // fill with same as ws[i]
                for (int j = 0; j < i; ++j) {
                    ws[j] = ws[i];
                }
                continue;
            }

            double quarterDistance = 0.25*distance;

            // Now further seek back for the max height

            double restDistance = Math.max(0.0, quarterDistance - rest);
            --back;

            double mkmw = ws[i];
            double mkm  = km[0];

            while (back > -1) {
                double d = Math.abs(km[back+1] - km[back]);
                restDistance -= d;
                if (restDistance > 0.0) {
                    --back;
                    continue;
                }
                if (restDistance < 0.0) {
                    // need to calculate intersection
                    if (km[back] == km[back+1]) { // should not happen
                        mkm = km[back];
                        mkmw = 0.5*(ws[back] + ws[back+1]);
                    }
                    else {
                        double m = (ws[back+1]-ws[back])/(km[back+1]-km[back]);
                        double b = ws[back] - km[back]*m;
                        mkm = km[back] + restDistance;
                        mkmw = m*mkm + b;
                    }
                }
                else {
                    // exact match
                    mkm  = km[back];
                    mkmw = ws[back];
                }
                break;
            }

            double factor = back >= 0 && Math.abs(restDistance) < 1e-4
                ? 1.0
                : 1.0 - Math.min(1, Math.max(0, restDistance/quarterDistance));

            double ikmw = factor*0.25*(mkmw-ws[i]) + ws[i];

            double end = ikm + quarterDistance*factor;

            double [] x = { mkm,  ikm,  end   };
            double [] y = { mkmw, ikmw, ws[i] };

            if (interpolator == null) {
                interpolator = new SplineInterpolator();
            }

            if (log.isDebugEnabled()) {
                for (int j = 0; j < x.length; ++j) {
                    log.debug("   " + x[j] + " -> " + y[j]);
                }
            }

            PolynomialSplineFunction spline;

            try {
                spline = interpolator.interpolate(x, y);
            }
            catch (MathIllegalArgumentException miae) {
                // TODO: I18N
                errors.addProblem("creating spline interpolation failed.");
                log.error(miae);
                continue;
            }

            try {
                if (log.isDebugEnabled()) {
                    log.debug("spline points:");
                    for (int j = 0; j < x.length; ++j) {
                        log.debug(x[j] + " " + y[j] + " " + spline.value(x[j]));
                    }
                }

                for (back = Math.max(back, 0);
                     back < i && km[back] < end;
                     ++back
                ) {
                    // to 3/4 point fill with spline values
                    ws[back] = spline.value(km[back]);
                }
                while (back < i) {
                    // fill the rest with ws[i]
                    ws[back++] = ws[i];
                }
            }
            catch (ArgumentOutsideDomainException aode) {
                // TODO: I18N
                errors.addProblem("spline interpolation failed.");
                log.error("spline interpolation failed", aode);
            }
        } // for all km

        return !backjumps.isEmpty();
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org