Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java @ 9617:1d4262a68f1f
#12 Minuend/Subtrahend + MergeConflict #19 CollisionCalculation
author | dnt_bjoernsen <d.tironi@bjoernsen.de> |
---|---|
date | Thu, 10 Oct 2019 15:29:02 +0200 |
parents | f2473dc34535 |
children |
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.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NavigableSet; import java.util.TreeMap; import java.util.TreeSet; 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.GaugeMainValueFinder; 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.DischargeTable; 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); final int qfinderProblemCount = 0; // 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<>(); final List<DateRange> years = new ArrayList<>(); final NavigableSet<Integer> detailYears = new TreeSet<>(); final List<CollisionCalcOverviewResult> singleYearResults = new ArrayList<>(); if (access.getYears() != null) { for (final int year : access.getYears()) { calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false); years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))); detailYears.add(Integer.valueOf(year)); } } else { for (final DateRange dr : access.getEpochs()) { calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), true); years.add(dr); detailYearsAdd(detailYears, dr); } for (final Integer year : detailYears) { final Collection<ResultRow> yearRows = new ArrayList<>(); calculateOverview(yearRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false); if (!yearRows.isEmpty()) { final DateRange yearRange = new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year)); final CollisionCalcOverviewResult yearResult = new CollisionCalcOverviewResult(Integer.toString(year), false, Collections.singleton(yearRange), yearRows); singleYearResults.add(yearResult); } } } final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), (access.getYears() == null), years, overViewRows); results.addResult(overviewResult, problems); /* add the single year from epochs, after the epochs, so they will be exported at the end of the table etc. */ for (final CollisionCalcOverviewResult result : singleYearResults) results.addResult(result, problems); // calculate secondary results for each year final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders = new HashMap<>(); final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>(); final Collection<ResultRow> detailsRows = new ArrayList<>(); for (final Integer year : detailYears) calculateDetails(detailsRows, river, access, year, qFinders, zoneFinders, problems, years, qfinderProblemCount); final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows); results.addResult(detailResult, problems); return new CalculationResult(results, problems); } /** * Adds all years of an epoch to a set */ private void detailYearsAdd(final NavigableSet<Integer> detailYears, final DateRange epoch) { for (int year = epoch.getFromYear(); year <= epoch.getToYear(); year++) detailYears.add(Integer.valueOf(year)); } /** * 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, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount())); } } /** * Returns the string representation of a year or epoch */ public static String yearsToString(final boolean isEpoch, final DateRange years) { return yearsToString(isEpoch, years.getFromYear(), years.getToYear()); } /** * Returns the string representation of a year or epoch */ public static String yearsToString(final boolean isEpoch, final int fromYear, final int toYear) { return (isEpoch ? String.format("%d-%d", fromYear, toYear) : Integer.toString(fromYear)); } /** * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection * * @param qfinderProblemCount */ private void calculateDetails(final Collection<ResultRow> rows, final River river, final CollisionAccess access, final int year, final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final Map<String, GaugeMainValueFinder> zoneFinders, final Calculation problems, final List<DateRange> years, final int qfinderProblemCount) { final double fromKm = access.getLowerKm(); final double toKm = access.getUpperKm(); for (final CollisionValue collision : CollisionValue.getValues(river, fromKm, toKm, DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))) { final String gaugeName = collision.getGaugeName(); final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate(), river, problems, years, qfinderProblemCount); final double qOut = Double.isInfinite(q) ? Double.NaN : q; final String zone = getZone(zoneFinders, gaugeName, q, river, problems); rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation()) .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW()) .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut) .putValue(SInfoResultType.dischargeZone, zone)); } } /** * Gets the discharge of a gauge and a W * * @param years * @param qfinderProblemCount */ private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when, final River river, final Calculation problems, final List<DateRange> years, int qfinderProblemCount) { // Find the gauge and load its discharge table, if not already in the map final String gnKey = gaugeName.toLowerCase(); if (!qFinders.containsKey(gnKey)) addQFinders(qFinders, gaugeName, problems, river, years); // Interpolate W. // Interpolate W. final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when); if (qFinder == null) { qfinderProblemCount++; if (qfinderProblemCount == 1) problems.addProblem("gauge_discharge_table.missing", gaugeName); return Double.NaN; } return qFinder.getDischarge(w); } /** * Add the discharge finders for a gauge and the active time period */ private void addQFinders(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final Calculation problems, final River river, final List<DateRange> years) { final String gnKey = gaugeName.toLowerCase(); final Gauge gauge = river.determineGaugeByName(gaugeName); if (gauge == null) { qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); return; } final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, years.get(0).getFrom(), years.get(years.size() - 1).getTo()); qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>()); for (final DischargeTable qtable : qtables) qFinders.get(gnKey).put(qtable.getTimeInterval().getStartTime(), GaugeDischargeValuesFinder.loadValues(qtable, river, gaugeName, problems)); } /** * Searches a q values finder map for a date time */ private GaugeDischargeValuesFinder getQFinder(final TreeMap<Date, GaugeDischargeValuesFinder> qFinders, final Date when) { if (qFinders.containsKey(when)) return qFinders.get(when); final Entry<Date, GaugeDischargeValuesFinder> found = qFinders.floorEntry(when); if (found == null) return null; if ((found.getValue().getEndTime() != null) && found.getValue().getEndTime().before(when)) return null; return found.getValue(); } /** * Gets the main value zone name of a gauge and a Q */ private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q, final River river, final Calculation problems) { // Find the gauge and load its main value list, if not already in the map final String gnKey = gaugeName.toLowerCase(); if (!zoneFinders.containsKey(gnKey)) zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, river, gaugeName, problems, "GLQ")); // Build the zone name if (zoneFinders.get(gnKey) == null) return ""; return zoneFinders.get(gnKey).findZoneName(q); } }