changeset 9176:1614cb14308f

Work on calculations for S-Info flood duration workflow
author mschaefer
date Mon, 25 Jun 2018 19:21:11 +0200
parents 34dc0163ad2d
children 51b316de0d64
files artifacts/doc/conf/artifacts/manualpoints.xml artifacts/doc/conf/artifacts/sinfo.xml artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml artifacts/doc/conf/generators/longitudinal-diagrams.xml artifacts/doc/conf/meta-data.xml artifacts/doc/conf/themes.xml artifacts/doc/conf/themes/default.xml artifacts/doc/conf/themes/second.xml artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractResultType.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcDetailResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDurationValuesFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueNameFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/WQBaseTableFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResults.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/RiversideRadioChoice.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties backend/src/main/java/org/dive4elements/river/model/Annotation.java backend/src/main/java/org/dive4elements/river/model/Attribute.java backend/src/main/java/org/dive4elements/river/model/MainValue.java backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties
diffstat 35 files changed, 1284 insertions(+), 355 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/manualpoints.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/artifacts/manualpoints.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -42,6 +42,7 @@
             <facet name="sinfo_flow_depth_development_peryear.manualpoints" />
             <facet name="sinfo_tkk" />
             <facet name="sinfo_collision.manualpoints" />
+            <facet name="sinfo_flood_duration.manualpoints" />
           </facets>
         </outputmode>
       </outputmodes>
--- a/artifacts/doc/conf/artifacts/sinfo.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/artifacts/sinfo.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -335,8 +335,6 @@
       <condition data="calculation_mode" value="sinfo_calc_flood_duration" operator="equal" />
     </transition>
 
-
-
     <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
       <from state="state.sinfo.wspl" />
       <to state="state.sinfo.wq" />
@@ -359,7 +357,29 @@
       <outputmodes>
         <outputmode name="sinfo_flood_duration" description="output.sinfo_flood_duration" mime-type="image/png" type="chart">
           <facets>
-
+            <facet name="sinfo_facet_flood_duration" description="flood duration of the heights of the infrastructures (points)"/>
+            <facet name="mainvalue.1.duration" description="flood duration of first W main value"/>
+            <facet name="mainvalue.2.duration" description="flood duration of second W main value"/>
+            <facet name="mainvalue.3.duration" description="flood duration of third W main value"/>
+          </facets>
+        </outputmode>
+        <outputmode name="sinfo_flood_height" description="output.sinfo_flood_height" mime-type="image/png" type="chart">
+          <facets>
+            <facet name="infrastructure.flood.height" description="flood heights of the infrastructures (points)"/>
+            <facet name="mainvalue.1.w" description="W of first main value"/>
+            <facet name="mainvalue.2.w" description="W of second main value"/>
+            <facet name="mainvalue.3.w" description="W of third main value"/>
+          </facets>
+        </outputmode>
+        <outputmode name="sinfo_flood_duration_curve" description="output.sinfo_duration_curve" mime-type="image/png" type="chart">
+          <facets>
+            <facet name="duration_curve.w" description="facet.duration_curve.w"/>
+            <facet name="duration_curve.q" description="facet.duration_curve.q"/>
+            <facet name="infrastructure.w" description="geodetic height of the infrastructure"/>
+            <facet name="mainvalues.w" description="W Main Values"/>
+            <facet name="mainvalues.q" description="Q Main Values at optional second axis"/>
+            <facet name="infrastructure.q" description="discharge of the infrastructure height"/>
+            <facet name="duration_curve.manualpoints" description="Manuelle Punkte"/>
           </facets>
         </outputmode>
 
--- a/artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -19,6 +19,7 @@
     <axis name="flowdepthDevelopmentAxis"/>
     <axis name="flowdepthDevelopmentPerYearAxis"/>
     <axis name="countAxis" include-zero="true"/>
+    <axis name="durationAxis" include-zero="true"/>
 
     <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km" inverted="org.dive4elements.river.exports.IsKmUpEvaluator()">
         <arg expr="artifact.river"/>
@@ -57,6 +58,7 @@
     <processor class="org.dive4elements.river.artifacts.sinfo.common.FlowDepthDevelopmentProcessor" axis="flowdepthDevelopmentAxis"/>
     <processor class="org.dive4elements.river.artifacts.sinfo.common.FlowDepthDevelopmentPerYearProcessor" axis="flowdepthDevelopmentPerYearAxis"/>
     <processor class="org.dive4elements.river.artifacts.sinfo.common.CollisionCalcProcessor" axis="countAxis"/>
+    <processor class="org.dive4elements.river.artifacts.sinfo.common.FloodDurationProcessor" axis="durationAxis"/>
     
     <processor class="org.dive4elements.river.artifacts.sinfo.common.PredefinedChannelWidthProcessor" axis="Width"/>
     <processor class="org.dive4elements.river.artifacts.sinfo.common.PredefinedChannelDepthProcessor" axis="flowdepthAxis"/>
--- a/artifacts/doc/conf/generators/longitudinal-diagrams.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/generators/longitudinal-diagrams.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -165,5 +165,18 @@
             <arg expr="artifact.river"/>
         </subtitle>
     </output-generator>
+
+    <output-generator
+        names="sinfo_flood_duration,sinfo_flood_duration_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="sinfo.chart.flood_duration.section.title" default="Überflutungsdauer Infrastruktur BWaStr (DEFAULT)"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="durationAxis"/>
+        <subtitle key="chart.w_differences.subtitle" default="-">
+            <arg expr="artifact.river"/>
+        </subtitle>
+    </output-generator>
     
 </longitudinal-diagrams>
\ No newline at end of file
--- a/artifacts/doc/conf/meta-data.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/meta-data.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -136,6 +136,15 @@
                   <dc:when test="$out = 'sinfo_collision'">
                     <dc:call-macro name="annotations" />
                   </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_duration'">
+                    <dc:call-macro name="annotations" />
+                  </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_height'">
+                    <dc:call-macro name="annotations" />
+                  </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_duration_curve'">
+                    <dc:call-macro name="annotations" />
+                  </dc:when>
                 </dc:choose>
               </dc:iterate>
             </dc:when>
@@ -303,6 +312,15 @@
                   <dc:when test="$out = 'sinfo_collision'">
                     <dc:call-macro name="longitudinal-section-prototype" />
                   </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_duration'">
+                    <dc:call-macro name="longitudinal-section-prototype" />
+                  </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_height'">
+                    <dc:call-macro name="longitudinal-section-prototype" />
+                  </dc:when>
+                  <dc:when test="$out = 'sinfo_flood_duration_curve'">
+                    <dc:call-macro name="longitudinal-section-prototype" />
+                  </dc:when>
                 </dc:choose>
               </dc:iterate>
             </dc:otherwise>
@@ -1653,6 +1671,7 @@
       <dc:call-macro name="sinfo_flow_depth_development" />
       <dc:call-macro name="sinfo_tkh" />
       <dc:call-macro name="sinfo_collision" />
+      <dc:call-macro name="sinfo_flood_duration" />
 
       <dc:comment> WINFO/DIFF/FIX </dc:comment>
       <dc:call-macro name="longitudinal" />
@@ -3414,6 +3433,24 @@
       </dc:filter>
     </dc:macro>
 
+    <dc:macro name="sinfo_flood_duration">
+      <dc:filter expr="$a_state = 'state.sinfo.flood_duration'">
+        <dc:if test="dc:has-result()">
+          <sinfo_flood_duration>
+            <dc:for-each>
+              <dc:element name="${facet_name}">
+                <dc:attribute name="factory" value="sinfo" />
+                <dc:attribute name="target_out" value="${out}" />
+                <dc:attribute name="description" value="${facet_description}" />
+                <dc:attribute name="ids" value="${facet_num}" />
+                <dc:attribute name="artifact-id" value="${a_gid}" />
+                <dc:attribute name="out" value="${out_name}" />
+              </dc:element>
+            </dc:for-each>
+          </sinfo_flood_duration>
+        </dc:if>
+      </dc:filter>
+    </dc:macro>
     <!-- channel size imported from CSV-files for S-INFO -->
     <dc:macro name="sinfo_predefined_channel">
       <dc:context>
--- a/artifacts/doc/conf/themes.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/themes.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -442,6 +442,7 @@
         <mapping from="sinfo_facet_bedheight_difference.filtered" to="SInfoBedHeightDifference" />
         
         <mapping from ="sinfo_facet_collision_calc_count" to="SInfoCollisionCount" />
+        <mapping from ="sinfo_facet_flood_duration" to="SInfoFloodDuration" />
         
         <mapping from="sinfo_facet_predefined_channel_width" to="SInfoPredefinedChannelWidth" />
         <mapping from="sinfo_facet_predefined_channel_depth" to="SInfoPredefinedChannelDepth" />
@@ -458,5 +459,6 @@
         <mapping from="sinfo_flow_depth_development_peryear.manualpoints" to="ManualPoints" />
         <mapping from="sinfo_tkk" to="ManualPoints" />
         <mapping from="sinfo_collision.manualpoints" to="ManualPoints" />
+        <mapping from="sinfo_flood_duration.manualpoints" to="ManualPoints" />
     </mappings>
 </themes>
\ No newline at end of file
--- a/artifacts/doc/conf/themes/default.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/themes/default.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -3063,4 +3063,13 @@
             <field name="pointcolor" type="Color" display="Punktfarbe" default="48, 96, 255" />
         </fields>
     </theme>
+    <theme name="SInfoFloodDuration">
+        <inherits>
+            <inherit from="LongitudinalSectionPoints" />
+        </inherits>
+        <fields>
+            <field name="pointsize" type="int" display="Punktdicke" default="3" />
+            <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
+        </fields>
+    </theme>
 </themegroup>
\ No newline at end of file
--- a/artifacts/doc/conf/themes/second.xml	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/doc/conf/themes/second.xml	Mon Jun 25 19:21:11 2018 +0200
@@ -3050,4 +3050,13 @@
             <field name="pointcolor" type="Color" display="Punktfarbe" default="48, 96, 192" />
         </fields>
     </theme>
+    <theme name="SInfoFloodDuration">
+        <inherits>
+            <inherit from="LongitudinalSectionPoints" />
+        </inherits>
+        <fields>
+            <field name="pointsize" type="int" display="Punktdicke" default="3" />
+            <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 192, 40" />
+        </fields>
+    </theme>
 </themegroup>
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractResultType.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractResultType.java	Mon Jun 25 19:21:11 2018 +0200
@@ -86,14 +86,14 @@
         return this.formatters.get(locale);
     }
 
+    protected abstract NumberFormat createFormatter(CallContext context);
+
     protected final String exportDateValue(final CallContext context, final Date value) {
         final Locale locale = Resources.getLocale(context.getMeta());
         final DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
         return df.format(value);
     }
 
