view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java @ 8915:d9dbf0b74bc2

Refaktoring of flow depth calculation, extracting tkh part. First implementation of tkh calculation.
author gernotbelger
date Wed, 28 Feb 2018 17:27:15 +0100
parents
children 5d5d0051723f
line wrap: on
line source
/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
 * Software engineering by
 *  Björnsen Beratende Ingenieure GmbH
 *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
 *
 * 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.sinfo.tkhstate;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.WINFOArtifact;
import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.model.Calculation.Problem;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.WQKms;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.TkhCalculator;
import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
import org.dive4elements.river.artifacts.states.WaterlevelData;
import org.dive4elements.river.model.River;

/**
 * @author Gernot Belger
 */
final class TkhCalculation {

    private final CallContext context;

    public TkhCalculation(final CallContext context) {
        this.context = context;
    }

    public CalculationResult calculate(final SINFOArtifact sinfo) {

        /* access input data */
        final TkhAccess access = new TkhAccess(sinfo);
        final River river = access.getRiver();
        final RiverInfo riverInfo = new RiverInfo(river);
        final DoubleRange calcRange = access.getRange();

        final Calculation problems = new Calculation();

        /* find relevant bed-heights */
        final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(river, problems, calcRange);

        /* calculate waterlevels */
        final WQKms[] kms = calculateWaterlevels(sinfo, problems);

        final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);

        final String user = CalculationUtils.findArtifactUser(this.context, sinfo);

        final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());

        /* for each waterlevel, do a tkh calculation */
        final TkhCalculationResults results = new TkhCalculationResults(calcModeLabel, user, riverInfo, calcRange);

        for (final WQKms wqKms : kms) {

            final TkhCalculationResult result = calculateResult(calcRange, infoProvider, wqKms, bedHeights, problems);
            if (result != null)
                // FIXME: must be sorted by station!
                results.addResult(result);
        }

        return new CalculationResult(results, problems);
    }

    private WQKms[] calculateWaterlevels(final SINFOArtifact sinfo, final Calculation problems) {

        /* misuse winfo-artifact to calculate waterlevels in the same way */
        final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);

        final CalculationResult waterlevelData = winfo.getWaterlevelData(this.context);

        /* copy all problems */
        final Calculation winfoProblems = waterlevelData.getReport();
        for (final Problem problem : winfoProblems.getProblems()) {
            problems.addProblem(problem);
        }

        return (WQKms[]) waterlevelData.getData();
    }

    private TkhCalculationResult calculateResult(final DoubleRange calcRange, final RiverInfoProvider riverInfo, final WQKms wkms,
            final Collection<BedHeightsFinder> bedHeights, final Calculation problems) {

        // FIXME: wo kommt das her? via winfo kein jahr vorhanden, oder doch? aber soll in metadaten ausgegeben werden...
        final int wspYear = -1;
        // FIXME: richtig? vgl. WInfo?
        final boolean showAllGauges = false;
        final WaterlevelData waterlevel = new WaterlevelData(wkms, wspYear, showAllGauges);

        final RiverInfoProvider riverInfoProvider = riverInfo.forWaterlevel(waterlevel);

        final String label = waterlevel.getName();

        final WstInfo wstInfo = new WstInfo(label, wspYear, riverInfoProvider.getReferenceGauge());

        final Collection<TkhResultRow> rows = new ArrayList<>();

        /*
         * for each separate bed height dataset we do the calculation and put everything into one result, bed heights must not
         * overlap accordingly
         */
        for (final BedHeightsFinder bedHeightsProvider : bedHeights) {

            final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wkms);

            /* initialize tkh calculator */
            final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(true, this.context, problems, label, riverInfoProvider.getRiver(), calcRange,
                    dischargeProvider, bedHeightsProvider);
            if (tkhCalculator == null) {
                /* just abort, problems have already been updated by buildTkhCalculator() */
                return null;
            }

            /* using wst-kms as basis, because we know that they are generated wst's with a fixed km-step */

            // FIXME: das führt dazu, das aktuell die Sohlhöhen beliebig linear interpolierrt werden. ist das immer richtig? z.b.
            // bei großen abständen?

            final int size = wkms.size();
            for (int i = 0; i < size; i++) {

                final double station = wkms.getKm(i);
                final double wst = wkms.getW(i);

                final Tkh tkh = tkhCalculator.getTkh(station, wst);

                final String gaugeLabel = riverInfoProvider.findGauge(station);
                final String location = riverInfoProvider.getLocation(station);

                rows.add(new TkhResultRow(tkh, label, gaugeLabel, location));
            }
        }

        return new TkhCalculationResult(label, wstInfo, true, rows);
    }
}

http://dive4elements.wald.intevation.org