view artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation4.java @ 9479:2b83d3a96703

i18n TODO "benutzerdefiniert" = "custom" fixed
author gernotbelger
date Mon, 10 Sep 2018 15:31:55 +0200
parents 5e38e2924c07
children 33ce8eba9806
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.artifacts.model;

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

import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallMeta;
import org.dive4elements.river.artifacts.access.Calculation4Access;
import org.dive4elements.river.artifacts.math.BackJumpCorrector;
import org.dive4elements.river.artifacts.math.Function;
import org.dive4elements.river.artifacts.math.Identity;
import org.dive4elements.river.artifacts.math.Linear;
import org.dive4elements.river.artifacts.model.WstValueTable.QPosition;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.model.River;
import org.dive4elements.river.utils.DoubleUtil;

public class Calculation4 extends Calculation {
    private static Logger log = Logger.getLogger(Calculation4.class);

    public static final double MINIMAL_STEP_WIDTH = 1e-5;

    protected List<Segment> segments;

    protected boolean isQ;
    protected double from;
    protected double to;
    protected double step;
    protected String river;

    public Calculation4() {
    }

    public Calculation4(final Calculation4Access access) {
        log.debug("Calculation4Access.cnst");
        final String river = access.getRiverName();
        final List<Segment> segments = access.getSegments();
        final double[] range = access.getFromToStep();
        final boolean isQ = access.isQ();

        if (river == null) {
            addProblem("no.river.selected");
        }

        if (range == null) {
            addProblem("no.range.found");
        }

        if (segments == null || segments.isEmpty()) {
            addProblem("cannot.create.segments");
        }

        if (!hasProblems()) {
            this.river = river;
            this.segments = segments;
            this.from = range[0];
            this.to = range[1];
            this.step = range[2];
            this.isQ = isQ;
        }
    }

    public CalculationResult calculate(final CallMeta meta) {
        if (hasProblems()) {
            return new CalculationResult(new WQKms[0], this);
        }

        WstValueTable table = null;
        final River r = RiverFactory.getRiver(this.river);
        if (r == null) {
            addProblem("no.river.found");
        } else {
            table = WstValueTableFactory.getTable(r);
            if (table == null) {
                addProblem("no.wst.for.river");
            } else {
                Segment.setReferencePointConvertQ(this.segments, r, this.isQ, this);
            }
        }

        return hasProblems() ? new CalculationResult(new WQKms[0], this) : innerCalculate(table, meta);
    }

    protected CalculationResult innerCalculate(final WstValueTable table, final CallMeta meta) {
        final boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug("calculate from " + this.from + " to " + this.to + " step " + this.step);
            log.debug("# segments: " + this.segments.size());
            for (final Segment segment : this.segments) {
                log.debug("  " + segment);
            }
        }

        final int numResults = this.segments.get(0).values.length;

        if (numResults < 1) {
            log.debug("no values given");
            addProblem("no.values.given");
            return new CalculationResult(new WQKms[0], this);
        }

        final WQKms[] results = new WQKms[numResults];
        for (int i = 0; i < results.length; ++i) {
            results[i] = new WQKms();
        }

        if (Math.abs(this.step) < MINIMAL_STEP_WIDTH) {
            this.step = MINIMAL_STEP_WIDTH;
        }

        if (this.from > this.to) {
            this.step = -this.step;
        }

        final QPosition[] qPositions = new QPosition[numResults];

        final Function[] functions = new Function[numResults];

        final double[] out = new double[2];

        final Segment sentinel = new Segment(Double.MAX_VALUE);
        Segment s1 = sentinel, s2 = sentinel;