-    protected abstract NumberFormat createFormatter(CallContext context);
-
     @Override
     public final String getCsvHeader() {
         return this.csvHeader;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcDetailResult.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcDetailResult.java	Mon Jun 25 19:21:11 2018 +0200
@@ -45,16 +45,16 @@
 
         final int colSize = 6;
         exportContextCSV.writeTitleForTabs("sinfo.export.csv.title.collision.detail", 6); // Voraussetzung für Tabs ist, dass der Titel vor den Headern
-                                                                                          // geschrieben wird.
+        // geschrieben wird.
         // Das ist etwas doof.
 
         final Collection<String> header = new ArrayList<>(colSize);
 
         header.add(exportContextCSV.formatCsvHeader(GeneralResultType.station));
         header.add(exportContextCSV.formatCsvHeader(GeneralResultType.date));
-        header.add(exportContextCSV.formatCsvHeader(SInfoResultType.collisionGaugeW));
+        header.add(exportContextCSV.msgUnitCSV(SInfoResultType.collisionGaugeW, SInfoResultType.collisionGaugeW.getUnit()));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.gaugeLabel));
-        header.add(exportContextCSV.msgUnitCSV(SInfoResultType.discharge));
+        header.add(exportContextCSV.msgUnitCSV(SInfoResultType.discharge, SInfoResultType.discharge.getUnit()));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.dischargeZone));
 
         exportContextCSV.writeCSVLine(header.toArray(new String[colSize]));
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java	Mon Jun 25 19:21:11 2018 +0200
@@ -23,12 +23,15 @@
 import org.dive4elements.river.artifacts.model.DateRange;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.sinfo.common.GaugeDischargeValuesFinder;
+import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueNameFinder;
 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 import org.dive4elements.river.backend.utils.DateUtil;
 import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
 import org.dive4elements.river.model.River;
 import org.dive4elements.river.model.sinfo.CollisionAggregateValue;
 import org.dive4elements.river.model.sinfo.CollisionValue;
@@ -77,10 +80,10 @@
         // create q-for-w-finders for all gauges of the calculation km range
         final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
         final Map<Gauge, GaugeDischargeValuesFinder> qFinders = new HashMap<>();
-        final Map<Gauge, GaugeDischargeZoneFinder> zoneFinders = new HashMap<>();
+        final Map<Gauge, GaugeMainValueNameFinder> zoneFinders = new HashMap<>();
         for (final Gauge gauge : river.determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) {
             qFinders.put(gauge, GaugeDischargeValuesFinder.loadValues(gauge, problems));
-            zoneFinders.put(gauge, GaugeDischargeZoneFinder.loadValues(gauge, problems));
+            zoneFinders.put(gauge, GaugeMainValueNameFinder.loadValues(MainValueTypeKey.Q, gauge, problems));
         }
         final Collection<ResultRow> detailsRows = new ArrayList<>();
 
@@ -118,7 +121,7 @@
      */
     private void calculateDetails(final Collection<ResultRow> rows, final RiverInfoProvider riverInfo, final double fromKm, final double toKm,
             final int fromYear, final int toYear, final Map<Gauge, GaugeDischargeValuesFinder> qFinders,
-            final Map<Gauge, GaugeDischargeZoneFinder> zoneFinders) {
+            final Map<Gauge, GaugeMainValueNameFinder> zoneFinders) {
         for (final CollisionValue collision : CollisionValue.getValues(riverInfo.getRiver(), fromKm, toKm, DateUtil.getStartDateFromYear(fromYear),
                 DateUtil.getEndDateFromYear(toYear))) {
             final Gauge gauge = riverInfo.getGauge(collision.getStation(), true);
@@ -129,7 +132,7 @@
                     .putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW())
                     .putValue(SInfoResultType.gaugeLabel, collision.getGaugeName())
                     .putValue(SInfoResultType.discharge, qOut)
-                    .putValue(SInfoResultType.dischargeZone, zoneFinders.get(gauge).getDischargeZone(q)));
+                    .putValue(SInfoResultType.dischargeZone, zoneFinders.get(gauge).getZoneName(q)));
         }
     }
 }
