# 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 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 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 determineCalculationSteps(final BedHeightsFinder currentSounding, final BedHeightsFinder historicalSounding) { + + final Collection 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 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 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 @@ *
* 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).
- * - * 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 *****/ - - 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 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, null 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 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 kmMobility; + private final NavigableMap 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 queryRows) { + this.problems = problems; - private SoilKindKmValueFinder(final List 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 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 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 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 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 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 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 createTkhBedHeights(final DoubleRange range, final Collection bedHeights) { + public static Collection createTkhBedHeights(final Calculation problems, final DoubleRange range, + final Collection bedHeights) { final List 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 null 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 values) { + private BedHeightsFinder(final Calculation problems, final BedHeightInfo info, final NavigableMap 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 floorEntry = this.values.floorEntry(km); final Entry 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 defaultBedHeights = new DefaultBedHeights(river).getBedHeights(problems); - final Collection bedHeights = BedHeightsFinder.createTkhBedHeights(calcRange, defaultBedHeights); + final Collection 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); - for (final WQKms wqKms : kms) { + /* determine calculation steps */ + final Collection allStations = determineCalculationSteps(bedHeights); - final TkhCalculationResult result = calculateResult(calcRange, infoProvider, wqKms, bedHeights, descBuilder, problems); + for (final WQKms wqKms : kms) { + 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 determineCalculationSteps(final Collection bedHeights) { + + final Collection allStations = new TreeSet<>(); + + for (final BedHeightsFinder bedHeight : bedHeights) { + final Collection 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 bedHeights, final WaterlevelDescriptionBuilder descBuilder, final Calculation problems) { + private TkhCalculationResult calculateResult(final DoubleRange calcRange, final Collection allStations, final RiverInfoProvider riverInfo, + final WQKms wkms, final Collection 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 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 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 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 data, final double maxDistance) { + this.problems = problems; + this.data = data; + this.maxDistance = maxDistance; + } + + public double value(final double value) { + + final Entry floorEntry = this.data.floorEntry(value); + final Entry 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