        for (double pos = this.from; this.from < this.to ? pos <= this.to : pos >= this.to; pos = DoubleUtil.round(pos + this.step)) {
            if (pos < s1.referencePoint || pos > s2.referencePoint) {
                if (debug) {
                    log.debug("need to find new interval for " + pos);
                }
                // find new interval
                if (pos <= this.segments.get(0).referencePoint) {
                    // before first segment -> "gleichwertig"
                    if (debug) {
                        log.debug("before first segment -> gleichwertig");
                    }
                    final Segment first = this.segments.get(0);
                    final double[] values = first.values;
                    final double refPos = first.referencePoint;
                    for (int i = 0; i < qPositions.length; ++i) {
                        qPositions[i] = table.getQPosition(refPos, values[i]);
                    }
                    sentinel.setReferencePoint(-Double.MAX_VALUE);
                    s1 = sentinel;
                    s2 = this.segments.get(0);
                    Arrays.fill(functions, Identity.IDENTITY);
                } else if (pos >= this.segments.get(this.segments.size() - 1).referencePoint) {
                    // after last segment -> "gleichwertig"
                    if (debug) {
                        log.debug("after last segment -> gleichwertig");
                    }
                    final Segment last = this.segments.get(this.segments.size() - 1);
                    final double[] values = last.values;
                    final double refPos = last.referencePoint;
                    for (int i = 0; i < qPositions.length; ++i) {
                        qPositions[i] = table.getQPosition(refPos, values[i]);
                    }
                    sentinel.setReferencePoint(Double.MAX_VALUE);
                    s1 = last;
                    s2 = sentinel;
                    Arrays.fill(functions, Identity.IDENTITY);
                } else { // "ungleichwertig"
                         // find matching interval
                    if (debug) {
                        log.debug("in segments -> ungleichwertig");
                    }
                    s1 = s2 = null;
                    for (int i = 1, N = this.segments.size(); i < N; ++i) {
                        final Segment si1 = this.segments.get(i - 1);
                        final Segment si = this.segments.get(i);
                        if (debug) {
                            log.debug("check " + pos + " in " + si1.referencePoint + " - " + si.referencePoint);
                        }
                        if (pos >= si1.referencePoint && pos <= si.referencePoint) {
                            s1 = si1;
                            s2 = si;
                            break;
                        }
                    }

                    if (s1 == null) {
                        throw new IllegalStateException("no interval found");
                    }

                    Segment anchor, free;

                    if (this.from > this.to) {
                        anchor = s1;
                        free = s2;
                    } else {
                        anchor = s2;
                        free = s1;
                    }

                    // build transforms based on "gleichwertiger" phase
                    for (int i = 0; i < qPositions.length; ++i) {
                        final QPosition qi = table.getQPosition(anchor.referencePoint, anchor.values[i]);

                        if ((qPositions[i] = qi) == null) {
                            addProblem(pos, "cannot.find.q", anchor.values[i]);
                            functions[i] = Identity.IDENTITY;
                        } else {
                            final double qA = table.getQ(qi, anchor.referencePoint);
                            final double qF = table.getQ(qi, free.referencePoint);

                            functions[i] = Double.isNaN(qA) || Double.isNaN(qF) ? Identity.IDENTITY : new Linear(qA, qF, anchor.values[i], free.values[i]);

                            if (debug) {
                                log.debug(anchor.referencePoint + ": " + qA + " -> " + functions[i].value(qA) + " / " + free.referencePoint + ": " + qF + " -> "
                                        + functions[i].value(qF));
                            }
                        }
                    } // build transforms
                } // "ungleichwertiges" interval
            } // find matching interval

            for (int i = 0; i < qPositions.length; ++i) {
                final QPosition qPosition = qPositions[i];

                if (qPosition == null) {
                    continue;
                }

                if (table.interpolate(pos, out, qPosition, functions[i])) {
                    results[i].add(out[0], out[1], pos);
                } else {
                    addProblem(pos, "cannot.interpolate.w.q");
                }
            }
        }
        final String custom = Resources.format(meta, "common.custom");

        // Backjump correction
        for (int i = 0; i < results.length; ++i) {
            final BackJumpCorrector bjc = new BackJumpCorrector();

            final double[] ws = results[i].getWs();
            final double[] kms = results[i].getKms();

            if (bjc.doCorrection(kms, ws, this)) {
                results[i] = new WQCKms(results[i], bjc.getCorrected());
            }
        }

        // Name the curves.
        for (int i = 0; i < results.length; ++i) {
            results[i].setName(createName(i, custom));
        }

        // Generate the "Umhuellende".
        final ConstantWQKms[] infoldings = generateInfolding(table, results, this.from, this.to, this.step);

        // TODO: Use qkms in a new result type.
        final WQKms[] newResults = new WQKms[results.length + infoldings.length];
        System.arraycopy(results, 0, newResults, 0, results.length);
        System.arraycopy(infoldings, 0, newResults, results.length, infoldings.length);

        return new CalculationResult(newResults, this);
    }

    protected ConstantWQKms[] generateInfolding(final WstValueTable wst, final WQKms[] results, final double from, final double to, final double step) {
        final WstValueTable.Column[] columns = wst.getColumns();

        final InfoldingColumns ic = new InfoldingColumns(columns);
        ic.markInfoldingColumns(results);

        final List<ConstantWQKms> infoldings = new ArrayList<>();

        final boolean[] infoldingColumns = ic.getInfoldingColumns();

        double[] kms = null;
        double[] ws = null;

        for (int i = 0; i < infoldingColumns.length; ++i) {
            if (!infoldingColumns[i]) {
                continue;
            }

            if (kms == null) {
                kms = DoubleUtil.explode(from, to, step);
                ws = new double[kms.length];
            }

            final QRangeTree.QuickQFinder qf = columns[i].getQRangeTree().new QuickQFinder();

            final int numProblemsBefore = numProblems();
            final double[] qs = qf.findQs(kms, this);

            final String name = columns[i].getName();
            final ConstantWQKms infolding = new ConstantWQKms(kms, qs, ws, name);

            if (numProblems() > numProblemsBefore) {
                infolding.removeNaNs();
            }

            infoldings.add(infolding);
        }

        for (int i = 0, I = infoldings.size(); i < I; i++) {
            final ConstantWQKms infolding = infoldings.get(i);
            final String name = infolding.getName();
            // TODO: i18n
            if (i == 0) {
                infolding.setName("untere Umh\u00fcllende " + name);
            } else if (i == I - 1) {
                infolding.setName("obere Umh\u00fcllende " + name);
            } else {
                infolding.setName("geschnitten " + name);
            }
        }

        return infoldings.toArray(new ConstantWQKms[infoldings.size()]);
    }

    // TODO: issue1109/2, merge with FixRealizingCalculation
    protected String createName(final int index, final String custom) {

        final StringBuilder sb = new StringBuilder(this.isQ ? "Q" : "W");
        sb.append(" ").append(custom).append(" (");
        for (int i = 0, N = this.segments.size(); i < N; ++i) {
            if (i > 0) {
                sb.append("; ");
            }
            final Segment segment = this.segments.get(i);
            sb.append((segment.backup != null ? segment.backup : segment.values)[index]);
        }
        sb.append(')');
        return sb.toString();
    }
}

http://dive4elements.wald.intevation.org