\ 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/FloodDurationProcessor.java	Mon Jun 25 19:21:11 2018 +0200
@@ -0,0 +1,73 @@
+/** 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.HashSet;
+import java.util.Set;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
+
+/**
+ * Processor to generate the facet and data series of infrastructure flood durations
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class FloodDurationProcessor extends AbstractSInfoLineProcessor<AbstractCalculationResult> {
+
+    public static final String FACET_FLOOD_DURATION = "sinfo_facet_flood_duration";
+
+    public static final String FACET_FLOOD_DURATION_DESCRIPTION = "sinfo_facet_flood_duration.description";
+
+    public static final String FACET_MAIN_VALUE_1_DURATION = "mainvalue.1.duration";
+
+    public static final String FACET_MAIN_VALUE_DURATION_DESCRIPTION = "mainvalue.duration.description";
+
+    private static final String I18N_AXIS_LABEL = "sinfo.chart.flood_duration.section.yaxis.label";
+
+    private static final Set<String> HANDLED_FACET_TYPES = new HashSet<>();
+
+    static {
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_DURATION);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_1_DURATION);
+    }
+
+    public FloodDurationProcessor() {
+        super(I18N_AXIS_LABEL, HANDLED_FACET_TYPES);
+    }
+
+
+    @Override
+    protected double[][] doGetPoints(final AbstractCalculationResult data, final String facetName) {
+        if (FACET_FLOOD_DURATION.contentEquals(facetName))
+            return data.getStationPoints(SInfoResultType.floodDuration);
+
+        if (FACET_MAIN_VALUE_1_DURATION.contentEquals(facetName))
+            return data.getStationPoints(SInfoResultType.mainValue1Duration);
+
+        final String error = String.format("Unknown facet name: %s", facetName);
+        throw new UnsupportedOperationException(error);
+    }
+
+    public static Facet createFloodDurationFacet(final CallContext context, final String hash, final String id,
+            final AbstractCalculationResult result, final int index) {
+        return AbstractSInfoLineProcessor.createFacet(context, hash, id, result, index, I18N_AXIS_LABEL,
+                FACET_MAIN_VALUE_1_DURATION, FACET_MAIN_VALUE_DURATION_DESCRIPTION);
+    }
+
+    public static Facet createMainValueDurationFacet(final CallContext context, final String hash, final String id,
+            final AbstractCalculationResult result, final int index) {
+        return AbstractSInfoLineProcessor.createFacet(context, hash, id, result, index, I18N_AXIS_LABEL,
+                FACET_FLOOD_DURATION, FACET_FLOOD_DURATION_DESCRIPTION);
+    }
+}
\ 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/GaugeDischargeValuesFinder.java	Mon Jun 25 19:21:11 2018 +0200
@@ -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.common;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.DischargeTableValue;
+import org.dive4elements.river.model.Gauge;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Loading and search/interpolation of a gauge's discharge table (.at)
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class GaugeDischargeValuesFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(GaugeDischargeValuesFinder.class);
+
+    private final Gauge gauge;
+
+    private Calculation problems;
+
+    private final UnivariateRealFunction wInterpolator;
+
+    private final DoubleRange wRange;
+
+
+    /***** CONSTRUCTORS *****/
+
+    private GaugeDischargeValuesFinder(final Gauge gauge, final Calculation problems, final DischargeTable dischargeTable) {
+        this.gauge = gauge;
+        this.problems = problems;
+        final TDoubleArrayList ws = new TDoubleArrayList();
+        final TDoubleArrayList qs = new TDoubleArrayList();
+        for (final DischargeTableValue v : DischargeTable.getValuesSortedByW(dischargeTable)) {
+            ws.add(v.getW().doubleValue());
+            qs.add(v.getQ().doubleValue());
+        }
+        if (ws.size() >= 2) {
+            this.wInterpolator = new LinearInterpolator().interpolate(ws.toNativeArray(), qs.toNativeArray());
+            this.wRange = new DoubleRange(ws.get(0), ws.get(ws.size() - 1));
+        }
+        else {
+            this.wInterpolator = null;
+            this.wRange = null;
+        }
+        if ((this.wInterpolator == null) && (this.problems != null)) {
+            this.problems.addProblem("gauge_discharge_table.missing", gauge.getName());
+            // Report only once
+            this.problems = null;
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the main discharge table of a gauge (GAUGE.at)
+     *
+     * @return The discharge table values finder of the gauge, or null
+     */
+    public static GaugeDischargeValuesFinder loadValues(final Gauge gauge, final Calculation problems) {
+        final DischargeTable table = DischargeTable.getGaugeMainDischargeTable(gauge);
+        if ((table == null) || (table.getDischargeTableValues().size() == 0)) {
+            problems.addProblem("gauge_discharge_table.missing", gauge.getName());
+            return null;
+        }
+        else
+            return new GaugeDischargeValuesFinder(gauge, problems, table);
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.wInterpolator != null);
+    }
+
+    /**
+     * Discharge for a W
+     *
+     * @param w
+     *            W in cm above gauge datum
+     * @return Q, or NegInf for w less than all, or PosInf for w greater then all, or NaN in case of exception
+     */
+    public double getDischarge(final double w) {
+        try {
+            if (this.wInterpolator == null)
+                return Double.NaN;
+            else if (w < this.wRange.getMinimumDouble())
+                return Double.NEGATIVE_INFINITY;
+            else if (w > this.wRange.getMaximumDouble())
+                return Double.POSITIVE_INFINITY;
+            else
+                return this.wInterpolator.value(w);
+        }
+        catch (@SuppressWarnings("unused") final FunctionEvaluationException e) {
+            // ignore exception because this can/will happen regularly
+            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/common/GaugeDurationValuesFinder.java	Mon Jun 25 19:21:11 2018 +0200
@@ -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.common;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Loading and search/interpolate the duration main values of a gauge
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class GaugeDurationValuesFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(GaugeDurationValuesFinder.class);
+
+    private final Gauge gauge;
+
+    private Calculation problems;
+
+    private UnivariateRealFunction qInterpolator;
+
+    private DoubleRange qRange;
+
+    private UnivariateRealFunction durInterpolator;
+
+    private DoubleRange durRange;
+
+    private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht
+
+
+    /***** CONSTRUCTORS *****/
+
+    private GaugeDurationValuesFinder(final Gauge gauge, final Calculation problems) {
+        this.gauge = gauge;
+        this.problems = problems;
+        final TDoubleArrayList qs = new TDoubleArrayList();
+        final TDoubleArrayList durs = new TDoubleArrayList();
+        for (final MainValue v : MainValue.getValuesOfGaugeAndType(gauge, MainValueTypeKey.DURATION)) {
+            qs.add(v.getValue().doubleValue());
+            durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue());
+        }
+        try {
+            this.qInterpolator = new LinearInterpolator().interpolate(qs.toNativeArray(), durs.toNativeArray());
+            this.qRange = new DoubleRange(qs.get(0), qs.get(qs.size() - 1));
+        }
+        catch (final Exception e) {
+            this.qInterpolator = null;
+            this.qRange = null;
+        }
+        qs.clear();
+        durs.clear();
+        for (final MainValue v : MainValue.getDurationDischargesOfGauge(gauge)) {
+            durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue());
+            qs.add(v.getValue().doubleValue());
+        }
+        try {
+            this.durInterpolator = new LinearInterpolator().interpolate(durs.toNativeArray(), qs.toNativeArray());
+            this.durRange = new DoubleRange(durs.get(0), durs.get(durs.size() - 1));
+        }
+        catch (final Exception e) {
+            this.durInterpolator = null;
+            this.durRange = null;
+        }
+        if (((this.qInterpolator == null) || (this.durInterpolator == null)) && (this.problems != null)) {
+            this.problems.addProblem("gauge_duration.missing", gauge.getName());
+            // Report only once
+            this.problems = null;
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the discharge-duration table of a gauge (GAUGE.glt)
+     *
+     * @return The main values finder of a a gauge, or null
+     */
+    public static GaugeDurationValuesFinder loadValues(final Gauge gauge, final Calculation problems) {
+        return new GaugeDurationValuesFinder(gauge, problems);
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.qInterpolator != null);
+    }
+
+    /**
+     * Discharge for a duration
+     *
+     * @return Q, or NegInf for duration less than all, or PosInf for duration greater then all, or NaN in case of exception
+     */
+    public double getDischarge(final int duration) {
+        try {
+            if (this.durInterpolator == null)
+                return Double.NaN;
+            else if (duration < this.durRange.getMinimumDouble())
+                return Double.NEGATIVE_INFINITY;
+            else if (duration > this.durRange.getMaximumDouble())
+                return Double.POSITIVE_INFINITY;
+            else
+                return this.durInterpolator.value(duration);
+        }
+        catch (@SuppressWarnings("unused") final FunctionEvaluationException e) {
+            // ignore exception because this can/will happen regularly
+            return Double.NaN;
+        }
+    }
+
+    /**
+     * Duration for a discharge
+     *
+     * @return duration, or 0 for Q less than all, or 365 for duration greater then all, or -1 in case of exception
+     */
+    public double getDuration(final double q) {
+        try {
+            if (this.qInterpolator == null)
+                return -1;
+            else if (q < this.qRange.getMinimumDouble())
+                return 0;
+            else if (q > this.qRange.getMaximumDouble())
+                return 365;
+            else
+                return this.qInterpolator.value(q);
+        }
+        catch (@SuppressWarnings("unused") final FunctionEvaluationException e) {
+            // ignore exception because this can/will happen regularly
+            return -1;
+        }
+    }
+}
\ 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/GaugeMainValueNameFinder.java	Mon Jun 25 19:21:11 2018 +0200
@@ -0,0 +1,115 @@
+/** 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.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
+
+/**
+ * Loading and search the main values of a gauge to find and build a name
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class GaugeMainValueNameFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(GaugeMainValueNameFinder.class);
+
+    private final Gauge gauge;
+
+    private Calculation problems;
+
+    private final NavigableMap<Double, MainValue> mainValues;
+
+    private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht
+
+
+    /***** CONSTRUCTORS *****/
+
+    private GaugeMainValueNameFinder(final MainValueTypeKey type, final Gauge gauge, final Calculation problems) {
+        this.gauge = gauge;
+        this.problems = problems;
+        this.mainValues = new TreeMap<>();
+        for (final MainValue mainValue : MainValue.getValuesOfGaugeAndType(gauge, type))
+            this.mainValues.put(Double.valueOf(mainValue.getValue().doubleValue()), mainValue);
+        if (this.mainValues.isEmpty() && (this.problems != null)) {
+            this.problems.addProblem("gauge_main_values.missing", gauge.getName());
+            // Report only once
+            this.problems = null;
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the main values table of a type and a gauge (GAUGE.glt)
+     *
+     * @return The main values finder of a type and a gauge, or null
+     */
+    public static GaugeMainValueNameFinder loadValues(final MainValueTypeKey type, final Gauge gauge, final Calculation problems) {
+        return new GaugeMainValueNameFinder(type, gauge, problems);
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.mainValues != null);
+    }
+
+    /**
+     * Main value zone for a value.
+     */
+    public String getZoneName(final double value) {
+        if (!this.isValid())
+            return "";
+        if (Double.isNaN(value))
+            return "";
+
+        // Exact match
+        if (this.mainValues.containsKey(Double.valueOf(value)))
+            return this.mainValues.get(Double.valueOf(value)).getMainValue().getName();
+
+        // Clearly below or just (max. 10%) below lowest named value
+        final Entry<Double, MainValue> lowerZone = this.mainValues.floorEntry(Double.valueOf(value));
+        if (lowerZone == null) {
+            if (value >= this.mainValues.firstKey().doubleValue() * 0.9)
+                return this.approxPrefix + this.mainValues.firstEntry().getValue().getMainValue().getName();
+            else
+                return "<" + this.mainValues.firstEntry().getValue().getMainValue().getName();
+        }
+
+        // Clearly above or just (max. 10%) above highest named value
+        final Entry<Double, MainValue> higherZone = this.mainValues.ceilingEntry(Double.valueOf(value));
+        if (higherZone == null) {
+            if (value <= this.mainValues.lastKey().doubleValue() * 1.1)
+                return this.approxPrefix + this.mainValues.lastEntry().getValue().getMainValue().getName();
+            else
+                return ">" + this.mainValues.lastEntry().getValue().getMainValue().getName();
+        }
+
+        // Near (10%) one of the borders of a zone interval, or clearly within a zone
+        if (value <= lowerZone.getKey().doubleValue() * 1.1)
+            return this.approxPrefix + lowerZone.getValue().getMainValue().getName();
+        else if (value >= higherZone.getKey().doubleValue() * 0.9)
+            return this.approxPrefix + higherZone.getValue().getMainValue().getName();
+        else
+            return lowerZone.getValue().getMainValue().getName() + "-" + higherZone.getValue().getMainValue().getName();
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Mon Jun 25 19:21:11 2018 +0200
@@ -55,20 +55,14 @@
 
     };
 
-    public static final SInfoResultType inundationdurationq = new SInfoResultType(null, "sinfo.export.flood_duration.csv.header.inundation_duration_q",
-            "sinfo.export.flood_duration.pdf.header.inundation_duration_q") {
+    public static final SInfoResultType floodDischarge = new SInfoResultType(I18NStrings.UNIT_CUBIC_M, "sinfo.export.flood_duration.csv.header.discharge",
+            "sinfo.export.flood_duration.pdf.header.discharge") {
         private static final long serialVersionUID = 1L;
 
         @Override
         public String exportValue(final CallContext context, final Object value) {
             final double doubleValue = asDouble(value);
-            return exportDoubleValue(context, doubleValue); // integer
-            // als
-            // double?
-            // finde
-            // gerade
-            // kein
-            // int-beispiel
+            return exportDoubleValue(context, doubleValue);
         }
 
         @Override
@@ -77,20 +71,30 @@
         }
     };
 
-    public static final SInfoResultType inundationduration = new SInfoResultType(null, "sinfo.export.flood_duration.csv.header.inundation_duration",
-            "sinfo.export.flood_duration.pdf.header.inundation_duration") {
+    public static final SInfoResultType floodDuration = new SInfoResultType(null, "sinfo.export.flood_duration.csv.header.duration",
+            "sinfo.export.flood_duration.pdf.header.duration") {
         private static final long serialVersionUID = 1L;
 
         @Override
         public String exportValue(final CallContext context, final Object value) {
             final double doubleValue = asDouble(value);
-            return exportDoubleValue(context, doubleValue); // integer
-            // als
-            // double?
-            // finde
-            // gerade
-            // kein
-            // int-beispiel
+            return exportDoubleValue(context, doubleValue);
+        }
+
+        @Override
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getIntegerFormatter(context);
+        }
+    };
+
+    public static final SInfoResultType mainValue1Duration = new SInfoResultType(null, "sinfo.export.main_value_1_duration.csv.header.duration",
+            "sinfo.export.main_value_1_duration.pdf.header.duration") {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String exportValue(final CallContext context, final Object value) {
+            final double doubleValue = asDouble(value);
+            return exportDoubleValue(context, doubleValue);
         }
 
         @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/WQBaseTableFinder.java	Mon Jun 25 19:21:11 2018 +0200
@@ -0,0 +1,185 @@
+/** 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.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.River;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Loading and search/interpolation of a W/Q base table of a river
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class WQBaseTableFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(WQTableFinder.class);
+
+    private final River river;
+
+    private Calculation problems;
+
+    private final List<String> columnNames;
+
+    private final NavigableMap<Double, PolynomialSplineFunction> kmWs;
+
+    private final NavigableMap<Double, PolynomialSplineFunction> kmQs;
+
+    private static final String SQLCOLUMNS = "SELECT wc.position AS colindex, wc.name AS qzone"
+            + " FROM wsts w"
+            + "  INNER JOIN wst_columns wc ON w.id=wc.wst_id"
+            + " WHERE w.river_id = :river_id"
+            + "  AND w.kind = 0"
+            + " ORDER BY wc.position ASC";
+
+    private static final String SQLMAIN = "SELECT wcv.position AS station, wc.position AS colindex, wcv.w, wqr.q"
+            + " FROM wsts w"
+            + "  INNER JOIN wst_columns wc ON w.id=wc.wst_id"
+            + "  INNER JOIN wst_column_values wcv ON wc.id=wcv.wst_column_id"
+            + "  INNER JOIN wst_column_q_ranges wcqr ON wc.id=wcqr.wst_column_id"
+            + "  INNER JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id=wqr.id"
+            + "  INNER JOIN ranges r ON wqr.range_id=r.id AND wcv.position BETWEEN r.a AND r.b"
+            + " WHERE w.river_id = :river_id"
+            + "  AND w.kind = 0"
+            + "  AND r.river_id = :river_id"
+            + "  AND wcv.position BETWEEN :kmfrom - 1 AND :kmto + 1" // some tolerance for start and end of list
+            + " ORDER BY wcv.position ASC, "
+            + "  wc.position ASC";
+
+
+    /***** CONSTRUCTORS *****/
+
+    private WQBaseTableFinder(final River river, final Calculation problems, final List<Object[]> colnames, final List<Object[]> rows) {
+        this.river = river;
+        this.problems = problems;
+        this.columnNames = new ArrayList<>();
+        for (final Object[] colname : colnames)
+            this.columnNames.add(colname[1].toString());
+        this.kmWs = new TreeMap<>();
+        this.kmQs = new TreeMap<>();
+        final TDoubleArrayList ws = new TDoubleArrayList();
+        final TDoubleArrayList qs = new TDoubleArrayList();
+        double km = Double.NaN;
+        for (int i = 0; i <= rows.size() - 1; i++) {
+            if (ws.isEmpty() || ((double) rows.get(i)[0] <= km + 0.0001)) {
+                if (ws.isEmpty())
+                    km = (double) rows.get(i)[0];
+                ws.add((double) rows.get(i)[2]);
+                qs.add((double) rows.get(i)[3]);
+            }
+            if ((i == rows.size() - 1) || ((double) rows.get(i)[0] > km + 0.0001)) {
+                try {
+                    this.kmWs.put(km, new LinearInterpolator().interpolate(ws.toNativeArray(), qs.toNativeArray()));
+                    this.kmQs.put(km, new LinearInterpolator().interpolate(qs.toNativeArray(), ws.toNativeArray()));
+                }
+                catch (final Exception e) {
+                    if (this.problems != null) {
+                        this.problems.addProblem(km, "wq_base_data.missing");
+                        // Report only once
+                        this.problems = null;
+                    }
+                }
+                ws.clear();
+                qs.clear();
+                km = (double) rows.get(i)[0];
+                ws.add((double) rows.get(i)[2]);
+                qs.add((double) rows.get(i)[3]);
+            }
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the W/Q tables of a km range of a river
+     *
+     * @return The W/Q table finder of the river, or null
+     */
+    public static WQBaseTableFinder loadValues(final River river, final double fromKm, final double toKm, final Calculation problems) {
+        final Session session = SessionHolder.HOLDER.get();
+        final SQLQuery colQuery = session.createSQLQuery(SQLCOLUMNS)
+                .addScalar("colindex", StandardBasicTypes.INTEGER)
+                .addScalar("qzone", StandardBasicTypes.STRING);
+        colQuery.setParameter("river_id", river.getId());
+        final List<Object[]> colnames = colQuery.list();
+        if ((colnames == null) || colnames.isEmpty()) {
+            problems.addProblem("wq_table.missing");
+            return null;
+        }
+        final SQLQuery wqQuery = session.createSQLQuery(SQLMAIN)
+                .addScalar("station", StandardBasicTypes.DOUBLE)
+                .addScalar("colindex", StandardBasicTypes.INTEGER)
+                .addScalar("w", StandardBasicTypes.DOUBLE)
+                .addScalar("q", StandardBasicTypes.DOUBLE);
+        wqQuery.setParameter("river_id", river.getId());
+        wqQuery.setParameter("kmfrom", fromKm);
+        wqQuery.setParameter("kmto", toKm);
+        final List<Object[]> rows = wqQuery.list();
+        if ((rows != null) && !rows.isEmpty())
+            return new WQBaseTableFinder(river, problems, colnames, rows);
+        else {
+            problems.addProblem("wq_table.missing");
+            return null;
+        }
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.kmWs != null);
+    }
+
+    /**
+     * Discharge for a W
+     *
+     * @param station
+     *            station to find or interpolate
+     * @param w
+     *            W in m+NN or m+NHN
+     * @return Q, or NegInf for w less than all, or PosInf for w greater then all, or NaN in case of exception
+     */
+    public double getDischarge(final double station, final double w) {
+        if (this.kmWs == null)
+            return Double.NaN;
+        final Entry<Double, PolynomialSplineFunction> lowerEntry = this.kmWs.floorEntry(Double.valueOf(station));
+        final Entry<Double, PolynomialSplineFunction> upperEntry = this.kmWs.ceilingEntry(Double.valueOf(station));
+        if ((lowerEntry == null) || (upperEntry == null))
+            return Double.NaN;
+        try {
+            final double lowerQ = lowerEntry.getValue().value(w);
+            final double upperQ = upperEntry.getValue().value(w);
+            return Linear.linear(station, lowerEntry.getKey().doubleValue(), upperEntry.getKey().doubleValue(), lowerQ, upperQ);
+        }
+        catch (@SuppressWarnings("unused") final FunctionEvaluationException e) {
+            // ignore exception because this can/will happen regularly
+            return Double.NaN;
+        }
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java	Mon Jun 25 19:21:11 2018 +0200
@@ -12,12 +12,11 @@
 
 import org.apache.log4j.Logger;
 import org.dive4elements.river.artifacts.access.RangeAccess;
-import org.dive4elements.river.artifacts.common.EpochYearAccessHelper;
 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.
+ * Access to the flood duration 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.
@@ -28,14 +27,11 @@
 
     private static Logger log = Logger.getLogger(FloodDurationAccess.class);
 
-    private final EpochYearAccessHelper helper;
-
     public FloodDurationAccess(final SINFOArtifact artifact) {
         super(artifact);
 
         /* assert calculation mode */
         final SinfoCalcMode calculationMode = artifact.getCalculationMode();
-        this.helper = new EpochYearAccessHelper(artifact);
         assert (calculationMode == SinfoCalcMode.sinfo_calc_flood_duration);
     }
 
@@ -47,5 +43,4 @@
     public String getRiverside() {
         return super.getString("riverside");
     }
-
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Mon Jun 25 19:21:11 2018 +0200
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+/** 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
@@ -11,19 +11,25 @@
 
 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.CalculationResult;
 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.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
 import org.dive4elements.river.artifacts.sinfo.tkhstate.WinfoArtifactWrapper;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
-import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.sinfo.Infrastructure;
 
-class FloodDurationCalculation {
-
-    // private static Logger log = Logger.getLogger(FloodDurationCalculation.class);
+/**
+ * Calculation of the flood durations of the infrastructures of the km range of a river
+ *
+ * @author Matthias Schäfer
+ */
+final class FloodDurationCalculation {
 
     private final CallContext context;
 
@@ -38,44 +44,39 @@
         /* access input data */
         final FloodDurationAccess access = new FloodDurationAccess(sinfo);
         final River river = access.getRiver();
-
+        final RiverInfo riverInfo = new RiverInfo(river);
         final DoubleRange calcRange = access.getRange();
 
-        /* calculate results for each diff pair */
-        final Calculation problems = new Calculation();
-
         final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
-
         final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
+        final String riverside = Resources.getMsg(this.context.getMeta(), access.getRiverside());
+        final Infrastructure infrasSeries = Infrastructure.getSeries(river);
+        final String infrasType = (infrasSeries != null) ? infrasSeries.getType().getName() : "?";
 
-        // TODO: use enum for riverside
-        final String riverside = access.getRiverside();
-        // more
-        // TODO: mis- ups.. re-use WINFO Artifact as in TkhState
-        final WinfoArtifactWrapper winfo = new WinfoArtifactWrapper(sinfo);
-        // winfo.computeWaterlevelData();
-        final FloodDurationCalculationResults results = calculateResult(calcModeLabel, riverside, calcRange, infoProvider, user, problems);
+        final Calculation problems = new Calculation();
+
+        final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange);
+
+        final FloodDurationCalculationResult result = calculateResult(calcModeLabel, infrasType, riverside, calcRange, infoProvider,
+                RiversideChoiceKey.fromKey(access.getRiverside()), user, problems);
+        results.addResult(result, problems);
+
+        // Calculate the selected main values, if any
+        /* misuse winfo-artifact to calculate waterlevels in the same way */
+        final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
 
         return new CalculationResult(results, problems);
     }
 
     /**
-     * Calculates FAKE Flood Duration
-     *
-     * @param riverside
-     * @param calcModeLabel
-     * @param user
-     *
-     * @param infoProvider
+     * Calculates the flood durations of the infrastructures of a km range of a river
      */
-    private FloodDurationCalculationResults calculateResult(final String calcModeLabel, final String riverside, final DoubleRange calcRange,
-            final RiverInfoProvider riverInfoProvider, final String user, final Calculation problems) {
+    private FloodDurationCalculationResult calculateResult(final String calcModeLabel, final String infrastructureType, final String riverside,
+            final DoubleRange calcRange, final RiverInfoProvider riverInfoProvider, final RiversideChoiceKey riversideKey, final String user,
+            final Calculation problems) {
 
         final FloodDurationCalculator calculator = new FloodDurationCalculator(this.context, riverInfoProvider);
-        final String wspLabel = "WSP-Name";// wstKms.getName();
-        final int wspYear = 9999; // waterlevel.getYear();
-        final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, riverInfoProvider.getReferenceGauge());
-        final String label = String.format("%s - %s", wspLabel, " soundingLabel");
-        return calculator.execute(problems, label, wstInfo, calcModeLabel, calcRange, riverside, user);
+        final String label = infrastructureType + ", " + riverside;
+        return calculator.execute(problems, label, calcModeLabel, calcRange, riversideKey, user).getResult();
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Mon Jun 25 19:21:11 2018 +0200
@@ -9,191 +9,115 @@
  */
 package org.dive4elements.river.artifacts.sinfo.flood_duration;
 
-import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 
+import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
 import org.dive4elements.river.artifacts.common.ExportContextCSV;
 import org.dive4elements.river.artifacts.common.GeneralResultType;
 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 import org.dive4elements.river.artifacts.common.ResultRow;
-import org.dive4elements.river.artifacts.sinfo.common.AbstractTkhCalculationResult;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
-import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
 
 /**
- * Contains the results of a {@link FloodDurationCalculation}.
+ * Contains the result of a {@link FloodDurationCalculation}.
  *
  * @author Gernot Belger
  */
-final class FloodDurationCalculationResult extends AbstractTkhCalculationResult<FloodDurationCalculationResults> {
+final class FloodDurationCalculationResult extends AbstractCalculationExportableResult<FloodDurationCalculationResults> {
 
     private static final long serialVersionUID = 1L;
-    private final int waterlevelCount; // TODO: ggf 2 verschiedene Result-Klassen?
-    // private final WstInfo wstInfo;
-    private final int maxWaterlevelPdf = 3;
 
-    private enum ExportMode {
-        pdf, csv
+    private static final String JASPER_FILE = "/jasper/templates/sinfo.floodduration.jrxml";
+
+
+    public FloodDurationCalculationResult(final String label, final Collection<ResultRow> rows) {
+        super(label, rows);
     }
 
-    public FloodDurationCalculationResult(final String label, final WstInfo wstInfo, final Collection<ResultRow> rows, final boolean hasTkh,
-            final int waterlevelCount) {
-        super(label, wstInfo, hasTkh, rows);
-        this.waterlevelCount = waterlevelCount;
-        // this.wstInfo = wstInfo;
+
+    @Override
+    protected void writeCSVResultMetadata(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results) {
+        // TODO Metadaten der Wasserspiegellage(n) falls gewählt
+        // exportContextCSV.writeCSVWaterlevelMetadata(this.wstInfo);
+        // exportContextCSV.writeBlankLine();
+        // writer.writeNext(new String[] { "" }); // break line
+    }
+
+    @Override
+    protected String getJasperFile() {
+        // TODO Variante mit Wasserspiegellage(n)
+        return JASPER_FILE;
+    }
+
+    protected String[] formatRow(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results, final ResultRow row) {
+
+        final Collection<String> lines = new ArrayList<>(10);
+
+        lines.add(exportContextCSV.formatRowValue(row, GeneralResultType.station));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.riverside));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.floodDuration));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.floodDischarge));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructureHeight));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructuretype));
+
+        // TODO Wasserspiegellage(n) und Dauerzahlen falls gewählt
+
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.gaugeLabel));
+        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.location));
+
+        return lines.toArray(new String[lines.size()]);
     }
 
     @Override
     public void writeCSVHeader(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results, final RiverInfo river) {
 
-        final Collection<String> header = new ArrayList<>(99);
+        final Collection<String> header = new ArrayList<>(20);
 
         header.add(exportContextCSV.formatCsvHeader(GeneralResultType.station));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.riverside));
