[PATCH] Minor changes to tkh calculation. Loading default bed heights form config file
Wald Commits
scm-commit at wald.intevation.org
Mon Mar 12 17:20:50 CET 2018
# HG changeset patch
# User gernotbelger
# Date 1520617626 -3600
# Node ID 11bf13cf046360cc4944de4b3b70df540e2309e8
# Parent a9950a3a71e514e3aa376816eddc207384dacce6
Minor changes to tkh calculation. Loading default bed heights form config file.
diff -r a9950a3a71e5 -r 11bf13cf0463 artifacts/doc/conf/sinfo_tkh_bedheights.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/doc/conf/sinfo_tkh_bedheights.properties Fri Mar 09 18:47:06 2018 +0100
@@ -0,0 +1,9 @@
+# Konfigurationsdatei f\u00fcr die in der Berechnungsart SINFO-Transportk\u00f6rperh\u00f6hen zu verwendenden Sohlh\u00f6hen.
+# Die Datei ist im Charset 'ISO-8859-1' zu encodieren.
+# Jede Zeile entspricht: <gew\u00e4ssername> = <sohlh\u00f6he1>, <Sohlh\u00f6he2>, ...
+# Alle Daten beziehen sich auf die FLYS Datenbank (d4e Datenbank)
+# der <gew\u00e4ssername> entspricht dem Feld 'name' der Tabelle 'rivers'
+# die Sohlh\u00f6hen dem Feld 'description' aus der Tabelle 'bed_heights'
+# Leerzeichen im Ge\u00e4ssernamen m\u00fcssen mit u0020 kodiert werden.
+Beispielfluss=DGM-2004_Epoche-2-SOBEK
+Beispiel\u0020fluss=DGM-2004_Epoche-2-SOBEK
\ No newline at end of file
diff -r a9950a3a71e5 -r 11bf13cf0463 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 07 17:36:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Fri Mar 09 18:47:06 2018 +0100
@@ -11,7 +11,6 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
@@ -19,11 +18,9 @@
import org.apache.commons.lang.math.DoubleRange;
import org.dive4elements.river.artifacts.math.Linear;
-import org.dive4elements.river.artifacts.model.Calculation;
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.River;
/**
* Provides bed heigts for vcarious calculations.
@@ -37,23 +34,14 @@
private final NavigableMap<Double, BedHeightValue> values;
/**
- * Create specific bed heights used in tkh-calculation
- *
- * @param problems
+ * Create bed height finders from a collection of bed heights.
*/
- public static Collection<BedHeightsFinder> createTkhBedHeights(final River river, final Calculation problems, final DoubleRange range) {
- // FIXME: determine relevant bed-heights by river: read from some configuration file
- // '3' is already the right one for demo-model == '"DGM-2004_Epoche-2-SOBEK"'
- final int bedheightId = 3;
-
- final Collection<BedHeight> bedHeights = Collections.singletonList(BedHeight.getBedHeightById(bedheightId));
-
- // TODO: check for overlapping ranges... and provide a warning message, else we get problems later
-
+ public static Collection<BedHeightsFinder> createTkhBedHeights(final DoubleRange range, final Collection<BedHeight> bedHeights) {
final List<BedHeightsFinder> result = new ArrayList<>(bedHeights.size());
for (final BedHeight bedHeight : bedHeights) {
- result.add(createBedHeights(bedHeight, range));
+ final BedHeightsFinder finder = createBedHeights(bedHeight, range);
+ result.add(finder);
}
return result;
diff -r a9950a3a71e5 -r 11bf13cf0463 artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java Fri Mar 09 18:47:06 2018 +0100
@@ -0,0 +1,146 @@
+/** 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.tkhstate;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberRange;
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.model.BedHeight;
+import org.dive4elements.river.model.Range;
+import org.dive4elements.river.model.River;
+
+/**
+ * This class knows how to find the default bed heights defined for tkh calculation
+ *
+ * @author Gernot Belger
+ */
+final class DefaultBedHeights {
+ private static final String CONFIG_FILE = "sinfo_tkh_bedheights.properties";
+ private final River river;
+
+ public DefaultBedHeights(final River river) {
+ this.river = river;
+ }
+
+ public List<BedHeight> getBedHeights(final Calculation problems) {
+ final Collection<String> bedHeightNames = loadBedHeightDefaultsForRiver(this.river, problems);
+
+ final List<BedHeight> defaultBedHeights = loadBedHeightsByName(this.river, bedHeightNames, problems);
+
+ final List<BedHeight> validBedHeights = new ArrayList<>(defaultBedHeights.size());
+
+ // REMARK: check for bad ranges because db schema allow for incomplete ranges, and ignore if this is the case
+ for (final BedHeight bedHeight : defaultBedHeights) {
+
+ final Range range = bedHeight.getRange();
+
+ if (range.getA() == null || range.getB() == null)
+ problems.addProblem("sinfo.bedheightsfinder.badrange", bedHeight.getDescription());
+ else
+ validBedHeights.add(bedHeight);
+ }
+
+ /* check for overlapping ranges, N2-search, but we expect only have small numbers of bed heights */
+ final List<BedHeight> result = new ArrayList<>(defaultBedHeights.size());
+
+ for (int i = 0; i < defaultBedHeights.size(); i++) {
+ final BedHeight bedHeight = validBedHeights.get(i);
+
+ final Range range = bedHeight.getRange();
+ final NumberRange bedRange = new NumberRange(range.getA(), range.getB());
+
+ if (overlapsRange(bedRange, validBedHeights, i + 1)) {
+ problems.addProblem("sinfo.bedheightsfinder.overlappingrange", bedHeight.getDescription());
+ } else
+ result.add(bedHeight);
+ }
+
+ return result;
+ }
+
+ private static Collection<String> loadBedHeightDefaultsForRiver(final River river, final Calculation problems) {
+ final File configDir = Config.getConfigDirectory();
+ final File configFile = new File(configDir, CONFIG_FILE);
+
+ final Properties properties = new Properties();
+ try (final InputStreamReader reader = new InputStreamReader(Files.newInputStream(configFile.toPath()), StandardCharsets.ISO_8859_1)) {
+ properties.load(reader);
+
+ final String value = properties.getProperty(river.getName());
+ final String[] split = StringUtils.split(StringUtils.trim(value), ',');
+ if (ArrayUtils.isEmpty(split)) {
+ problems.addProblem("sinfo.bedheightsfinder.configfile.missingriver", CONFIG_FILE, river.getName());
+ return Collections.emptyList();
+ }
+
+ return Arrays.asList(split);
+ }
+ catch (final IOException e) {
+ e.printStackTrace();
+ problems.addProblem("sinfo.bedheightsfinder.configfile.loaderror", CONFIG_FILE, e.getMessage());
+ return Collections.emptyList();
+ }
+ }
+
+ private static List<BedHeight> loadBedHeightsByName(final River shouldBeRiver, final Collection<String> bedHeightNames, final Calculation problems) {
+
+ final List<BedHeight> bedHeights = new ArrayList<>(bedHeightNames.size());
+
+ for (final String name : bedHeightNames) {
+ try {
+ final BedHeight bedHeight = BedHeight.getBedHeightByDescription(name);
+ if (bedHeight == null)
+ problems.addProblem("sinfo.bedheightsfinder.missingdescription", name);
+ else {
+ final River river = bedHeight.getRiver();
+ if (!shouldBeRiver.getId().equals(river.getId()))
+ problems.addProblem("sinfo.bedheightsfinder.wrongriver", name, shouldBeRiver.getName());
+ else
+ bedHeights.add(bedHeight);
+ }
+ }
+ catch (final Exception e) {
+ e.printStackTrace();
+ problems.addProblem("sinfo.bedheightsfinder.missingdescription", name);
+ }
+ }
+
+ return bedHeights;
+ }
+
+ private static boolean overlapsRange(final NumberRange bedRange, final List<BedHeight> result, final int startIndex) {
+
+ for (int i = startIndex; i < result.size(); i++) {
+
+ final BedHeight compareBed = result.get(i);
+ final Range range = compareBed.getRange();
+ final NumberRange compareRange = new NumberRange(range.getA(), range.getB());
+
+ if (compareRange.overlapsRange(bedRange))
+ return true;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff -r a9950a3a71e5 -r 11bf13cf0463 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 07 17:36:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java Fri Mar 09 18:47:06 2018 +0100
@@ -11,9 +11,14 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import org.apache.commons.lang.math.DoubleRange;
+import org.apache.commons.lang.math.NumberRange;
import org.dive4elements.artifacts.CallContext;
import org.dive4elements.river.artifacts.WINFOArtifact;
import org.dive4elements.river.artifacts.model.Calculation;
@@ -26,11 +31,13 @@
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh;
import org.dive4elements.river.artifacts.sinfo.tkhcalculation.TkhCalculator;
+import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
import org.dive4elements.river.artifacts.states.WaterlevelData;
import org.dive4elements.river.exports.WaterlevelDescriptionBuilder;
+import org.dive4elements.river.model.BedHeight;
import org.dive4elements.river.model.River;
/**
@@ -55,7 +62,8 @@
final Calculation problems = new Calculation();
/* find relevant bed-heights */
- final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(river, problems, calcRange);
+ final List<BedHeight> defaultBedHeights = new DefaultBedHeights(river).getBedHeights(problems);
+ final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(calcRange, defaultBedHeights);
/* misuse winfo-artifact to calculate waterlevels in the same way */
final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
@@ -118,46 +126,77 @@
final WstInfo wstInfo = new WstInfo(wstLabel, wspYear, riverInfoProvider.getReferenceGauge());
+ /* build tkh calculators per bedheight */
+ final Map<NumberRange, TkhCalculator> calculatorsByRanges = buildCalculators(calcRange, wkms, bedHeights, problems, riverInfoProvider, wstLabel);
+ if (calculatorsByRanges.isEmpty()) {
+ /* there should already be some problems, so just abort */
+ return null;
+ }
+
final Collection<TkhResultRow> rows = new ArrayList<>();
- /*
- * for each separate bed height dataset we do the calculation and put everything into one result, bed heights must not
- * overlap accordingly
- */
+ /* 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?
+
+ final int size = wkms.size();
+ for (int i = 0; i < size; i++) {
+
+ final double station = wkms.getKm(i);
+ final double wst = wkms.getW(i);
+
+ /* find the right calculator (i.e. bedheigh) depending on station, there should only be one maximal */
+ final TkhCalculator tkhCalculator = findCalculator(calculatorsByRanges, station);
+ if (tkhCalculator == null)
+ continue;
+
+ final Tkh tkh = tkhCalculator.getTkh(station, wst);
+
+ final String description = descBuilder.getDesc(wkms);
+ final String gaugeLabel = riverInfoProvider.findGauge(station);
+ final String location = riverInfoProvider.getLocation(station);
+
+ rows.add(new TkhResultRow(tkh, description, gaugeLabel, location));
+ }
+
+ return new TkhCalculationResult(wstLabel, wstInfo, true, rows);
+ }
+
+ private TkhCalculator findCalculator(final Map<NumberRange, TkhCalculator> calculators, final double station) {
+
+ // REMAKR: linear search at this point, put we expect the number of bed heights to be very small (1-2 items)
+ final Set<Entry<NumberRange, TkhCalculator>> x = calculators.entrySet();
+ for (final Entry<NumberRange, TkhCalculator> entry : x) {
+ final NumberRange range = entry.getKey();
+ // FIXME: check if we need comparison with a tolerance
+ if (range.containsDouble(station))
+ return entry.getValue();
+ }
+
+ return null;
+ }
+
+ private Map<NumberRange, TkhCalculator> buildCalculators(final DoubleRange calcRange, final WQKms wkms, final Collection<BedHeightsFinder> bedHeights,
+ final Calculation problems, final RiverInfoProvider riverInfoProvider, final String wstLabel) {
+ final Map<NumberRange, TkhCalculator> calculatorByRanges = new HashMap<>();
for (final BedHeightsFinder bedHeightsProvider : bedHeights) {
+ final BedHeightInfo info = bedHeightsProvider.getInfo();
+
+ final NumberRange range = new NumberRange(info.getFrom(), info.getTo());
+
final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wkms);
/* initialize tkh calculator */
final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(true, this.context, problems, wstLabel, riverInfoProvider.getRiver(),
- calcRange,
- dischargeProvider, bedHeightsProvider);
- if (tkhCalculator == null) {
- /* just abort, problems have already been updated by buildTkhCalculator() */
- return null;
- }
+ calcRange, dischargeProvider, bedHeightsProvider);
- /* 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?
-
- final int size = wkms.size();
- for (int i = 0; i < size; i++) {
-
- final double station = wkms.getKm(i);
- final double wst = wkms.getW(i);
-
- final Tkh tkh = tkhCalculator.getTkh(station, wst);
-
- final String description = descBuilder.getDesc(wkms);
- final String gaugeLabel = riverInfoProvider.findGauge(station);
- final String location = riverInfoProvider.getLocation(station);
-
- rows.add(new TkhResultRow(tkh, description, gaugeLabel, location));
+ if (tkhCalculator != null) {
+ /* just ignore null ones, problems have already been updated by buildTkhCalculator() */
+ calculatorByRanges.put(range, tkhCalculator);
}
}
- return new TkhCalculationResult(wstLabel, wstInfo, true, rows);
+ return calculatorByRanges;
}
}
\ No newline at end of file
diff -r a9950a3a71e5 -r 11bf13cf0463 artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/BedHeightInfo.java
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/BedHeightInfo.java Wed Mar 07 17:36:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/BedHeightInfo.java Fri Mar 09 18:47:06 2018 +0100
@@ -10,6 +10,7 @@
package org.dive4elements.river.artifacts.sinfo.util;
import java.io.Serializable;
+import java.math.BigDecimal;
import org.dive4elements.river.model.BedHeight;
@@ -36,6 +37,9 @@
private final String oldElevationModelUnit;
+ private final BigDecimal from;
+
+ private final BigDecimal to;
public static BedHeightInfo from(final BedHeight bedHeight) {
return new BedHeightInfo(bedHeight);
@@ -49,6 +53,9 @@
this.locationSystem = bedHeight.getLocationSystem().getName();
this.curElevationModelUnit = bedHeight.getCurElevationModel().getUnit().getName();
this.oldElevationModelUnit = bedHeight.getCurElevationModel().getUnit().getName();
+
+ this.from = bedHeight.getRange().getA();
+ this.to = bedHeight.getRange().getB();
}
public Integer getYear() {
@@ -78,4 +85,12 @@
public String getOldElevationModelUnit() {
return this.oldElevationModelUnit;
}
-}
+
+ public BigDecimal getFrom() {
+ return this.from;
+ }
+
+ public BigDecimal getTo() {
+ return this.to;
+ }
+}
\ No newline at end of file
diff -r a9950a3a71e5 -r 11bf13cf0463 artifacts/src/main/resources/messages.properties
--- a/artifacts/src/main/resources/messages.properties Wed Mar 07 17:36:04 2018 +0100
+++ b/artifacts/src/main/resources/messages.properties Fri Mar 09 18:47:06 2018 +0100
@@ -779,6 +779,13 @@
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.
+sinfo.bedheightsfinder.missingdescription = No bed heights found with description = {0}
+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_calc_flow_depth_development=Flie\u00dftiefenentwicklung
sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe
sinfo_calc_grounding=Grundber\u00fchrungen
diff -r a9950a3a71e5 -r 11bf13cf0463 artifacts/src/main/resources/messages_de.properties
--- a/artifacts/src/main/resources/messages_de.properties Wed Mar 07 17:36:04 2018 +0100
+++ b/artifacts/src/main/resources/messages_de.properties Fri Mar 09 18:47:06 2018 +0100
@@ -779,6 +779,13 @@
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.
+sinfo.bedheightsfinder.missingdescription = Sohlh\u00f6he mit -description- {0} nicht vorhanden
+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_calc_flow_depth_development=Flie\u00dftiefenentwicklung
sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe
sinfo_calc_grounding=Grundber\u00fchrungen
diff -r a9950a3a71e5 -r 11bf13cf0463 backend/src/main/java/org/dive4elements/river/model/BedHeight.java
--- a/backend/src/main/java/org/dive4elements/river/model/BedHeight.java Wed Mar 07 17:36:04 2018 +0100
+++ b/backend/src/main/java/org/dive4elements/river/model/BedHeight.java Fri Mar 09 18:47:06 2018 +0100
@@ -262,5 +262,17 @@
return singles != null ? singles.get(0) : null;
}
+
+ public static BedHeight getBedHeightByDescription(final String description) {
+
+ final Session session = SessionHolder.HOLDER.get();
+
+ final Query query = session.createQuery("from BedHeight where description=:description");
+ query.setParameter("description", description);
+
+ final List<BedHeight> singles = query.list();
+
+ return singles != null ? singles.get(0) : null;
+ }
}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
\ No newline at end of file
More information about the Dive4Elements-commits
mailing list