view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java @ 9176:1614cb14308f

Work on calculations for S-Info flood duration workflow
author mschaefer
date Mon, 25 Jun 2018 19:21:11 +0200
parents f9bb5d0a6ff3
children b4402594213b
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.collision;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.common.GeneralResultType;
import org.dive4elements.river.artifacts.common.ResultRow;
import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.DateRange;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
import org.dive4elements.river.artifacts.sinfo.common.GaugeDischargeValuesFinder;
import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueNameFinder;
import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
import org.dive4elements.river.backend.utils.DateUtil;
import org.dive4elements.river.model.Gauge;
import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
import org.dive4elements.river.model.River;
import org.dive4elements.river.model.sinfo.CollisionAggregateValue;
import org.dive4elements.river.model.sinfo.CollisionValue;

class CollisionCalculation {

    // private static Logger log = Logger.getLogger(FloodDurationCalculation.class);

    private final CallContext context;

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

    public CalculationResult calculate(final SINFOArtifact sinfo) {

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

        // access input data
        final CollisionAccess access = new CollisionAccess(sinfo);
        final River river = access.getRiver();
        final RiverInfo riverInfo = new RiverInfo(river);

        final DoubleRange calcRange = access.getRange();

        // calculate results for each year or epoch
        final Calculation problems = new Calculation();

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

        final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, access.getYearsHeader());

        final Collection<ResultRow> overViewRows = new ArrayList<>();

        if (access.getYears() != null) {
            for (final int year : access.getYears())
                calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false);
        }
        else {
            for (final DateRange dr : access.getEpochs())
                calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), true);
        }
        final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), overViewRows);
        results.addResult(overviewResult, problems);

        // create q-for-w-finders for all gauges of the calculation km range
        final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
        final Map<Gauge, GaugeDischargeValuesFinder> qFinders = new HashMap<>();
        final Map<Gauge, GaugeMainValueNameFinder> zoneFinders = new HashMap<>();
        for (final Gauge gauge : river.determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) {
            qFinders.put(gauge, GaugeDischargeValuesFinder.loadValues(gauge, problems));
            zoneFinders.put(gauge, GaugeMainValueNameFinder.loadValues(MainValueTypeKey.Q, gauge, problems));
        }
        final Collection<ResultRow> detailsRows = new ArrayList<>();

        // calculate secondary results for each year or epoch
        if (access.getYears() != null) {
            for (final int year : access.getYears())
                calculateDetails(detailsRows, infoProvider, access.getLowerKm(), access.getUpperKm(), year, year, qFinders, zoneFinders);
        }
        else {
            for (final DateRange dr : access.getEpochs())
                calculateDetails(detailsRows, infoProvider, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), qFinders, zoneFinders);
        }
        final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows);
        results.addResult(detailResult, problems);

        return new CalculationResult(results, problems);
    }

    /**
     * Calculates the collision counts for a km range of a river and a year or year range (epoch),
     * and adds them to a ResultRow collection
     */
    private void calculateOverview(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int fromYear,
            final int toYear, final boolean isEpoch) {
        for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(river, fromKm, toKm, fromYear, toYear)) {
            rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation())
                    .putValue(SInfoResultType.years, (isEpoch? String.format("%d-%d", fromYear, toYear) : Integer.toString(fromYear)))
                    .putValue(SInfoResultType.collisionCount, aggregate.getCount()));
        }
    }

    /**
     * Calculates the collision details for a km range of a river and a year or year range (epoch),
     * and adds them to a ResultRow collection
     */
    private void calculateDetails(final Collection<ResultRow> rows, final RiverInfoProvider riverInfo, final double fromKm, final double toKm,
            final int fromYear, final int toYear, final Map<Gauge, GaugeDischargeValuesFinder> qFinders,
            final Map<Gauge, GaugeMainValueNameFinder> zoneFinders) {
        for (final CollisionValue collision : CollisionValue.getValues(riverInfo.getRiver(), fromKm, toKm, DateUtil.getStartDateFromYear(fromYear),
                DateUtil.getEndDateFromYear(toYear))) {
            final Gauge gauge = riverInfo.getGauge(collision.getStation(), true);
            final double q = qFinders.get(gauge).getDischarge(collision.getGaugeW());
            final double qOut = Double.isInfinite(q) ? Double.NaN : q;
            rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation())
                    .putValue(GeneralResultType.date, collision.getEventDate())
                    .putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW())
                    .putValue(SInfoResultType.gaugeLabel, collision.getGaugeName())
                    .putValue(SInfoResultType.discharge, qOut)
                    .putValue(SInfoResultType.dischargeZone, zoneFinders.get(gauge).getZoneName(q)));
        }
    }
}

http://dive4elements.wald.intevation.org