-        header.add(exportContextCSV.formatCsvHeader(SInfoResultType.inundationduration));
-        header.add(exportContextCSV.formatCsvHeader(SInfoResultType.inundationdurationq));
-        header.add(exportContextCSV.formatCsvHeader(SInfoResultType.infrastructureHeight));
+        header.add(exportContextCSV.formatCsvHeader(SInfoResultType.floodDuration));
+        header.add(exportContextCSV.msgUnitCSV(SInfoResultType.floodDischarge, SInfoResultType.floodDischarge.getUnit()));
+        header.add(exportContextCSV.msgUnitCSV(SInfoResultType.infrastructureHeight, SInfoResultType.infrastructureHeight.getUnit()));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.infrastructuretype));
 
-        // add dynamic headers
-        final int waterlevelCount = // results.
-                getWaterlevelCount();
-        for (int i = 0; i < waterlevelCount; i++) {
-            final int naturalIndex = i + 1;
-            final String appendIndex = new StringBuilder().append("_").append(naturalIndex).toString();
-            final Object[] args = new Object[] { appendIndex };
-            // new StringBuilder().append('\u2081').toString(); // schlechter UTF-8-Support für subscript ints
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderW(), new Object[] { appendIndex, "results.getRiver().getWstUnit()" }));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderFloodDurPerYear(), args));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderQ(), args));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderBezeichn(), args));
-        }
+        // TODO Je vier Spalten der bis zu drei Wasserspiegellagen
 
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.gaugeLabel));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.location));
 
         exportContextCSV.writeCSVLine(header.toArray(new String[header.size()]));
