[PATCH 1 of 3] Nachtrag Pos. 19: Q calculation with historical discharge tables instead of master discharge table
Wald Commits
scm-commit at wald.intevation.org
Wed Oct 9 19:18:23 CEST 2019
# HG changeset patch
# User mschaefer
# Date 1570539804 -7200
# Tue Oct 08 15:03:24 2019 +0200
# Node ID f2473dc34535fd33cf09c7d47901c2ec8cd6aa6f
# Parent 6b2496d719361280e96bcb74a893cda9b7d432ce
Nachtrag Pos. 19: Q calculation with historical discharge tables instead of master discharge table
diff -r 6b2496d71936 -r f2473dc34535 artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java Tue Feb 12 14:08:16 2019 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java Tue Oct 08 15:03:24 2019 +0200
@@ -11,10 +11,13 @@
import java.util.ArrayList;
import java.util.Collection;
+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;
@@ -32,6 +35,8 @@
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;
@@ -43,6 +48,12 @@
private final CallContext context;
+ private CollisionAccess access;
+ private River river;
+ private List<DateRange> years;
+ private Calculation problems;
+ private int qfinderProblemCount;
+
public CollisionCalculation(final CallContext context) {
this.context = context;
}
@@ -52,50 +63,49 @@
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();
+ this.access = new CollisionAccess(sinfo);
+ this.river = this.access.getRiver();
+ final RiverInfo riverInfo = new RiverInfo(this.river);
+ final DoubleRange calcRange = this.access.getRange();
// calculate results for each year or epoch
- final Calculation problems = new Calculation();
-
+ this.problems = new Calculation();
+ this.qfinderProblemCount = 0;
final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
- final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, access.getYearsHeader());
+ final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, this.access.getYearsHeader());
final Collection<ResultRow> overViewRows = new ArrayList<>();
- final List<DateRange> years = new ArrayList<>();
+ this.years = new ArrayList<>();
final NavigableSet<Integer> detailYears = new TreeSet<>();
- 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)));
+ if (this.access.getYears() != null) {
+ for (final int year : this.access.getYears()) {
+ calculateOverview(overViewRows, year, year, false);
+ this.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);
+ for (final DateRange dr : this.access.getEpochs()) {
+ calculateOverview(overViewRows, dr.getFromYear(), dr.getToYear(), true);
+ this.years.add(dr);
detailYearsAdd(detailYears, dr);
}
}
- final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), (access.getYears() == null), years,
- overViewRows);
- results.addResult(overviewResult, problems);
+ final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(this.access.getYearsHeader(), (this.access.getYears() == null),
+ this.years, overViewRows);
+ results.addResult(overviewResult, this.problems);
// calculate secondary results for each year
- final Map<String, GaugeDischargeValuesFinder> qFinders = new HashMap<>();
+ 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.getLowerKm(), access.getUpperKm(), year, qFinders, zoneFinders, problems);
+ calculateDetails(detailsRows, year, qFinders, zoneFinders);
final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows);
- results.addResult(detailResult, problems);
+ results.addResult(detailResult, this.problems);
- return new CalculationResult(results, problems);
+ return new CalculationResult(results, this.problems);
}
/**
@@ -110,9 +120,9 @@
* 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)) {
+ private void calculateOverview(final Collection<ResultRow> rows, final int fromYear, final int toYear, final boolean isEpoch) {
+ for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(this.river, this.access.getLowerKm(), this.access.getUpperKm(),
+ fromYear, toYear)) {
rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation())
.putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount()));
}
@@ -135,15 +145,15 @@
/**
* Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection
*/
- private void calculateDetails(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int year,
- final Map<String, GaugeDischargeValuesFinder> qFinders, final Map<String, GaugeMainValueFinder> zoneFinders, final Calculation problems) {
+ private void calculateDetails(final Collection<ResultRow> rows, final int year, final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders,
+ final Map<String, GaugeMainValueFinder> zoneFinders) {
- for (final CollisionValue collision : CollisionValue.getValues(river, fromKm, toKm, DateUtil.getStartDateFromYear(year),
- DateUtil.getEndDateFromYear(year))) {
+ for (final CollisionValue collision : CollisionValue.getValues(this.river, this.access.getLowerKm(), this.access.getUpperKm(),
+ DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))) {
final String gaugeName = collision.getGaugeName();
- final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), river, problems);
+ final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate());
final double qOut = Double.isInfinite(q) ? Double.NaN : q;
- final String zone = getZone(zoneFinders, gaugeName, q, river, problems);
+ final String zone = getZone(zoneFinders, gaugeName, q);
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)
@@ -154,27 +164,62 @@
/**
* Gets the discharge of a gauge and a W
*/
- private double getQ(final Map<String, GaugeDischargeValuesFinder> qFinders, final String gaugeName, final double w, final River river,
- final Calculation problems) {
+ private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when) {
// Find the gauge and load its discharge table, if not already in the map
final String gnKey = gaugeName.toLowerCase();
if (!qFinders.containsKey(gnKey))
- qFinders.put(gnKey, GaugeDischargeValuesFinder.loadValues(river, gaugeName, problems));
+ addQFinders(qFinders, gaugeName);
// Interpolate W.
- if (qFinders.get(gnKey) == null)
+ final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when);
+ if (qFinder == null) {
+ this.qfinderProblemCount++;
+ if (this.qfinderProblemCount == 1)
+ this.problems.addProblem("gauge_discharge_table.missing", gaugeName);
return Double.NaN;
- return qFinders.get(gnKey).getDischarge(w);
+ }
+ 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 String gnKey = gaugeName.toLowerCase();
+ final Gauge gauge = this.river.determineGaugeByName(gaugeName);
+ if (gauge == null) {
+ qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>());
+ return;
+ }
+ final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, this.years.get(0).getFrom(),
+ this.years.get(this.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, this.river, gaugeName, this.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) {
+ private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q) {
// 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"));
+ zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, this.river, gaugeName, this.problems, "GLQ"));
// Build the zone name
if (zoneFinders.get(gnKey) == null)
return "";
diff -r 6b2496d71936 -r f2473dc34535 artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java Tue Feb 12 14:08:16 2019 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java Tue Oct 08 15:03:24 2019 +0200
@@ -9,6 +9,8 @@
*/
package org.dive4elements.river.artifacts.sinfo.common;
+import java.util.Date;
+
import org.apache.commons.lang.math.DoubleRange;
import org.apache.commons.math.FunctionEvaluationException;
import org.apache.commons.math.analysis.UnivariateRealFunction;
@@ -41,6 +43,10 @@
private final DoubleRange wRange;
+ private final Date startTime;
+
+ private final Date endTime;
+
/***** CONSTRUCTORS *****/
@@ -48,6 +54,8 @@
// Load W-Q-values from database
this.gauge = gauge;
this.problems = problems;
+ this.startTime = dischargeTable.getTimeInterval().getStartTime();
+ this.endTime = dischargeTable.getTimeInterval().getStopTime();
final TDoubleArrayList ws = new TDoubleArrayList();
final TDoubleArrayList qs = new TDoubleArrayList();
for (final DischargeTableValue v : DischargeTable.fetchValuesSortedByW(dischargeTable)) {
@@ -74,7 +82,7 @@
/***** METHODS *****/
/**
- * Loads the the main discharge table of a gauge ({gauge}.at)
+ * Loads the main discharge table of a gauge ({gauge}.at)
*
* @return The discharge table values finder of the gauge, or null
*/
@@ -83,7 +91,7 @@
}
/**
- * Loads the the main discharge table of a river's gauge ({gauge}.at)
+ * Loads the main discharge table of a river's gauge ({gauge}.at)
*
* @return The discharge table values finder of the gauge, or null
*/
@@ -92,8 +100,21 @@
return loadValues(gauge, gaugeName, problems);
}
+ /**
+ * Loads a discharge table of a river's gauge (*.at)
+ *
+ * @return The discharge table values finder, or null
+ */
+ public static GaugeDischargeValuesFinder loadValues(final DischargeTable table, final River river, final String gaugeName, final Calculation problems) {
+ final Gauge gauge = river.determineGaugeByName(gaugeName);
+ return loadValues(table, gauge, gaugeName, problems);
+ }
+
private static GaugeDischargeValuesFinder loadValues(final Gauge gauge, final String gaugeName, final Calculation problems) {
- final DischargeTable table = (gauge != null) ? gauge.fetchMasterDischargeTable() : null;
+ return loadValues((gauge != null) ? gauge.fetchMasterDischargeTable() : null, gauge, gaugeName, problems);
+ }
+
+ private static GaugeDischargeValuesFinder loadValues(final DischargeTable table, final Gauge gauge, final String gaugeName, final Calculation problems) {
if ((table == null) || (table.getDischargeTableValues().size() == 0)) {
problems.addProblem("gauge_discharge_table.missing", gaugeName);
return null;
@@ -132,4 +153,22 @@
return Double.NaN;
}
}
+
+ /**
+ * Start of the discharge table's time interval.
+ *
+ * @return
+ */
+ public Date getStartTime() {
+ return this.startTime;
+ }
+
+ /**
+ * End of the discharge table's time interval.
+ *
+ * @return
+ */
+ public Date getEndTime() {
+ return this.endTime;
+ }
}
\ No newline at end of file
diff -r 6b2496d71936 -r f2473dc34535 backend/src/main/java/org/dive4elements/river/model/DischargeTable.java
--- a/backend/src/main/java/org/dive4elements/river/model/DischargeTable.java Tue Feb 12 14:08:16 2019 +0100
+++ b/backend/src/main/java/org/dive4elements/river/model/DischargeTable.java Tue Oct 08 15:03:24 2019 +0200
@@ -9,6 +9,7 @@
package org.dive4elements.river.model;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -223,6 +224,28 @@
}
/**
+ * Fetches from the database a gauge's historical discharge tables of a time range in chronological order.
+ */
+ public static List<DischargeTable> fetchHistoricalDischargeTables(final Gauge gauge, final Date from, final Date to) {
+
+ final Session session = SessionHolder.HOLDER.get();
+
+ final Query query = session.createQuery("FROM DischargeTable"
+ + " WHERE (kind = 1) AND (gauge.id = :gauge_id)"
+ + " AND (timeInterval.startTime <= :to)"
+ + " AND ((timeInterval.stopTime IS NULL) OR (timeInterval.stopTime >= :from))"
+ + " ORDER BY timeInterval.startTime");
+
+ query.setParameter("gauge_id", gauge.getId());
+ query.setParameter("from", from);
+ query.setParameter("to", to);
+ final List<DischargeTable> qtables = new ArrayList<>();
+ for (final Object qtable : query.list())
+ qtables.add((DischargeTable) qtable);
+ return qtables;
+ }
+
+ /**
* Selects from the database the values of a discharge table sorted by W
*/
public static List<DischargeTableValue> fetchValuesSortedByW(final DischargeTable dischargeTable) {
More information about the Dive4Elements-commits
mailing list