Mercurial > dive4elements > river
changeset 8915:d9dbf0b74bc2
Refaktoring of flow depth calculation, extracting tkh part. First implementation of tkh calculation.
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/sinfo.xml Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/doc/conf/artifacts/sinfo.xml Wed Feb 28 17:27:15 2018 +0100 @@ -118,7 +118,7 @@ <condition data="calculation_mode" value="sinfo_calc_transport_bodies_heights" operator="equal"/> </transition> - <state id="state.sinfo.transport_bodies_heights" description="state.sinfo.transport_bodies_heights" state="org.dive4elements.river.artifacts.sinfo.tkh.TkhState" helpText="help.state.sinfo.transport_bodies_heights"> + <state id="state.sinfo.transport_bodies_heights" description="state.sinfo.transport_bodies_heights" state="org.dive4elements.river.artifacts.sinfo.tkhstate.TkhState" helpText="help.state.sinfo.transport_bodies_heights"> <outputmodes> <!-- <outputmode name="sinfo_flow_depth" description="output.flow_depth" mime-type="image/png" type="chart"> <facets> <facet name="sinfo_flow_depth.filtered" description="Facet for mean flow depth, filtered by current zoom state"/> <facet name="sinfo_flow_depth.tkh.filtered" description="Facet for mean flow depth including tkh, filtered by current zoom state"/> <facet name="sinfo_flow_depth.tkh" description="Facet for tkh"/> <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/> </facets> </outputmode> <outputmode name="sinfo_flowdepth_export" description="output.sinfo_flowdepth_export" mime-type="text/plain" type="export"> <facets> <facet name="csv" description="facet.sinfo_flowdepth_export.csv"/> <facet name="pdf" description="facet.sinfo_flowdepth_export.pdf"/> </facets> </outputmode> <outputmode name="sinfo_flowdepth_report" description="output.sinfo_flowdepth_report" mime-type="text/xml" type="report"> <facets> <facet name="report" description="facet.sinfo_flowdepth_report"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoCalculationResult.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,134 @@ +/** 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.common; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.SoilKind; +import org.dive4elements.river.artifacts.sinfo.util.WstInfo; + +import gnu.trove.TDoubleArrayList; + +/** + * @author Gernot Belger + */ +public abstract class AbstractSInfoCalculationResult<ROW extends AbstractSInfoResultRow> implements Serializable { + + private static final long serialVersionUID = 1L; + + private final Collection<ROW> rows; + + private final String label; + + private final boolean hasTkh; + + private final WstInfo wst; + + public AbstractSInfoCalculationResult(final String label, final WstInfo wst, final boolean hasTkh, final Collection<ROW> rows) { + this.label = label; + this.wst = wst; + this.hasTkh = hasTkh; + this.rows = new ArrayList<>(rows); + } + + public final String getLabel() { + return this.label; + } + + public final boolean hasTkh() { + return this.hasTkh; + } + + public final WstInfo getWst() { + return this.wst; + } + + public final void addRow(final ROW resultRow) { + this.rows.add(resultRow); + } + + public final Collection<ROW> getRows() { + return Collections.unmodifiableCollection(this.rows); + } + + public final double[][] getTkhUpPoints() { + final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); + final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); + final List<SoilKind> kinds = new ArrayList<>(this.rows.size()); + + for (final ROW row : this.rows) { + xPoints.add(row.getStation()); + yPoints.add(row.getTkhUp()); + kinds.add(row.getTkhKind()); + } + + return adjustTkhVisualization(xPoints, yPoints, kinds); + } + + public final double[][] getTkhDownPoints() { + final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); + final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); + final List<SoilKind> kinds = new ArrayList<>(this.rows.size()); + + for (final ROW row : this.rows) { + xPoints.add(row.getStation()); + yPoints.add(row.getTkhDown()); + kinds.add(row.getTkhKind()); + } + + return adjustTkhVisualization(xPoints, yPoints, kinds); + } + + /** + * the up and down points must be further adjusted for visualization, see Mail Hr. Reiß + * basically we need to introduce extra points when the kind changes, so we get vertical lines in that case + */ + private double[][] adjustTkhVisualization(final TDoubleArrayList xPoints, final TDoubleArrayList yPoints, final List<SoilKind> kinds) { + + final TDoubleArrayList adjustedX = new TDoubleArrayList(xPoints.size()); + final TDoubleArrayList adjustedY = new TDoubleArrayList(yPoints.size()); + + adjustedX.add(xPoints.get(0)); + adjustedY.add(yPoints.get(0)); + + for (int i = 1; i < xPoints.size(); i++) { + + final SoilKind kind1 = kinds.get(i - 1); + final SoilKind kind2 = kinds.get(i); + + if (kind1 != kind2) { + /* introduce two extra points in order to create a vertical line in the middle of the two adjacent points */ + final double x1 = xPoints.get(i - 1); + final double y1 = yPoints.get(i - 1); + final double x2 = xPoints.get(i); + final double y2 = yPoints.get(i); + + final double middleX = (x1 + x2) / 2; + + // REMARK: we can't produce a 100% vertical line, as the area-renderer will not work correctly + adjustedX.add(middleX - 0.0001); + adjustedY.add(y1); + + adjustedX.add(middleX + 0.0001); + adjustedY.add(y2); + } + + /* always add the real point now */ + adjustedX.add(xPoints.get(i)); + adjustedY.add(yPoints.get(i)); + } + + return new double[][] { adjustedX.toNativeArray(), adjustedY.toNativeArray() }; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoResultRow.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,83 @@ +/** 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.common; + +import java.io.Serializable; + +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.SoilKind; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh; + +/** + * Contains common result data of flow-depth- and tkh-calculations. + * + * @author Gernot Belger + */ +public abstract class AbstractSInfoResultRow implements Serializable { + private static final long serialVersionUID = 1L; + + private final Tkh tkh; + + private final String waterlevelLabel; + + private final String gauge; + + private final String location; + + public AbstractSInfoResultRow(final Tkh tkh, final String waterlevelLabel, final String gauge, final String location) { + this.tkh = tkh; + this.waterlevelLabel = waterlevelLabel; + this.gauge = gauge; + this.location = location; + } + + public final double getStation() { + return this.tkh.getStation(); + } + + public final SoilKind getTkhKind() { + return this.tkh.getKind(); + } + + public final double getTkh() { + return this.tkh.getTkh(); + } + + public final double getTkhUp() { + return this.tkh.getUp(); + } + + public final double getTkhDown() { + return this.tkh.getDown(); + } + + public final double getWaterlevel() { + return this.tkh.getWaterlevel(); + } + + public final double getDischarge() { + return this.tkh.getDischarge(); + } + + public final String getWaterlevelLabel() { + return this.waterlevelLabel; + } + + public final String getGauge() { + return this.gauge; + } + + public final double getMeanBedHeight() { + return this.tkh.getMeanBedHeight(); + } + + public final String getLocation() { + return this.location; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/RiverInfoProvider.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,116 @@ +/** 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.common; + +import java.util.List; + +import org.apache.commons.lang.math.DoubleRange; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.model.LocationProvider; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.resources.Resources; +import org.dive4elements.river.artifacts.states.WaterlevelData; +import org.dive4elements.river.model.Gauge; +import org.dive4elements.river.model.River; +import org.dive4elements.river.utils.GaugeIndex; + +/** + * @author Gernot Belger + * + */ +public final class RiverInfoProvider { + + private static final String CSV_NOT_IN_GAUGE_RANGE = "export.waterlevel.csv.not.in.gauge.range"; + + private final River river; + private final GaugeIndex gaugeIndex; + private final Gauge refGauge; + private final boolean showAllGauges; + private final String notinrange; + + public static RiverInfoProvider forRange(final CallContext context, final River river, final DoubleRange calcRange) { + + final List<Gauge> gauges = river.determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); + final GaugeIndex gaugeIndex = new GaugeIndex(gauges); + + final String notinrange = Resources.getMsg(context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, CSV_NOT_IN_GAUGE_RANGE); + + return new RiverInfoProvider(notinrange, river, false, gaugeIndex, null); + } + + private RiverInfoProvider(final String notinrange, final River river, final boolean showAllGauges, final GaugeIndex gaugeIndex, final Gauge refGauge) { + this.notinrange = notinrange; + this.river = river; + this.showAllGauges = showAllGauges; + this.gaugeIndex = gaugeIndex; + this.refGauge = refGauge; + } + + public RiverInfoProvider forWaterlevel(final WaterlevelData waterlevel) { + final WKms wstKms = waterlevel.getWkms(); + final Gauge waterlevelRefGauge = findReferenceGauge(wstKms); + final boolean waterlevelShowAllGauges = waterlevel.isShowAllGauges(); + + return new RiverInfoProvider(this.notinrange, this.river, waterlevelShowAllGauges, this.gaugeIndex, waterlevelRefGauge); + } + + /** + * Re-determines the reference gauge, in the same way as the WaterlevelArtifact would do it + */ + private Gauge findReferenceGauge(final WKms wkms) { + + final double[] wstFromTo = findWstFromTo(wkms); + return this.river.determineRefGauge(wstFromTo, true); + } + + private static double[] findWstFromTo(final WKms wkms) { + + final double from = wkms.getKm(0); + final double to = wkms.getKm(wkms.size() - 1); + + final boolean waterIncreasing = wkms.guessWaterIncreasing(); + if (waterIncreasing) + return new double[] { to, from }; + + return new double[] { from, to }; + } + + public String getLocation(final double km) { + return LocationProvider.getLocation(this.river.getName(), km); + } + + public String findGauge(final double km) { + // REMARK: access the gauge once only during calculation + final Gauge gauge = getGauge(km); + + return gauge == null ? this.notinrange : gauge.getName(); + } + + private Gauge getGauge(final double km) { + + // REMARK: using same logic as in WaterlevelExporter here + + if (this.showAllGauges) + return this.gaugeIndex.findGauge(km); + + if (this.refGauge.getRange().contains(km)) + return this.refGauge; + + return null; + } + + public String getReferenceGauge() { + return this.refGauge == null ? this.notinrange : this.refGauge.getName(); + } + + public River getRiver() { + return this.river; + } +} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedHeightStationComparator.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +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.flowdepth; - -import java.util.Comparator; - -import org.dive4elements.river.model.BedHeightValue; - -/** - * @author Gernot Belger - */ -public class BedHeightStationComparator implements Comparator<BedHeightValue> { - - @Override - public int compare(final BedHeightValue o1, final BedHeightValue o2) { - final Double s1 = o1.getStation(); - final Double s2 = o2.getStation(); - return Double.compare(s1, s2); - } -} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedQualityD50KmValueFinder.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,235 +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.flowdepth; - -import java.util.Date; -import java.util.List; - -import org.apache.commons.lang.math.DoubleRange; -import org.apache.commons.math.ArgumentOutsideDomainException; -import org.apache.commons.math.analysis.interpolation.LinearInterpolator; -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.DateRange; -import org.dive4elements.river.backend.SedDBSessionHolder; -import org.dive4elements.river.model.River; -import org.dive4elements.river.utils.DoubleUtil; -import org.hibernate.SQLQuery; -import org.hibernate.Session; -import org.hibernate.type.StandardBasicTypes; - -import gnu.trove.TDoubleArrayList; - -/** - * Searchable sorted km array with parallel bed measurements value array and linear interpolation for km and d50 between the array elements.<br /> - * <br /> - * See comment of SQL command on how the values are filtered and aggregated. - * - * @author Matthias Schäfer - * - */ -public class BedQualityD50KmValueFinder { - - /***** INNER CLASSES *****/ - - /** - * A bed measurements aggregate with its d50 characteristic grain diameter - */ - private class D50Measurement { - private double km; - public double getKm() { - return km; - } - private Date mindate; - public Date getMinDate() { - return mindate; - } - private Date maxdate; - public Date getMaxDate() { - return maxdate; - } - private int cnt; - public int getCnt() { - return cnt; - } - private double mindepth; - public double getMinDepth() { - return mindepth; - } - private double maxdepth; - public double getMaxDepth() { - return maxdepth; - } - private double d50; - /** - * D50 in m - */ - public double getD50() { - return d50; - } - /** - * Parameter constructor - */ - public D50Measurement(double km, Date mindate, Date maxdate, int cnt, double mindepth, double maxdepth, double d50mm) { - this.km = km; - this.mindate = mindate; - this.maxdate = maxdate; - this.cnt = cnt; - this.mindepth = mindepth; - this.maxdepth = maxdepth; - this.d50 = d50mm / 1000; - } - - /** - * Query result row constructor - */ - public D50Measurement(Object[] tuple, String[] aliases) { - km = 0; - mindate = null; - maxdate = null; - cnt = 0; - mindepth = Double.NaN; - maxdepth = Double.NaN; - d50 = Double.NaN; - for (int i = 0; i < tuple.length; ++i) { - if (tuple[i] == null) - continue; - switch (aliases[i]) { - case "km": - km = ((Number) tuple[i]).doubleValue(); - break; - case "mindate": - mindate = (Date) tuple[i]; - break; - case "maxdate": - maxdate = (Date) tuple[i]; - break; - case "cnt": - cnt = ((Number) tuple[i]).intValue(); - break; - case "mindepth": - mindepth = ((Number) tuple[i]).doubleValue(); - break; - case "maxdepth": - maxdepth = ((Number) tuple[i]).doubleValue(); - break; - case "d50": - d50 = ((Number) tuple[i]).doubleValue() / 1000; // mm to m - break; - default: - break; - } - } - } - } - - /***** FIELDS *****/ - - /** - * Private log to use here. - */ - private static Logger log = Logger.getLogger(BedQualityD50KmValueFinder.class); - - /** - * Query that aggregates by km for a km range and a time period all sub layer bed measurements with their d50<br /> - * <br /> - * A km may have bed measurements for multiple dates, multiple distances from the river bank, and multiple depth layers. - * The query filters by km range, time period and layer (sub layer: below bed to max. 50 cm depth). - * Those measurements are then grouped by km, and the D50 aggregated as average value. - */ - private static final String SQL_BED_D50_SUBLAYER_MEASUREMENT = - "SELECT t.km, MIN(t.datum) AS mindate, MAX(t.datum) AS maxdate, COUNT(*) AS cnt," - + " MIN(p.tiefevon) AS mindepth, MAX(p.tiefebis) AS maxdepth, AVG(a.d50) AS 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)" - + " GROUP BY t.km" - + " ORDER BY t.km"; - private static final String[] SQL_BED_D50_SELECT_ALIAS = {"km", "mindate", "maxdate", "cnt", "mindepth", "maxdepth", "d50"}; - - /** - * Real linear interpolator for kms and d50 values - */ - private PolynomialSplineFunction interpolator; - - - /***** METHODS *****/ - - /** - * Returns the d50 value interpolated according to a km - * @return d50 (mm) of the km, or NaN - */ - public double findD50(double km) throws ArgumentOutsideDomainException { - return 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; */ - } - - /** - * Loads the range of the river's kms with their associated values. - * @return Whether the load has been successful - */ - public boolean loadValues(final River river, final DoubleRange kmRange, final DateRange dateRange) { - log.debug(String.format("loadValues km %.3f - %.3f %tF - %tF", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), dateRange.getFrom(), dateRange.getTo())); - Session session = SedDBSessionHolder.HOLDER.get(); - SQLQuery sqlQuery = session.createSQLQuery(SQL_BED_D50_SUBLAYER_MEASUREMENT) - .addScalar("km", StandardBasicTypes.DOUBLE) - .addScalar("mindate", StandardBasicTypes.DATE) - .addScalar("maxdate", StandardBasicTypes.DATE) - .addScalar("cnt", StandardBasicTypes.INTEGER) - .addScalar("mindepth", StandardBasicTypes.DOUBLE) - .addScalar("maxdepth", StandardBasicTypes.DOUBLE) - .addScalar("d50", StandardBasicTypes.DOUBLE); - String seddbRiver = river.nameForSeddb(); - sqlQuery.setString("name", seddbRiver); - sqlQuery.setDouble("fromkm", kmRange.getMinimumDouble()); - sqlQuery.setDouble("tokm", kmRange.getMaximumDouble()); - sqlQuery.setDate("fromdate", dateRange.getFrom()); - sqlQuery.setDate("todate", dateRange.getTo()); - @SuppressWarnings("unchecked") - final List<Object[]> rows = sqlQuery.list(); - final double[] kms = new double[rows.size()]; - final double[] values = new double[rows.size()]; - D50Measurement measurement; - int i = -1; - for (Object[] row : rows) { - measurement = new D50Measurement(row, SQL_BED_D50_SELECT_ALIAS); - i++; - kms[i] = measurement.getKm(); - values[i] = measurement.getD50(); - log.debug(String.format("loadValues km %.3f d50(mm) %.1f count %d", kms[i], values[i], measurement.getCnt())); - } - try { - interpolator = new LinearInterpolator().interpolate(kms, values); - return true; - } catch (Exception e) { - interpolator = null; - return false; - } - } - -}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthAccess.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthAccess.java Wed Feb 28 17:27:15 2018 +0100 @@ -1,6 +1,6 @@ /* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde - * Software engineering by - * Björnsen Beratende Ingenieure GmbH + * 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) @@ -14,6 +14,7 @@ import java.util.Collection; import java.util.Collections; +import org.apache.commons.lang.math.DoubleRange; import org.dive4elements.river.artifacts.access.RangeAccess; import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; import org.dive4elements.river.artifacts.sinfo.SinfoCalcMode; @@ -21,68 +22,71 @@ /** * Access to the flow depth calculation type specific SInfo artifact data. - * REMARK: this class is NOT intended to be hold in the results (or anywhere else), in order to avoid a permanent reference to the artifact instance. + * REMARK: this class is NOT intended to be hold in the results (or anywhere else), in order to avoid a permanent + * reference to the artifact instance. * Hence we do NOT cache any data. - * + * * @author Gernot Belger */ -public class FlowDepthAccess -extends RangeAccess -{ - public static class DifferencesPair - { - private final String wstId; - private final String soundingId; - - public DifferencesPair( final String wstId, final String soundingId ) { - this.wstId = wstId; - this.soundingId = soundingId; - } - - public String getWstId() { - return this.wstId; - } - - public String getSoundingId() { - return this.soundingId; - } - } - - private static final String FIELD_USE_TKH = "use_transport_bodies"; //$NON-NLS-1$ - - public FlowDepthAccess(final SINFOArtifact artifact) { - super(artifact); +final class FlowDepthAccess extends RangeAccess { + public static class DifferencesPair { + private final String wstId; + private final String soundingId; - /* assert calculation mode */ - final SinfoCalcMode calculationMode = artifact.getCalculationMode(); - assert(calculationMode == SinfoCalcMode.sinfo_calc_flow_depth); - } - - public boolean isUseTransportBodies() { - final Boolean useTkh = artifact.getDataAsBoolean( FIELD_USE_TKH ); - return useTkh == null ? false : useTkh; - } - - public Collection<DifferencesPair> getDifferencePairs() { - - final Collection<DifferencesPair> diffPairs = new ArrayList<>(); + public DifferencesPair(final String wstId, final String soundingId) { + this.wstId = wstId; + this.soundingId = soundingId; + } - final String diffids = super.getString("diffids"); - if( diffids == null ) - { - // Should never happen as this is handled by the ui - return Collections.emptyList(); - } + public String getWstId() { + return this.wstId; + } - // FIXME: this way of parsing the datacage-ids is repeated all over flys! - final String datas[] = diffids.split("#"); - for(int i = 0; i < datas.length; i+=2) { - final String leftId = StringUtil.unbracket( datas[i] ); - final String rightId = StringUtil.unbracket( datas[i+1] ); + public String getSoundingId() { + return this.soundingId; + } + } - diffPairs.add(new DifferencesPair(leftId, rightId)); - } + private static final String FIELD_USE_TKH = "use_transport_bodies"; //$NON-NLS-1$ - return Collections.unmodifiableCollection(diffPairs); - } + public FlowDepthAccess(final SINFOArtifact artifact) { + super(artifact); + + /* assert calculation mode */ + final SinfoCalcMode calculationMode = artifact.getCalculationMode(); + assert (calculationMode == SinfoCalcMode.sinfo_calc_flow_depth); + } + + public DoubleRange getRange() { + final double from = getFrom(); + final double to = getTo(); + return new DoubleRange(from, to); + } + + public boolean isUseTransportBodies() { + final Boolean useTkh = this.artifact.getDataAsBoolean(FIELD_USE_TKH); + return useTkh == null ? false : useTkh; + } + + public Collection<DifferencesPair> getDifferencePairs() { + + final Collection<DifferencesPair> diffPairs = new ArrayList<>(); + + final String diffids = super.getString("diffids"); + if (diffids == null) { + // Should never happen as this is handled by the ui + return Collections.emptyList(); + } + + // FIXME: this way of parsing the datacage-ids is repeated all over flys! + final String datas[] = diffids.split("#"); + for (int i = 0; i < datas.length; i += 2) { + final String leftId = StringUtil.unbracket(datas[i]); + final String rightId = StringUtil.unbracket(datas[i + 1]); + + diffPairs.add(new DifferencesPair(leftId, rightId)); + } + + return Collections.unmodifiableCollection(diffPairs); + } } \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Wed Feb 28 17:27:15 2018 +0100 @@ -9,49 +9,33 @@ */ package org.dive4elements.river.artifacts.sinfo.flowdepth; -import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; import org.apache.commons.lang.math.DoubleRange; -import org.apache.commons.math.FunctionEvaluationException; -import org.apache.commons.math.analysis.UnivariateRealFunction; -import org.dive4elements.artifacts.ArtifactDatabase; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.BedHeightsArtifact; import org.dive4elements.river.artifacts.model.Calculation; import org.dive4elements.river.artifacts.model.CalculationResult; -import org.dive4elements.river.artifacts.model.DateRange; -import org.dive4elements.river.artifacts.model.LocationProvider; -import org.dive4elements.river.artifacts.model.QKms; import org.dive4elements.river.artifacts.model.WKms; import org.dive4elements.river.artifacts.resources.Resources; import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; +import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthAccess.DifferencesPair; -import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.TkhCalculator; +import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; +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.artifacts.states.WaterlevelFetcher; -import org.dive4elements.river.model.BedHeight; -import org.dive4elements.river.model.BedHeightValue; -import org.dive4elements.river.model.Gauge; import org.dive4elements.river.model.River; -import org.dive4elements.river.utils.DoubleUtil; -import org.dive4elements.river.utils.GaugeIndex; import org.dive4elements.river.utils.RiverUtils; class FlowDepthCalculation { // private static Logger log = Logger.getLogger(FlowDepthCalculation.class); - private static final int VALID_BED_MEASUREMENT_YEARS = 20; - - private static final String CSV_NOT_IN_GAUGE_RANGE = "export.waterlevel.csv.not.in.gauge.range"; - private final CallContext context; public FlowDepthCalculation(final CallContext context) { @@ -60,12 +44,7 @@ public CalculationResult calculate(final SINFOArtifact sinfo) { - /* - * find the user of this artifact, sadly this is not part of the calling context, so instead we determine the - * owner oft the artifact - */ - final ArtifactDatabase database = this.context.getDatabase(); - final String user = database.findArtifactUser(sinfo.identifier()); + final String user = CalculationUtils.findArtifactUser(this.context, sinfo); /* access input data */ final FlowDepthAccess access = new FlowDepthAccess(sinfo); @@ -74,24 +53,21 @@ final Collection<DifferencesPair> diffPairs = access.getDifferencePairs(); - final double from = access.getFrom(); - final double to = access.getTo(); - final DoubleRange calcRange = new DoubleRange(from, to); + final DoubleRange calcRange = access.getRange(); final boolean useTkh = access.isUseTransportBodies(); /* calculate results for each diff pair */ final Calculation problems = new Calculation(); - final List<Gauge> gauges = river.determineGauges(from, to); - final GaugeIndex gaugeIndex = new GaugeIndex(gauges); + final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange); final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); final FlowDepthCalculationResults results = new FlowDepthCalculationResults(calcModeLabel, user, riverInfo, calcRange, useTkh); for (final DifferencesPair diffPair : diffPairs) { - final FlowDepthCalculationResult result = calculateResult(river, calcRange, diffPair, problems, gaugeIndex, useTkh); + final FlowDepthCalculationResult result = calculateResult(calcRange, diffPair, problems, infoProvider, useTkh); if (result != null) results.addResult(result); } @@ -101,15 +77,17 @@ /** * Calculates one W-MSH differences pair. + * + * @param infoProvider */ - private FlowDepthCalculationResult calculateResult(final River river, final DoubleRange calcRange, final DifferencesPair diffPair, - final Calculation problems, final GaugeIndex gaugeIndex, final boolean useTkh) { + private FlowDepthCalculationResult calculateResult(final DoubleRange calcRange, final DifferencesPair diffPair, + final Calculation problems, final RiverInfoProvider infoProvider, final boolean useTkh) { /* access real input data from database */ final String soundingId = diffPair.getSoundingId(); final String wstId = diffPair.getWstId(); - final BedHeight bedHeight = loadBedHeight(soundingId); + final BedHeightsFinder bedHeight = loadBedHeight(soundingId, calcRange); if (bedHeight == null) { final String message = Resources.format(this.context.getMeta(), "Failed to access sounding with id '{0}'", soundingId); problems.addProblem(message); @@ -126,223 +104,29 @@ final WKms wstKms = waterlevel.getWkms(); final String wspLabel = wstKms.getName(); - final String soundingLabel = bedHeight.getDescription(); + final String soundingLabel = bedHeight.getInfo().getDescription(); final String label = String.format("%s - %s", wspLabel, soundingLabel); - checkYearDifference(label, waterlevel, bedHeight, problems); + checkYearDifference(label, waterlevel, 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 String notinrange = Resources.getMsg(this.context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, CSV_NOT_IN_GAUGE_RANGE); - - final Gauge refGauge = waterlevel.findReferenceGauge(river); - final String refGaugeName = refGauge == null ? notinrange : refGauge.getName(); - - final BedHeightInfo sounding = BedHeightInfo.from(bedHeight); - final int wspYear = waterlevel.getYear(); - final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, refGaugeName); - - final FlowDepthCalculationResult resultData = new FlowDepthCalculationResult(label, wstInfo, sounding); - - boolean doCalcTkh = useTkh; - if (doCalcTkh && !(wstKms instanceof QKms)) { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); - problems.addProblem(message); - doCalcTkh = false; - } - - BedQualityD50KmValueFinder bedMeasurementsFinder = null; - if (doCalcTkh) { - bedMeasurementsFinder = loadBedMeasurements(river, calcRange, sounding.getYear().intValue()); - if (bedMeasurementsFinder == null) { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); - problems.addProblem(message); - doCalcTkh = false; - } - } - - final String bedHeightLabel = bedHeight.getDescription(); - final String wstLabel = wstKms.getName(); - - final UnivariateRealFunction wstInterpolator = DoubleUtil.getLinearInterpolator(wstKms.allKms(), wstKms.allWs()); - UnivariateRealFunction qInterpolator = null; - DoubleRange qRange = null; - if (doCalcTkh) { - qInterpolator = DoubleUtil.getLinearInterpolator(((QKms) wstKms).allKms(), ((QKms) wstKms).allQs()); - if (qInterpolator != null) - qRange = new DoubleRange(((QKms) wstKms).allQs().min(), ((QKms) wstKms).allQs().max()); - else { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); - problems.addProblem(message); - doCalcTkh = false; - } - } - - // FIXME: sort by station first, but in what direction? - // FIXME: using river.getKmUp()? - final List<BedHeightValue> values = bedHeight.getValues(); - - final List<BedHeightValue> sortedValues = new ArrayList<>(values); - Collections.sort(sortedValues, new BedHeightStationComparator()); - - // FIXME: wie wird ggf. interpoliert? prüfung ob werte vorhanden? - /* SoilKind lastKind = SoilKind.mobil; */ - SoilKindKmValueFinder soilKindFinder = null; - if (doCalcTkh) { - soilKindFinder = new SoilKindKmValueFinder(); - if (!soilKindFinder.loadValues(river, calcRange)) { - doCalcTkh = false; - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); - problems.addProblem(message); - } - } - - FlowVelocityModelKmValueFinder flowVelocitiesFinder = null; - if (doCalcTkh) { - flowVelocitiesFinder = new FlowVelocityModelKmValueFinder(); - if (!flowVelocitiesFinder.loadValues(river, calcRange, qRange)) { - doCalcTkh = false; - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingVelocity", null, label); - problems.addProblem(message); - } - } - - for (final BedHeightValue bedHeightValue : sortedValues) { - - final Double station = bedHeightValue.getStation(); - if (station == null || station.isNaN()) - continue; - - final Double meanBedHeightDbl = bedHeightValue.getHeight(); - if (meanBedHeightDbl == null || meanBedHeightDbl.isNaN()) - continue; - - final double km = station; - final double meanBedHeight = meanBedHeightDbl; - - if (!calcRange.containsDouble(km)) - continue; + final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel); - try { - // FIXME: check out of range - final double wst = wstInterpolator.value(km); - - final double flowDepth = wst - meanBedHeight; - - // FIXME: piecewise constant interpolation? - // final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; - double discharge; - if (qInterpolator != null) - discharge = qInterpolator.value(km); - else - discharge = Double.NaN; - - // Calculate tkh - double tkh = 0; - if (doCalcTkh) { - double d50 = Double.NaN; - try { - d50 = bedMeasurementsFinder.findD50(km); - } - catch (final Exception e) { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingD50", null, label); - problems.addProblem(km, message); - // FIXME: cumulate problems to one message? - } - if (!Double.isNaN(d50)) { - if (flowVelocitiesFinder.findKmQValues(km, discharge)) { - tkh = calculateTkh(wst - meanBedHeight, flowVelocitiesFinder.getFindVmainFound(), d50, flowVelocitiesFinder.getFindTauFound()); - if (!Double.isNaN(tkh) && (tkh < 0)) - tkh = 0; - /* - * log.debug(String.format("calculateTkh km %.3f q %.0f w %.2f mbh %.2f vm %.1f tau %.1f d50(mm) %.1f tkh(cm) %.1f", - * km, discharge, wst, meanBedHeight, flowVelocitiesFinder.getFindVmainFound(), flowVelocitiesFinder.getFindTauFound(), - * d50*1000, tkh)); - */ - } else { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); - problems.addProblem(km, message); - // FIXME: cumulate problems to one message? - } - } else - tkh = Double.NaN; - } + final int wspYear = waterlevel.getYear(); + final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, riverInfoProvider.getReferenceGauge()); - // Soil kind - SoilKind kind = SoilKind.mobil; - if (doCalcTkh) { - try { - kind = soilKindFinder.findSoilKind(km); - } - catch (final Exception e) { - final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); - problems.addProblem(km, message); - // FIXME: cumulate problems to one message? - } - } - - final double flowDepthTkh; - final double tkhUp; - final double tkhDown; - switch (kind) { - case starr: - flowDepthTkh = wst - (meanBedHeight + tkh / 100); - tkhUp = tkh; - tkhDown = 0; - break; + final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms); - case mobil: - default: - flowDepthTkh = wst - (meanBedHeight + tkh / 200); - tkhUp = tkh / 2; - tkhDown = -tkh / 2; - break; - } - - // REMARK: access the location once only during calculation - final String location = LocationProvider.getLocation(river.getName(), km); + final River river = riverInfoProvider.getRiver(); + final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(useTkh, this.context, problems, label, river, calcRange, dischargeProvider, + bedHeight); - // REMARK: access the gauge once only during calculation - final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); - - final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); - - resultData.addRow(km, flowDepth, flowDepthTkh, kind, tkh, tkhUp, tkhDown, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, bedHeightLabel, - location); - } - catch (final FunctionEvaluationException e) { - /* should only happen if out of range */ - e.printStackTrace(); - /* simply ignore */ - } - } - - return resultData; + final FlowDepthCalculator calculator = new FlowDepthCalculator(riverInfoProvider, wstKms, dischargeProvider, bedHeight, tkhCalculator); + return calculator.execute(label, wstInfo, calcRange); } - /** - * Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) - * Abhängig von Peiljahr - */ - private BedQualityD50KmValueFinder loadBedMeasurements(final River river, final DoubleRange kmRange, final int soundingYear) { - - /* construct valid measurement time range */ - final Calendar cal = Calendar.getInstance(); - cal.clear(); - - cal.set(soundingYear - VALID_BED_MEASUREMENT_YEARS, 0, 1); - final Date startTime = cal.getTime(); - - cal.set(soundingYear + VALID_BED_MEASUREMENT_YEARS, 11, 31); - final Date endTime = cal.getTime(); - - final BedQualityD50KmValueFinder finder = new BedQualityD50KmValueFinder(); - if (finder.loadValues(river, kmRange, new DateRange(startTime, endTime))) - return finder; - else - return null; - } /** * Checks the year difference between waterlevels and sounding, and issues a warning if too big. @@ -353,9 +137,7 @@ * 1918 ≤ X < 1958 ± 12 * X < 1918 ± 25 */ - private void checkYearDifference(final String label, final WaterlevelData waterlevel, final BedHeight sounding, final Calculation problems) { - - final Integer soundingYear = sounding.getYear(); + private void checkYearDifference(final String label, final WaterlevelData waterlevel, final Integer soundingYear, final Calculation problems) { if (soundingYear == null) return; @@ -388,21 +170,6 @@ return 3; } - private Gauge findGauge(final WaterlevelData waterlevel, final Gauge refGauge, final GaugeIndex gaugeIndex, final double km) { - - // REMARK: using same logic as in WaterlevelExporter here - - final boolean showAllGauges = waterlevel.isShowAllGauges(); - - if (showAllGauges) - return gaugeIndex.findGauge(km); - - if (refGauge.getRange().contains(km)) - return refGauge; - - return null; - } - /* Checks if the discretisation of the waterlevel exceeds 1000m */ private void checkWaterlevelDiscretisation(final WKms wstKms, final DoubleRange calcRange, final Calculation problems) { @@ -424,7 +191,7 @@ } } - private BedHeight loadBedHeight(final String soundingId) { + private BedHeightsFinder loadBedHeight(final String soundingId, final DoubleRange calcRange) { // REMARK: absolutely unbelievable.... // The way how bed-heights (and other data too) is accessed is different for nearly every calculation-type @@ -438,6 +205,11 @@ final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); final Integer bedheightId = artifact.getDataAsInteger("height_id"); + if (bedheightId == null) { + // FIXME: error message! + return null; + } + // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the // other type means) // Luckily, the requirement is to only access 'single' data here. @@ -449,35 +221,6 @@ // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); - return BedHeight.getBedHeightById(bedheightId); - } - - /** - * Calculates a transport body height - * - * @param h - * flow depth in m - * @param vm - * flow velocity in m - * @param d50 - * grain diameter D50 in m (!) - * @param tau - * shear stress in N/m^2 - * @return transport body height in cm (!) - */ - private double calculateTkh(final double h, final double vm, final double d50, final double tau) { - final double PHYS_G = 9.81; - final double PHYS_SPECGRAV_S = 2.6; - final double PHYS_VELOCCOEFF_N = 6; - final double PHYS_FORMCOEFF_ALPHA = 0.7; - final double PHYS_VISCOSITY_NUE = 1.3e-6; - final double PHYS_GRAIN_DENSITY_RHOS = 2603; - final double PHYS_WATER_DENSITY_RHO = 999.97; - - final double froude = vm / Math.sqrt(PHYS_G * h); - final double partReynolds = Math.sqrt((PHYS_SPECGRAV_S - 1) * PHYS_G * d50) / PHYS_VISCOSITY_NUE * d50; - final double critShields = 0.22 * Math.pow(partReynolds, -0.6) + 0.06 * Math.pow(10, 7.7 * Math.pow(partReynolds, -0.6)); - final double critTau = critShields * (PHYS_GRAIN_DENSITY_RHOS - PHYS_WATER_DENSITY_RHO) * PHYS_G * d50; - return 100 * h * (1 - Math.pow(froude, 2)) / (2 * PHYS_VELOCCOEFF_N * PHYS_FORMCOEFF_ALPHA) * (1 - critTau / tau); + return BedHeightsFinder.forId(bedheightId, calcRange); } } \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculationResult.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculationResult.java Wed Feb 28 17:27:15 2018 +0100 @@ -9,12 +9,9 @@ */ package org.dive4elements.river.artifacts.sinfo.flowdepth; -import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; -import java.util.List; +import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoCalculationResult; import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; import org.dive4elements.river.artifacts.sinfo.util.WstInfo; @@ -25,53 +22,31 @@ * * @author Gernot Belger */ -class FlowDepthCalculationResult implements Serializable { +final class FlowDepthCalculationResult extends AbstractSInfoCalculationResult<FlowDepthRow> { private static final long serialVersionUID = 1L; - private final Collection<FlowDepthRow> rows = new ArrayList<>(); - - private final String label; - private final BedHeightInfo sounding; - private final WstInfo wst; - - public FlowDepthCalculationResult(final String label, final WstInfo wst, final BedHeightInfo sounding) { - this.label = label; - this.wst = wst; - this.sounding = sounding; - } + public FlowDepthCalculationResult(final String label, final WstInfo wst, final BedHeightInfo sounding, final boolean hasTkh, + final Collection<FlowDepthRow> rows) { + super(label, wst, hasTkh, rows); - public void addRow(final double station, final double flowDepth, final double flowDepthWithTkh, final SoilKind tkhKind, final double tkh, - final double tkhUp, final double tkhDown, final double waterlevel, final double discharge, final String waterlevelLabel, final String gauge, - final double meanBedHeight, final String sondageLabel, final String location) { - this.rows.add(new FlowDepthRow(station, flowDepth, flowDepthWithTkh, tkhKind, tkh, tkhUp, tkhDown, waterlevel, discharge, waterlevelLabel, gauge, - meanBedHeight, sondageLabel, location)); - } - - public String getLabel() { - return this.label; - } - - public WstInfo getWst() { - return this.wst; + this.sounding = sounding; } public BedHeightInfo getSounding() { return this.sounding; } - public Collection<FlowDepthRow> getRows() { - return Collections.unmodifiableCollection(this.rows); - } - public double[][] getFlowDepthPoints() { - final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); - final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); + final Collection<FlowDepthRow> rows = getRows(); - for (final FlowDepthRow row : this.rows) { + final TDoubleArrayList xPoints = new TDoubleArrayList(rows.size()); + final TDoubleArrayList yPoints = new TDoubleArrayList(rows.size()); + + for (final FlowDepthRow row : rows) { xPoints.add(row.getStation()); yPoints.add(row.getFlowDepth()); } @@ -81,85 +56,16 @@ public double[][] getFlowDepthTkhPoints() { - final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); - final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); + final Collection<FlowDepthRow> rows = getRows(); - for (final FlowDepthRow row : this.rows) { + final TDoubleArrayList xPoints = new TDoubleArrayList(rows.size()); + final TDoubleArrayList yPoints = new TDoubleArrayList(rows.size()); + + for (final FlowDepthRow row : rows) { xPoints.add(row.getStation()); yPoints.add(row.getFlowDepthWithTkh()); } return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() }; } - - public double[][] getTkhUpPoints() { - final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); - final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); - final List<SoilKind> kinds = new ArrayList<>(this.rows.size()); - - for (final FlowDepthRow row : this.rows) { - xPoints.add(row.getStation()); - yPoints.add(row.getTkhUp()); - kinds.add(row.getTkhKind()); - } - - return adjustTkhVisualization(xPoints, yPoints, kinds); - } - - public double[][] getTkhDownPoints() { - final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size()); - final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size()); - final List<SoilKind> kinds = new ArrayList<>(this.rows.size()); - - for (final FlowDepthRow row : this.rows) { - xPoints.add(row.getStation()); - yPoints.add(row.getTkhDown()); - kinds.add(row.getTkhKind()); - } - - return adjustTkhVisualization(xPoints, yPoints, kinds); - } - - /** - * the up and down points must be further adjusted for visualization, see Mail Hr. Reiß - * basically we need to introduce extra points when the kind changes, so we get vertical lines in that case - */ - private double[][] adjustTkhVisualization(final TDoubleArrayList xPoints, final TDoubleArrayList yPoints, final List<SoilKind> kinds) { - - final TDoubleArrayList adjustedX = new TDoubleArrayList(xPoints.size()); - final TDoubleArrayList adjustedY = new TDoubleArrayList(yPoints.size()); - - adjustedX.add(xPoints.get(0)); - adjustedY.add(yPoints.get(0)); - - for (int i = 1; i < xPoints.size(); i++) { - - final SoilKind kind1 = kinds.get(i - 1); - final SoilKind kind2 = kinds.get(i); - - if (kind1 != kind2) { - /* introduce two extra points in order to create a vertical line in the middle of the two adjacent points */ - final double x1 = xPoints.get(i - 1); - final double y1 = yPoints.get(i - 1); - final double x2 = xPoints.get(i); - final double y2 = yPoints.get(i); - - final double middleX = (x1 + x2) / 2; - - // REMARK: we can't produce a 100% vertical line, as the area-renderer will not work correctly - adjustedX.add(middleX - 0.0001); - adjustedY.add(y1); - - adjustedX.add(middleX + 0.0001); - adjustedY.add(y2); - } - - /* always add the real point now */ - adjustedX.add(xPoints.get(i)); - adjustedY.add(yPoints.get(i)); - } - - - return new double[][] { adjustedX.toNativeArray(), adjustedY.toNativeArray() }; - } } \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculationResults.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculationResults.java Wed Feb 28 17:27:15 2018 +0100 @@ -20,7 +20,7 @@ /** * @author Gernot Belger */ -public final class FlowDepthCalculationResults implements Serializable { +final class FlowDepthCalculationResults implements Serializable { private static final long serialVersionUID = 1L; private final List<FlowDepthCalculationResult> results = new ArrayList<>();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculator.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,129 @@ +/** 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.flowdepth; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang.math.DoubleRange; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.SoilKind; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.TkhCalculator; +import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; +import org.dive4elements.river.artifacts.sinfo.util.WstInfo; +import org.dive4elements.river.utils.DoubleUtil; + +/** + * @author Gernot Belger + */ +final class FlowDepthCalculator { + + private final Collection<FlowDepthRow> rows = new ArrayList<>(); + + private final DischargeValuesFinder dischargeProvider; + + private final BedHeightsFinder bedHeight; + + private final TkhCalculator tkhCalculator; + + private final PolynomialSplineFunction wstInterpolator; + + private final RiverInfoProvider riverInfoProvider; + + private final String bedHeightLabel; + + private final String wstLabel; + + public FlowDepthCalculator(final RiverInfoProvider riverInfoProvider, final WKms wstKms, + final DischargeValuesFinder dischargeProvider, final BedHeightsFinder bedHeight, final TkhCalculator tkhCalculator) { + + this.riverInfoProvider = riverInfoProvider; + + this.dischargeProvider = dischargeProvider; + this.bedHeight = bedHeight; + this.tkhCalculator = tkhCalculator; + + this.wstInterpolator = DoubleUtil.getLinearInterpolator(wstKms.allKms(), wstKms.allWs()); + + this.bedHeightLabel = bedHeight.getInfo().getDescription(); + this.wstLabel = wstKms.getName(); + } + + public FlowDepthCalculationResult execute(final String label, final WstInfo wstInfo, final DoubleRange calcRange) { + + final Collection<Double> stations = this.bedHeight.getStations(); + for (final Double station : stations) { + if (calcRange.containsDouble(station)) + calculateResultRow(station); + } + + return new FlowDepthCalculationResult(label, wstInfo, this.bedHeight.getInfo(), this.tkhCalculator != null, this.rows); + } + + private void calculateResultRow(final double station) { + + try { + // FIXME: check out of range of waterlevel? + final double wst = this.wstInterpolator.value(station); + + final Tkh tkh = calculateTkh(station, wst); + + final double meanBedHeight = tkh.getMeanBedHeight(); + + final double flowDepth = wst - meanBedHeight; + final double flowDepthTkh = calculateFlowDepthTkh(tkh, wst, meanBedHeight); + + // REMARK: access the location once only during calculation + final String location = this.riverInfoProvider.getLocation(station); + + // REMARK: access the gauge once only during calculation + final String gaugeLabel = this.riverInfoProvider.findGauge(station); + + this.rows.add(new FlowDepthRow(flowDepth, flowDepthTkh, tkh, this.wstLabel, gaugeLabel, this.bedHeightLabel, location)); + } + catch (final FunctionEvaluationException e) { + /* should only happen if out of range */ + e.printStackTrace(); + /* simply ignore */ + } + } + + private Tkh calculateTkh(final double station, final double wst) throws FunctionEvaluationException { + if (this.tkhCalculator == null) { + final double discharge = this.dischargeProvider.getDischarge(station); + final double meanBedHeight = this.bedHeight.getMeanBedHeight(station); + return new Tkh(station, wst, meanBedHeight, discharge); + } + + return this.tkhCalculator.getTkh(station, wst); + } + + private double calculateFlowDepthTkh(final Tkh tkh, final double wst, final double meanBedHeight) { + final double tkhValue = tkh.getTkh(); + final SoilKind tkhKind = tkh.getKind(); + + if (Double.isNaN(tkhValue) || tkhKind == null) + return Double.NaN; + + switch (tkhKind) { + case starr: + return wst - (meanBedHeight + tkhValue / 100); + + case mobil: + default: + return wst - (meanBedHeight + tkhValue / 200); + } + } +} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthRow.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthRow.java Wed Feb 28 17:27:15 2018 +0100 @@ -9,66 +9,31 @@ */ package org.dive4elements.river.artifacts.sinfo.flowdepth; -import java.io.Serializable; +import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoResultRow; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh; /** * Part of {@link FlowDepthCalculationResult} which represents one calculated row of flow depth data. * * @author Gernot Belger */ -final class FlowDepthRow implements Serializable { +final class FlowDepthRow extends AbstractSInfoResultRow { private static final long serialVersionUID = 1L; - private final double station; - private final double flowDepth; private final double flowDepthWithTkh; - private final SoilKind tkhKind; - - private final double tkh; - - private final double tkhUp; - - private final double tkhDown; - - private final double waterlevel; - - private final double discharge; + private final String soundingLabel; - private final String waterlevelLabel; - - private final String gauge; - - private final double meanBedHeight; + public FlowDepthRow(final double flowDepth, final double flowDepthWithTkh, final Tkh tkh, final String waterlevelLabel, + final String gauge, final String soundingLabel, final String location) { - private final String soundageLabel; - - private final String location; + super(tkh, waterlevelLabel, gauge, location); - public FlowDepthRow(final double station, final double flowDepth, final double flowDepthWithTkh, final SoilKind tkhKind, final double tkh, - final double tkhUp, final double tkhDown, - final double waterlevel, final double discharge, final String waterlevelLabel, final String gauge, final double meanBedHeight, - final String soundageLabel, final String location) { - this.station = station; this.flowDepth = flowDepth; this.flowDepthWithTkh = flowDepthWithTkh; - this.tkhKind = tkhKind; - this.tkh = tkh; - this.tkhUp = tkhUp; - this.tkhDown = tkhDown; - this.waterlevel = waterlevel; - this.discharge = discharge; - this.waterlevelLabel = waterlevelLabel; - this.gauge = gauge; - this.meanBedHeight = meanBedHeight; - this.soundageLabel = soundageLabel; - this.location = location; - } - - public double getStation() { - return this.station; + this.soundingLabel = soundingLabel; } public double getFlowDepth() { @@ -79,47 +44,7 @@ return this.flowDepthWithTkh; } - public SoilKind getTkhKind() { - return this.tkhKind; - } - - public double getTkh() { - return this.tkh; - } - - public double getTkhUp() { - return this.tkhUp; - } - - public double getTkhDown() { - return this.tkhDown; - } - - public double getWaterlevel() { - return this.waterlevel; - } - - public double getDischarge() { - return this.discharge; - } - - public String getWaterlevelLabel() { - return this.waterlevelLabel; - } - - public String getGauge() { - return this.gauge; - } - - public double getMeanBedHeight() { - return this.meanBedHeight; - } - public String getSoundageLabel() { - return this.soundageLabel; - } - - public String getLocation() { - return this.location; + return this.soundingLabel; } } \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowVelocityModelKmValueFinder.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,330 +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.flowdepth; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang.math.DoubleRange; -import org.apache.log4j.Logger; -import org.hibernate.SQLQuery; -import org.hibernate.Session; -import org.hibernate.type.StandardBasicTypes; - -import gnu.trove.TDoubleArrayList; - -import org.dive4elements.river.artifacts.math.Linear; -import org.dive4elements.river.artifacts.math.Utils; -import org.dive4elements.river.backend.SessionHolder; -import org.dive4elements.river.model.River; - -/** - * Searchable sorted km array with parallel FlowVelocityKmModelValues array and linear interpolation for km and the model values between the array elements.<br /> - * {@link loadValues} loads all the model values for a given km range of a river.<br /> - * {@link findKmQValues} then searches a km in the values table or the nearest including km interval, resp. - * The v and tau values for a given discharge are either found directly or also interpolated linearly.<br /> - * - * (Created based on a copy of FlowVelocityMeasurementFactory.) - * - * @author Matthias Schäfer - * - */ -public class FlowVelocityModelKmValueFinder -{ - /***** FIELDS *****/ - - /** - * Private log to use here. - */ - private static Logger log = Logger.getLogger(FlowVelocityModelKmValueFinder.class); - - /** - * Query for a range of stations of a river with all their q, main-v and tau values.<br /> - * (Might be several 10000 rows if many stations and large q range) - */ - private static final String SQL_SELECT_ALL = - "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" - + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" - + " INNER JOIN flow_velocity_model_values fvmv ON fvm.id = fvmv.flow_velocity_model_id" - + " WHERE (dz.river_id = :river_id) AND (fvmv.station BETWEEN :kmfrom - 0.0001 AND :kmto + 0.0001)" - /* + " WHERE (dz.river_id = :river_id) AND (fvmv.q BETWEEN :qmin AND :qmax)" */ - + " ORDER BY fvmv.station ASC, fvmv.q ASC"; - - /** - * Query for a river's max km below a limit with all its q, main-v and tau values. - */ - private static final String SQL_SELECT_KMLOWER = - "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" - + " FROM flow_velocity_model_values fvmv" - + " INNER JOIN (SELECT MAX(fvmvi.station) AS kmmax" - + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" - + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id" - + " WHERE (dz.river_id = :river_id) AND (fvmvi.station < :kmfrom - 0.0001)) finf ON fvmv.station = finf.kmmax" - + " ORDER BY fvmv.q ASC"; - - /** - * Query for a river's min km above a limit with all its q, main-v and tau values. - */ - private static final String SQL_SELECT_KMUPPER = - "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" - + " FROM flow_velocity_model_values fvmv" - + " INNER JOIN (SELECT MIN(fvmvi.station) AS kmmin" - + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" - + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id" - + " WHERE (dz.river_id = :river_id) AND (fvmvi.station > :kmto + 0.0001)) fsup ON fvmv.station = fsup.kmmin" - + " ORDER BY fvmv.q ASC"; - - /** - * Query to select all km-q-v-tau of a river that are the q maxima below a q limit - */ - private static final String SQL_SELECT_QLOWER = - "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" - + " FROM flow_velocity_model_values fvmv" - + " INNER JOIN (SELECT fv2.station, MAX(fv2.q) AS q" - + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" - + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id" - + " WHERE (dz.river_id = :river_id) AND (fv2.q < :qlim) GROUP BY fv2.station) qx" - + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)" - + " ORDER BY fvmv.station ASC"; - - /** - * Query to select all km-q-v-tau of a river that are the q minima above a q limit - */ - private static final String SQL_SELECT_QUPPER = - "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" - + " FROM flow_velocity_model_values fvmv" - + " INNER JOIN (SELECT fv2.station, MIN(fv2.q) AS q" - + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" - + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id" - + " WHERE (dz.river_id = :river_id) AND (fv2.q > :qlim) GROUP BY fv2.station) qx" - + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)" - + " ORDER BY fvmv.station ASC"; - - /** - * Kms of the loaded river range - */ - private TDoubleArrayList kms; - - /** - * For each km in kms a list of q-v-tau-tupels - */ - private List<FlowVelocityKmModelValues> values; - - /** - * Searched km of the last findKmValue - */ - private double findKm; - - /** - * kms and values index of the interval start found by the last findKmValue - */ - private int leftIndexFound = -1; - - /** - * kms and values index of the interval end found by the last findKmValue - */ - private int rightIndexFound = -1; - - /** - * Q of the last findKmQValues - */ - private double findQ; - - /***** METHODS *****/ - - /** - * Discharge of the last {@link findKmQValue} - */ - public double getFindQ() { - return findQ; - } - - /** - * Velocity of the last {@link findKmQValues} - */ - public double getFindVmainFound() { - if (leftIndexFound < 0) - return Double.NaN; - else if (leftIndexFound == rightIndexFound) - return getLeftValues().getVmainFound(); - else - return Linear.linear(findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getVmainFound(), getRightValues().getVmainFound()); - } - - /** - * Shear stress tau of the last {@link findKmQValues} - */ - public double getFindTauFound() { - if (leftIndexFound < 0) - return Double.NaN; - else if (leftIndexFound == rightIndexFound) - return getLeftValues().getTauFound(); - else - return Linear.linear(findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getTauFound(), getRightValues().getTauFound()); - } - - /** - * Whether the discharge has been interpolated in the last {@link findKmQValues} - */ - public boolean getFindIsQInterpolated() { - return (getLeftValues() != null) && (getLeftValues().getIsInterpolated() || getRightValues().getIsInterpolated()); - } - - /** - * Queries a range of a river's kms with all their q-v-tau values. - * @return Whether the load has been successful - */ - @SuppressWarnings("unchecked") - public boolean loadValues(River river, DoubleRange kmRange, DoubleRange qRange) { - // DB session - log.debug(String.format("loadValues km %.3f - %.3f / q %.1f - %.1f", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), qRange.getMinimumDouble(), qRange.getMaximumDouble())); - kms = new TDoubleArrayList(); - values = new ArrayList<FlowVelocityKmModelValues>(); - boolean isDemoValuesCorrection = river.getName().equalsIgnoreCase("beispielfluss"); - final Session session = SessionHolder.HOLDER.get(); - - // Select km infimum - SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_KMLOWER) - .addScalar("station", StandardBasicTypes.DOUBLE) - .addScalar("q", StandardBasicTypes.DOUBLE) - .addScalar("vmain", StandardBasicTypes.DOUBLE) - .addScalar("tau", StandardBasicTypes.DOUBLE); - sqlQuery.setParameter("river_id", river.getId()); - sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble()); - addKms(sqlQuery.list(), isDemoValuesCorrection); - - // Select km range - sqlQuery = session.createSQLQuery(SQL_SELECT_ALL) - .addScalar("station", StandardBasicTypes.DOUBLE) - .addScalar("q", StandardBasicTypes.DOUBLE) - .addScalar("vmain", StandardBasicTypes.DOUBLE) - .addScalar("tau", StandardBasicTypes.DOUBLE); - sqlQuery.setParameter("river_id", river.getId()); - sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble()); - sqlQuery.setParameter("kmto", kmRange.getMaximumDouble()); - //sqlQuery.setParameter("qmin", qRange.getMinimumDouble()); - //sqlQuery.setParameter("qmax", qRange.getMaximumDouble()); - int kmcount = kms.size(); - int rowcount = addKms(sqlQuery.list(), isDemoValuesCorrection); - kmcount = kms.size() - kmcount; - - // Select km supremum - sqlQuery = session.createSQLQuery(SQL_SELECT_KMUPPER) - .addScalar("station", StandardBasicTypes.DOUBLE) - .addScalar("q", StandardBasicTypes.DOUBLE) - .addScalar("vmain", StandardBasicTypes.DOUBLE) - .addScalar("tau", StandardBasicTypes.DOUBLE); - sqlQuery.setParameter("river_id", river.getId()); - sqlQuery.setParameter("kmto", kmRange.getMaximumDouble()); - int supcnt = addKms(sqlQuery.list(), isDemoValuesCorrection); - - // Add copy of last km for search of max km value - if ((supcnt == 0) && (values.size() >= 1)) { - kms.add(kms.getQuick(kms.size()) + 0.0001); - values.add(new FlowVelocityKmModelValues(kms.getQuick(kms.size()-1), values.get(values.size()-1))); - } - - // log.debug - if (values.size() - 1 >= 0) { - log.debug(String.format("loadValues %d: km %.3f - %d values", 0, values.get(0).getKm(), values.get(0).size())); - if (values.size() - 1 >= 1) { - log.debug(String.format("loadValues %d: km %.3f - %d values", 1, values.get(1).getKm(), values.get(1).size())); - if (values.size() - 1 >= 2) - log.debug("loadValues ..."); - if (values.size() - 2 >= 3) - log.debug(String.format("loadValues %d: km %.3f - %d values", values.size()-2, values.get(values.size()-2).getKm(), values.get(values.size()-2).size())); - if (values.size() - 1 >= 3) - log.debug(String.format("loadValues %d: km %.3f - %d values", values.size()-1, values.get(values.size()-1).getKm(), values.get(values.size()-1).size())); - } - } - log.debug(String.format("loadValues %d kms, %d values loaded", kmcount, rowcount)); - return (kms.size() >= 1); - } - - /** - * Adds the km-q-v-tau values of a query result row to the last km of the list, or a new one resp. - * @return Number of rows - */ - private int addKms(List<Object[]> rows, boolean isDemoValuesCorrection) { - for (Object[] row : rows) { - if ((kms.size() == 0) || !Utils.epsilonEquals(kms.get(kms.size()-1), (double) row[0], 0.0001)) { - kms.add((double) row[0]); - values.add(new FlowVelocityKmModelValues(kms.get(kms.size()-1))); - } - if (isDemoValuesCorrection) - // "Verfremdung" der v-Werte etwas korrigieren (Originalwerte wurden mit Zufallswert zwischen 10 und 20 multipliziert) - values.get(values.size()-1).addValues((double) row[1], ((double) row[2]) / 10, (double) row[3]); - else - values.get(values.size()-1).addValues((double) row[1], (double) row[2], (double) row[3]); - } - return rows.size(); - } - - /** - * Searches a km and finds or interpolates the velocity and shear stress values for a discharge<br /> - * The values may be got via {@link getVmainFound} etc. - * @return Whether values have been found - */ - public boolean findKmQValues(double km, double q) { - findQ = q; - if (!searchKm(km)) - return false; - if (leftIndexFound == rightIndexFound) { - // Exact km match - final double qfound = getLeftValues().findQ(q); - log.debug(String.format("findKmQValues km %.3f q %.0f = %.0f (%d)", km, q, qfound, 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], leftIndexFound, - getLeftValues().getKm(), qfound[1], rightIndexFound, getRightValues().getKm())); - return !Double.isNaN(qfound[0]) && !Double.isNaN(qfound[1]); - } - } - - /** - * Searches a km - * @return Whether the km was within the supported range - */ - private boolean searchKm(double km) { - findKm = km; - leftIndexFound = -1; - rightIndexFound = -1; - if ((kms == null) || (kms.size() == 0)) - return false; - int i = kms.binarySearch(km); - if (i >= 0) { - // Exact km match - leftIndexFound = i; - rightIndexFound = i; - return true; - } - else { - // Out of range or within km interval - if (i < 0) - i = -i - 1; - if ((i <= 0) || (i >= kms.size())) - return false; - leftIndexFound = i - 1; - rightIndexFound = i; - return true; - } - } - - private FlowVelocityKmModelValues getLeftValues() { - return values.get(leftIndexFound); - } - private FlowVelocityKmModelValues getRightValues() { - return values.get(rightIndexFound); - } - -}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/SoilKind.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +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.flowdepth; - -public enum SoilKind { - mobil, starr -} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/SoilKindKmValueFinder.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +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.flowdepth; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang.math.DoubleRange; -import org.apache.commons.math.ArgumentOutsideDomainException; -import org.apache.log4j.Logger; -import org.dive4elements.river.model.River; - -import gnu.trove.TDoubleArrayList; - -/** - * @author matthias - * - */ -public class SoilKindKmValueFinder -{ - /** - * Private log to use here. - */ - private static Logger log = Logger.getLogger(SoilKindKmValueFinder.class); - - private TDoubleArrayList kms; - - private List<SoilKind> values; - - /***** METHODS *****/ - - /** - * Searches a km with its soil kind - */ - public SoilKind findSoilKind(double km) throws ArgumentOutsideDomainException { - if ((kms == null) || (kms.size() == 0)) - throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN); - int i = kms.binarySearch(km); - if (i >= 0) { - // Exact km match - return values.get(i); - } - else { - // Out of range or within km interval - if (i < 0) - i = -i - 1; - if ((i <= 0) || (i >= kms.size())) - throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN); - if (km <= ((kms.get(i-1) + kms.get(i)) / 2)) - return values.get(i-1); - else - return values.get(i); - } - } - - /** - * Loads the range of the river's kms with their soil kind. - * @return Whether the load has been successful - */ - public boolean loadValues(River river, DoubleRange kmRange) { - kms = new TDoubleArrayList(); - values = new ArrayList<SoilKind>(); - //FIXME Echte Daten aus der Datenbank abfragen - addKmKind(0, SoilKind.starr); - addKmKind(15.7, SoilKind.mobil); - addKmKind(15.8, SoilKind.mobil); - addKmKind(15.9, SoilKind.starr); - addKmKind(108.7, SoilKind.mobil); - addKmKind(108.8, SoilKind.mobil); - addKmKind(108.9, SoilKind.starr); - addKmKind(119.1, SoilKind.mobil); - addKmKind(119.4, SoilKind.mobil); - addKmKind(119.5, SoilKind.starr); - addKmKind(128.3, SoilKind.mobil); - addKmKind(128.9, SoilKind.mobil); - addKmKind(129, SoilKind.starr); - addKmKind(133.1, SoilKind.mobil); - addKmKind(135.9, SoilKind.mobil); - addKmKind(136, SoilKind.starr); - addKmKind(136.5, SoilKind.mobil); - addKmKind(139.9, SoilKind.mobil); - addKmKind(140, SoilKind.starr); - addKmKind(140.5, SoilKind.mobil); - addKmKind(165, SoilKind.mobil); - addKmKind(165.1, SoilKind.starr); - addKmKind(165.9, SoilKind.mobil); - addKmKind(180.8, SoilKind.mobil); - addKmKind(180.9, SoilKind.starr); - addKmKind(182, SoilKind.mobil); - addKmKind(221.3, SoilKind.mobil); - return true; - } - - private void addKmKind(double km, SoilKind kind) { - kms.add(km); - values.add(kind); - } -}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkh/TkhState.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde - * Software engineering by Intevation GmbH - * - * 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.tkh; - -import java.util.List; - -import org.dive4elements.artifactdatabase.state.Facet; -import org.dive4elements.artifacts.CallContext; -import org.dive4elements.river.artifacts.ChartArtifact; -import org.dive4elements.river.artifacts.D4EArtifact; -import org.dive4elements.river.artifacts.WINFOArtifact; -import org.dive4elements.river.artifacts.model.Calculation; -import org.dive4elements.river.artifacts.model.CalculationResult; -import org.dive4elements.river.artifacts.model.EmptyFacet; -import org.dive4elements.river.artifacts.model.WQKms; -import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; -import org.dive4elements.river.artifacts.states.DefaultState; - -/** State in which a waterlevel has been calculated. */ -public class TkhState extends DefaultState { - - /// ** The log that is used in this state. */ - // private static Logger log = Logger.getLogger(FlowDepthState.class); - - private static final long serialVersionUID = 1L; - - private static final String I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION = "sinfo.facet.flow_depth.filtered.description"; - - private static final String I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION = "sinfo.facet.flow_depth.tkh.filtered.description"; - - private static final String I18N_FACET_TKH_DESCRIPTION = "sinfo.facet.tkh.description"; - - private static final String SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL = "sinfo.chart.flow_depth.yaxis.label"; - - private static final String SINFO_CHART_TKX_YAXIS_LABEL = "sinfo.chart.tkh.yaxis.label"; - - /** - * From this state can only be continued trivially. - */ - @Override - protected String getUIProvider() { - return "continue"; - } - - @Override - public Object computeFeed(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) { - // FIXME: why is this necessary? - if (artifact instanceof ChartArtifact) { - facets.add(new EmptyFacet()); - return null; - } - - return compute((SINFOArtifact) artifact, context, hash, facets, old); - } - - @Override - public Object computeAdvance(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) { - if (artifact instanceof ChartArtifact) { - facets.add(new EmptyFacet()); - return null; - } - return compute((SINFOArtifact) artifact, context, hash, facets, old); - } - - /** - * Compute result or returned object from cache, create facets. - * - * @param old - * Object that was cached. - */ - private Object compute(final SINFOArtifact sinfo, final CallContext context, final String hash, final List<Facet> facets, final Object old) { - - final CalculationResult res = doCompute(sinfo, context, old); - - if (facets == null) - return res; - - // final FlowDepthCalculationResults results = (FlowDepthCalculationResults) res.getData(); - // - // /* add themes for chart, for each result */ - // final List<FlowDepthCalculationResult> resultList = results.getResults(); - // for (int index = 0; index < resultList.size(); index++) { - // - // final FlowDepthCalculationResult result = resultList.get(index); - // - // /* filtered (zoom dependent mean) flow depth */ - // final String facetFlowDepthFilteredDescription = Resources.getMsg(context.getMeta(), - // I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION, - // I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION, result.getLabel()); - // facets.add(new FlowDepthFacet(index, FlowDepthProcessor.FACET_FLOW_DEPTH_FILTERED, facetFlowDepthFilteredDescription, - // SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL, ComputeType.ADVANCE, this.id, hash)); - // - // if (results.isUseTkh()) { - // /* filtered (zoom dependent mean) flow depth including tkh */ - // final String facetFlowDepthTkhFilteredDescription = Resources.getMsg(context.getMeta(), - // I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION, - // I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION, result.getLabel()); - // facets.add(new FlowDepthFacet(index, FlowDepthProcessor.FACET_FLOW_DEPTH_TKH_FILTERED, - // facetFlowDepthTkhFilteredDescription, - // SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL, ComputeType.ADVANCE, this.id, hash)); - // - // // FIXME: add other themes - // // - Streckenfavoriten - // - // // FIXME: - // // - Gemittelte Linie der Fließtiefe mitsamt TKH - // // - Transportkörperhöhen (oben/unten/schraffur) - // final String facetTkhDescription = Resources.getMsg(context.getMeta(), I18N_FACET_TKH_DESCRIPTION, - // I18N_FACET_TKH_DESCRIPTION, - // result.getLabel()); - // facets.add(new FlowDepthFacet(index, TkhProcessor.FACET_TKH, facetTkhDescription, SINFO_CHART_TKX_YAXIS_LABEL, - // ComputeType.ADVANCE, this.id, - // hash)); - // } - // - // // FIXME: Datenkorbkonfiguration - // } - // - // if (!resultList.isEmpty()) { - // final Facet csv = new DataFacet(FacetTypes.CSV, "CSV data", ComputeType.ADVANCE, hash, this.id); - // final Facet pdf = new DataFacet(FacetTypes.PDF, "PDF data", ComputeType.ADVANCE, hash, this.id); - // - // facets.add(csv); - // facets.add(pdf); - // } - // - // final Calculation report = res.getReport(); - // - // if (report.hasProblems()) { - // facets.add(new ReportFacet(ComputeType.ADVANCE, hash, this.id)); - // } - // - // return res; - return null; - } - - private CalculationResult doCompute(final SINFOArtifact sinfo, final CallContext context, final Object old) { - if (old instanceof CalculationResult) - return (CalculationResult) old; - - // res = new FlowDepthCalculation(context).calculate(sinfo); - - final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo); - - final CalculationResult waterlevelData = winfo.getWaterlevelData(context); - final Calculation winfoProblems = waterlevelData.getReport(); - - final WQKms[] kms = (WQKms[]) waterlevelData.getData(); - - final Object result = new Object(); - final Calculation problems = new Calculation(); - - return new CalculationResult(result, problems); - } -} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkh/WinfoArtifactWrapper.java Tue Feb 27 18:06:52 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +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.tkh; - -import java.util.Collection; - -import org.dive4elements.artifactdatabase.data.DefaultStateData; -import org.dive4elements.artifactdatabase.data.StateData; -import org.dive4elements.river.artifacts.D4EArtifact; -import org.dive4elements.river.artifacts.WINFOArtifact; - -/** - * Ugly wrapper around WINfoArtifact in order to a) not to break serialization of WInfoArtifact b) be able to copy data - * into it - * - * @author Gernot Belger - * - */ -class WinfoArtifactWrapper extends WINFOArtifact { - - private static final long serialVersionUID = 1L; - - public WinfoArtifactWrapper(final D4EArtifact dataSource) { - final Collection<StateData> allData = dataSource.getAllData(); - for (final StateData stateData : allData) { - - final DefaultStateData clonedData = new DefaultStateData(); - clonedData.set(stateData); - - addData(clonedData.getName(), clonedData); - } - - addStringData("calculation_mode", "calc.surface.curve"); - } -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/BedQualityD50KmValueFinder.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,260 @@ +/* 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.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.math.DoubleRange; +import org.apache.commons.math.ArgumentOutsideDomainException; +import org.apache.commons.math.analysis.interpolation.LinearInterpolator; +import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; +import org.apache.log4j.Logger; +import org.dive4elements.river.backend.SedDBSessionHolder; +import org.dive4elements.river.model.River; +import org.hibernate.SQLQuery; +import org.hibernate.Session; +import org.hibernate.type.StandardBasicTypes; + +/** + * Searchable sorted km array with parallel bed measurements value array and linear interpolation for km and d50 between + * the array elements.<br /> + * <br /> + * See comment of SQL command on how the values are filtered and aggregated. + * + * @author Matthias Schäfer + * + */ +final class BedQualityD50KmValueFinder { + + /***** INNER CLASSES *****/ + + /** + * A bed measurements aggregate with its d50 characteristic grain diameter + */ + private static class D50Measurement { + private double km; + + public double getKm() { + return this.km; + } + + // private Date mindate; + + // public Date getMinDate() { + // return this.mindate; + // } + + // private Date maxdate; + + // public Date getMaxDate() { + // return this.maxdate; + // } + + private int cnt; + + public int getCnt() { + return this.cnt; + } + + // private double mindepth; + + // public double getMinDepth() { + // return this.mindepth; + // } + + // private double maxdepth; + + // public double getMaxDepth() { + // return this.maxdepth; + // } + + private double d50; + + /** + * D50 in m + */ + public double getD50() { + return this.d50; + } + + // /** + // * Parameter constructor + // */ + // public D50Measurement(final double km, final Date mindate, final Date maxdate, final int cnt, final double mindepth, + // final double maxdepth, + // final double d50mm) { + // this.km = km; + // this.mindate = mindate; + // this.maxdate = maxdate; + // this.cnt = cnt; + // this.mindepth = mindepth; + // this.maxdepth = maxdepth; + // this.d50 = d50mm / 1000; + // } + + /** + * Query result row constructor + */ + public D50Measurement(final Object[] tuple, final String[] aliases) { + this.km = 0; + // this.mindate = null; + // this.maxdate = null; + this.cnt = 0; + // this.mindepth = Double.NaN; + // this.maxdepth = Double.NaN; + this.d50 = Double.NaN; + for (int i = 0; i < tuple.length; ++i) { + if (tuple[i] == null) + continue; + switch (aliases[i]) { + case "km": + this.km = ((Number) tuple[i]).doubleValue(); + break; + // case "mindate": + // this.mindate = (Date) tuple[i]; + // break; + // case "maxdate": + // this.maxdate = (Date) tuple[i]; + // break; + case "cnt": + this.cnt = ((Number) tuple[i]).intValue(); + break; + // case "mindepth": + // this.mindepth = ((Number) tuple[i]).doubleValue(); + // break; + // case "maxdepth": + // this.maxdepth = ((Number) tuple[i]).doubleValue(); + // break; + case "d50": + this.d50 = ((Number) tuple[i]).doubleValue() / 1000; // mm to m + break; + default: + break; + } + } + } + } + + /***** FIELDS *****/ + + /** + * Private log to use here. + */ + private static Logger log = Logger.getLogger(BedQualityD50KmValueFinder.class); + + /** + * Query that aggregates by km for a km range and a time period all sub layer bed measurements with their d50<br /> + * <br /> + * A km may have bed measurements for multiple dates, multiple distances from the river bank, and multiple depth layers. + * The query filters by km range, time period and layer (sub layer: below bed to max. 50 cm depth). + * Those measurements are then grouped by km, and the D50 aggregated as average value. + */ + private static final String SQL_BED_D50_SUBLAYER_MEASUREMENT = + "SELECT t.km, MIN(t.datum) AS mindate, MAX(t.datum) AS maxdate, COUNT(*) AS cnt," // + + " MIN(p.tiefevon) AS mindepth, MAX(p.tiefebis) AS maxdepth, AVG(a.d50) AS 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)" // + + " GROUP BY t.km" // + + " ORDER BY t.km"; // + + private static final String[] SQL_BED_D50_SELECT_ALIAS = { "km", "mindate", "maxdate", "cnt", "mindepth", "maxdepth", "d50" }; + + /** + * Real linear interpolator for kms and d50 values + */ + private final PolynomialSplineFunction interpolator; + + /***** METHODS *****/ + + private BedQualityD50KmValueFinder(final double[] kms, final double[] values) { + this.interpolator = new LinearInterpolator().interpolate(kms, values); + } + + /** + * Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) + * Abhängig von Peiljahr + */ + public static BedQualityD50KmValueFinder loadBedMeasurements(final River river, final DoubleRange kmRange, final int soundingYear, final int validYears) { + + /* construct valid measurement time range */ + final Calendar cal = Calendar.getInstance(); + cal.clear(); + + cal.set(soundingYear - validYears, 0, 1); + final Date startTime = cal.getTime(); + + cal.set(soundingYear + validYears, 11, 31); + final Date endTime = cal.getTime(); + + 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("mindate", StandardBasicTypes.DATE).addScalar("maxdate", StandardBasicTypes.DATE).addScalar("cnt", StandardBasicTypes.INTEGER) + .addScalar("mindepth", StandardBasicTypes.DOUBLE).addScalar("maxdepth", StandardBasicTypes.DOUBLE).addScalar("d50", StandardBasicTypes.DOUBLE); + final String seddbRiver = river.nameForSeddb(); + sqlQuery.setString("name", seddbRiver); + sqlQuery.setDouble("fromkm", kmRange.getMinimumDouble()); + sqlQuery.setDouble("tokm", kmRange.getMaximumDouble()); + sqlQuery.setDate("fromdate", startTime); + sqlQuery.setDate("todate", endTime); + + final List<Object[]> rows = sqlQuery.list(); + final double[] kms = new double[rows.size()]; + final double[] values = new double[rows.size()]; + D50Measurement measurement; + int i = -1; + for (final Object[] row : rows) { + measurement = new D50Measurement(row, SQL_BED_D50_SELECT_ALIAS); + i++; + kms[i] = measurement.getKm(); + values[i] = measurement.getD50(); + log.debug(String.format("loadValues km %.3f d50(mm) %.1f count %d", kms[i], values[i], measurement.getCnt())); + } + try { + return new BedQualityD50KmValueFinder(kms, values); + } + catch (final Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * Returns the d50 value interpolated according to a km + * + * @return d50 (mm) 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; + */ + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/DischargeValuesFinder.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,61 @@ +/** 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 org.apache.commons.lang.math.DoubleRange; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.UnivariateRealFunction; +import org.dive4elements.river.artifacts.model.QKms; +import org.dive4elements.river.artifacts.model.WKms; +import org.dive4elements.river.artifacts.model.WQKms; +import org.dive4elements.river.utils.DoubleUtil; + +/** + * @author Gernot Belger + */ +public final class DischargeValuesFinder { + + private final UnivariateRealFunction qInterpolator; + private final QKms qKms; + + /** + * Create an instance from a {@link WKms} object. If the given {@link WKms} is not a {@link WQKms}, a finder that always + * returns {@link Double#NaN} is returned. + */ + public static DischargeValuesFinder fromKms(final WKms wstKms) { + if (!(wstKms instanceof QKms)) { + return new DischargeValuesFinder(null); + } + + final QKms qKms = (QKms) wstKms; + + return new DischargeValuesFinder(qKms); + } + + public DischargeValuesFinder(final QKms qKms) { + this.qKms = qKms; + this.qInterpolator = qKms == null ? null : DoubleUtil.getLinearInterpolator(qKms.allKms(), qKms.allQs()); + } + + /** + * If this provider may return valid data at all. + */ + public boolean isValid() { + return this.qInterpolator != null; + } + + public DoubleRange getRange() { + return new DoubleRange(this.qKms.allQs().min(), this.qKms.allQs().max()); + } + + public double getDischarge(final double station) throws FunctionEvaluationException { + return this.qInterpolator.value(station); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/FlowVelocityModelKmValueFinder.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,337 @@ +/* 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.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.math.DoubleRange; +import org.apache.log4j.Logger; +import org.dive4elements.river.artifacts.math.Linear; +import org.dive4elements.river.artifacts.math.Utils; +import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowVelocityKmModelValues; +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; + +import gnu.trove.TDoubleArrayList; + +/** + * Searchable sorted km array with parallel FlowVelocityKmModelValues array and linear interpolation for km and the + * model values between the array elements.<br /> + * {@link loadValues} loads all the model values for a given km range of a river.<br /> + * {@link findKmQValues} then searches a km in the values table or the nearest including km interval, resp. + * The v and tau values for a given discharge are either found directly or also interpolated linearly.<br /> + * + * (Created based on a copy of FlowVelocityMeasurementFactory.) + * + * @author Matthias Schäfer + */ +final class FlowVelocityModelKmValueFinder { + /***** FIELDS *****/ + + /** + * Private log to use here. + */ + private static Logger log = Logger.getLogger(FlowVelocityModelKmValueFinder.class); + + /** + * Query for a range of stations of a river with all their q, main-v and tau values.<br /> + * (Might be several 10000 rows if many stations and large q range) + */ + private static final String SQL_SELECT_ALL = // + "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" + + " INNER JOIN flow_velocity_model_values fvmv ON fvm.id = fvmv.flow_velocity_model_id" + + " WHERE (dz.river_id = :river_id) AND (fvmv.station BETWEEN :kmfrom - 0.0001 AND :kmto + 0.0001)" + /* + " WHERE (dz.river_id = :river_id) AND (fvmv.q BETWEEN :qmin AND :qmax)" */ + + " ORDER BY fvmv.station ASC, fvmv.q ASC"; + + /** + * Query for a river's max km below a limit with all its q, main-v and tau values. + */ + private static final String SQL_SELECT_KMLOWER = // + "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + " FROM flow_velocity_model_values fvmv" + + " INNER JOIN (SELECT MAX(fvmvi.station) AS kmmax" + + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" + + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id" + + " WHERE (dz.river_id = :river_id) AND (fvmvi.station < :kmfrom - 0.0001)) finf ON fvmv.station = finf.kmmax" + + " ORDER BY fvmv.q ASC"; + + /** + * Query for a river's min km above a limit with all its q, main-v and tau values. + */ + private static final String SQL_SELECT_KMUPPER = // + "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + " FROM flow_velocity_model_values fvmv" + + " INNER JOIN (SELECT MIN(fvmvi.station) AS kmmin" + + " FROM(discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" + + " INNER JOIN flow_velocity_model_values fvmvi ON fvm.id = fvmvi.flow_velocity_model_id" + + " WHERE (dz.river_id = :river_id) AND (fvmvi.station > :kmto + 0.0001)) fsup ON fvmv.station = fsup.kmmin" + + " ORDER BY fvmv.q ASC"; + + // /** + // * Query to select all km-q-v-tau of a river that are the q maxima below a q limit + // */ + // private static final String SQL_SELECT_QLOWER = + // "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + // + " FROM flow_velocity_model_values fvmv" + // + " INNER JOIN (SELECT fv2.station, MAX(fv2.q) AS q" + // + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" + // + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id" + // + " WHERE (dz.river_id = :river_id) AND (fv2.q < :qlim) GROUP BY fv2.station) qx" + // + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)" + // + " ORDER BY fvmv.station ASC"; + // + // /** + // * Query to select all km-q-v-tau of a river that are the q minima above a q limit + // */ + // private static final String SQL_SELECT_QUPPER = + // "SELECT fvmv.station AS station, fvmv.q AS q, fvmv.main_channel AS vmain, fvmv.shear_stress AS tau" + // + " FROM flow_velocity_model_values fvmv" + // + " INNER JOIN (SELECT fv2.station, MIN(fv2.q) AS q" + // + " FROM (discharge_zone dz INNER JOIN flow_velocity_model fvm ON dz.id = fvm.discharge_zone_id)" + // + " INNER JOIN flow_velocity_model_values fv2 ON fvm.id = fv2.flow_velocity_model_id" + // + " WHERE (dz.river_id = :river_id) AND (fv2.q > :qlim) GROUP BY fv2.station) qx" + // + " ON (fvmv.station=qx.station) AND (fvmv.q=qx.q)" + // + " ORDER BY fvmv.station ASC"; + + /** + * Kms of the loaded river range + */ + private final TDoubleArrayList kms = new TDoubleArrayList(); + + /** + * For each km in kms a list of q-v-tau-tupels + */ + private final List<FlowVelocityKmModelValues> values = new ArrayList<>(); + + /** + * Searched km of the last findKmValue + */ + private double findKm; + + /** + * kms and values index of the interval start found by the last findKmValue + */ + private int leftIndexFound = -1; + + /** + * kms and values index of the interval end found by the last findKmValue + */ + private int rightIndexFound = -1; + + /** + * Q of the last findKmQValues + */ + private double findQ; + + /***** METHODS *****/ + + /** + * Discharge of the last {@link findKmQValue} + */ + public double getFindQ() { + return this.findQ; + } + + /** + * Velocity of the last {@link findKmQValues} + */ + public double getFindVmainFound() { + if (this.leftIndexFound < 0) + return Double.NaN; + else if (this.leftIndexFound == this.rightIndexFound) + return getLeftValues().getVmainFound(); + else + return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getVmainFound(), + getRightValues().getVmainFound()); + } + + /** + * Shear stress tau of the last {@link findKmQValues} + */ + public double getFindTauFound() { + if (this.leftIndexFound < 0) + return Double.NaN; + else if (this.leftIndexFound == this.rightIndexFound) + return getLeftValues().getTauFound(); + else + return Linear.linear(this.findKm, getLeftValues().getKm(), getRightValues().getKm(), getLeftValues().getTauFound(), getRightValues().getTauFound()); + } + + /** + * Whether the discharge has been interpolated in the last {@link findKmQValues} + */ + public boolean getFindIsQInterpolated() { + return (getLeftValues() != null) && (getLeftValues().getIsInterpolated() || getRightValues().getIsInterpolated()); + } + + /** + * Static constructor: queries a range of a river's kms with all their q-v-tau values. + * + * @return Whether the load has been successful the new instance, <code>null</code> otherwise. + */ + public static FlowVelocityModelKmValueFinder loadValues(final River river, final DoubleRange kmRange, final DoubleRange qRange) { + // 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 TDoubleArrayList kms = instance.kms; + final List<FlowVelocityKmModelValues> values = instance.values; + + final boolean isDemoValuesCorrection = river.getName().equalsIgnoreCase("beispielfluss"); + final Session session = SessionHolder.HOLDER.get(); + + // Select km infimum + SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_KMLOWER).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE) + .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE); + sqlQuery.setParameter("river_id", river.getId()); + sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble()); + instance.addKms(sqlQuery.list(), isDemoValuesCorrection); + + // Select km range + sqlQuery = session.createSQLQuery(SQL_SELECT_ALL).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE) + .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE); + sqlQuery.setParameter("river_id", river.getId()); + sqlQuery.setParameter("kmfrom", kmRange.getMinimumDouble()); + sqlQuery.setParameter("kmto", kmRange.getMaximumDouble()); + // sqlQuery.setParameter("qmin", qRange.getMinimumDouble()); + // sqlQuery.setParameter("qmax", qRange.getMaximumDouble()); + + int kmcount = kms.size(); + final int rowcount = instance.addKms(sqlQuery.list(), isDemoValuesCorrection); + kmcount = kms.size() - kmcount; + + // Select km supremum + sqlQuery = session.createSQLQuery(SQL_SELECT_KMUPPER).addScalar("station", StandardBasicTypes.DOUBLE).addScalar("q", StandardBasicTypes.DOUBLE) + .addScalar("vmain", StandardBasicTypes.DOUBLE).addScalar("tau", StandardBasicTypes.DOUBLE); + sqlQuery.setParameter("river_id", river.getId()); + sqlQuery.setParameter("kmto", kmRange.getMaximumDouble()); + final int supcnt = instance.addKms(sqlQuery.list(), isDemoValuesCorrection); + + // Add copy of last km for search of max km value + if ((supcnt == 0) && (values.size() >= 1)) { + kms.add(kms.getQuick(kms.size()) + 0.0001); + values.add(new FlowVelocityKmModelValues(kms.getQuick(kms.size() - 1), values.get(values.size() - 1))); + } + + // log.debug + if (values.size() - 1 >= 0) { + log.debug(String.format("loadValues %d: km %.3f - %d values", 0, values.get(0).getKm(), values.get(0).size())); + + if (values.size() - 1 >= 1) { + log.debug(String.format("loadValues %d: km %.3f - %d values", 1, values.get(1).getKm(), values.get(1).size())); + + if (values.size() - 1 >= 2) + log.debug("loadValues ..."); + + if (values.size() - 2 >= 3) + log.debug(String.format("loadValues %d: km %.3f - %d values", values.size() - 2, values.get(values.size() - 2).getKm(), + values.get(values.size() - 2).size())); + + if (values.size() - 1 >= 3) + log.debug(String.format("loadValues %d: km %.3f - %d values", values.size() - 1, values.get(values.size() - 1).getKm(), + values.get(values.size() - 1).size())); + } + } + + log.debug(String.format("loadValues %d kms, %d values loaded", kmcount, rowcount)); + + if (kms.size() == 0) + return null; + + return instance; + } + + /** + * Adds the km-q-v-tau values of a query result row to the last km of the list, or a new one resp. + * + * @return Number of rows + */ + private int addKms(final List<Object[]> rows, final boolean isDemoValuesCorrection) { + for (final Object[] row : rows) { + if ((this.kms.size() == 0) || !Utils.epsilonEquals(this.kms.get(this.kms.size() - 1), (double) row[0], 0.0001)) { + this.kms.add((double) row[0]); + this.values.add(new FlowVelocityKmModelValues(this.kms.get(this.kms.size() - 1))); + } + if (isDemoValuesCorrection) + // "Verfremdung" der v-Werte etwas korrigieren (Originalwerte wurden mit Zufallswert zwischen 10 und 20 multipliziert) + this.values.get(this.values.size() - 1).addValues((double) row[1], ((double) row[2]) / 10, (double) row[3]); + else + this.values.get(this.values.size() - 1).addValues((double) row[1], (double) row[2], (double) row[3]); + } + return rows.size(); + } + + /** + * Searches a km and finds or interpolates the velocity and shear stress values for a discharge<br /> + * The values may be got via {@link getVmainFound} etc. + * + * @return Whether values have been found + */ + public boolean findKmQValues(final double km, final double q) { + this.findQ = 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]); + } + } + + /** + * Searches a km + * + * @return Whether the km was within the supported range + */ + private boolean searchKm(final double km) { + 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; + } + } + + private FlowVelocityKmModelValues getLeftValues() { + return this.values.get(this.leftIndexFound); + } + + private FlowVelocityKmModelValues getRightValues() { + return this.values.get(this.rightIndexFound); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/SoilKind.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,14 @@ +/** 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; + +public enum SoilKind { + mobil, starr +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/SoilKindKmValueFinder.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,118 @@ +/* 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.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.math.DoubleRange; +import org.apache.commons.math.ArgumentOutsideDomainException; +import org.dive4elements.river.model.River; + +import gnu.trove.TDoubleArrayList; + +/** + * @author Matthias Schäfer + */ +final class SoilKindKmValueFinder { + // /** + // * Private log to use here. + // */ + // private static Logger log = Logger.getLogger(SoilKindKmValueFinder.class); + + private final TDoubleArrayList kms = new TDoubleArrayList(); + + private final List<SoilKind> values = new ArrayList<>(); + + /** + * Loads the range of the river's kms with their soil kind. + * + * @return Whether the load has been successful + */ + public static SoilKindKmValueFinder loadValues(final River river, final DoubleRange kmRange) { + + final SoilKindKmValueFinder instance = new SoilKindKmValueFinder(); + + // FIXME Echte Daten aus der Datenbank abfragen + instance.addKmKind(0, SoilKind.starr); + instance.addKmKind(15.7, SoilKind.mobil); + instance.addKmKind(15.8, SoilKind.mobil); + instance.addKmKind(15.9, SoilKind.starr); + instance.addKmKind(108.7, SoilKind.mobil); + instance.addKmKind(108.8, SoilKind.mobil); + instance.addKmKind(108.9, SoilKind.starr); + instance.addKmKind(119.1, SoilKind.mobil); + instance.addKmKind(119.4, SoilKind.mobil); + instance.addKmKind(119.5, SoilKind.starr); + instance.addKmKind(128.3, SoilKind.mobil); + instance.addKmKind(128.9, SoilKind.mobil); + instance.addKmKind(129, SoilKind.starr); + instance.addKmKind(133.1, SoilKind.mobil); + instance.addKmKind(135.9, SoilKind.mobil); + instance.addKmKind(136, SoilKind.starr); + instance.addKmKind(136.5, SoilKind.mobil); + instance.addKmKind(139.9, SoilKind.mobil); + instance.addKmKind(140, SoilKind.starr); + instance.addKmKind(140.5, SoilKind.mobil); + instance.addKmKind(165, SoilKind.mobil); + instance.addKmKind(165.1, SoilKind.starr); + instance.addKmKind(165.9, SoilKind.mobil); + instance.addKmKind(180.8, SoilKind.mobil); + instance.addKmKind(180.9, SoilKind.starr); + instance.addKmKind(182, SoilKind.mobil); + instance.addKmKind(221.3, SoilKind.mobil); + + return instance; + } + + private SoilKindKmValueFinder() { + /* only instantiate me via static constructor */ + } + + /***** METHODS *****/ + + /** + * Searches a km with its soil kind + */ + public SoilKind findSoilKind(final double km) throws ArgumentOutsideDomainException { + if ((this.kms == null) || (this.kms.size() == 0)) + throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN); + + // TODO: Voraussetzung für die binäre suche ist, dass nach km sortiert ist. + // In diesem Fall könnte man ggf. auch gleich eine bessere Datenklasse benutzen, z.B. eine TreeMap<Double, SoilKind> + // (also station -> art), und deren funktionen zum finden verwenden: + // final double station = 0.0; + // final NavigableMap<Double, SoilKind> data = new TreeMap<>(); + // data.ceilingEntry(station); + // data.floorEntry(station); + + int i = this.kms.binarySearch(km); + if (i >= 0) { + // Exact km match + return this.values.get(i); + } else { + // Out of range or within km interval + if (i < 0) + i = -i - 1; + if ((i <= 0) || (i >= this.kms.size())) + throw new ArgumentOutsideDomainException(km, Double.NaN, Double.NaN); + if (km <= ((this.kms.get(i - 1) + this.kms.get(i)) / 2)) + return this.values.get(i - 1); + else + return this.values.get(i); + } + } + + private void addKmKind(final double km, final SoilKind kind) { + this.kms.add(km); + this.values.add(kind); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/Tkh.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,86 @@ +/** 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 discharge; + + private final SoilKind kind; + + private final double tkh; + + private final double tkhUp; + + private final double tkhDown; + + public Tkh(final double km, final double wst, final double meanBedHeight, final double discharge) { + this(km, wst, meanBedHeight, discharge, null, Double.NaN, Double.NaN, Double.NaN); + } + + public Tkh(final double km, final double wst, final double meanBedHeight, final double discharge, final SoilKind kind, final double tkh, final double tkhUp, + final double tkhDown) { + this.km = km; + this.wst = wst; + this.meanBedHeight = meanBedHeight; + this.discharge = discharge; + this.kind = kind; + this.tkh = tkh; + this.tkhUp = tkhUp; + this.tkhDown = tkhDown; + } + + 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; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhcalculation/TkhCalculator.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,232 @@ +/** 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 org.apache.commons.lang.math.DoubleRange; +import org.apache.commons.math.ArgumentOutsideDomainException; +import org.apache.commons.math.FunctionEvaluationException; +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.tkhstate.BedHeightsFinder; +import org.dive4elements.river.model.River; + +/** + * @author Gernot Belger + */ +public final class TkhCalculator { + + 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; + + private final BedHeightsFinder bedHeightsProvider; + + private final DischargeValuesFinder dischargeProvider; + + private final FlowVelocityModelKmValueFinder flowVelocitiesFinder; + + public static TkhCalculator buildTkhCalculator(final boolean useTkh, final CallContext context, final Calculation problems, final String label, + final River river, final DoubleRange calcRange, final DischargeValuesFinder dischargeProvider, final BedHeightsFinder bedHeightsProvider) { + + if (!useTkh) + return null; + + if (!dischargeProvider.isValid()) { + final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); + problems.addProblem(message); + return null; + } + + final Integer soundingYear = bedHeightsProvider.getInfo().getYear(); + final BedQualityD50KmValueFinder bedMeasurementsFinder = BedQualityD50KmValueFinder.loadBedMeasurements(river, calcRange, soundingYear, + VALID_BED_MEASUREMENT_YEARS); + + if (bedMeasurementsFinder == null) { + final String message = Resources.getMsg(context.getMeta(), "sinfo_calc_flow_depth.warning.missingSoilKind", null, label); + problems.addProblem(message); + return 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 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 null; + } + + return new TkhCalculator(problems, label, context, bedMeasurementsFinder, dischargeProvider, bedHeightsProvider, soilKindFinder, flowVelocitiesFinder); + } + + private TkhCalculator(final Calculation problems, final String problemLabel, final CallContext context, + final BedQualityD50KmValueFinder bedMeasurementsFinder, 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.dischargeProvider = dischargeProvider; + this.bedHeightsProvider = bedHeightsProvider; + this.soilKindFinder = soilKindFinder; + this.flowVelocitiesFinder = flowVelocitiesFinder; + } + + private double getDischarge(final double km) { + + try { + return this.dischargeProvider.getDischarge(km); + } + catch (final FunctionEvaluationException e) { + // TODO: exceptions nicht komplett schlucken? evtl. mit log.debug(e) ausgeben + return Double.NaN; + } + } + + private SoilKind getSoilKind(final double km) { + + try { + 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; + } + } + + 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; + } + } + + public Tkh getTkh(final double km, final double wst) { + + final SoilKind kind = getSoilKind(km); + + final double meanBedHeight = this.bedHeightsProvider.getMeanBedHeight(km); + + final double discharge = getDischarge(km); + if (Double.isNaN(discharge)) { + + // final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, + // this.problemLabel); + // this.problems.addProblem(km, message); + + // TODO: nochmal gemeinsam überlegen welche probleme wir loggen, an dieser stelle müsste man ggf. die station + // mitausgeben + + return new Tkh(km, wst, meanBedHeight, Double.NaN, kind, Double.NaN, Double.NaN, Double.NaN); + } + + final double d50 = getBedMeasurement(km); + if (Double.isNaN(d50)) + return new Tkh(km, wst, meanBedHeight, discharge, kind, Double.NaN, Double.NaN, Double.NaN); + + if (!this.flowVelocitiesFinder.findKmQValues(km, discharge)) { + // TODO: ggf. station in Fehlermeldung? + final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, this.problemLabel); + this.problems.addProblem(km, message); + // FIXME: cumulate problems to one message? + } + + final double tkh = calculateTkh(wst - meanBedHeight, this.flowVelocitiesFinder.getFindVmainFound(), d50, this.flowVelocitiesFinder.getFindTauFound()); + // FIXME: noch mal prüfen, im alten code wurde hier immer auf 0 gesetzt + if (Double.isNaN(tkh) || (tkh < 0)) { + // TODO: ggf. station in Fehlermeldung? + + // FIXME: Fehlermeldung nicht korrekt, passiert mit Wasserspiegel 'MHQ' und 'QP-1993': alle Daten (auch Abfluss) + // vorhanden, aber tkh negativ... + final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, this.problemLabel); + this.problems.addProblem(km, message); + + return new Tkh(km, wst, meanBedHeight, discharge, kind, Double.NaN, Double.NaN, Double.NaN); + } + + /* + * log.debug(String.format("calculateTkh km %.3f q %.0f w %.2f mbh %.2f vm %.1f tau %.1f d50(mm) %.1f tkh(cm) %.1f", + * km, discharge, wst, meanBedHeight, flowVelocitiesFinder.getFindVmainFound(), flowVelocitiesFinder.getFindTauFound(), + * d50*1000, tkh)); + */ + + double tkhUp; + double tkhDown; + switch (kind) { + case starr: + tkhUp = tkh; + tkhDown = 0; + break; + + case mobil: + default: + tkhUp = tkh / 2; + tkhDown = -tkh / 2; + break; + } + + return new Tkh(km, wst, meanBedHeight, discharge, kind, tkh, tkhUp, tkhDown); + } + + /** + * Calculates a transport body height + * + * @param h + * flow depth in m + * @param vm + * flow velocity in m + * @param d50 + * grain diameter D50 in m (!) + * @param tau + * shear stress in N/m^2 + * @return transport body height in cm (!) + */ + private double calculateTkh(final double h, final double vm, final double d50, final double tau) { + final double PHYS_G = 9.81; + final double PHYS_SPECGRAV_S = 2.6; + final double PHYS_VELOCCOEFF_N = 6; + final double PHYS_FORMCOEFF_ALPHA = 0.7; + final double PHYS_VISCOSITY_NUE = 1.3e-6; + final double PHYS_GRAIN_DENSITY_RHOS = 2603; + final double PHYS_WATER_DENSITY_RHO = 999.97; + + final double froude = vm / Math.sqrt(PHYS_G * h); + final double partReynolds = Math.sqrt((PHYS_SPECGRAV_S - 1) * PHYS_G * d50) / PHYS_VISCOSITY_NUE * d50; + final double critShields = 0.22 * Math.pow(partReynolds, -0.6) + 0.06 * Math.pow(10, 7.7 * Math.pow(partReynolds, -0.6)); + final double critTau = critShields * (PHYS_GRAIN_DENSITY_RHOS - PHYS_WATER_DENSITY_RHO) * PHYS_G * d50; + return 100 * h * (1 - Math.pow(froude, 2)) / (2 * PHYS_VELOCCOEFF_N * PHYS_FORMCOEFF_ALPHA) * (1 - critTau / tau); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,134 @@ +/** 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.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; + +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. + * + * @author Gernot Belger + */ +public final class BedHeightsFinder { + + private final BedHeightInfo info; + + private final NavigableMap<Double, BedHeightValue> values; + + /** + * Create specific bed heights used in tkh-calculation + * + * @param problems + */ + 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 + + final List<BedHeightsFinder> result = new ArrayList<>(bedHeights.size()); + + for (final BedHeight bedHeight : bedHeights) { + result.add(createBedHeights(bedHeight, range)); + } + + return result; + } + + /** + * Creates a {@link BedHeightsFinder} for a dataset from the database, specified by its id. + * + * @return <code>null</code> if no bed height with the given id exists. + */ + public static BedHeightsFinder forId(final int id, final DoubleRange range) { + + final BedHeight bedHeight = BedHeight.getBedHeightById(id); + if (bedHeight == null) + return null; + + return BedHeightsFinder.createBedHeights(bedHeight, range); + } + + /** + * Create a finder for a given bed height. + * + * @param range + */ + private static BedHeightsFinder createBedHeights(final BedHeight bedHeight, final DoubleRange range) { + + // FIXME: sort by station, but in what direction? + // FIXME: using river.getKmUp()? + final NavigableMap<Double, BedHeightValue> values = new TreeMap<>(); + + for (final BedHeightValue bedHeightValue : bedHeight.getValues()) { + final Double station = bedHeightValue.getStation(); + if (station != null && range.containsDouble(station)) { + + if (bedHeightValue.getHeight() != null) + values.put(station, bedHeightValue); + } + } + + final BedHeightInfo info = BedHeightInfo.from(bedHeight); + + return new BedHeightsFinder(info, values); + } + + private BedHeightsFinder(final BedHeightInfo info, final NavigableMap<Double, BedHeightValue> values) { + this.info = info; + this.values = values; + } + + public BedHeightInfo getInfo() { + return this.info; + } + + public Collection<Double> getStations() { + return this.values.keySet(); + } + + public double getMeanBedHeight(final double km) { + + if (this.values.containsKey(km)) + return this.values.get(km).getHeight(); + + final Entry<Double, BedHeightValue> floorEntry = this.values.floorEntry(km); + final Entry<Double, BedHeightValue> ceilingEntry = this.values.ceilingEntry(km); + + if (floorEntry == null || ceilingEntry == null) + return Double.NaN; + + final double floorKm = floorEntry.getKey(); + final double floorHeight = floorEntry.getValue().getHeight(); + final double ceilKm = ceilingEntry.getKey(); + final double ceilHeight = ceilingEntry.getValue().getHeight(); + + // FIXME: check if we always want that... + + return Linear.linear(km, floorKm, ceilKm, floorHeight, ceilHeight); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhAccess.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,40 @@ +/* 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 org.apache.commons.lang.math.DoubleRange; +import org.dive4elements.river.artifacts.access.RangeAccess; +import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; +import org.dive4elements.river.artifacts.sinfo.SinfoCalcMode; + +/** + * Access to the flow depth calculation type specific SInfo artifact data. + * REMARK: this class is NOT intended to be hold in the results (or anywhere else), in order to avoid a permanent + * reference to the artifact instance. + * Hence we do NOT cache any data. + * + * @author Gernot Belger + */ +final class TkhAccess extends RangeAccess { + public TkhAccess(final SINFOArtifact artifact) { + super(artifact); + + /* assert calculation mode */ + final SinfoCalcMode calculationMode = artifact.getCalculationMode(); + assert (calculationMode == SinfoCalcMode.sinfo_calc_transport_bodies_heights); + } + + public DoubleRange getRange() { + final double from = getFrom(); + final double to = getTo(); + return new DoubleRange(from, to); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,152 @@ +/** 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.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang.math.DoubleRange; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.WINFOArtifact; +import org.dive4elements.river.artifacts.model.Calculation; +import org.dive4elements.river.artifacts.model.Calculation.Problem; +import org.dive4elements.river.artifacts.model.CalculationResult; +import org.dive4elements.river.artifacts.model.WQKms; +import org.dive4elements.river.artifacts.resources.Resources; +import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; +import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; +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.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.model.River; + +/** + * @author Gernot Belger + */ +final class TkhCalculation { + + private final CallContext context; + + public TkhCalculation(final CallContext context) { + this.context = context; + } + + public CalculationResult calculate(final SINFOArtifact sinfo) { + + /* access input data */ + final TkhAccess access = new TkhAccess(sinfo); + final River river = access.getRiver(); + final RiverInfo riverInfo = new RiverInfo(river); + final DoubleRange calcRange = access.getRange(); + + final Calculation problems = new Calculation(); + + /* find relevant bed-heights */ + final Collection<BedHeightsFinder> bedHeights = BedHeightsFinder.createTkhBedHeights(river, problems, calcRange); + + /* calculate waterlevels */ + final WQKms[] kms = calculateWaterlevels(sinfo, problems); + + final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange); + + final String user = CalculationUtils.findArtifactUser(this.context, sinfo); + + final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); + + /* for each waterlevel, do a tkh calculation */ + final TkhCalculationResults results = new TkhCalculationResults(calcModeLabel, user, riverInfo, calcRange); + + for (final WQKms wqKms : kms) { + + final TkhCalculationResult result = calculateResult(calcRange, infoProvider, wqKms, bedHeights, problems); + if (result != null) + // FIXME: must be sorted by station! + results.addResult(result); + } + + return new CalculationResult(results, problems); + } + + private WQKms[] calculateWaterlevels(final SINFOArtifact sinfo, final Calculation problems) { + + /* misuse winfo-artifact to calculate waterlevels in the same way */ + final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo); + + final CalculationResult waterlevelData = winfo.getWaterlevelData(this.context); + + /* copy all problems */ + final Calculation winfoProblems = waterlevelData.getReport(); + for (final Problem problem : winfoProblems.getProblems()) { + problems.addProblem(problem); + } + + return (WQKms[]) waterlevelData.getData(); + } + + private TkhCalculationResult calculateResult(final DoubleRange calcRange, final RiverInfoProvider riverInfo, final WQKms wkms, + final Collection<BedHeightsFinder> bedHeights, final Calculation problems) { + + // FIXME: wo kommt das her? via winfo kein jahr vorhanden, oder doch? aber soll in metadaten ausgegeben werden... + final int wspYear = -1; + // FIXME: richtig? vgl. WInfo? + final boolean showAllGauges = false; + final WaterlevelData waterlevel = new WaterlevelData(wkms, wspYear, showAllGauges); + + final RiverInfoProvider riverInfoProvider = riverInfo.forWaterlevel(waterlevel); + + final String label = waterlevel.getName(); + + final WstInfo wstInfo = new WstInfo(label, wspYear, riverInfoProvider.getReferenceGauge()); + + 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 + */ + for (final BedHeightsFinder bedHeightsProvider : bedHeights) { + + final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wkms); + + /* initialize tkh calculator */ + final TkhCalculator tkhCalculator = TkhCalculator.buildTkhCalculator(true, this.context, problems, label, riverInfoProvider.getRiver(), calcRange, + dischargeProvider, bedHeightsProvider); + if (tkhCalculator == null) { + /* just abort, problems have already been updated by buildTkhCalculator() */ + return null; + } + + /* 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 gaugeLabel = riverInfoProvider.findGauge(station); + final String location = riverInfoProvider.getLocation(station); + + rows.add(new TkhResultRow(tkh, label, gaugeLabel, location)); + } + } + + return new TkhCalculationResult(label, wstInfo, true, rows); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculationResult.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,29 @@ +/* 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.util.Collection; + +import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoCalculationResult; +import org.dive4elements.river.artifacts.sinfo.util.WstInfo; + +/** + * Contains the results of a {@link FlowDepthCalculation}. + * + * @author Gernot Belger + */ +final class TkhCalculationResult extends AbstractSInfoCalculationResult<TkhResultRow> { + + private static final long serialVersionUID = 1L; + + public TkhCalculationResult(final String label, final WstInfo wst, final boolean hasTkh, final Collection<TkhResultRow> rows) { + super(label, wst, hasTkh, rows); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculationResults.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,66 @@ +/** 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.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.math.DoubleRange; +import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; + +/** + * @author Gernot Belger + */ +final class TkhCalculationResults implements Serializable { + private static final long serialVersionUID = 1L; + + private final List<TkhCalculationResult> results = new ArrayList<>(); + + private final String calcModeLabel; + + private final String user; + + private final RiverInfo river; + + private final DoubleRange calcRange; + + public TkhCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange) { + this.calcModeLabel = calcModeLabel; + this.user = user; + this.river = river; + this.calcRange = calcRange; + } + + public String getCalcModeLabel() { + return this.calcModeLabel; + } + + public String getUser() { + return this.user; + } + + public RiverInfo getRiver() { + return this.river; + } + + public DoubleRange getCalcRange() { + return this.calcRange; + } + + void addResult(final TkhCalculationResult result) { + this.results.add(result); + } + + public List<TkhCalculationResult> getResults() { + return Collections.unmodifiableList(this.results); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhResultRow.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,25 @@ +/** 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 org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoResultRow; +import org.dive4elements.river.artifacts.sinfo.tkhcalculation.Tkh; + +/** + * @author Gernot Belger + */ +final class TkhResultRow extends AbstractSInfoResultRow { + + private static final long serialVersionUID = 1L; + + public TkhResultRow(final Tkh tkh, final String waterlevelLabel, final String gauge, final String location) { + super(tkh, waterlevelLabel, gauge, location); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhState.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,146 @@ +/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde + * Software engineering by Intevation GmbH + * + * 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.util.List; + +import org.dive4elements.artifactdatabase.state.Facet; +import org.dive4elements.artifacts.CallContext; +import org.dive4elements.river.artifacts.ChartArtifact; +import org.dive4elements.river.artifacts.D4EArtifact; +import org.dive4elements.river.artifacts.model.CalculationResult; +import org.dive4elements.river.artifacts.model.EmptyFacet; +import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; +import org.dive4elements.river.artifacts.states.DefaultState; + +/** State in which a waterlevel has been calculated. */ +public class TkhState extends DefaultState { + + /// ** The log that is used in this state. */ + // private static Logger log = Logger.getLogger(FlowDepthState.class); + + private static final long serialVersionUID = 1L; + + private static final String I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION = "sinfo.facet.flow_depth.filtered.description"; + + private static final String I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION = "sinfo.facet.flow_depth.tkh.filtered.description"; + + private static final String I18N_FACET_TKH_DESCRIPTION = "sinfo.facet.tkh.description"; + + private static final String SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL = "sinfo.chart.flow_depth.yaxis.label"; + + private static final String SINFO_CHART_TKX_YAXIS_LABEL = "sinfo.chart.tkh.yaxis.label"; + + /** + * From this state can only be continued trivially. + */ + @Override + protected String getUIProvider() { + return "continue"; + } + + @Override + public Object computeFeed(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) { + // FIXME: why is this necessary? + if (artifact instanceof ChartArtifact) { + facets.add(new EmptyFacet()); + return null; + } + + return compute((SINFOArtifact) artifact, context, hash, facets, old); + } + + @Override + public Object computeAdvance(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) { + if (artifact instanceof ChartArtifact) { + facets.add(new EmptyFacet()); + return null; + } + return compute((SINFOArtifact) artifact, context, hash, facets, old); + } + + /** + * Compute result or returned object from cache, create facets. + * + * @param old + * Object that was cached. + */ + private Object compute(final SINFOArtifact sinfo, final CallContext context, final String hash, final List<Facet> facets, final Object old) { + + final CalculationResult res = doCompute(sinfo, context, old); + + if (facets == null) + return res; + + // final FlowDepthCalculationResults results = (FlowDepthCalculationResults) res.getData(); + // + // /* add themes for chart, for each result */ + // final List<FlowDepthCalculationResult> resultList = results.getResults(); + // for (int index = 0; index < resultList.size(); index++) { + // + // final FlowDepthCalculationResult result = resultList.get(index); + // + // /* filtered (zoom dependent mean) flow depth */ + // final String facetFlowDepthFilteredDescription = Resources.getMsg(context.getMeta(), + // I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION, + // I18N_FACET_FLOW_DEPTH_FILTERED_DESCRIPTION, result.getLabel()); + // facets.add(new FlowDepthFacet(index, FlowDepthProcessor.FACET_FLOW_DEPTH_FILTERED, facetFlowDepthFilteredDescription, + // SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL, ComputeType.ADVANCE, this.id, hash)); + // + // if (results.isUseTkh()) { + // /* filtered (zoom dependent mean) flow depth including tkh */ + // final String facetFlowDepthTkhFilteredDescription = Resources.getMsg(context.getMeta(), + // I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION, + // I18N_FACET_FLOW_DEPTH_TKH_FILTERED_DESCRIPTION, result.getLabel()); + // facets.add(new FlowDepthFacet(index, FlowDepthProcessor.FACET_FLOW_DEPTH_TKH_FILTERED, + // facetFlowDepthTkhFilteredDescription, + // SINFO_CHART_FLOW_DEPTH_YAXIS_LABEL, ComputeType.ADVANCE, this.id, hash)); + // + // // FIXME: add other themes + // // - Streckenfavoriten + // + // // FIXME: + // // - Gemittelte Linie der Fließtiefe mitsamt TKH + // // - Transportkörperhöhen (oben/unten/schraffur) + // final String facetTkhDescription = Resources.getMsg(context.getMeta(), I18N_FACET_TKH_DESCRIPTION, + // I18N_FACET_TKH_DESCRIPTION, + // result.getLabel()); + // facets.add(new FlowDepthFacet(index, TkhProcessor.FACET_TKH, facetTkhDescription, SINFO_CHART_TKX_YAXIS_LABEL, + // ComputeType.ADVANCE, this.id, + // hash)); + // } + // + // // FIXME: Datenkorbkonfiguration + // } + // + // if (!resultList.isEmpty()) { + // final Facet csv = new DataFacet(FacetTypes.CSV, "CSV data", ComputeType.ADVANCE, hash, this.id); + // final Facet pdf = new DataFacet(FacetTypes.PDF, "PDF data", ComputeType.ADVANCE, hash, this.id); + // + // facets.add(csv); + // facets.add(pdf); + // } + // + // final Calculation report = res.getReport(); + // + // if (report.hasProblems()) { + // facets.add(new ReportFacet(ComputeType.ADVANCE, hash, this.id)); + // } + // + // return res; + return null; + } + + private CalculationResult doCompute(final SINFOArtifact sinfo, final CallContext context, final Object old) { + if (old instanceof CalculationResult) + return (CalculationResult) old; + + return new TkhCalculation(context).calculate(sinfo); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/WinfoArtifactWrapper.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,42 @@ +/** 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.util.Collection; + +import org.dive4elements.artifactdatabase.data.DefaultStateData; +import org.dive4elements.artifactdatabase.data.StateData; +import org.dive4elements.river.artifacts.D4EArtifact; +import org.dive4elements.river.artifacts.WINFOArtifact; + +/** + * Ugly wrapper around WINfoArtifact in order to a) not to break serialization of WInfoArtifact b) be able to copy data + * into it + * + * @author Gernot Belger + * + */ +class WinfoArtifactWrapper extends WINFOArtifact { + + private static final long serialVersionUID = 1L; + + public WinfoArtifactWrapper(final D4EArtifact dataSource) { + final Collection<StateData> allData = dataSource.getAllData(); + for (final StateData stateData : allData) { + + final DefaultStateData clonedData = new DefaultStateData(); + clonedData.set(stateData); + + addData(clonedData.getName(), clonedData); + } + + addStringData("calculation_mode", "calc.surface.curve"); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/CalculationUtils.java Wed Feb 28 17:27:15 2018 +0100 @@ -0,0 +1,37 @@ +/** 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 org.dive4elements.artifacts.Artifact; +import org.dive4elements.artifacts.ArtifactDatabase; +import org.dive4elements.artifacts.CallContext; + +/** + * @author Gernot Belger + */ +public final class CalculationUtils { + + + private CalculationUtils() { + throw new UnsupportedOperationException("Helper class"); + } + + /** + * Find the the user of the given artifact, sadly this is not part of the calling context, so instead we determine the + * owner oft the artifact + * + * @param artifact + * @param context + */ + public static String findArtifactUser(final CallContext context, final Artifact artifact) { + final ArtifactDatabase database = context.getDatabase(); + return database.findArtifactUser(artifact.identifier()); + } +} \ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/WstInfo.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/util/WstInfo.java Wed Feb 28 17:27:15 2018 +0100 @@ -24,10 +24,10 @@ private final String gauge; - public WstInfo(final String label, final int year, final String gauge) { + public WstInfo(final String label, final int year, final String refGauge) { this.label = label; this.year = year; - this.gauge = gauge; + this.gauge = refGauge; } public String getLabel() {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Tue Feb 27 18:06:52 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Wed Feb 28 17:27:15 2018 +0100 @@ -10,8 +10,6 @@ package org.dive4elements.river.artifacts.states; import org.dive4elements.river.artifacts.model.WKms; -import org.dive4elements.river.model.Gauge; -import org.dive4elements.river.model.River; /** * Represents a waterlevel fetched with the {@link WaterlevelFetcher}. @@ -68,23 +66,6 @@ return this.showAllGauges; } - public Gauge findReferenceGauge(final River river) { - final double[] wstFromTo = findWstFromTo(); - return river.determineRefGauge(wstFromTo, true); - } - - private double[] findWstFromTo() { - - final double from = this.wkms.getKm(0); - final double to = this.wkms.getKm(this.wkms.size() - 1); - - final boolean waterIncreasing = this.wkms.guessWaterIncreasing(); - if (waterIncreasing) - return new double[] { to, from }; - - return new double[] { from, to }; - } - public int getYear() { return this.year; }