-    }
-
-    @Override
-    protected void writeCSVResultMetadata(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results) {
-
-        exportContextCSV.writeCSVWaterlevelMetadata(super.getWst()); // -> export in super? -> TODO
-        exportContextCSV.writeBlankLine();
-        // writer.writeNext(new String[] { "" }); // break line
 
     }
 
     @Override
     protected String[] formatCSVRow(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results, final ResultRow row) {
-        // TODO Auto-generated method stub
-        return formatRow(exportContextCSV, results, row, ExportMode.csv);
-    }
-
-    @Override
-    protected String[] formatPDFRow(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results, final ResultRow row) {
 
-        return formatRow(exportContextCSV, results, row, ExportMode.pdf);
-    }
-
-    @Override
-    protected String getJasperFile() {
-        if (this.waterlevelCount <= 1)
-            return "/jasper/templates/sinfo.floodduration.jrxml"; // TODO 2 different result-classes?
-        else
-            return "/jasper/templates/sinfo.floodduration2.jrxml";
+        return this.formatRow(exportContextCSV, results, row);
     }
 
     @Override
-    protected void addJRTableHeader(final ExportContextCSV exportContextCSV, final MetaAndTableJRDataSource source,
-            final FloodDurationCalculationResults results) {
-        /* column headings */
-        exportContextCSV.addJRMetadata(source, "station_header", GeneralResultType.station);
-        exportContextCSV.addJRMetadata(source, "riverside_header", SInfoResultType.riverside);
-        exportContextCSV.addJRMetadata(source, "inundationduration_header", SInfoResultType.inundationduration);
-        exportContextCSV.addJRMetadata(source, "inundationduration_q_header", SInfoResultType.inundationdurationq);
-        exportContextCSV.addJRMetadata(source, "infrastructure_height_header", SInfoResultType.infrastructureHeightFloodDur);
-        exportContextCSV.addJRMetadata(source, "infrastructure_type_header", SInfoResultType.infrastructuretype);
-
-        // add dynamic headers
-
-        if (this.waterlevelCount == 0 || this.waterlevelCount == 2) {
-            source.addMetaData("dummy", "dummy");
-            source.addMetaData("dummy", "dummy");
-            source.addMetaData("dummy", "dummy");
-            source.addMetaData("dummy", "dummy");
-        }
+    protected String[] formatPDFRow(final ExportContextCSV exportContextPDF, final FloodDurationCalculationResults results, final ResultRow row) {
 
-        for (int i = 0; i < this.waterlevelCount; i++) {
-            final int naturalIndex = i + 1;
-
-            final Object[] args = new String[] { new StringBuilder().append("_").append(naturalIndex).toString() };
-            exportContextCSV.addJRMetadata(source, getPdfHeader("w", naturalIndex), exportContextCSV.msg(DurationWaterlevel.getHeaderW(), args));
-            exportContextCSV.addJRMetadata(source, getPdfHeader("duration", naturalIndex),
-                    exportContextCSV.msg(DurationWaterlevel.getHeaderFloodDurPerYear(), args));
-            exportContextCSV.addJRMetadata(source, getPdfHeader("q", naturalIndex), exportContextCSV.msg(DurationWaterlevel.getHeaderQ(), args));
-            exportContextCSV.addJRMetadata(source, getPdfHeader("bezeichnung", naturalIndex),
-                    exportContextCSV.msg(DurationWaterlevel.getHeaderBezeichn(), args));
-
-        }
-
-        exportContextCSV.addJRMetadata(source, "gauge_header", SInfoResultType.gaugeLabel);
-        exportContextCSV.addJRMetadata(source, "location_header", SInfoResultType.location);
-
-    }
-
-    public int getWaterlevelCount() { // der exporter muss daran, um spalten auszublenden
-        return this.waterlevelCount;
+        return this.formatRow(exportContextPDF, results, row);
     }
 
-    private String[] formatRow(final ExportContextCSV exportContextCSV, final FloodDurationCalculationResults results, final ResultRow row,
-            final ExportMode mode) {
-
-        final Collection<String> lines = new ArrayList<>(99);
-
-        lines.add(exportContextCSV.formatRowValue(row, GeneralResultType.station));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.riverside));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.inundationduration));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.inundationdurationq));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructureHeight));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructuretype));
-
-        final List<DurationWaterlevel> waterlevelList = (List<DurationWaterlevel>) row.getValue(SInfoResultType.customMultiRowColWaterlevel);
-        if (waterlevelList != null) {
-            final NumberFormat wFormatter = exportContextCSV.getFlowDepthFormatter();
-            final NumberFormat qFormatter = exportContextCSV.getQFormatter();
-
-            for (int i = 0; i < waterlevelList.size(); i++) {
-
-                if (i == this.maxWaterlevelPdf && mode == ExportMode.pdf)
-                    break;
+    @Override
+    protected void addJRTableHeader(final ExportContextCSV exportContextPDF, final MetaAndTableJRDataSource source,
+            final FloodDurationCalculationResults results) {
 
-                final DurationWaterlevel item = waterlevelList.get(i);
-                lines.add(item.getWFormatted(wFormatter));
-                lines.add(item.getFloodDurDaysPerYearFormatted());
-                lines.add(item.getQFormatted(qFormatter));
-                lines.add(item.getBezeichnung());
-            }
-        }
+        /* column headings */
+        exportContextPDF.addJRMetadata(source, "station_header", GeneralResultType.station);
+        exportContextPDF.addJRMetadata(source, "riverside_header", SInfoResultType.riverside);
+        exportContextPDF.addJRMetadata(source, "inundationduration_header", SInfoResultType.floodDuration);
+        exportContextPDF.addJRMetadata(source, "inundationduration_q_header", SInfoResultType.floodDischarge);
+        exportContextPDF.addJRMetadata(source, "infrastructure_height_header", SInfoResultType.infrastructureHeightFloodDur);
+        exportContextPDF.addJRMetadata(source, "infrastructure_type_header", SInfoResultType.infrastructuretype);
 
-        if ((this.waterlevelCount == 0 || this.waterlevelCount == 2) && mode == ExportMode.pdf) {
-            lines.add("dummy");
-            lines.add("dummy");
-            lines.add("dummy");
-            lines.add("dummy");
-        }
+        // TODO Je vier Spalten der bis zu drei Wasserspiegellagen
 
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.gaugeLabel));
-        lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.location));
-        return lines.toArray(new String[lines.size()]);
-    }
-
-    private String getPdfHeader(final String rootStr, final int index) {
-        final String hd = "_header";
-        final StringBuilder builder = new StringBuilder();
-        return builder.append(rootStr).append("_").append(index).append(hd).toString();
+        exportContextPDF.addJRMetadata(source, "gauge_header", SInfoResultType.gaugeLabel);
+        exportContextPDF.addJRMetadata(source, "location_header", SInfoResultType.location);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResults.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResults.java	Mon Jun 25 19:21:11 2018 +0200
@@ -9,6 +9,8 @@
  */
 package org.dive4elements.river.artifacts.sinfo.flood_duration;
 
+import java.util.List;
+
 import org.apache.commons.lang.math.DoubleRange;
 import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
 import org.dive4elements.river.artifacts.common.AbstractCalculationResults;
@@ -21,12 +23,18 @@
 
     private static final long serialVersionUID = 1L;
 
-    private final String riverside;
-
-    public FloodDurationCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange,
-            final String riverside) {
+    public FloodDurationCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange) {
         super(calcModeLabel, user, river, calcRange);
-        this.riverside = riverside; // lieber in Result?
     }
 
