[PATCH] Code cleanup concerning calculations: improved error handling; improved interpolation; bed heights are now always used for spatial discretisation
Wald Commits
scm-commit at wald.intevation.org
Thu Mar 29 15:48:22 CEST 2018
# HG changeset patch
# User gernotbelger
# Date 1522331297 -7200
# Node ID 45f1ad66560e8bc6be7ecc4f7d0eb6269ea79978
# Parent b98fbd91f64a21d7e2adf5c0147309757e858319
Code cleanup concerning calculations: improved error handling; improved interpolation; bed heights are now always used for spatial discretisation
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/BedHeightsArtifact.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/BedHeightsArtifact.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/BedHeightsArtifact.java Thu Mar 29 15:48:17 2018 +0200
@@ -22,11 +22,11 @@
import org.dive4elements.river.artifacts.model.FacetTypes;
import org.dive4elements.river.artifacts.model.minfo.BedHeightFacet;
import org.dive4elements.river.artifacts.model.minfo.BedHeightMinMaxFacet;
-import org.dive4elements.river.artifacts.model.minfo.BedHeightMinMaxFacet.BedHeightValueType;
import org.dive4elements.river.artifacts.model.minfo.BedHeightSoundingWidthFacet;
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.states.StaticState;
import org.dive4elements.river.exports.process.BedHeightProcessor;
+import org.dive4elements.river.model.BedHeightValueType;
import org.w3c.dom.Document;
public class BedHeightsArtifact extends AbstractStaticStateArtifact implements FacetTypes {
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightMinMaxFacet.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightMinMaxFacet.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightMinMaxFacet.java Thu Mar 29 15:48:17 2018 +0200
@@ -19,6 +19,7 @@
import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.model.BedHeight;
import org.dive4elements.river.model.BedHeightValue;
+import org.dive4elements.river.model.BedHeightValueType;
import gnu.trove.TDoubleArrayList;
@@ -32,29 +33,6 @@
private static final long serialVersionUID = 1L;
- public static enum BedHeightValueType {
- min {
- @Override
- public Double getValue(final BedHeightValue bedheightValue) {
- return bedheightValue.getMinHeight();
- }
- },
- max {
- @Override
- public Double getValue(final BedHeightValue bedheightValue) {
- return bedheightValue.getMaxHeight();
- }
- },
- value {
- @Override
- public Double getValue(final BedHeightValue bedheightValue) {
- return bedheightValue.getHeight();
- }
- };
-
- public abstract Double getValue(final BedHeightValue bedheightValue);
- }
-
private final BedHeightValueType valueType;
public BedHeightMinMaxFacet(final String name, final String description, final BedHeightValueType valueType) {
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Thu Mar 29 15:48:17 2018 +0200
@@ -90,7 +90,7 @@
return null;
/* REMARK: fetch ALL wst kms, because we want to determine the original reference gauge */
- final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
+ final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, calcRange, problems);
if (waterlevel == null)
return null;
@@ -101,8 +101,6 @@
final String label = String.format("%s - %s", wspLabel, soundingLabel);
FlowDepthUtils.checkYearDifference(label, waterlevel.getYear(), bedHeight.getInfo().getYear(), problems);
- checkWaterlevelDiscretisation(wstKms, calcRange, problems);
- // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
/* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel);
@@ -110,35 +108,14 @@
final int wspYear = waterlevel.getYear();
final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, riverInfoProvider.getReferenceGauge());
- final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(wstKms);
+ final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(problems, wstKms);
final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms);
final River river = riverInfoProvider.getRiver();
- final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(useTkh, this.context, problems, label, river, calcRange, waterlevelProvider,
- dischargeProvider,
- bedHeight);
+ final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(useTkh, problems, label, river, calcRange, waterlevelProvider,
+ dischargeProvider, bedHeight);
final FlowDepthCalculator calculator = new FlowDepthCalculator(riverInfoProvider, wspLabel, bedHeight, tkhCalculator);
return calculator.execute(label, wstInfo, calcRange);
}
-
- /* Checks if the discretisation of the waterlevel exceeds 1000m */
- private void checkWaterlevelDiscretisation(final WKms wstKms, final DoubleRange calcRange, final Calculation problems) {
-
- final int size = wstKms.size();
- for (int i = 0; i < size - 2; i++) {
- final double kmPrev = wstKms.getKm(i);
- final double kmNext = wstKms.getKm(i + 1);
-
- /* only check if we are within the calculation range */
- if (calcRange.overlapsRange(new DoubleRange(kmPrev, kmNext))) {
- if (Math.abs(kmPrev - kmNext) > 1) {
- final String label = wstKms.getName();
-
- final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.waterlevel_discretisation", null, label);
- problems.addProblem(kmPrev, message);
- }
- }
- }
- }
}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowVelocityKmModelValues.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowVelocityKmModelValues.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowVelocityKmModelValues.java Thu Mar 29 15:48:17 2018 +0200
@@ -17,170 +17,173 @@
/**
* Sorted arrays of a station's q, v, and tau model values, running in parallel
+ *
* @author Matthias Schäfer
*
*/
public class FlowVelocityKmModelValues {
/***** FIELDS *****/
-
+
/**
* Km
*/
- private double km;
-
+ private final double km;
+
/**
* The station's discharge model values, sorted in ascending order
*/
- private TDoubleArrayList qs;
-
+ private final TDoubleArrayList qs;
+
/**
* The station's main section velocity for the q values
*/
- private TDoubleArrayList vmains;
-
+ private final TDoubleArrayList vmains;
+
/**
* The station's shear stress (tau) values for the q values
*/
- private TDoubleArrayList taus;
+ private final TDoubleArrayList taus;
/**
* Discharge found by the last findQ
*/
private double findQ;
-
+
/**
* Velocity found by the last {@link findQ}
*/
private double vmainFound;
-
+
/**
* Shear stress found by the last {@link findQ}
*/
private double tauFound;
-
+
/**
* Whether qFound has been interpolated
*/
private boolean isInterpolated;
-
+
/**
* Real linear interpolator for q and v values
*/
private PolynomialSplineFunction vInterpolator;
-
+
/**
* Real linear interpolator for q and tau values
*/
private PolynomialSplineFunction tauInterpolator;
-
-
+
/***** CONSTRUCTORS *****/
-
+
/**
* Constructor with km parameter
*/
- public FlowVelocityKmModelValues(double km) {
+ public FlowVelocityKmModelValues(final double km) {
this.km = km;
- qs = new TDoubleArrayList();
- vmains = new TDoubleArrayList();
- taus = new TDoubleArrayList();
- vInterpolator = null;
- tauInterpolator = null;
+ this.qs = new TDoubleArrayList();
+ this.vmains = new TDoubleArrayList();
+ this.taus = new TDoubleArrayList();
+ this.vInterpolator = null;
+ this.tauInterpolator = null;
}
/**
* Copy constructor with new km
*/
- public FlowVelocityKmModelValues(double km, FlowVelocityKmModelValues src) {
+ public FlowVelocityKmModelValues(final double km, final FlowVelocityKmModelValues src) {
this(km);
- src.copyTo(qs, vmains, taus);
+ src.copyTo(this.qs, this.vmains, this.taus);
}
-
+
/***** METHODS *****/
-
+
/**
* Number of the q-v-tau tuples
*/
public int size() {
- if (qs != null)
- return qs.size();
- else
- return 0;
+ return this.qs.size();
}
-
+
/**
* Km
*/
public double getKm() {
- return km;
+ return this.km;
}
-
+
/**
* Adds all q-v-tau to another set of arrays
*/
- void copyTo(TDoubleArrayList dstqs, TDoubleArrayList dstvmains, TDoubleArrayList dsttaus) {
- for (int i = 0; i <= qs.size(); i++) {
- dstqs.add(qs.getQuick(i));
- dstvmains.add(vmains.getQuick(i));
- dsttaus.add(taus.getQuick(i));
+ void copyTo(final TDoubleArrayList dstqs, final TDoubleArrayList dstvmains, final TDoubleArrayList dsttaus) {
+ for (int i = 0; i <= this.qs.size(); i++) {
+ dstqs.add(this.qs.getQuick(i));
+ dstvmains.add(this.vmains.getQuick(i));
+ dsttaus.add(this.taus.getQuick(i));
}
}
-
+
/**
* Discharge found by the last {@link findQ}
+ *
* @return
*/
public double getFindQ() {
- return findQ;
+ return this.findQ;
}
-
+
/**
* Velocity found by the last {@link findQ}
*/
public double getVmainFound() {
- return vmainFound;
+ return this.vmainFound;
}
-
+
/**
* Shear stress found by the last {@link findQ}
*/
public double getTauFound() {
- return tauFound;
+ return this.tauFound;
}
-
+
/**
- * Whether qFound has been interpolated
+ * Whether qFound has been interpolated
*/
public boolean getIsInterpolated() {
- return isInterpolated;
+ return this.isInterpolated;
}
-
+
/**
- * Adds a q-v-tau value triple.
+ * Adds a q-v-tau value triple.
*/
- public void addValues(double q, double vmain, double tau) {
- qs.add(q);
- vmains.add(vmain);
- taus.add(tau);
+ public void addValues(final double q, final double vmain, final double tau) {
+ this.qs.add(q);
+ this.vmains.add(vmain);
+ this.taus.add(tau);
}
-
+
/**
* Searches a discharge value and returns it or the interpolated value
+ *
* @return Found or interpolated discharge, or NaN otherwise
*/
- public double findQ(double q) {
- if (vInterpolator == null) {
- vInterpolator = new LinearInterpolator().interpolate(qs.toNativeArray(), vmains.toNativeArray());
- tauInterpolator = new LinearInterpolator().interpolate(qs.toNativeArray(), taus.toNativeArray());
+ public double findQ(final double q) {
+ if (this.vInterpolator == null) {
+ this.vInterpolator = new LinearInterpolator().interpolate(this.qs.toNativeArray(), this.vmains.toNativeArray());
+ this.tauInterpolator = new LinearInterpolator().interpolate(this.qs.toNativeArray(), this.taus.toNativeArray());
}
- findQ = q;
+
+ this.findQ = q;
+
try {
- vmainFound = vInterpolator.value(q);
- tauFound = tauInterpolator.value(q);
- } catch (Exception e) {
- q = Double.NaN;
+ this.vmainFound = this.vInterpolator.value(q);
+ this.tauFound = this.tauInterpolator.value(q);
+ return q;
}
- return q;
+ catch (final Exception e) {
+ e.printStackTrace();
+ return Double.NaN;
+ }
}
-}
+}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java Thu Mar 29 15:48:17 2018 +0200
@@ -11,6 +11,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.TreeSet;
import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.artifacts.CallContext;
@@ -79,11 +80,11 @@
final WstSoundingIdPair histPair, final Calculation problems, final RiverInfoProvider infoProvider) {
/* access real input data from database */
- final WaterlevelData currentWaterlevel = loadWaterlevel(currentPair, problems);
+ final WaterlevelData currentWaterlevel = loadWaterlevel(currentPair, calcRange, problems);
if (currentWaterlevel == null)
return null;
- final WaterlevelData historicalWaterlevel = loadWaterlevel(histPair, problems);
+ final WaterlevelData historicalWaterlevel = loadWaterlevel(histPair, calcRange, problems);
if (historicalWaterlevel == null)
return null;
@@ -112,9 +113,6 @@
FlowDepthUtils.checkYearDifference("", currentWstYear, currentSoundingYear, problems);
FlowDepthUtils.checkYearDifference("", historicalWstYear, historicalSoundingYear, problems);
- // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
- // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
-
/* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
final RiverInfoProvider currentRiverInfoProvider = infoProvider.forWaterlevel(currentWaterlevel);
final RiverInfoProvider histRiverInfoProvider = infoProvider.forWaterlevel(historicalWaterlevel);
@@ -123,35 +121,27 @@
final WstInfo historicalWstInfo = new WstInfo(historicalWaterlevel.getName(), historicalWstYear, histRiverInfoProvider.getReferenceGauge());
final WKms currentWkms = currentWaterlevel.getWkms();
- final WaterlevelValuesFinder currentWstProvider = WaterlevelValuesFinder.fromKms(currentWkms);
- // final DischargeValuesFinder currentDischargeProvider = DischargeValuesFinder.fromKms(currentWkms);
+ final WaterlevelValuesFinder currentWstProvider = WaterlevelValuesFinder.fromKms(problems, currentWkms);
final WKms historicalWkms = historicalWaterlevel.getWkms();
- final WaterlevelValuesFinder historicalWstProvider = WaterlevelValuesFinder.fromKms(historicalWkms);
- // final DischargeValuesFinder historicalDischargeProvider = DischargeValuesFinder.fromKms(historicalWkms);
+ final WaterlevelValuesFinder historicalWstProvider = WaterlevelValuesFinder.fromKms(problems, historicalWkms);
final int currentMeanYear = (currentWstYear + currentSoundingYear) / 2;
final int historcialMeanYear = (historicalWstYear + historicalSoundingYear) / 2;
- // final String waterlevelLabel = waterlevel.getName();
- // final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight);
-
final double diffYear = currentMeanYear - historcialMeanYear;
/* real calculation loop */
final Collection<SInfoResultRow> rows = new ArrayList<>();
- // FIXME: determine what is the spatial discretisation that we will use...
- final double[] allKms = currentWkms.allKms().toNativeArray();
- for (final double station : allKms) {
+ final Collection<Double> stations = determineCalculationSteps(currentSounding, historicalSounding);
+ for (final double station : stations) {
if (calcRange.containsDouble(station)) {
final double currentWst = currentWstProvider.getWaterlevel(station);
- // final double currentDischarge = currentDischargeProvider.getDischarge(station);
final double currentBedHeight = currentSounding.getMeanBedHeight(station);
final double historicalWst = historicalWstProvider.getWaterlevel(station);
- // final double historicalDischarge = historicalDischargeProvider.getDischarge(station);
final double historicalBedHeight = historicalSounding.getMeanBedHeight(station);
final double diffWst = (currentWst - historicalWst) * 100;
@@ -186,6 +176,19 @@
rows);
}
+ /**
+ * Calculation steps are simply the union of all stations of all involved bed-height datasets
+ */
+ private Collection<Double> determineCalculationSteps(final BedHeightsFinder currentSounding, final BedHeightsFinder historicalSounding) {
+
+ final Collection<Double> allStations = new TreeSet<>();
+
+ allStations.addAll(currentSounding.getStations());
+ allStations.addAll(historicalSounding.getStations());
+
+ return allStations;
+ }
+
private String buildLabel(final WaterlevelData currentWaterlevel, final BedHeightInfo currentSounding, final WaterlevelData historicalWaterlevel,
final BedHeightInfo historicalSounding) {
@@ -201,9 +204,9 @@
}
/* REMARK: fetch ALL wst kms, because we need to determine the original reference gauge */
- private WaterlevelData loadWaterlevel(final WstSoundingIdPair pair, final Calculation problems) {
+ private WaterlevelData loadWaterlevel(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) {
final String wstId = pair.getWstId();
- return new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
+ return new WaterlevelFetcher().findWaterlevel(this.context, wstId, calcRange, problems);
}
private BedHeightsFinder loadBedHeight(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) {
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java Thu Mar 29 15:48:17 2018 +0200
@@ -87,6 +87,9 @@
/* access real input data from database */
final String wstId = minMaxPair.getWstId();
+
+ // FIXME: bfg überzeugen dass man immer nur pärchen auswählen kann --> min/max id ist gleich!
+
final String minSoundingId = minMaxPair.getMinSoundingId();
final String maxSoundingId = minMaxPair.getMaxSoundingId();
@@ -96,7 +99,7 @@
return null;
/* REMARK: fetch ALL wst kms, because we want to determine the original reference gauge */
- final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
+ final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, calcRange, problems);
if (waterlevel == null)
return null;
@@ -106,9 +109,6 @@
final int soundingYear = checkSoundingYear(minBedHeight, maxBedHeight, problems);
FlowDepthUtils.checkYearDifference(label, waterlevel.getYear(), soundingYear, problems);
- // FIXME
- // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
- // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
/* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel);
@@ -116,7 +116,7 @@
final int wspYear = waterlevel.getYear();
final WstInfo wstInfo = new WstInfo(waterlevel.getName(), wspYear, riverInfoProvider.getReferenceGauge());
- final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(wstKms);
+ final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(problems, wstKms);
final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms);
final String waterlevelLabel = waterlevel.getName();
@@ -125,9 +125,9 @@
/* real calculation loop */
final Collection<SInfoResultRow> rows = new ArrayList<>();
- // FIXME: determine what is the spatial discretisation that we will use...
- final double[] allKms = wstKms.allKms().toNativeArray();
- for (final double station : allKms) {
+ // FIXME: we use the stations of one of the bed heights atm, because we probably will later use only data from one bed heights datasets!
+ final Collection<Double> stations = minBedHeight == null ? maxBedHeight.getStations() : minBedHeight.getStations();
+ for (final double station : stations) {
if (calcRange.containsDouble(station)) {
final double wst = waterlevelProvider.getWaterlevel(station);
@@ -140,6 +140,7 @@
final double maxFlowDepth = wst - maxBedHeightValue;
// FIXME: unclear what is meant here...
+ // FIXME: this will simply the bed height of 'the' bed height if we reduce this to simply using one sounding dataset
final double meanBedHeight = Double.NaN;
// REMARK: access the location once only during calculation
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/BedQualityD50KmValueFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/BedQualityD50KmValueFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/BedQualityD50KmValueFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -20,6 +20,7 @@
import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
import org.apache.log4j.Logger;
import org.dive4elements.river.artifacts.math.Utils;
+import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.backend.SedDBSessionHolder;
import org.dive4elements.river.model.River;
import org.hibernate.SQLQuery;
@@ -51,39 +52,42 @@
* <br />
* A km may have bed measurements for multiple dates, multiple distances from the river bank, and multiple depth layers.
* The query filters by km range, time period and layer (sub layer: below bed to max. 50 cm depth).<br />
- *
- * If PostgreSQL would support a median aggregate function like Oracle does, the aggregation could be placed into this query.
+ *
+ * If PostgreSQL would support a median aggregate function like Oracle does, the aggregation could be placed into this
+ * query.
*/
- private static final String SQL_BED_D50_SUBLAYER_MEASUREMENT =
- "SELECT t.km, t.datum, p.tiefevon, p.tiefebis, a.d50"
- + " FROM sohltest t INNER JOIN station s ON t.stationid = s.stationid"
- + " INNER JOIN gewaesser g ON s.gewaesserid = g.gewaesserid"
- + " INNER JOIN sohlprobe p ON t.sohltestid = p.sohltestid"
- + " INNER JOIN siebanalyse a ON p.sohlprobeid = a.sohlprobeid"
- + " WHERE (g.name = :name) AND (s.km BETWEEN :fromkm - 0.0001 AND :tokm + 0.0001)"
- + " AND (p.tiefevon > 0.0) AND (p.tiefebis <= 0.5)"
- + " AND (t.datum BETWEEN :fromdate AND :todate)"
- + " ORDER BY t.km ASC, a.d50 ASC";
+ private static final String SQL_BED_D50_SUBLAYER_MEASUREMENT = "SELECT t.km, t.datum, p.tiefevon, p.tiefebis, a.d50"
+ + " FROM sohltest t INNER JOIN station s ON t.stationid = s.stationid" + " INNER JOIN gewaesser g ON s.gewaesserid = g.gewaesserid"
+ + " INNER JOIN sohlprobe p ON t.sohltestid = p.sohltestid" + " INNER JOIN siebanalyse a ON p.sohlprobeid = a.sohlprobeid"
+ + " WHERE (g.name = :name) AND (s.km BETWEEN :fromkm - 0.0001 AND :tokm + 0.0001)" + " AND (p.tiefevon > 0.0) AND (p.tiefebis <= 0.5)"
+ + " AND (t.datum BETWEEN :fromdate AND :todate)" + " ORDER BY t.km ASC, a.d50 ASC";
+
+ private Calculation problems;
/**
* Real linear interpolator for kms and d50 values (m)
*/
private final PolynomialSplineFunction interpolator;
+ /***** CONSTRUCTORS *****/
- /***** CONSTRUCTORS *****/
-
- private BedQualityD50KmValueFinder(final double[] kms, final double[] values) {
+ private BedQualityD50KmValueFinder(final Calculation problems, final double[] kms, final double[] values) {
+ this.problems = problems;
+
+ // FIXME: check: max distance prüfen? dann D4E-LinearInterpolator verwenden
this.interpolator = new LinearInterpolator().interpolate(kms, values);
}
/***** METHODS *****/
-
+
/**
* Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb)
* Abhängig von Peiljahr
+ *
+ * @param problems
*/
- public static BedQualityD50KmValueFinder loadBedMeasurements(final River river, final DoubleRange kmRange, final int soundingYear, final int validYears) {
+ public static BedQualityD50KmValueFinder loadBedMeasurements(final Calculation problems, final River river, final DoubleRange kmRange,
+ final int soundingYear, final int validYears) {
/* construct valid measurement time range */
final Calendar cal = Calendar.getInstance();
@@ -98,8 +102,8 @@
log.debug(String.format("loadValues km %.3f - %.3f %tF - %tF", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), startTime, endTime));
final Session session = SedDBSessionHolder.HOLDER.get();
final SQLQuery sqlQuery = session.createSQLQuery(SQL_BED_D50_SUBLAYER_MEASUREMENT).addScalar("km", StandardBasicTypes.DOUBLE)
- .addScalar("datum", StandardBasicTypes.DATE).addScalar("tiefevon", StandardBasicTypes.DOUBLE)
- .addScalar("tiefebis", StandardBasicTypes.DOUBLE).addScalar("d50", StandardBasicTypes.DOUBLE);
+ .addScalar("datum", StandardBasicTypes.DATE).addScalar("tiefevon", StandardBasicTypes.DOUBLE).addScalar("tiefebis", StandardBasicTypes.DOUBLE)
+ .addScalar("d50", StandardBasicTypes.DOUBLE);
final String seddbRiver = river.nameForSeddb();
sqlQuery.setString("name", seddbRiver);
sqlQuery.setDouble("fromkm", kmRange.getMinimumDouble());
@@ -111,21 +115,29 @@
final TDoubleArrayList kms = new TDoubleArrayList();
final TDoubleArrayList values = new TDoubleArrayList();
final TDoubleArrayList kmd50s = new TDoubleArrayList();
+
for (int i = 0; i <= rows.size() - 1; i++) {
kmd50s.add((double) rows.get(i)[4]);
- if (((i == rows.size() - 1) || !Utils.epsilonEquals((double) rows.get(i)[0], (double) rows.get(i+1)[0], 0.0001))) {
- int k = kmd50s.size() / 2;
- values.add(((k + k < kmd50s.size()) ? kmd50s.get(k) : (kmd50s.get(k-1) + kmd50s.get(k)) / 2) / 1000);
+ if (((i == rows.size() - 1) || !Utils.epsilonEquals((double) rows.get(i)[0], (double) rows.get(i + 1)[0], 0.0001))) {
+ final int k = kmd50s.size() / 2;
+ values.add(((k + k < kmd50s.size()) ? kmd50s.get(k) : (kmd50s.get(k - 1) + kmd50s.get(k)) / 2) / 1000);
kms.add((double) rows.get(i)[0]);
- log.debug(String.format("loadValues km %.3f d50(mm) %.1f count %d", kms.get(kms.size()-1), values.get(values.size()-1), kmd50s.size()));
+ log.debug(String.format("loadValues km %.3f d50(mm) %.1f count %d", kms.get(kms.size() - 1), values.get(values.size() - 1), kmd50s.size()));
kmd50s.clear();
+ }
}
+
+ if (kms.size() < 2 || values.size() < 2) {
+ problems.addProblem("bedqualityd50kmvaluefinder.empty", soundingYear);
+ return null;
}
+
try {
- return new BedQualityD50KmValueFinder(kms.toNativeArray(), values.toNativeArray());
+ return new BedQualityD50KmValueFinder(problems, kms.toNativeArray(), values.toNativeArray());
}
catch (final Exception e) {
e.printStackTrace();
+ problems.addProblem("bedqualityd50kmvaluefinder.error", e.getLocalizedMessage());
return null;
}
}
@@ -135,22 +147,20 @@
*
* @return d50 (m) of the km, or NaN
*/
- public double findD50(final double km) throws ArgumentOutsideDomainException {
- return this.interpolator.value(km);
- /*
- * ohne interpolation:
- * if ((kms == null) || (kms.size() == 0))
- * return Double.NaN;
- * int i = kms.binarySearch(km);
- * if (i >= 0)
- * return values.get(i);
- * i = -i - 1;
- * if ((i - 1 >= 0) && Utils.epsilonEquals(km, kms.get(i - 1), 0.0001))
- * return values.get(i - 1);
- * else if ((i >= 0) && (i <= kms.size() - 1) && Utils.epsilonEquals(km, kms.get(i), 0.0001))
- * return values.get(i);
- * else
- * return Double.NaN;
- */
+ public double findD50(final double km) {
+ try {
+ return this.interpolator.value(km);
+ }
+ catch (final ArgumentOutsideDomainException e) {
+ e.printStackTrace();
+
+ if (this.problems != null) {
+ this.problems.addProblem(km, "bedqualityd50kmvaluefinder.missing");
+ // Report only once
+ this.problems = null;
+ }
+
+ return Double.NaN;
+ }
}
}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/DischargeValuesFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/DischargeValuesFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/DischargeValuesFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -77,6 +77,8 @@
public double getDischarge(final double station) {
try {
+ // FIXME: check: ich dachte wir interpolieren den abfluss nicht linear?
+
// IMPORTANT: we first try to retrieve the exact value if it is present, to avoid rounding changes due to interpolation.
// This is important because in the WaterlevelExporter code, these values are double-compared (with '==' ...) in order
// to find the corresponding main-value.
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/FlowVelocityModelKmValueFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/FlowVelocityModelKmValueFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/FlowVelocityModelKmValueFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -17,6 +17,7 @@
import org.apache.log4j.Logger;
import org.dive4elements.river.artifacts.math.Linear;
import org.dive4elements.river.artifacts.math.Utils;
+import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowVelocityKmModelValues;
import org.dive4elements.river.backend.SessionHolder;
import org.dive4elements.river.model.River;
@@ -37,6 +38,7 @@
*
* @author Matthias Schäfer
*/
+// TODO: noch mal prüfen, ob wir eine interpolationsschranke brauchen (max. km-abstand)
final class FlowVelocityModelKmValueFinder {
/***** FIELDS *****/
@@ -115,6 +117,8 @@
*/
private final List<FlowVelocityKmModelValues> values = new ArrayList<>();
+ private Calculation problems;
+
/**
* Searched km of the last findKmValue
*/
@@ -135,6 +139,10 @@
*/
private double findQ;
+ public FlowVelocityModelKmValueFinder(final Calculation problems) {
+ this.problems = problems;
+ }
+
/***** METHODS *****/
/**
@@ -163,10 +171,11 @@
public double getFindTauFound() {
if (this.leftIndexFound < 0)
return Double.NaN;
- else if (this.leftIndexFound == this.rightIndexFound)
+
+ if (this.leftIndexFound == this.rightIndexFound)
return getLeftValues().getTauFound();
- else
- return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getTauFound(), getRightValues().getTauFound());
+
+ return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getTauFound(), getRightValues().getTauFound());
}
/**
@@ -179,14 +188,17 @@
/**
* Static constructor: queries a range of a river's kms with all their q-v-tau values.
*
+ * @param problems
+ *
* @return Whether the load has been successful the new instance, <code>null</code> otherwise.
*/
- public static FlowVelocityModelKmValueFinder loadValues(final River river, final DoubleRange kmRange, final DoubleRange qRange) {
+ public static FlowVelocityModelKmValueFinder loadValues(final Calculation problems, final River river, final DoubleRange kmRange,
+ final DoubleRange qRange) {
// DB session
log.debug(String.format("loadValues km %.3f - %.3f / q %.1f - %.1f", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), qRange.getMinimumDouble(),
qRange.getMaximumDouble()));
- final FlowVelocityModelKmValueFinder instance = new FlowVelocityModelKmValueFinder();
+ final FlowVelocityModelKmValueFinder instance = new FlowVelocityModelKmValueFinder(problems);
final TDoubleArrayList kms = instance.kms;
final List<FlowVelocityKmModelValues> values = instance.values;
@@ -249,8 +261,10 @@
log.debug(String.format("loadValues %d kms, %d values loaded", kmcount, rowcount));
- if (kms.size() == 0)
+ if (kms.size() == 0) {
+ problems.addProblem("flowvelocitymodelkmvaluefinder.empty");
return null;
+ }
return instance;
}
@@ -283,19 +297,35 @@
*/
public boolean findKmQValues(final double km, final double q) {
this.findQ = q;
+
+ final boolean found = doFindKmQValues(km, q);
+
+ if (this.problems != null) {
+
+ this.problems.addProblem(km, "flowvelocitymodelkmvaluefinder.missing");
+
+ // report only once
+ this.problems = null;
+ }
+
+ return found;
+ }
+
+ private boolean doFindKmQValues(final double km, final double q) {
if (!searchKm(km))
return false;
+
if (this.leftIndexFound == this.rightIndexFound) {
// Exact km match
final double qfound = getLeftValues().findQ(q);
log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d)", km, q, qfound, this.leftIndexFound));
return !Double.isNaN(qfound);
- } else {
- final double[] qfound = { getLeftValues().findQ(q), getRightValues().findQ(q) };
- log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d, %.3f) - %.0f (%d, %.3f)", km, q, qfound[0], this.leftIndexFound,
- getLeftValues().getKm(), qfound[1], this.rightIndexFound, getRightValues().getKm()));
- return !Double.isNaN(qfound[0]) && !Double.isNaN(qfound[1]);
}
+
+ final double[] qfound = { getLeftValues().findQ(q), getRightValues().findQ(q) };
+ log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d, %.3f) - %.0f (%d, %.3f)", km, q, qfound[0], this.leftIndexFound,
+ getLeftValues().getKm(), qfound[1], this.rightIndexFound, getRightValues().getKm()));
+ return !Double.isNaN(qfound[0]) && !Double.isNaN(qfound[1]);
}
/**
@@ -307,24 +337,23 @@
this.findKm = km;
this.leftIndexFound = -1;
this.rightIndexFound = -1;
- if ((this.kms == null) || (this.kms.size() == 0))
- return false;
+
int i = this.kms.binarySearch(km);
if (i >= 0) {
// Exact km match
this.leftIndexFound = i;
this.rightIndexFound = i;
return true;
- } else {
- // Out of range or within km interval
- if (i < 0)
- i = -i - 1;
- if ((i <= 0) || (i >= this.kms.size()))
- return false;
- this.leftIndexFound = i - 1;
- this.rightIndexFound = i;
- return true;
}
+
+ // Out of range or within km interval
+ if (i < 0)
+ i = -i - 1;
+ if ((i <= 0) || (i >= this.kms.size()))
+ return false;
+ this.leftIndexFound = i - 1;
+ this.rightIndexFound = i;
+ return true;
}
private FlowVelocityKmModelValues getLeftValues() {
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/SoilKindKmValueFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/SoilKindKmValueFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/SoilKindKmValueFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -16,14 +16,13 @@
import java.util.TreeMap;
import org.apache.commons.lang.math.DoubleRange;
-import org.apache.commons.math.ArgumentOutsideDomainException;
+import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.backend.SessionHolder;
import org.dive4elements.river.model.River;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.type.StandardBasicTypes;
-
/**
* @author Matthias Schäfer
*/
@@ -38,21 +37,17 @@
*/
private static final String SQL_BED_MOBILITY = "SELECT bmv.station, bmv.moving"
+ " FROM bed_mobility bm INNER JOIN bed_mobility_values bmv ON bm.id = bmv.bed_mobility_id"
- + " WHERE (bm.river_id=:river_id) AND (bmv.station BETWEEN (:fromkm-0.0001) AND (:tokm+0.0001))"
- + " ORDER BY bmv.station ASC";
+ + " WHERE (bm.river_id=:river_id) AND (bmv.station BETWEEN (:fromkm-0.0001) AND (:tokm+0.0001))" + " ORDER BY bmv.station ASC";
- private final NavigableMap<Double, SoilKind> kmMobility;
+ private final NavigableMap<Double, SoilKind> kmMobility = new TreeMap<>();
+ private Calculation problems;
/***** CONSTRUCTORS *****/
- private SoilKindKmValueFinder() {
- /* only instantiate me via static constructor */
- this.kmMobility = null;
- }
+ private SoilKindKmValueFinder(final Calculation problems, final List<Object[]> queryRows) {
+ this.problems = problems;
- private SoilKindKmValueFinder(final List<Object[]> queryRows) {
- this.kmMobility = new TreeMap<>();
for (int i = 0; i <= queryRows.size() - 1; i++) {
this.kmMobility.put(Double.valueOf((double) queryRows.get(i)[0]), (((int) queryRows.get(i)[1]) == 1) ? SoilKind.mobil : SoilKind.starr);
}
@@ -65,18 +60,19 @@
*
* @return Whether the load has been successful
*/
- public static SoilKindKmValueFinder loadValues(final River river, final DoubleRange kmRange) {
+ public static SoilKindKmValueFinder loadValues(final Calculation problems, final River river, final DoubleRange kmRange) {
final Session session = SessionHolder.HOLDER.get();
- final SQLQuery sqlQuery = session.createSQLQuery(SQL_BED_MOBILITY).addScalar("station", StandardBasicTypes.DOUBLE)
- .addScalar("moving", StandardBasicTypes.INTEGER);
+ final SQLQuery sqlQuery = session.createSQLQuery(SQL_BED_MOBILITY).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("moving",
+ StandardBasicTypes.INTEGER);
sqlQuery.setInteger("river_id", river.getId().intValue());
sqlQuery.setDouble("fromkm", kmRange.getMinimumDouble());
sqlQuery.setDouble("tokm", kmRange.getMaximumDouble());
final List<Object[]> rows = sqlQuery.list();
if (rows.size() >= 1)
- return new SoilKindKmValueFinder(rows);
- else
- return null;
+ return new SoilKindKmValueFinder(problems, rows);
+
+ problems.addProblem("soilkindkmvaluefinder.empty");
+ return null;
}
/***** METHODS *****/
@@ -84,26 +80,41 @@
/**
* Searches a km with its soil kind
*/
- public SoilKind findSoilKind(final double km) throws ArgumentOutsideDomainException {
- if ((this.kmMobility == null) || this.kmMobility.isEmpty())
- throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN);
- else if (this.kmMobility.containsKey(Double.valueOf(km)))
+ public SoilKind findSoilKind(final double km) {
+
+ if (this.kmMobility.containsKey(Double.valueOf(km)))
return this.kmMobility.get(Double.valueOf(km));
- else {
- // FIXME: Assert minimum distance between neighbouring stations and candidate km?
- final Entry<Double, SoilKind> streamUp = this.kmMobility.floorEntry(Double.valueOf(km));
- if (streamUp == null)
- throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN);
- else {
- // Return the soil kind of the neighbouring station with the shorter distance to the candidate.
- final Entry<Double, SoilKind> streamDown = this.kmMobility.ceilingEntry(Double.valueOf(km));
- if (streamDown == null)
- return streamUp.getValue();
- else if ((streamUp.getKey().doubleValue() + streamDown.getKey().doubleValue()) / 2 <= km)
- return streamUp.getValue();
- else
- return streamDown.getValue();
- }
+
+ final Entry<Double, SoilKind> streamUp = this.kmMobility.floorEntry(Double.valueOf(km));
+ if (streamUp == null)
+ {
+ reportProblem(km);
+ return null;
}
+
+ // FIXME: Assert minimum distance between neighbouring stations and candidate km?
+
+ // Return the soil kind of the neighbouring station with the shorter distance to the candidate.
+ final Entry<Double, SoilKind> streamDown = this.kmMobility.ceilingEntry(Double.valueOf(km));
+ if (streamDown == null)
+ return streamUp.getValue();
+
+ final double streamUpValue = streamUp.getKey().doubleValue();
+ final double streamDownValue = streamDown.getKey().doubleValue();
+
+ if ((streamUpValue + streamDownValue) / 2 <= km)
+ return streamUp.getValue();
+
+ return streamDown.getValue();
+ }
+
+ private void reportProblem(final double km) {
+ if (this.problems == null)
+ return;
+
+ this.problems.addProblem(km, "soilkindkmvaluefinder.missing");
+
+ // report problem only once
+ this.problems = null;
}
}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/Tkh.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/Tkh.java Wed Mar 28 17:04:20 2018 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/** 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.tkhcalculation;
-
-import java.io.Serializable;
-
-/**
- * Result of a transport bodies height calculation.
- *
- * @author Gernot Belger
- */
-public final class Tkh implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- private final double km;
-
- private final double wst;
-
- private final double meanBedHeight;
-
- private final double flowDepth;
-
- private final double flowDepthTkh;
-
- private final double discharge;
-
- private final SoilKind kind;
-
- private final double tkh;
-
- private final double tkhUp;
-
- private final double tkhDown;
-
- private final double velocity;
-
- private final double d50;
-
- private final double tau;
-
- public Tkh(final double km, final double wst, final double meanBedHeight, final double flowDepth, final double discharge) {
- this(km, wst, meanBedHeight, flowDepth, discharge, null);
- }
-
- public Tkh(final double km, final double wst, final double meanBedHeight, final double flowDepth, final double discharge, final SoilKind kind) {
- this(km, wst, meanBedHeight, flowDepth, Double.NaN, discharge, kind, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN);
- }
-
- public Tkh(final double km, final double wst, final double meanBedHeight, final double flowDepth, final double flowDepthTkh, final double discharge,
- final SoilKind kind, final double tkh, final double tkhUp, final double tkhDown, final double velocity, final double d50, final double tau) {
- this.km = km;
- this.wst = wst;
- this.meanBedHeight = meanBedHeight;
- this.flowDepth = flowDepth;
- this.flowDepthTkh = flowDepthTkh;
- this.discharge = discharge;
- this.kind = kind;
- this.tkh = tkh;
- this.tkhUp = tkhUp;
- this.tkhDown = tkhDown;
- this.velocity = velocity;
- this.d50 = d50;
- this.tau = tau;
- }
-
- public double getStation() {
- return this.km;
- }
-
- public double getTkh() {
- return this.tkh;
- }
-
- public SoilKind getKind() {
- return this.kind;
- }
-
- public double getUp() {
- return this.tkhUp;
- }
-
- public double getDown() {
- return this.tkhDown;
- }
-
- public double getWaterlevel() {
- return this.wst;
- }
-
- public double getDischarge() {
- return this.discharge;
- }
-
- public double getMeanBedHeight() {
- return this.meanBedHeight;
- }
-
- public double getFlowDepth() {
- return this.flowDepth;
- }
-
- public double getFlowDepthTkh() {
- return this.flowDepthTkh;
- }
-
- public double getVelocity() {
- return this.velocity;
- }
-
- public double getD50() {
- return this.d50;
- }
-
- public double getTau() {
- return this.tau;
- }
-}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/TkhCalculator.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/TkhCalculator.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/TkhCalculator.java Thu Mar 29 15:48:17 2018 +0200
@@ -10,10 +10,7 @@
package org.dive4elements.river.artifacts.sinfo.tkhcalculation;
import org.apache.commons.lang.math.DoubleRange;
-import org.apache.commons.math.ArgumentOutsideDomainException;
-import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.model.Calculation;
-import org.dive4elements.river.artifacts.resources.Resources;
import org.dive4elements.river.artifacts.sinfo.common.SInfoResultRow;
import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder;
@@ -26,12 +23,6 @@
private static final int VALID_BED_MEASUREMENT_YEARS = 20;
- private final Calculation problems;
-
- private final String problemLabel;
-
- private final CallContext context;
-
private final BedQualityD50KmValueFinder bedMeasurementsFinder;
private final SoilKindKmValueFinder soilKindFinder;
@@ -44,56 +35,42 @@
private final FlowVelocityModelKmValueFinder flowVelocitiesFinder;
- public static TkhCalculator buildTkhCalculator(final boolean useTkh, final CallContext context, final Calculation problems, final String label,
+ public static TkhCalculator buildTkhCalculator(final boolean useTkh, final Calculation problems, final String label,
final River river, final DoubleRange calcRange, final WaterlevelValuesFinder waterlevelProvider, final DischargeValuesFinder dischargeProvider,
final BedHeightsFinder bedHeightsProvider) {
if (!useTkh)
- return new TkhCalculator(problems, label, context, null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
+ return new TkhCalculator(null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
if (!dischargeProvider.isValid()) {
- final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label);
- problems.addProblem(message);
- return new TkhCalculator(problems, label, context, null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
+ problems.addProblem("sinfo_calc_flow_depth.warning.missingQ", label);
+ return new TkhCalculator(null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
}
+ /* access bed quality data */
final int soundingYear = bedHeightsProvider.getInfo().getYear();
- final BedQualityD50KmValueFinder bedMeasurementsFinder = BedQualityD50KmValueFinder.loadBedMeasurements(river, calcRange, soundingYear,
+ final BedQualityD50KmValueFinder bedMeasurementsFinder = BedQualityD50KmValueFinder.loadBedMeasurements(problems, river, calcRange, soundingYear,
VALID_BED_MEASUREMENT_YEARS);
+ if (bedMeasurementsFinder == null)
+ return new TkhCalculator(null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
- if (bedMeasurementsFinder == null) {
- final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingD50", null, label);
- problems.addProblem(message);
- return new TkhCalculator(problems, label, context, null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
- }
-
- // FIXME: wie wird ggf. interpoliert? prüfung ob werte vorhanden?
- final SoilKindKmValueFinder soilKindFinder = SoilKindKmValueFinder.loadValues(river, calcRange);
- if (soilKindFinder == null) {
- final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label);
- problems.addProblem(message);
- return new TkhCalculator(problems, label, context, null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
- }
+ /* access bed soil kind data */
+ final SoilKindKmValueFinder soilKindFinder = SoilKindKmValueFinder.loadValues(problems, river, calcRange);
+ if (soilKindFinder == null)
+ return new TkhCalculator(null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
final DoubleRange qRange = dischargeProvider.getRange();
- final FlowVelocityModelKmValueFinder flowVelocitiesFinder = FlowVelocityModelKmValueFinder.loadValues(river, calcRange, qRange);
- if (flowVelocitiesFinder == null) {
- final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingVelocity", null, label);
- problems.addProblem(message);
- return new TkhCalculator(problems, label, context, null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
- }
+ final FlowVelocityModelKmValueFinder flowVelocitiesFinder = FlowVelocityModelKmValueFinder.loadValues(problems, river, calcRange, qRange);
+ if (flowVelocitiesFinder == null)
+ return new TkhCalculator(null, waterlevelProvider, dischargeProvider, bedHeightsProvider, null, null);
- return new TkhCalculator(problems, label, context, bedMeasurementsFinder, waterlevelProvider, dischargeProvider, bedHeightsProvider, soilKindFinder,
+ return new TkhCalculator(bedMeasurementsFinder, waterlevelProvider, dischargeProvider, bedHeightsProvider, soilKindFinder,
flowVelocitiesFinder);
}
- private TkhCalculator(final Calculation problems, final String problemLabel, final CallContext context,
- final BedQualityD50KmValueFinder bedMeasurementsFinder, final WaterlevelValuesFinder waterlevelProvider,
+ private TkhCalculator(final BedQualityD50KmValueFinder bedMeasurementsFinder, final WaterlevelValuesFinder waterlevelProvider,
final DischargeValuesFinder dischargeProvider, final BedHeightsFinder bedHeightsProvider, final SoilKindKmValueFinder soilKindFinder,
final FlowVelocityModelKmValueFinder flowVelocitiesFinder) {
- this.problems = problems;
- this.problemLabel = problemLabel;
- this.context = context;
this.bedMeasurementsFinder = bedMeasurementsFinder;
this.waterlevelProvider = waterlevelProvider;
this.dischargeProvider = dischargeProvider;
@@ -121,32 +98,14 @@
private SoilKind getSoilKind(final double km) {
- try {
- if (this.soilKindFinder == null)
- return null;
+ if (this.soilKindFinder == null)
+ return null;
- return this.soilKindFinder.findSoilKind(km);
- }
- catch (final ArgumentOutsideDomainException e) {
- // FIXME: cumulate problems to one message?
- final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, this.problemLabel);
- this.problems.addProblem(km, message);
- return null;
- }
+ return this.soilKindFinder.findSoilKind(km);
}
private double getBedMeasurement(final double km) {
-
- try {
- return this.bedMeasurementsFinder.findD50(km);
- }
- catch (final Exception e) {
- // FIXME: cumulate problems to one message?
- final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingD50", null, this.problemLabel);
- this.problems.addProblem(km, message);
-
- return Double.NaN;
- }
+ return this.bedMeasurementsFinder.findD50(km);
}
public void calculateTkh(final double km, final SInfoResultRow row) {
@@ -178,13 +137,8 @@
return;
row.putValue(SInfoResultType.d50, d50);
- if (!this.flowVelocitiesFinder.findKmQValues(km, discharge)) {
- // TODO: ggf. station in Fehlermeldung?
- final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingVelocity", null, this.problemLabel);
- this.problems.addProblem(km, message);
- // FIXME: cumulate problems to one message?
+ if (!this.flowVelocitiesFinder.findKmQValues(km, discharge))
return;
- }
final double velocity = this.flowVelocitiesFinder.getFindVmainFound();
row.putValue(SInfoResultType.velocity, velocity);
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/WaterlevelValuesFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/WaterlevelValuesFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/WaterlevelValuesFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -9,10 +9,9 @@
*/
package org.dive4elements.river.artifacts.sinfo.tkhcalculation;
-import org.apache.commons.math.ArgumentOutsideDomainException;
-import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.dive4elements.river.artifacts.model.Calculation;
import org.dive4elements.river.artifacts.model.WKms;
-import org.dive4elements.river.utils.DoubleUtil;
+import org.dive4elements.river.artifacts.sinfo.util.LinearInterpolator;
/**
* Abstraction for access to waterlevels by station.
@@ -21,23 +20,19 @@
*/
public class WaterlevelValuesFinder {
- public static WaterlevelValuesFinder fromKms(final WKms wkms) {
- return new WaterlevelValuesFinder(wkms);
+ private static final double MAX_DSTANCE_KM = 1.0;
+
+ public static WaterlevelValuesFinder fromKms(final Calculation problems, final WKms wkms) {
+ return new WaterlevelValuesFinder(problems, wkms);
}
- private final PolynomialSplineFunction wstInterpolator;
+ private final LinearInterpolator wstInterpolator;
- private WaterlevelValuesFinder(final WKms wkms) {
- this.wstInterpolator = DoubleUtil.getLinearInterpolator(wkms.allKms(), wkms.allWs());
+ private WaterlevelValuesFinder(final Calculation problems, final WKms wkms) {
+ this.wstInterpolator = LinearInterpolator.create(problems, wkms.allKms(), wkms.allWs(), MAX_DSTANCE_KM);
}
public double getWaterlevel(final double km) {
- try {
- return this.wstInterpolator.value(km);
- }
- catch (final ArgumentOutsideDomainException e) {
- e.printStackTrace();
- return Double.NaN;
- }
+ return this.wstInterpolator.value(km);
}
}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Thu Mar 29 15:48:17 2018 +0200
@@ -24,33 +24,33 @@
import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
import org.dive4elements.river.model.BedHeight;
import org.dive4elements.river.model.BedHeightValue;
+import org.dive4elements.river.model.BedHeightValueType;
import org.dive4elements.river.utils.RiverUtils;
/**
- * Provides bed heigts for vcarious calculations.
+ * Provides bed heights for various calculations.
*
* @author Gernot Belger
*/
public final class BedHeightsFinder {
+ private static double MAX_DISTANCE_KM = 1;
+
private final BedHeightInfo info;
private final NavigableMap<Double, BedHeightValue> values;
- private double meanBedHeight;
-
- private double minBedHeight;
-
- private double maxBedHeight;
+ private Calculation problems;
/**
* Create bed height finders from a collection of bed heights.
*/
- public static Collection<BedHeightsFinder> createTkhBedHeights(final DoubleRange range, final Collection<BedHeight> bedHeights) {
+ public static Collection<BedHeightsFinder> createTkhBedHeights(final Calculation problems, final DoubleRange range,
+ final Collection<BedHeight> bedHeights) {
final List<BedHeightsFinder> result = new ArrayList<>(bedHeights.size());
for (final BedHeight bedHeight : bedHeights) {
- final BedHeightsFinder finder = createBedHeights(bedHeight, range);
+ final BedHeightsFinder finder = createBedHeights(problems, bedHeight, range);
result.add(finder);
}
@@ -83,13 +83,18 @@
// BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes.
// return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to);
- final BedHeightsFinder bedHeight = bedheightId == null ? null : BedHeightsFinder.forId(bedheightId, calcRange);
- if (bedHeight != null)
- return bedHeight;
+ final BedHeightsFinder bedHeight = bedheightId == null ? null : BedHeightsFinder.forId(problems, bedheightId, calcRange);
+ if (bedHeight == null) {
+ problems.addProblem("sinfo.bedheightsfinder.notfound", soundingId);
+ return null;
+ }
- // FIXME: 10n
- problems.addProblem("Failed to access sounding with id '{0}'", soundingId);
- return null;
+ if (bedHeight.isEmpty()) {
+ problems.addProblem("sinfo.bedheightsfinder.empty");
+ return null;
+ }
+
+ return bedHeight;
}
/**
@@ -97,21 +102,20 @@
*
* @return <code>null</code> if no bed height with the given id exists.
*/
- private static BedHeightsFinder forId(final int id, final DoubleRange range) {
+ private static BedHeightsFinder forId(final Calculation problems, final int id, final DoubleRange range) {
final BedHeight bedHeight = BedHeight.getBedHeightById(id);
if (bedHeight == null)
return null;
- return BedHeightsFinder.createBedHeights(bedHeight, range);
+ return BedHeightsFinder.createBedHeights(problems, bedHeight, range);
}
/**
* Create a finder for a given bed height.
*
- * @param range
*/
- private static BedHeightsFinder createBedHeights(final BedHeight bedHeight, final DoubleRange range) {
+ private static BedHeightsFinder createBedHeights(final Calculation problems, final BedHeight bedHeight, final DoubleRange range) {
// FIXME: sort by station, but in what direction?
// FIXME: using river.getKmUp()?
@@ -128,12 +132,17 @@
final BedHeightInfo info = BedHeightInfo.from(bedHeight);
- return new BedHeightsFinder(info, values);
+ return new BedHeightsFinder(problems, info, values);
}
- private BedHeightsFinder(final BedHeightInfo info, final NavigableMap<Double, BedHeightValue> values) {
+ private BedHeightsFinder(final Calculation problems, final BedHeightInfo info, final NavigableMap<Double, BedHeightValue> values) {
this.info = info;
this.values = values;
+ this.problems = problems;
+ }
+
+ public boolean isEmpty() {
+ return this.values.isEmpty();
}
public BedHeightInfo getInfo() {
@@ -145,53 +154,46 @@
}
public double getMeanBedHeight(final double km) {
- getBedHeights(km);
- return this.meanBedHeight;
+ return interpolateBedHeights(km, BedHeightValueType.value);
}
public double getMinBedHeight(final double km) {
- getBedHeights(km);
- return this.minBedHeight;
+ return interpolateBedHeights(km, BedHeightValueType.min);
}
public double getMaxBedHeight(final double km) {
- getBedHeights(km);
- return this.maxBedHeight;
+ return interpolateBedHeights(km, BedHeightValueType.max);
}
- private boolean getBedHeights(final double km) {
- if (this.values.containsKey(km)) {
- this.meanBedHeight = (this.values.get(km).getHeight() != null) ? this.values.get(km).getHeight().doubleValue() : Double.NaN;
- this.minBedHeight = (this.values.get(km).getMinHeight() != null) ? this.values.get(km).getMinHeight().doubleValue() : Double.NaN;
- this.maxBedHeight = (this.values.get(km).getMaxHeight() != null) ? this.values.get(km).getMaxHeight().doubleValue() : Double.NaN;
- return true;
+ private double interpolateBedHeights(final double km, final BedHeightValueType type) {
+ if (this.values.containsKey(km))
+ {
+ final Double value = type.getValue(this.values.get(km));
+ return value == null ? Double.NaN : value.doubleValue();
}
final Entry<Double, BedHeightValue> floorEntry = this.values.floorEntry(km);
final Entry<Double, BedHeightValue> ceilingEntry = this.values.ceilingEntry(km);
- if (floorEntry == null || ceilingEntry == null) {
- this.meanBedHeight = Double.NaN;
- this.minBedHeight = Double.NaN;
- this.maxBedHeight = Double.NaN;
- return false;
- }
+ if (floorEntry == null || ceilingEntry == null)
+ return Double.NaN;
final double floorKm = floorEntry.getKey().doubleValue();
final double ceilKm = ceilingEntry.getKey().doubleValue();
- // FIXME: check if we always want that...
+ /* report once if the interpolation distance exceeds 1000m */
+ if (Math.abs(floorKm - ceilKm) > MAX_DISTANCE_KM && this.problems != null) {
+ this.problems.addProblem(km, "linearInterpolator.maxdistance", MAX_DISTANCE_KM * 1000);
+ this.problems = null;
+ return Double.NaN;
+ }
- this.meanBedHeight = interpolate(km, floorKm, ceilKm, floorEntry.getValue().getHeight(), ceilingEntry.getValue().getHeight());
- this.minBedHeight = interpolate(km, floorKm, ceilKm, floorEntry.getValue().getMinHeight(), ceilingEntry.getValue().getMinHeight());
- this.maxBedHeight = interpolate(km, floorKm, ceilKm, floorEntry.getValue().getMaxHeight(), ceilingEntry.getValue().getMaxHeight());
- return true;
- }
+ final Double floorHeight = type.getValue(floorEntry.getValue());
+ final Double ceilingHeight = type.getValue(ceilingEntry.getValue());
- private double interpolate(final double km, final double floorKm, final double ceilKm, final Double floorHeight, final Double ceilHeight) {
- if ((floorHeight != null) && (ceilHeight != null))
- return Linear.linear(km, floorKm, ceilKm, floorHeight, ceilHeight);
- else
+ if (floorHeight == null || ceilingHeight == null)
return Double.NaN;
+
+ return Linear.linear(km, floorKm, ceilKm, floorHeight, ceilingHeight);
}
}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java Thu Mar 29 15:48:17 2018 +0200
@@ -16,6 +16,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.TreeSet;
import org.apache.commons.lang.math.DoubleRange;
import org.apache.commons.lang.math.NumberRange;
@@ -65,7 +66,7 @@
/* find relevant bed-heights */
final List<BedHeight> defaultBedHeights = new DefaultBedHeights(river).getBedHeights(problems);
- final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(calcRange, defaultBedHeights);
+ final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(problems, calcRange, defaultBedHeights);
/* misuse winfo-artifact to calculate waterlevels in the same way */
final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
@@ -85,17 +86,33 @@
/* for each waterlevel, do a tkh calculation */
final TkhCalculationResults results = new TkhCalculationResults(calcModeLabel, user, riverInfo, calcRange, descriptionHeader);
+ /* determine calculation steps */
+ final Collection<Double> allStations = determineCalculationSteps(bedHeights);
+
for (final WQKms wqKms : kms) {
-
- final TkhCalculationResult result = calculateResult(calcRange, infoProvider, wqKms, bedHeights, descBuilder, problems);
+ final TkhCalculationResult result = calculateResult(calcRange, allStations, infoProvider, wqKms, bedHeights, descBuilder, problems);
if (result != null)
- // FIXME: must be sorted by station!
results.addResult(result);
}
return new CalculationResult(results, problems);
}
+ /**
+ * Calculation steps are simply the union of all stations of all involved bed-height datasets
+ */
+ private Collection<Double> determineCalculationSteps(final Collection<BedHeightsFinder> bedHeights) {
+
+ final Collection<Double> allStations = new TreeSet<>();
+
+ for (final BedHeightsFinder bedHeight : bedHeights) {
+ final Collection<Double> stations = bedHeight.getStations();
+ allStations.addAll(stations);
+ }
+
+ return allStations;
+ }
+
private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final Calculation problems) {
final CalculationResult waterlevelData = winfo.getWaterlevelData(this.context);
@@ -112,12 +129,12 @@
return (WQKms[]) waterlevelData.getData();
}
- private TkhCalculationResult calculateResult(final DoubleRange calcRange, final RiverInfoProvider riverInfo, final WQKms wkms,
- final Collection<BedHeightsFinder> bedHeights, final WaterlevelDescriptionBuilder descBuilder, final Calculation problems) {
+ private TkhCalculationResult calculateResult(final DoubleRange calcRange, final Collection<Double> allStations, final RiverInfoProvider riverInfo,
+ final WQKms wkms, final Collection<BedHeightsFinder> bedHeights, final WaterlevelDescriptionBuilder descBuilder, final Calculation problems) {
- // FIXME: wo kommt das her? via winfo kein jahr vorhanden, oder doch? aber soll in metadaten ausgegeben werden...
+ // We have no wst year as the wst is created by a calculation; we do not need it though
final int wspYear = -1;
- // FIXME: richtig? vgl. WInfo?
+ // Remark: showAllGauges only true for Fixierungsanalyse, false for WInfo, so false here as well
final boolean showAllGauges = false;
final WaterlevelData waterlevel = new WaterlevelData(wkms, wspYear, showAllGauges);
@@ -137,14 +154,9 @@
final Collection<SInfoResultRow> rows = new ArrayList<>();
- /* 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?
+ for (final Double stationDbl : allStations) {
- final int size = wkms.size();
- for (int i = 0; i < size; i++) {
-
- final double station = wkms.getKm(i);
+ final double station = stationDbl;
/* find the right calculator (i.e. bed height) depending on station, there should only be one maximal */
final TkhCalculator tkhCalculator = findCalculator(calculatorsByRanges, station);
@@ -188,15 +200,15 @@
final NumberRange range = new NumberRange(info.getFrom(), info.getTo());
- final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(wkms);
+ final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(problems, wkms);
final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wkms);
/* initialize tkh calculator */
- final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(true, this.context, problems, wstLabel, riverInfoProvider.getRiver(),
- calcRange, waterlevelProvider, dischargeProvider, bedHeightsProvider);
+ final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(true, problems, wstLabel, riverInfoProvider.getRiver(), calcRange,
+ waterlevelProvider, dischargeProvider, bedHeightsProvider);
- if (tkhCalculator != null) {
- /* just ignore null ones, problems have already been updated by buildTkhCalculator() */
+ if (tkhCalculator.hasTkh()) {
+ /* just ignore invalid ones, problems have already been updated by buildTkhCalculator() */
calculatorByRanges.put(range, tkhCalculator);
}
}
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/LinearInterpolator.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/LinearInterpolator.java Thu Mar 29 15:48:17 2018 +0200
@@ -0,0 +1,77 @@
+/** 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.util;
+
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.model.Calculation;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Helper for interpolating values from a piecewise linear function defined by discrete values.
+ *
+ * @author Gernot Belger
+ */
+public final class LinearInterpolator {
+
+ private final NavigableMap<Double, Double> data;
+ private final double maxDistance;
+
+ private Calculation problems;
+
+ public static LinearInterpolator create(final Calculation problems, final TDoubleArrayList xs, final TDoubleArrayList ys, final double maxDistance) {
+ if (xs.size() != ys.size())
+ throw new IllegalArgumentException("Array sizes must be equal");
+
+ if (xs.size() < 2)
+ throw new IllegalArgumentException("Array must have at least 2 values");
+
+ final NavigableMap<Double, Double> data = new TreeMap<>();
+
+ for (int i = 0; i < xs.size(); i++) {
+ final double x = xs.getQuick(i);
+ final double y = ys.getQuick(i);
+ data.put(x, y);
+ }
+
+ return new LinearInterpolator(problems, data, maxDistance);
+ }
+
+ private LinearInterpolator(final Calculation problems, final NavigableMap<Double, Double> data, final double maxDistance) {
+ this.problems = problems;
+ this.data = data;
+ this.maxDistance = maxDistance;
+ }
+
+ public double value(final double value) {
+
+ final Entry<Double, Double> floorEntry = this.data.floorEntry(value);
+ final Entry<Double, Double> ceilingEntry = this.data.ceilingEntry(value);
+
+ if (floorEntry == null || ceilingEntry == null)
+ return Double.NaN;
+
+ final double floorKey = floorEntry.getKey();
+ final double floorValue = floorEntry.getValue();
+ final double ceilingKey = ceilingEntry.getKey();
+ final double ceilingValue = ceilingEntry.getValue();
+
+ if (Math.abs(floorKey - ceilingKey) > this.maxDistance && this.problems != null) {
+ this.problems.addProblem(value, "linearInterpolator.maxdistance", this.maxDistance * 1000);
+ this.problems = null;
+ }
+
+ return Linear.linear(value, floorKey, ceilingKey, floorValue, ceilingValue);
+ }
+}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Thu Mar 29 15:48:17 2018 +0200
@@ -9,8 +9,11 @@
*/
package org.dive4elements.river.artifacts.states;
+import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.river.artifacts.model.WKms;
+import gnu.trove.TDoubleArrayList;
+
/**
* Represents a waterlevel fetched with the {@link WaterlevelFetcher}.
*
@@ -69,4 +72,22 @@
public int getYear() {
return this.year;
}
-}
+
+ public boolean covers(final DoubleRange simulationRange) {
+
+ final TDoubleArrayList allKms = this.wkms.allKms();
+
+ if (allKms.isEmpty())
+ return false;
+
+ final double min = allKms.min();
+ if (min > simulationRange.getMaximumDouble())
+ return false;
+
+ final double max = allKms.max();
+ if (max < simulationRange.getMinimumDouble())
+ return false;
+
+ return true;
+ }
+}
\ No newline at end of file
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java Thu Mar 29 15:48:17 2018 +0200
@@ -13,6 +13,7 @@
import java.util.Date;
import java.util.List;
+import org.apache.commons.lang.math.DoubleRange;
import org.apache.log4j.Logger;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.D4EArtifact;
@@ -44,8 +45,12 @@
public class WaterlevelFetcher {
private static Logger log = Logger.getLogger(WaterlevelFetcher.class);
- public WaterlevelData findWaterlevel(final CallContext context, final String mingle, final double from,
- final double to, final Calculation problems) {
+ /**
+ * @param simulationRange
+ * This range is used to check if the found waterlevel covers it. It is NOT used to reduce the fetched data,
+ * because in case of waterlevels we always need to full set in order to determine the relevant gauge.
+ */
+ public WaterlevelData findWaterlevel(final CallContext context, final String mingle, final DoubleRange simulationRange, final Calculation problems) {
final String[] def = mingle.split(";");
final String uuid = def[0];
@@ -53,21 +58,25 @@
final int idx = Integer.parseInt(def[2]);
final String name = def[3];
final D4EArtifact d4eArtifact = RiverUtils.getArtifact(uuid, context);
+ if (d4eArtifact == null)
+ return null;
- final WaterlevelData data = fetchWaterlevelFromArtifact(context, d4eArtifact, idx, from, to);
- if (data != null)
- return data.withName(name);
+ final WaterlevelData data = fetchWaterlevelFromArtifact(context, d4eArtifact, idx, Double.NaN, Double.NaN);
+ if (data == null) {
+ problems.addProblem("waterlevelfetcher.missing", mingle);
+ return null;
+ }
- problems.addProblem("waterlevelfetcher.missing'", mingle);
- return null;
+ if (!data.covers(simulationRange)) {
+ problems.addProblem("waterlevelfetcher.empty", data.getName());
+ return null;
+ }
+
+ return data.withName(name);
}
- private WaterlevelData fetchWaterlevelFromArtifact(final CallContext context, final D4EArtifact d4eArtifact,
- final int idx, final double from, final double to) {
- if (d4eArtifact == null) {
- log.warn("One of the artifacts (1) for diff calculation " + "could not be loaded");
- return null;
- }
+ private WaterlevelData fetchWaterlevelFromArtifact(final CallContext context, final D4EArtifact d4eArtifact, final int idx, final double from,
+ final double to) {
if (d4eArtifact instanceof StaticWKmsArtifact) {
return fetchStaticWKmsArtifactWaterlevel((StaticWKmsArtifact) d4eArtifact, idx, from, to);
@@ -83,7 +92,7 @@
if (d4eArtifact instanceof FixationArtifact)
return fetchFixationArtifactWaterlevel(context, (FixationArtifact) d4eArtifact, idx, from, to);
- log.warn(String.format("Get Waterlevel from %s not implemented! )", d4eArtifact.getClass().getSimpleName()));
+ log.warn(String.format("Get Waterlevel from %s not implemented!", d4eArtifact.getClass().getSimpleName()));
return null;
}
@@ -91,15 +100,13 @@
// this logic back to the corresponding artifacts. However this will most certainly break existing
// artifact-serialization
- private WaterlevelData fetchStaticWKmsArtifactWaterlevel(final StaticWKmsArtifact staticWKms, final int idx,
- final double from, final double to) {
+ private WaterlevelData fetchStaticWKmsArtifactWaterlevel(final StaticWKmsArtifact staticWKms, final int idx, final double from, final double to) {
log.debug("WDifferencesState obtain data from StaticWKms");
final WKms wkms = staticWKms.getWKms(idx, from, to);
- if (wkms != null)
- {
+ if (wkms != null) {
final int year = fetchStaticWKmsYear(staticWKms);
return new WaterlevelData(wkms, year);
}
@@ -108,15 +115,13 @@
return null;
}
- private WaterlevelData fetchStaticWQKmsArtifactWaterlevel(final StaticWQKmsArtifact staticWKms, final double from,
- final double to) {
+ private WaterlevelData fetchStaticWQKmsArtifactWaterlevel(final StaticWQKmsArtifact staticWKms, final double from, final double to) {
log.debug("WDifferencesState obtain data from StaticWQKms");
final WQKms wkms = staticWKms.getWQKms(from, to);
- if (wkms != null)
- {
+ if (wkms != null) {
final int year = fetchStaticWKmsYear(staticWKms);
return new WaterlevelData(wkms, year);
}
@@ -125,8 +130,8 @@
return null;
}
- private WaterlevelData fetchWINFOArtifactWaterlevel(final CallContext context, final WINFOArtifact flys,
- final int idx, final double from, final double to) {
+ private WaterlevelData fetchWINFOArtifactWaterlevel(final CallContext context, final WINFOArtifact flys, final int idx, final double from,
+ final double to) {
log.debug("Get WKms from WINFOArtifact");
final WKms[] wkms = (WKms[]) flys.getWaterlevelData(context).getData();
@@ -146,8 +151,8 @@
return new WaterlevelData(wkms[idx], year).filterByRange(from, to);
}
- private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context,
- final FixationArtifact fixation, final int idx, final double from, final double to) {
+ private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context, final FixationArtifact fixation, final int idx, final double from,
+ final double to) {
log.debug("Get WKms from FixationArtifact.");
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/resources/messages.properties
--- a/artifacts/src/main/resources/messages.properties Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties Thu Mar 29 15:48:17 2018 +0200
@@ -775,9 +775,6 @@
sinfo_calc_flow_depth.warning.missingQ = {0}: no discharge available, calculation of transport body height not possible
sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m
sinfo_calc_flow_depth.warning.year_difference = {0}: Sie verwenden als Differenzenpaar eine Wasserspiegellage aus dem Jahr {1} und eine Peilung aus dem Jahr {2}. Dies kann zu unplausiblen Werten f\u00fchren.
-sinfo_calc_flow_depth.warning.missingSoilKind = {0}: no soil kind available, calculation of transport body height not possible
-sinfo_calc_flow_depth.warning.missingD50 = {0}: no D50 available, calculation of transport body height not possible
-sinfo_calc_flow_depth.warning.missingVelocity = {0}: no flow velocities available, calculation of transport body height not possible
sinfo.bedheightsfinder.badrange = Invalid range for bed heights {0}.
sinfo.bedheightsfinder.overlappingrange = Range of bed height {0} overlaps with other ranges.
@@ -785,6 +782,8 @@
sinfo.bedheightsfinder.wrongriver = Bed heights {0} does not belong to river {1}
sinfo.bedheightsfinder.configfile.missingriver = River not defined in config file '{0}': {1}
sinfo.bedheightsfinder.configfile.loaderror = Failed to load config file '{0}': {1}
+sinfo.bedheightsfinder.notfound = Failed to access sounding with id '{0}'
+sinfo.bedheightsfinder.empty = The bed heights do not contain any values for the selected calculation range
sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung
sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe
@@ -935,6 +934,19 @@
sinfo.export.flow_depth_minmax.csv.header.max = Maximale Flie\u00dftiefe
waterlevelfetcher.missing = Failed to access waterlevel with id '{0}'
+waterlevelfetcher.empty = The water level {0} does not contain any values for the selected calculation range
+
+bedqualityd50kmvaluefinder.error = Failed to access D50 data, calculation of transport body height not possible: {0}
+bedqualityd50kmvaluefinder.empty = The bed quality (D50) does not contain any values for the selected calculation range and sounding year {0}
+bedqualityd50kmvaluefinder.missing = Bed quality (D50) not available
+
+soilkindkmvaluefinder.empty = no soil kind available for the selected calculation range, calculation of transport body height not possible
+soilkindkmvaluefinder.missing = no soil kind available
+
+flowvelocitymodelkmvaluefinder.empty = no flow velocities available for the selected calculation range, calculation of transport body height not possible
+flowvelocitymodelkmvaluefinder.missing = no flow velocities available, calculation of transport body height not possible
+
+linearInterpolator.maxdistance = spatial discretisation exceeds {0}m, interpolation does not take place
sinfo.export.csv.meta.header.sounding.current = ##METADATEN PEILUNG aktuell
sinfo.export.csv.meta.header.sounding.historical = ##METADATEN PEILUNG historisch
diff -r b98fbd91f64a -r 45f1ad66560e artifacts/src/main/resources/messages_de.properties
--- a/artifacts/src/main/resources/messages_de.properties Wed Mar 28 17:04:20 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties Thu Mar 29 15:48:17 2018 +0200
@@ -775,9 +775,6 @@
sinfo_calc_flow_depth.warning.missingQ = {0}: keine Abflussdaten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m
sinfo_calc_flow_depth.warning.year_difference = {0}: Sie verwenden als Differenzenpaar eine Wasserspiegellage aus dem Jahr {1} und eine Peilung aus dem Jahr {2}. Dies kann zu unplausiblen Werten f\u00fchren.
-sinfo_calc_flow_depth.warning.missingSoilKind = {0}: keine Sohlart vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
-sinfo_calc_flow_depth.warning.missingD50 = {0}: kein D50 vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
-sinfo_calc_flow_depth.warning.missingVelocity = {0}: keine Flie\u00dfgeschwindigkeiten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
sinfo.bedheightsfinder.badrange = Ung\u00fcltige -range- f\u00fcr Sohlh\u00f6hen {0}.
sinfo.bedheightsfinder.overlappingrange = -Range- der Sohlh\u00f6hen {0} \u00fcberlappt andere Sohlh\u00f6hen.
@@ -785,6 +782,8 @@
sinfo.bedheightsfinder.wrongriver = Sohlh\u00f6he {0} geh\u00f6rt nicht zum Gew\u00e4sser {1}
sinfo.bedheightsfinder.configfile.missingriver = Gew\u00e4sser {1} ist in Konfigurationsdatei {0} nicht definiert.
sinfo.bedheightsfinder.configfile.loaderror = Fehler beim Laden der Konfigurationsdatei '{0}': {1}
+sinfo.bedheightsfinder.notfound = Keine Sohlh\u00f6he mit id '{0}' vorhanden
+sinfo.bedheightsfinder.empty = Die Sohlh\u00f6hen enthalten keine Werte f\u00fcr die gew\u00e4hlte Berechnungsstrecke
sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung
sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe
@@ -935,6 +934,19 @@
sinfo.export.flow_depth_minmax.csv.header.max = Maximale Flie\u00dftiefe
waterlevelfetcher.missing = Fehler beim Zugriff auf Wasserspiegel mit id '{0}'
+waterlevelfetcher.empty = Der Wasserspiegel '{0}' enth\u00e4lt keine Werte f\u00fcr die gew\u00e4hlte Berechnungsstrecke
+
+bedqualityd50kmvaluefinder.error = Fehler beim Zugriff auf die D50 Daten, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich: {0}
+bedqualityd50kmvaluefinder.empty = F\u00fcr das Jahr {0} liegen keine D50-Korndurchmesser f\u00fcr die gew\u00e4hlte Berechnungsstrecke vor
+bedqualityd50kmvaluefinder.missing = Keine D50-Korndurchmesser vorhanden
+
+soilkindkmvaluefinder.empty = Keine Sohlart f\u00fcr die gew\u00e4hlte Berechnungsstrecke vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
+soilkindkmvaluefinder.missing = keine Sohlart vorhanden
+
+flowvelocitymodelkmvaluefinder.empty = Keine Flie\u00dfgeschwindigkeiten f\u00fcr die gew\u00e4hlte Berechnungsstrecke vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
+flowvelocitymodelkmvaluefinder.missing = Keine Flie\u00dfgeschwindigkeiten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich
+
+linearInterpolator.maxdistance = R\u00e4umliche Aufl\u00f6sung gr\u00f6\u00dfer als {0}m, es findet keine Interpolation statt
sinfo.export.csv.meta.header.sounding.current = ##METADATEN PEILUNG aktuell
sinfo.export.csv.meta.header.sounding.historical = ##METADATEN PEILUNG historisch
diff -r b98fbd91f64a -r 45f1ad66560e backend/src/main/java/org/dive4elements/river/model/BedHeightValueType.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/backend/src/main/java/org/dive4elements/river/model/BedHeightValueType.java Thu Mar 29 15:48:17 2018 +0200
@@ -0,0 +1,39 @@
+/** 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.model;
+
+/**
+ * Enumerations that allows to access min/max/mean value of {@link BedHeight} in the same way.
+ *
+ * @author Gernot Belger
+ *
+ */
+public enum BedHeightValueType {
+ min {
+ @Override
+ public Double getValue(final BedHeightValue bedheightValue) {
+ return bedheightValue.getMinHeight();
+ }
+ },
+ max {
+ @Override
+ public Double getValue(final BedHeightValue bedheightValue) {
+ return bedheightValue.getMaxHeight();
+ }
+ },
+ value {
+ @Override
+ public Double getValue(final BedHeightValue bedheightValue) {
+ return bedheightValue.getHeight();
+ }
+ };
+
+ public abstract Double getValue(final BedHeightValue bedheightValue);
+}
\ No newline at end of file
More information about the Dive4Elements-commits
mailing list