+    /**
+     * We know that this type of results only has one result member, so we can directly access it.
+     */
+    public FloodDurationCalculationResult getResult() {
+        final List<AbstractCalculationExportableResult<FloodDurationCalculationResults>> results = getResults();
+        if (results.size() < 1)
+            return null;
+
+        return (FloodDurationCalculationResult) results.get(0);
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jun 25 19:21:11 2018 +0200
@@ -11,20 +11,29 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.commons.lang.math.DoubleRange;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.common.GeneralResultType;
 import org.dive4elements.river.artifacts.common.ResultRow;
 import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
+import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
-import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
+import org.dive4elements.river.model.Attribute.AttributeKey;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.sinfo.InfrastructureValue;
 
 /**
- * @author Gernot Belger
+ * Calculation of the result rows of the flood duration of the infrastructures in a river km range
+ * and selected main value durations
+ *
+ * @author Matthias Schäfer
  */
 final class FloodDurationCalculator {
 
@@ -34,52 +43,66 @@
 
     private final CallContext context;
 
+
     public FloodDurationCalculator(final CallContext context, final RiverInfoProvider riverInfoProvider) {
         this.context = context;
         this.riverInfoProvider = riverInfoProvider;
     }
 
-    public FloodDurationCalculationResults execute(final Calculation problems, final String label, final WstInfo wstInfo, final String calcModeLabel,
-            final DoubleRange calcRange, final String riverside, final String user) {
+    /**
+     * Calculate the result rows
+     *
+     * @return a result collection with one result
+     */
+    public FloodDurationCalculationResults execute(final Calculation problems, final String label, final String calcModeLabel,
+            final DoubleRange calcRange, final RiversideChoiceKey riverside, final String user) {
 
-        calculateResultRow(8888.888);
-        calculateResultRow(99);
-        calculateResultRow(77);
-        final boolean hasTkh = false; // TODO tkh richtig machen, oder anderen result-Type wählen als super-klasse für FloodDurationCalculationResult
+        final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
+                calcRange.getMaximumDouble(), problems);
+        final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>();
+        for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) {
+            durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems));
+        }
+        final AttributeKey bankKey = riverside.getAttributeKey();
+        for (final InfrastructureValue infrastructure : InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
+                calcRange.getMaximumDouble(), bankKey)) {
+            calculateResultRow(infrastructure, wqFinder, durFinders);
+        }
 
-        final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, wstInfo, this.rows, false, 4);
+        final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, this.rows);
 
         final RiverInfo riverInfo = new RiverInfo(this.riverInfoProvider.getRiver());
 
-        final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange, riverside);
+        final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange);
         results.addResult(result, problems);
         return results;
     }
 
-    private void calculateResultRow(final double station) {
+    /**
+     * Calculate the result row for one infrastructure
+     */
+    private void calculateResultRow(final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder,
+            final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
 
         final ResultRow row = ResultRow.create();
 
+        final Gauge gauge = this.riverInfoProvider.getGauge(infrastructure.getStation(), true);
+        final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight());
+        final double qOut = Double.isInfinite(q) ? Double.NaN : q;
         // REMARK: access the location once only during calculation
-        final String location = this.riverInfoProvider.getLocation(station);
-        row.putValue(GeneralResultType.station, station);
-        row.putValue(SInfoResultType.riverside, "todo:getRiverside");
-        row.putValue(SInfoResultType.inundationduration, 44);
-        row.putValue(SInfoResultType.inundationdurationq, 444);
-        row.putValue(SInfoResultType.infrastructureHeight, 55);
-        row.putValue(SInfoResultType.infrastructuretype, "todo_get_infrastructureType");
+        final String location = this.riverInfoProvider.getLocation(infrastructure.getStation());
+        row.putValue(GeneralResultType.station, infrastructure.getStation());
+        row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n
+        row.putValue(SInfoResultType.floodDuration, 365 - durFinders.get(gauge).getDuration(q));
+        row.putValue(SInfoResultType.floodDischarge, qOut);
+        row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
+        row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
 
-        // custom type; each entry produces 4 Columns
-        final List<DurationWaterlevel> rowWsps = new ArrayList<>();
+        // TODO Berechne W, Überflutungsdauer, Q und Bezeichnung von bis zu drei WSPL
+        // row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps);
 
-        rowWsps.add(new DurationWaterlevel(222, 30, 666, "1. Test"));
-        rowWsps.add(new DurationWaterlevel(111, 40, 555, "2. Test"));
-        rowWsps.add(new DurationWaterlevel(123, 333, 33, "3. Test"));
-        rowWsps.add(new DurationWaterlevel(444, 452, 55, "4. Test"));
-        row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps);
-
-        row.putValue(SInfoResultType.gaugeLabel, "todo:getReferencedGauge");
-        row.putValue(SInfoResultType.location, "location");
+        row.putValue(SInfoResultType.gaugeLabel, gauge.getName());
+        row.putValue(SInfoResultType.location, location);
 
         this.rows.add(row);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Mon Jun 25 19:21:11 2018 +0200
@@ -31,39 +31,6 @@
 public class FloodDurationExporter extends AbstractCommonExporter<FloodDurationCalculationResults> {
 
     @Override
-    protected void doWritePdf(final OutputStream out, final FloodDurationCalculationResults results) {
-
-        // TODO: Move to super (hier ist aber spezieller code drin...)
-        try {
-            final ExportContextCSV exportContextCSV = new ExportContextCSV(this.context, null);
-
-            final JasperReporter reporter = new JasperReporter();
-
-            for (final AbstractCalculationExportableResult<FloodDurationCalculationResults> result : results.getResults()) {
-                final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();
-                getHelper().addJRMetaDataUSINFO(source, results);
-
-                final JasperDesigner design = result.addReport(exportContextCSV, results, reporter, source);
-                if (result instanceof FloodDurationCalculationResult) {
-                    final int wlCount = ((FloodDurationCalculationResult) result).getWaterlevelCount();
-                    if (wlCount == 0 || wlCount == 2) {
-                        design.removeColumn("wOpt");
-                        design.removeColumn("qOpt");
-                        design.removeColumn("bezOpt");
-                        design.removeColumn("durOpt");
-                    }
-                }
-            }
-
-            reporter.exportPDF(out);
-        }
-        catch (final JRException je) {
-            getLog().warn("Error generating PDF Report!", je);
-        }
-
-    }
-
-    @Override
     protected void doWriteCSVData(final CSVWriter writer, final FloodDurationCalculationResults results) {
         // TODO: Diesen Ablauf in super?
 
@@ -96,4 +63,36 @@
 
     }
 
+    @Override
+    protected void doWritePdf(final OutputStream out, final FloodDurationCalculationResults results) {
+
+        // TODO: Move to super (hier ist aber spezieller code drin...)
+        try {
+            final ExportContextCSV exportContextCSV = new ExportContextCSV(this.context, null);
+
+            final JasperReporter reporter = new JasperReporter();
+
+            for (final AbstractCalculationExportableResult<FloodDurationCalculationResults> result : results.getResults()) {
+                final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();
+                getHelper().addJRMetaDataUSINFO(source, results);
+
+                final JasperDesigner design = result.addReport(exportContextCSV, results, reporter, source);
+                if (result instanceof FloodDurationCalculationResult) {
+                    // final int wlCount = ((FloodDurationCalculationResult) result).getWaterlevelCount();
+                    // if (wlCount == 0 || wlCount == 2) {
+                    design.removeColumn("wOpt");
+                    design.removeColumn("qOpt");
+                    design.removeColumn("bezOpt");
+                    design.removeColumn("durOpt");
+                    // }
+                }
+            }
+
+            reporter.exportPDF(out);
+        }
+        catch (final JRException je) {
+            getLog().warn("Error generating PDF Report!", je);
+        }
+
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Mon Jun 25 19:21:11 2018 +0200
@@ -14,19 +14,23 @@
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.ChartArtifact;
 import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
+import org.dive4elements.river.artifacts.model.Calculation;
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.DataFacet;
 import org.dive4elements.river.artifacts.model.EmptyFacet;
 import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.ReportFacet;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.sinfo.common.FloodDurationProcessor;
 import org.dive4elements.river.artifacts.states.DefaultState;
 
-/** State in which a waterlevel has been calculated. */
+/**
+ * Last state of the S-Info flood duration workflow that calculates and outputs the result
+ */
 public class FloodDurationState extends DefaultState {
 
     /// ** The log that is used in this state. */
-    // private static Logger log = Logger.getLogger(FlowDepthState.class);
+    // private static Logger log = Logger.getLogger(FloodDurationState.class);
 
     private static final long serialVersionUID = 1L;
 
@@ -44,7 +48,6 @@
             facets.add(new EmptyFacet());
             return null;
         }
-
         return compute((SINFOArtifact) artifact, context, hash, facets, old);
     }
 
@@ -71,32 +74,21 @@
             return res;
 
         final FloodDurationCalculationResults results = (FloodDurationCalculationResults) res.getData();
-
-        /* add themes for chart, for each result */
-        final List<AbstractCalculationExportableResult<FloodDurationCalculationResults>> resultList = results.getResults();
-        for (int index = 0; index < resultList.size(); index++) {
+        final FloodDurationCalculationResult result = results.getResult();
+        if (result != null) {
+            // add themes for chart
+            final int index = 0;
 
-            final AbstractCalculationExportableResult<FloodDurationCalculationResults> result = resultList.get(0);
+            facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, index));
+            facets.add(FloodDurationProcessor.createMainValueDurationFacet(context, hash, this.id, result, index));
 
-            /* filtered (zoom dependent mean) flow depth TODO: */
-            // facets.add(FloodDurationProcessor.createFlowDepthFilteredFacet(context, hash, this.id, result, index));
-            // facets.add(FloodDurationProcessor.createFlowDepthRawFacet(context, hash, this.id, result, index));
-
+            facets.add(new DataFacet(FacetTypes.CSV, "CSV data", ComputeType.ADVANCE, hash, this.id));
+            facets.add(new DataFacet(FacetTypes.PDF, "PDF data", ComputeType.ADVANCE, hash, this.id));
         }
 
-        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));
-        // }
+        final Calculation report = res.getReport();
+        if (report.hasProblems())
+            facets.add(new ReportFacet(ComputeType.ADVANCE, hash, this.id));
 
         return res;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/RiversideRadioChoice.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/RiversideRadioChoice.java	Mon Jun 25 19:21:11 2018 +0200
@@ -12,25 +12,63 @@
 import java.util.List;
 
 import org.dive4elements.river.artifacts.states.RadioSelect;
+import org.dive4elements.river.model.Attribute.AttributeKey;
 
 /**
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
 public class RiversideRadioChoice extends RadioSelect {
+
     private static final long serialVersionUID = 1L;
 
+    /***** TYPES *****/
+
+    public enum RiversideChoiceKey {
+        NONE("", AttributeKey.NONE), //
+        LEFT("state.sinfo.riverside.left", AttributeKey.LEFT), //
+        RIGHT("state.sinfo.riverside.right", AttributeKey.RIGHT), //
+        BOTH("state.sinfo.riverside.both", AttributeKey.UNKNOWN);
+
+        private final String key;
+        private final AttributeKey attributeKey;
+
+        RiversideChoiceKey(final String key, final AttributeKey attributeKey) {
+            this.key = key;
+            this.attributeKey = attributeKey;
+        }
+
+        public static RiversideChoiceKey fromKey(final String key) {
+            for (final RiversideChoiceKey v : values()) {
+                if (key.equalsIgnoreCase(v.key))
+                    return v;
+            }
+            return NONE;
+        }
+
+        public String getKey() {
+            return this.key;
+        }
+
+        public AttributeKey getAttributeKey() {
+            return this.attributeKey;
+        }
+    }
+
+    /***** CONSTRUCTORS *****/
+
     public RiversideRadioChoice() {
         super();
     }
 
+    /***** METHODS *****/
+
     @Override
     protected List<String> makeEntries() {
         final List<String> entries = new ArrayList<>();
-        entries.add("state.sinfo.riverside.left");
-        entries.add("state.sinfo.riverside.right");
-        entries.add("state.sinfo.riverside.both");
+        entries.add(RiversideChoiceKey.LEFT.getKey());
+        entries.add(RiversideChoiceKey.RIGHT.getKey());
+        entries.add(RiversideChoiceKey.BOTH.getKey());
         return entries;
-
     }
 
 }
\ No newline at end of file
--- a/artifacts/src/main/resources/messages.properties	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties	Mon Jun 25 19:21:11 2018 +0200
@@ -922,11 +922,11 @@
 sinfo.export.flow_depth.csv.header.location = Lage
 sinfo.export.flood_duration.csv.header.riverside = Uferseite
 sinfo.export.flood_duration.csv.header.infrastructure.height = H\u00f6he der Infrastruktur
-sinfo.export.flood_duration.csv.header.inundation_duration = \u00dcberflutungsdauer [d/a]
-sinfo.export.flood_duration.csv.header.inundation_duration_q = \u00dcberflutungsdauerabfluss Q[m\u00b3/s]
+sinfo.export.flood_duration.csv.header.duration = \u00dcberflutungsdauer [d/a]
+sinfo.export.flood_duration.csv.header.discharge = \u00dcberflutungsdauerabfluss Q
 sinfo.export.flood_duration.csv.header.infrastructure_type = Infrastrukturtyp
-sinfo.export.flood_duration.pdf.header.inundation_duration = \u00dcberflu-tungs-dauer [d/a]
-sinfo.export.flood_duration.pdf.header.inundation_duration_q = \u00dcberflu-tungs-dauer-abfluss Q[m\u00b3/s]
+sinfo.export.flood_duration.pdf.header.duration = \u00dcber-flutungs-dauer [d/a]
+sinfo.export.flood_duration.pdf.header.discharge = \u00dcber-flutungs-dauer-abfluss Q [m\u00b3/s]
 sinfo.export.flood_duration.pdf.header.infrastructure_type = Infra-struktur-typ
 sinfo.export.flood_duration.pdf.header.infrastructure.height = H\u00f6he der Infra-struktur
 
@@ -1113,7 +1113,7 @@
 sinfo.export.csv.header.collision.count = Anzahl der Grundber\u00fchrungen
 collision.count.title = Grundber\u00fchrungen {0}
 chart.collision_count.section.yaxis.label = H\u00e4ufigkeit
-sinfo.export.csv.header.collision.gaugew = W am Pegel
+sinfo.export.csv.header.collision.gaugew = Pegelstand
 collision.gaugew.title = W am Pegel {0}
 sinfo.export.csv.header.discharge.zone = Abflusszustand
 
@@ -1123,6 +1123,17 @@
 
 sinfo.chart.collision.section.title = Grundber\u00fchrungen
 
+sinfo.chart.flood_duration.section.title = \u00dcberflutungsdauer Infrastrukturen BWaStr
+sinfo.chart.flood_duration.section.yaxis.label = \u00dcberflutungsdauer [d/a]
+sinfo_facet_flood_duration = \u00dcberflutungsdauern
+sinfo_facet_flood_duration.description = \u00dcberflutungsdauern ({0})
+gauge_duration.missing = Keine Dauerzahlen vorhanden am Pegel {0}
+gauge_discharge_table.missing = No discharge table available for gauge {0}
+gauge_main_values.missing = No named main values available for gauge {0}
+wq_base_data.missing = No base waterlevel/discharge available
+mainvalue.duration = \u00dcberflutungsdauer
+mainvalue.duration.description = \u00dcberflutungsdauer ({0})
+
 bundu_bezugswst = Bezugswasserst\u00e4nde
 bundu_analysis = Fixinganalysis
 bundu_vollmer = relocated Waterlevel Calculation 
--- a/artifacts/src/main/resources/messages_de.properties	Mon Jun 25 17:58:11 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Mon Jun 25 19:21:11 2018 +0200
@@ -922,11 +922,11 @@
 sinfo.export.flow_depth.csv.header.location = Lage
 sinfo.export.flood_duration.csv.header.riverside = Uferseite
 sinfo.export.flood_duration.csv.header.infrastructure.height = H\u00f6he der Infrastruktur
-sinfo.export.flood_duration.csv.header.inundation_duration = \u00dcberflutungsdauer [d/a]
-sinfo.export.flood_duration.csv.header.inundation_duration_q = \u00dcberflutungsdauerabfluss Q[m\u00b3/s]
+sinfo.export.flood_duration.csv.header.duration = \u00dcberflutungsdauer [d/a]
+sinfo.export.flood_duration.csv.header.discharge = \u00dcberflutungsdauerabfluss Q
 sinfo.export.flood_duration.csv.header.infrastructure_type = Infrastrukturtyp
-sinfo.export.flood_duration.pdf.header.inundation_duration = \u00dcberflu-tungs-dauer [d/a]
-sinfo.export.flood_duration.pdf.header.inundation_duration_q = \u00dcberflu-tungs-dauer-abfluss Q[m\u00b3/s]
+sinfo.export.flood_duration.pdf.header.duration = \u00dcber-flutungs-dauer [d/a]
+sinfo.export.flood_duration.pdf.header.discharge = \u00dcber-flutungs-dauer-abfluss Q [m\u00b3/s]
 sinfo.export.flood_duration.pdf.header.infrastructure_type = Infra-struktur-typ
 sinfo.export.flood_duration.pdf.header.infrastructure.height = H\u00f6he der Infra-struktur
 
@@ -1113,7 +1113,7 @@
 sinfo.export.csv.header.collision.count = Anzahl der Grundber\u00fchrungen
 collision.count.title = Grundber\u00fchrungen {0}
 chart.collision_count.section.yaxis.label = H\u00e4ufigkeit
-sinfo.export.csv.header.collision.gaugew = Pegelstand [cm]
+sinfo.export.csv.header.collision.gaugew = Pegelstand
 collision.gaugew.title = W am Pegel {0}
 sinfo.export.csv.header.discharge.zone = Abflusszustand
 
@@ -1123,6 +1123,17 @@
 
 sinfo.chart.collision.section.title = Grundber\u00fchrungen
 
+sinfo.chart.flood_duration.section.title = \u00dcberflutungsdauer Infrastrukturen BWaStr
+sinfo.chart.flood_duration.section.yaxis.label = \u00dcberflutungsdauer [d/a]
+sinfo_facet_flood_duration = \u00dcberflutungsdauern
+sinfo_facet_flood_duration.description = \u00dcberflutungsdauern ({0})
+gauge_duration.missing = Dauerzahlen fehlen am Pegel {0}
+gauge_discharge_table.missing = Abflusstafel fehlt am Pegel {0}
+gauge_main_values.missing = Hauptwerte fehlen am Pegel {0}
+wq_base_data.missing = W/Q-Daten fehlen oder sind ung\00fcltig
+mainvalue.duration = \u00dcberflutungsdauer
+mainvalue.duration.description = \u00dcberflutungsdauer ({0})
+
 bundu_bezugswst = Bezugswasserst\u00e4nde
 bundu_analysis = Fixierungsanalyse
 bundu_vollmer = ausgelagerte Wasserspiegellage
--- a/backend/src/main/java/org/dive4elements/river/model/Annotation.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/Annotation.java	Mon Jun 25 19:21:11 2018 +0200
@@ -10,15 +10,18 @@
 
 import java.io.Serializable;
 
+import javax.persistence.Column;
 import javax.persistence.Entity;
-import javax.persistence.Id;
-import javax.persistence.Table;
 import javax.persistence.GeneratedValue;
-import javax.persistence.Column;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
 import javax.persistence.SequenceGenerator;
-import javax.persistence.GenerationType;
-import javax.persistence.OneToOne;
-import javax.persistence.JoinColumn;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import org.dive4elements.river.model.Attribute.AttributeKey;
 
 @Entity
 @Table(name = "annotations")
@@ -36,12 +39,12 @@
     }
 
     public Annotation(
-        Range          range,
-        Attribute      attribute,
-        Position       position,
-        Edge           edge,
-        AnnotationType type
-    ) {
+            final Range          range,
+            final Attribute      attribute,
+            final Position       position,
+            final Edge           edge,
+            final AnnotationType type
+            ) {
         this.range     = range;
         this.attribute = attribute;
         this.position  = position;
@@ -51,68 +54,73 @@
 
     @Id
     @SequenceGenerator(
-        name           = "SEQUENCE_ANNOTATIONS_ID_SEQ",
-        sequenceName   = "ANNOTATIONS_ID_SEQ",
-        allocationSize = 1)
+            name           = "SEQUENCE_ANNOTATIONS_ID_SEQ",
+            sequenceName   = "ANNOTATIONS_ID_SEQ",
+            allocationSize = 1)
     @GeneratedValue(
-        strategy  = GenerationType.SEQUENCE,
-        generator = "SEQUENCE_ANNOTATIONS_ID_SEQ")
+            strategy  = GenerationType.SEQUENCE,
+            generator = "SEQUENCE_ANNOTATIONS_ID_SEQ")
     @Column(name = "id")
     public Integer getId() {
-        return id;
+        return this.id;
     }
 
-    public void setId(Integer id) {
+    public void setId(final Integer id) {
         this.id = id;
     }
 
     @OneToOne
     @JoinColumn(name = "range_id")
     public Range getRange() {
-        return range;
+        return this.range;
     }
 
-    public void setRange(Range range) {
+    public void setRange(final Range range) {
         this.range = range;
     }
 
     @OneToOne
     @JoinColumn(name = "attribute_id")
     public Attribute getAttribute() {
-        return attribute;
+        return this.attribute;
     }
 
-    public void setAttribute(Attribute attribute) {
+    public void setAttribute(final Attribute attribute) {
         this.attribute = attribute;
     }
 
+    @Transient
+    public AttributeKey getAttributeKey() {
+        return this.getAttribute().getKey();
+    }
+
     @OneToOne
     @JoinColumn(name = "position_id")
     public Position getPosition() {
-        return position;
+        return this.position;
     }
 
-    public void setPosition(Position position) {
+    public void setPosition(final Position position) {
         this.position = position;
     }
 
     @OneToOne
     @JoinColumn(name = "edge_id")
     public Edge getEdge() {
-        return edge;
+        return this.edge;
     }
 
-    public void setEdge(Edge edge) {
+    public void setEdge(final Edge edge) {
         this.edge = edge;
     }
 
     @OneToOne
     @JoinColumn(name = "type_id")
     public AnnotationType getType() {
-        return type;
+        return this.type;
     }
 
-    public void setType(AnnotationType type) {
+    public void setType(final AnnotationType type) {
         this.type = type;
     }
 }
--- a/backend/src/main/java/org/dive4elements/river/model/Attribute.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/Attribute.java	Mon Jun 25 19:21:11 2018 +0200
@@ -9,20 +9,124 @@
 package org.dive4elements.river.model;
 
 import java.io.Serializable;
+import java.util.List;
 
+import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
 import javax.persistence.Id;
+import javax.persistence.SequenceGenerator;
 import javax.persistence.Table;
-import javax.persistence.GeneratedValue;
-import javax.persistence.Column;
-import javax.persistence.SequenceGenerator;
-import javax.persistence.GenerationType;
+import javax.persistence.Transient;
+
+import org.dive4elements.river.backend.SessionHolder;
+import org.hibernate.Query;
+import org.hibernate.Session;
 
 @Entity
 @Table(name = "attributes")
 public class Attribute
 implements   Serializable
 {
+
+    /***** TYPES *****/
+
+    /**
+     * River attribute (river side or range type)
+     *
+     */
+    public enum AttributeKey {
+        NONE(""), STRECKE(">>>>>>>>>>>>>>>"), LEFT("links"), RIGHT("rechts"), UNKNOWN("?");
+
+        private final String name;
+        private int id;
+        private boolean ready;
+
+        AttributeKey(final String name) {
+            this.name = name;
+            this.id = 0;
+            this.ready = false;
+        }
+
+        /**
+         * Type name in the database
+         */
+        public String getName() {
+            return this.name;
+        }
+
+        /**
+         * Type id in the database
+         */
+        public int getId() {
+            initFromDatabase();
+            return this.id;
+        }
+
+        /**
+         * Set the type id
+         */
+        public void setId(final int id) {
+            this.id = id;
+        }
+
+        protected boolean getReady() {
+            return this.ready;
+        }
+
+        protected void setReady(final boolean ready) {
+            this.ready = ready;
+        }
+
+        /**
+         * Main value type key for a database name value
+         */
+        public static AttributeKey forDbName(final String dbname) {
+            initFromDatabase();
+            for (final AttributeKey k : AttributeKey.values()) {
+                if (k.getName().equalsIgnoreCase(dbname))
+                    return k;
+            }
+            return UNKNOWN;
+        }
+
+        /**
+         * Main value type key for a database id value
+         */
+        public static AttributeKey forDbId(final int dbid) {
+            initFromDatabase();
+            for (final AttributeKey k : AttributeKey.values()) {
+                if (k.getId() == dbid)
+                    return k;
+            }
+            return UNKNOWN;
+        }
+
+        /**
+         * Initially queries the database ids
+         */
+        private static void initFromDatabase() {
+            if (STRECKE.getReady())
+                return;
+            // Avoid recursion
+            for (final AttributeKey k : AttributeKey.values())
+                k.setReady(true);
+            // Select database ids
+            final Session session = SessionHolder.HOLDER.get();
+            final Query query = session.createQuery("FROM Attribute");
+            final List<Attribute> rows = query.list();
+            if (!rows.isEmpty()) {
+                for (int i = 0; i <= rows.size() - 1; i++) {
+                    if (forDbName(rows.get(i).getValue()) != UNKNOWN)
+                        forDbName(rows.get(i).getValue()).setId(rows.get(i).getId());
+                }
+            }
+        }
+    }
+
+    /***** FIELDS *****/
+
     private Integer id;
 
     private String  value;
@@ -30,34 +134,43 @@
     public Attribute() {
     }
 
-    public Attribute(String value) {
+    /***** CONSTRUCTORS *****/
+
+    public Attribute(final String value) {
         this.value = value;
     }
 
+    /***** METHODS *****/
+
     @Id
     @SequenceGenerator(
-        name           = "SEQUENCE_ATTRIBUTES_ID_SEQ",
-        sequenceName   = "ATTRIBUTES_ID_SEQ",
-        allocationSize = 1)
+            name           = "SEQUENCE_ATTRIBUTES_ID_SEQ",
+            sequenceName   = "ATTRIBUTES_ID_SEQ",
+            allocationSize = 1)
     @GeneratedValue(
-        strategy  = GenerationType.SEQUENCE,
-        generator = "SEQUENCE_ATTRIBUTES_ID_SEQ")
+            strategy  = GenerationType.SEQUENCE,
+            generator = "SEQUENCE_ATTRIBUTES_ID_SEQ")
     @Column(name = "id")
     public Integer getId() {
-        return id;
+        return this.id;
     }
 
-    public void setId(Integer id) {
+    public void setId(final Integer id) {
         this.id = id;
     }
 
     @Column(name = "value")
     public String getValue() {
-        return value;
+        return this.value;
     }
 
-    public void setValue(String value) {
+    public void setValue(final String value) {
         this.value = value;
     }
+
+    @Transient
+    public AttributeKey getKey() {
+        return AttributeKey.forDbId(this.getId());
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/backend/src/main/java/org/dive4elements/river/model/MainValue.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/MainValue.java	Mon Jun 25 19:21:11 2018 +0200
@@ -128,5 +128,19 @@
         query.setParameter("typeid", typekey.getId());
         return query.list();
     }
+
+    /**
+     * Selects from the database the discharge-duration main values of a gauge sorted by duration
+     */
+    public static List<MainValue> getDurationDischargesOfGauge(final Gauge gauge) {
+        final Session session = SessionHolder.HOLDER.get();
+        final Query query = session.createQuery("SELECT mv"
+                + " FROM MainValue AS mv JOIN mv.mainValue AS nmv"
+                + " WHERE mv.gauge.id=:gaugeid AND nmv.type.id=:typeid"
+                + " ORDER BY CAST(nmv.name AS int)");
+        query.setParameter("gaugeid", gauge.getId());
+        query.setParameter("typeid", MainValueTypeKey.DURATION.getId());
+        return query.list();
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java	Mon Jun 25 19:21:11 2018 +0200
@@ -199,4 +199,18 @@
         query.setParameter("id", id);
         return (Infrastructure) query.list().get(0);
     }
+
+    /**
+     * Get first data series of a river
+     */
+    public static Infrastructure getSeries(final River river) {
+        final Session session = SessionHolder.HOLDER.get();
+        final Query query = session.createQuery("FROM Infrastructure WHERE (river=:river)");
+        query.setParameter("river", river);
+        final List rows = query.list();
+        if (!rows.isEmpty())
+            return (Infrastructure) rows.get(0);
+        else
+            return null;
+    }
 }
\ No newline at end of file
--- a/backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java	Mon Jun 25 19:21:11 2018 +0200
@@ -22,9 +22,12 @@
 import javax.persistence.OneToOne;
 import javax.persistence.SequenceGenerator;
 import javax.persistence.Table;
+import javax.persistence.Transient;
 
 import org.dive4elements.river.backend.SessionHolder;
 import org.dive4elements.river.model.Attribute;
+import org.dive4elements.river.model.Attribute.AttributeKey;
+import org.dive4elements.river.model.River;
 import org.hibernate.Query;
 import org.hibernate.Session;
 
@@ -117,6 +120,11 @@
         this.attribute = attribute;
     }
 
+    @Transient
+    public AttributeKey getAttributeKey() {
+        return this.getAttribute().getKey();
+    }
+
     @Column(name = "height")
     public Double getHeight() {
         return this.height;
@@ -127,7 +135,7 @@
     }
 
     /**
-     * Selects the infrastructure values of a data series in a km range from the database
+     * Selects from the database the infrastructure values of a data series in a km range
      */
     public static List<InfrastructureValue> getValues(final Infrastructure parent, final double kmLo, final double kmHi) {
         final Session session = SessionHolder.HOLDER.get();
@@ -138,4 +146,25 @@
         query.setParameter("kmHi", new Double(kmHi));
         return query.list();
     }
+
+    /**
+     * Selects from the database the infrastructure values of a km range of a river and a river side
+     */
+    public static List<InfrastructureValue> getValues(final River river, final double kmLo, final double kmHi, final AttributeKey riverside) {
+        final Session session = SessionHolder.HOLDER.get();
+        String riversideClause = "";
+        if ((riverside == AttributeKey.LEFT) || (riverside == AttributeKey.RIGHT))
+            riversideClause = " AND (v.attribute.id=:attr_id)";
+        final Query query = session.createQuery("FROM InfrastructureValue v"
+                + " WHERE (v.infrastructure.river=:river)"
+                + " AND (v.station BETWEEN :kmLo - 0.0001 AND :kmHi + 0.0001)"
+                + riversideClause
+                + " ORDER BY v.station, v.attribute.id");
+        query.setParameter("river", river);
+        query.setParameter("kmLo", new Double(kmLo));
+        query.setParameter("kmHi", new Double(kmHi));
+        if (!riversideClause.isEmpty())
+            query.setParameter("attr_id", riverside.getId());
+        return query.list();
+    }
 }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Mon Jun 25 17:58:11 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Mon Jun 25 19:21:11 2018 +0200
@@ -1536,4 +1536,6 @@
     String export_csv_title();
 
     String waterlevel_ground_state();
+
+    String sinfo_flood_duration();
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Mon Jun 25 17:58:11 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Mon Jun 25 19:21:11 2018 +0200
@@ -809,6 +809,8 @@
 sinfo_collisions = Grundber\u00fchrungen
 sinfo_collision = Grundber\u00fchrungen
 
+sinfo_flood_duration = \u00dcberflutungsdauer Infrastruktur BWaStr
+
 uinfo = U-INFO
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export
 uinfo_salix_line_export = Salix-Linie Export
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Mon Jun 25 17:58:11 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Mon Jun 25 19:21:11 2018 +0200
@@ -809,6 +809,8 @@
 sinfo_collisions = Grundber\u00fchrungen
 sinfo_collision = Grundber\u00fchrungen
 
+sinfo_flood_duration = \u00dcberflutungsdauer Infrastruktur BWaStr
+
 uinfo = U-INFO
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export
 uinfo_salix_line_export = Salix-Linie Export

http://dive4elements.wald.intevation.org