changeset 9202:b4402594213b

More work on calculations and output for S-Info flood duration workflow (chart types 1 and 2)
author mschaefer
date Mon, 02 Jul 2018 07:33:53 +0200
parents 491e1a434457
children 9831f6c60473
files artifacts/doc/conf/artifacts/sinfo.xml artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml artifacts/doc/conf/generators/longitudinal-diagrams.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/AbstractCalculationResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/common/ResultRow.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionArtifact.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/GaugeDischargeValuesFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodHeightProcessor.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/GaugeMainValueFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueNameFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoI18NStrings.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.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/resources/messages.properties artifacts/src/main/resources/messages_de.properties 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 32 files changed, 1056 insertions(+), 334 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/sinfo.xml	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/artifacts/sinfo.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -365,7 +365,7 @@
         </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="sinfo_facet_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"/>
--- a/artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/generators/longitudinal-diagram-defaults.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -59,6 +59,7 @@
     <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.FloodHeightProcessor" axis="W"/>
     
     <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	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/generators/longitudinal-diagrams.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -178,5 +178,18 @@
             <arg expr="artifact.river"/>
         </subtitle>
     </output-generator>
+
+    <output-generator
+        names="sinfo_flood_height,sinfo_flood_height_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="sinfo.chart.flood_duration.height.section.title" default="Überflutung Infrastruktur BWaStr (DEFAULT)"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="W"/>
+        <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/themes.xml	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/themes.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -441,8 +441,15 @@
         <mapping from="sinfo_facet_waterlevel_difference.filtered" to="SInfoWaterlevelDifference" />
         <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_collision_calc_count" to="SInfoCollisionCount" />
+        <mapping from="sinfo_facet_flood_duration" to="SInfoFloodDuration" />
+        <mapping from="mainvalue.1.duration" to="SInfoMainValues1" />
+        <mapping from="mainvalue.2.duration" to="SInfoMainValues2" />
+        <mapping from="mainvalue.3.duration" to="SInfoMainValues3" />
+        <mapping from="sinfo_facet_flood_height" to="SInfoInfrastructureHeight" />
+        <mapping from="mainvalue.1.w" to="SInfoMainValues1" />
+        <mapping from="mainvalue.2.w" to="SInfoMainValues2" />
+        <mapping from="mainvalue.3.w" to="SInfoMainValues3" />
         
         <mapping from="sinfo_facet_predefined_channel_width" to="SInfoPredefinedChannelWidth" />
         <mapping from="sinfo_facet_predefined_channel_depth" to="SInfoPredefinedChannelDepth" />
--- a/artifacts/doc/conf/themes/default.xml	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/themes/default.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -3082,4 +3082,28 @@
             <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
         </fields>
     </theme>
+    <theme name="SInfoMainValues1">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="0, 96, 192" />
+        </fields>
+    </theme>
+    <theme name="SInfoMainValues2">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="192, 0, 0" />
+        </fields>
+    </theme>
+    <theme name="SInfoMainValues3">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="96, 128, 0" />
+        </fields>
+    </theme>
 </themegroup>
\ No newline at end of file
--- a/artifacts/doc/conf/themes/second.xml	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/doc/conf/themes/second.xml	Mon Jul 02 07:33:53 2018 +0200
@@ -3070,4 +3070,28 @@
             <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
         </fields>
     </theme>
+    <theme name="MainValues1">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="0, 96, 192" />
+        </fields>
+    </theme>
+    <theme name="MainValues2">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="192, 0, 0" />
+        </fields>
+    </theme>
+    <theme name="MainValues3">
+        <inherits>
+            <inherit from="MainValuesW" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe" default="96, 128, 0" />
+        </fields>
+    </theme>
 </themegroup>
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractCalculationResult.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractCalculationResult.java	Mon Jul 02 07:33:53 2018 +0200
@@ -45,7 +45,7 @@
     // this.rows.add(resultRow);
     // }
 
-    public final Collection<ResultRow> getRows() {
+    public Collection<ResultRow> getRows() {
         return Collections.unmodifiableCollection(this.rows);
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/common/ResultRow.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/common/ResultRow.java	Mon Jul 02 07:33:53 2018 +0200
@@ -9,12 +9,11 @@
  */
 package org.dive4elements.river.artifacts.common;
 
-import java.io.Serializable; 
+import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.dive4elements.artifacts.CallContext;
-import org.dive4elements.river.artifacts.common.IResultType;
 
 /**
  * Generic container for results that come in rows.
@@ -22,14 +21,20 @@
  * @author Gernot Belger
  */
 public class ResultRow implements Serializable {
-    protected static final long serialVersionUID = 1L; //TODO: Make private (wenn SInfoResultRow gelöscht ist
+    private static final long serialVersionUID = 1L;
 
-    private final Map<IResultType, Object> values = new HashMap<IResultType, Object>();
+    private final Map<IResultType, Object> values = new HashMap<>();
 
     public static ResultRow create() {
         return new ResultRow();
     }
 
+    public static ResultRow create(final ResultRow src) {
+        final ResultRow dst = create();
+        dst.values.putAll(src.values);
+        return dst;
+    }
+
     protected ResultRow() {
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java	Mon Jul 02 07:33:53 2018 +0200
@@ -110,16 +110,44 @@
      * Facet-names (ManualPoints for example).
      */
     public enum ChartType {
-        FD("fix_derivate_curve"), LS("longitudinal_section"), CS("cross_section"), DLS("discharge_longitudinal_section"), CDC("computed_discharge_curve"), DUC(
-                "duration_curve"), DIC("discharge_curve"), RC("reference_curve"), RCN("reference_curve_normalized"), WD("wdifferences"), BHDY(
-                        "bedheight_difference_height_year"), BDY("bed_difference_year"), FWQC("fix_wq_curve"), FDWC("fix_deltawt_curve"), FLSC(
-                                "fix_longitudinal_section_curve"), FDC("fix_derivate_curve"), EWQ("extreme_wq_curve"), BHM("bedheight_middle"), BLS(
-                                        "bed_longitudinal_section"), SLS("sedimentload_ls"), FV(
-                                                "flow_velocity"), SQA("sq_relation_a"), SQB("sq_relation_b"), W_D("w_differences"), SQC("sq_relation_c"), SQD(
-                                                        "sq_relation_d"), SQE("sq_relation_e"), SQF("sq_relation_f"), HD("historical_discharge"), HDWQ(
-                                                                "historical_discharge_wq"), SFD("sinfo_flow_depth"), SFDMM("sinfo_flow_depth_minmax"), SFDD(
-                                                                        "sinfo_flow_depth_development"), SFDDPY("sinfo_flow_depth_development_peryear"), SC(
-                                                                                "sinfo_collision"), SFDUR("sinfo_flood_duration"), STKH("sinfo_tkk");
+        FD("fix_derivate_curve"), //
+        LS("longitudinal_section"), //
+        CS("cross_section"), //
+        DLS("discharge_longitudinal_section"), //
+        CDC("computed_discharge_curve"), //
+        DUC("duration_curve"), //
+        DIC("discharge_curve"), //
+        RC("reference_curve"), //
+        RCN("reference_curve_normalized"), //
+        WD("wdifferences"), //
+        BHDY("bedheight_difference_height_year"), //
+        BDY("bed_difference_year"), //
+        FWQC("fix_wq_curve"), //
+        FDWC("fix_deltawt_curve"), //
+        FLSC("fix_longitudinal_section_curve"), //
+        FDC("fix_derivate_curve"), //
+        EWQ("extreme_wq_curve"), //
+        BHM("bedheight_middle"), //
+        BLS("bed_longitudinal_section"), //
+        SLS("sedimentload_ls"), //
+        FV("flow_velocity"), //
+        SQA("sq_relation_a"), //
+        SQB("sq_relation_b"), //
+        W_D("w_differences"), //
+        SQC("sq_relation_c"), //
+        SQD("sq_relation_d"), //
+        SQE("sq_relation_e"), //
+        SQF("sq_relation_f"), //
+        HD("historical_discharge"), //
+        HDWQ("historical_discharge_wq"), //
+        SFD("sinfo_flow_depth"), //
+        SFDMM("sinfo_flow_depth_minmax"), //
+        SFDD("sinfo_flow_depth_development"), //
+        SFDDPY("sinfo_flow_depth_development_peryear"), //
+        SC("sinfo_collision"), //
+        SFDUR("sinfo_flood_duration"), //
+        STKH("sinfo_tkk"), //
+        SFW("sinfo_flood_height");
 
         private final String chartTypeString;
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionArtifact.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionArtifact.java	Mon Jul 02 07:33:53 2018 +0200
@@ -103,7 +103,7 @@
             log.error("Invalid datacage ID '" + code + "'");
             return;
         }
-        final ArrayList<Facet> facets = new ArrayList<>(2);
+        final ArrayList<Facet> facets = new ArrayList<>(1);
         facets.add(CollisionCountProcessor.createFacet(callMeta, year));
         // facets.add(CollisionGaugeWProcessor.createFacet(callMeta, seriesName)); //REMARK gauge_w is the cm above gauge datum,
         // therefore only useful with transformation to NHN
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java	Mon Jul 02 07:33:53 2018 +0200
@@ -24,7 +24,7 @@
 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.GaugeMainValueFinder;
 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
@@ -80,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, GaugeMainValueNameFinder> zoneFinders = new HashMap<>();
+        final Map<Gauge, GaugeMainValueFinder> zoneFinders = new HashMap<>();
         for (final Gauge gauge : river.determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) {
             qFinders.put(gauge, GaugeDischargeValuesFinder.loadValues(gauge, problems));
-            zoneFinders.put(gauge, GaugeMainValueNameFinder.loadValues(MainValueTypeKey.Q, gauge, problems));
+            zoneFinders.put(gauge, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, gauge, problems));
         }
         final Collection<ResultRow> detailsRows = new ArrayList<>();
 
@@ -121,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, GaugeMainValueNameFinder> zoneFinders) {
+            final Map<Gauge, GaugeMainValueFinder> 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);
@@ -132,7 +132,7 @@
                     .putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW())
                     .putValue(SInfoResultType.gaugeLabel, collision.getGaugeName())
                     .putValue(SInfoResultType.discharge, qOut)
-                    .putValue(SInfoResultType.dischargeZone, zoneFinders.get(gauge).getZoneName(q)));
+                    .putValue(SInfoResultType.dischargeZone, zoneFinders.get(gauge).findZoneName(q)));
         }
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/GaugeDischargeValuesFinder.java	Sun Jul 01 15:29:40 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
- * Software engineering by
- *  Björnsen Beratende Ingenieure GmbH
- *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-package org.dive4elements.river.artifacts.sinfo.collision;
-
-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
- *
- * @author Matthias Schäfer
- *
- */
-public final class GaugeDischargeValuesFinder {
-
-    /***** FIELDS *****/
-
-    // private static Logger log = Logger.getLogger(GaugeDischargeValuesFinder.class);
-
-    private final Gauge gauge;
-
-    private final 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.isEmpty())
-            this.wRange = new DoubleRange(ws.get(0), ws.get(ws.size() - 1));
-        else
-            this.wRange = null;
-        this.wInterpolator = new LinearInterpolator().interpolate(ws.toNativeArray(), qs.toNativeArray());
-    }
-
-
-    /***** 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))
-            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
-     *
-     * @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
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java	Mon Jul 02 07:33:53 2018 +0200
@@ -16,6 +16,9 @@
 import org.dive4elements.artifactdatabase.state.Facet;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
 /**
  * Processor to generate the facet and data series of infrastructure flood durations
@@ -31,6 +34,10 @@
 
     public static final String FACET_MAIN_VALUE_1_DURATION = "mainvalue.1.duration";
 
+    public static final String FACET_MAIN_VALUE_2_DURATION = "mainvalue.2.duration";
+
+    public static final String FACET_MAIN_VALUE_3_DURATION = "mainvalue.3.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";
@@ -40,6 +47,8 @@
     static {
         HANDLED_FACET_TYPES.add(FACET_FLOOD_DURATION);
         HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_1_DURATION);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_2_DURATION);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_3_DURATION);
     }
 
     public FloodDurationProcessor() {
@@ -49,11 +58,18 @@
 
     @Override
     protected double[][] doGetPoints(final AbstractCalculationResult data, final String facetName) {
+
         if (FACET_FLOOD_DURATION.contentEquals(facetName))
-            return data.getStationPoints(SInfoResultType.floodDuration);
+            return ((FloodDurationCalculationResult) data).fetchInfrastructurePoints(SInfoResultType.floodDuration);
 
         if (FACET_MAIN_VALUE_1_DURATION.contentEquals(facetName))
-            return data.getStationPoints(SInfoResultType.mainValue1Duration);
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.mainValue1Duration);
+
+        if (FACET_MAIN_VALUE_2_DURATION.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.mainValue2Duration);
+
+        if (FACET_MAIN_VALUE_3_DURATION.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.mainValue3Duration);
 
         final String error = String.format("Unknown facet name: %s", facetName);
         throw new UnsupportedOperationException(error);
@@ -61,13 +77,18 @@
 
     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);
+                FACET_FLOOD_DURATION, FACET_FLOOD_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);
+            final FloodDurationCalculationResult result, final int index) {
+
+        final String description = Resources.getMsg(context.getMeta(), FACET_MAIN_VALUE_DURATION_DESCRIPTION, FACET_MAIN_VALUE_DURATION_DESCRIPTION,
+                result.getMainValueLabel(index));
+        assert ((index >= 0) && (index <= 2));
+        final String facetName = new String[] { FACET_MAIN_VALUE_1_DURATION, FACET_MAIN_VALUE_2_DURATION, FACET_MAIN_VALUE_3_DURATION }[index];
+        return new SInfoResultFacet(0, facetName, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
     }
 }
\ 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/FloodHeightProcessor.java	Mon Jul 02 07:33:53 2018 +0200
@@ -0,0 +1,92 @@
+/** 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;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.exports.LongitudinalSectionGenerator;
+
+/**
+ * Processor to generate the facet and data series of infrastructure flood heights
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class FloodHeightProcessor extends AbstractSInfoLineProcessor<AbstractCalculationResult> {
+
+    public static final String FACET_FLOOD_HEIGHT = "sinfo_facet_flood_height";
+
+    public static final String FACET_FLOOD_HEIGHT_DESCRIPTION = "sinfo_facet_flood_height.description";
+
+    public static final String FACET_MAIN_VALUE_1_HEIGHT = "mainvalue.1.w";
+
+    public static final String FACET_MAIN_VALUE_2_HEIGHT = "mainvalue.2.w";
+
+    public static final String FACET_MAIN_VALUE_3_HEIGHT = "mainvalue.3.w";
+
+    public static final String FACET_MAIN_VALUE_HEIGHT_DESCRIPTION = "mainvalue.w.description";
+
+    private static final String I18N_AXIS_LABEL = LongitudinalSectionGenerator.I18N_YAXIS_LABEL;
+
+    private static final Set<String> HANDLED_FACET_TYPES = new HashSet<>();
+
+    static {
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_HEIGHT);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_1_HEIGHT);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_2_HEIGHT);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_3_HEIGHT);
+    }
+
+    public FloodHeightProcessor() {
+        super(I18N_AXIS_LABEL, HANDLED_FACET_TYPES);
+    }
+
+
+    @Override
+    protected double[][] doGetPoints(final AbstractCalculationResult data, final String facetName) {
+        if (FACET_FLOOD_HEIGHT.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchInfrastructurePoints(SInfoResultType.infrastructureHeight);
+
+        if (FACET_MAIN_VALUE_1_HEIGHT.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.waterlevel1);
+
+        if (FACET_MAIN_VALUE_2_HEIGHT.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.waterlevel2);
+
+        if (FACET_MAIN_VALUE_3_HEIGHT.contentEquals(facetName))
+            return ((FloodDurationCalculationResult) data).fetchMainValuePoints(SInfoResultType.waterlevel3);
+
+        final String error = String.format("Unknown facet name: %s", facetName);
+        throw new UnsupportedOperationException(error);
+    }
+
+    public static Facet createFloodHeightFacet(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_HEIGHT, FACET_FLOOD_HEIGHT_DESCRIPTION);
+    }
+
+    public static Facet createMainValueHeightFacet(final CallContext context, final String hash, final String id,
+            final FloodDurationCalculationResult result, final int index) {
+        final String description = Resources.getMsg(context.getMeta(), FACET_MAIN_VALUE_HEIGHT_DESCRIPTION, FACET_MAIN_VALUE_HEIGHT_DESCRIPTION,
+                result.getMainValueLabel(index));
+        assert ((index >= 0) && (index <= 2));
+        final String facetName = new String[] { FACET_MAIN_VALUE_1_HEIGHT, FACET_MAIN_VALUE_2_HEIGHT, FACET_MAIN_VALUE_3_HEIGHT }[index];
+        return new SInfoResultFacet(0, facetName, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDischargeValuesFinder.java	Mon Jul 02 07:33:53 2018 +0200
@@ -44,14 +44,16 @@
     /***** CONSTRUCTORS *****/
 
     private GaugeDischargeValuesFinder(final Gauge gauge, final Calculation problems, final DischargeTable dischargeTable) {
+        // Load W-Q-values from database
         this.gauge = gauge;
         this.problems = problems;
         final TDoubleArrayList ws = new TDoubleArrayList();
         final TDoubleArrayList qs = new TDoubleArrayList();
-        for (final DischargeTableValue v : DischargeTable.getValuesSortedByW(dischargeTable)) {
+        for (final DischargeTableValue v : DischargeTable.fetchValuesSortedByW(dischargeTable)) {
             ws.add(v.getW().doubleValue());
             qs.add(v.getQ().doubleValue());
         }
+        // Build interpolator
         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));
@@ -71,12 +73,12 @@
     /***** METHODS *****/
 
     /**
-     * Loads the the main discharge table of a gauge (GAUGE.at)
+     * 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);
+        final DischargeTable table = gauge.fetchMasterDischargeTable();
         if ((table == null) || (table.getDischargeTableValues().size() == 0)) {
             problems.addProblem("gauge_discharge_table.missing", gauge.getName());
             return null;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDurationValuesFinder.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeDurationValuesFinder.java	Mon Jul 02 07:33:53 2018 +0200
@@ -21,7 +21,7 @@
 import gnu.trove.TDoubleArrayList;
 
 /**
- * Loading and search/interpolate the duration main values of a gauge
+ * Search/interpolation of the duration main values of a gauge
  *
  * @author Matthias Schäfer
  *
@@ -49,6 +49,7 @@
     /***** CONSTRUCTORS *****/
 
     private GaugeDurationValuesFinder(final Gauge gauge, final Calculation problems) {
+        // Load the duration main values from the database (each duration has a Q)
         this.gauge = gauge;
         this.problems = problems;
         final TDoubleArrayList qs = new TDoubleArrayList();
@@ -57,6 +58,7 @@
             qs.add(v.getValue().doubleValue());
             durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue());
         }
+        // Build the duration-by-Q interpolator
         try {
             this.qInterpolator = new LinearInterpolator().interpolate(qs.toNativeArray(), durs.toNativeArray());
             this.qRange = new DoubleRange(qs.get(0), qs.get(qs.size() - 1));
@@ -65,12 +67,14 @@
             this.qInterpolator = null;
             this.qRange = null;
         }
+        // Load the Q values by duration from the database
         qs.clear();
         durs.clear();
         for (final MainValue v : MainValue.getDurationDischargesOfGauge(gauge)) {
             durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue());
             qs.add(v.getValue().doubleValue());
         }
+        // Build the Q-by-duration interpolator
         try {
             this.durInterpolator = new LinearInterpolator().interpolate(durs.toNativeArray(), qs.toNativeArray());
             this.durRange = new DoubleRange(durs.get(0), durs.get(durs.size() - 1));
@@ -79,6 +83,7 @@
             this.durInterpolator = null;
             this.durRange = null;
         }
+        // Report problems
         if (((this.qInterpolator == null) || (this.durInterpolator == null)) && (this.problems != null)) {
             this.problems.addProblem("gauge_duration.missing", gauge.getName());
             // Report only once
@@ -89,7 +94,7 @@
     /***** METHODS *****/
 
     /**
-     * Loads the the discharge-duration table of a gauge (GAUGE.glt)
+     * Loads the the discharge-duration table of a gauge ({gauge}.sta)
      *
      * @return The main values finder of a a gauge, or null
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueFinder.java	Mon Jul 02 07:33:53 2018 +0200
@@ -0,0 +1,239 @@
+/** 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 the main values of a gauge to find relative positions of a value and build a corresponding zone name
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class GaugeMainValueFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(GaugeMainValueNameFinder.class);
+
+    private final Gauge gauge;
+
+    private Calculation problems;
+
+    private final NavigableMap<Double, MainValue> mainValues;
+
+    private final MainValueTypeKey keyType;
+
+    private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht
+
+    private Entry<Double, MainValue> foundCeiling;
+
+    private Entry<Double, MainValue> foundFloor;
+
+    private double foundRelativeDistance;
+
+
+    /***** CONSTRUCTORS *****/
+
+    private GaugeMainValueFinder(final MainValueTypeKey keyType, final Gauge gauge, final Calculation problems) {
+        this.gauge = gauge;
+        this.problems = problems;
+        this.keyType = keyType;
+        this.mainValues = new TreeMap<>();
+        for (final MainValue mainValue : MainValue.getValuesOfGaugeAndType(gauge, keyType))
+            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.sta)
+     *
+     * @return The main values finder of a type and a gauge, or null
+     */
+    public static GaugeMainValueFinder loadValues(final MainValueTypeKey type, final Gauge gauge, final Calculation problems) {
+        return new GaugeMainValueFinder(type, gauge, problems);
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.mainValues != null);
+    }
+
+    /**
+     * Searches the main value zone for a value, and returns a textual description of the zone
+     * (name for an exact match, circa expression for +/- 10% match, less-than/between/greater-than expression otherwise)
+     */
+    public String findZoneName(final double value) {
+        if (!this.findValue(value))
+            return "";
+
+        // Clearly below or just (max. 10%) below lowest named value
+        if (this.foundFloor == null) {
+            if (Double.isInfinite(this.foundRelativeDistance))
+                return "<" + this.foundCeiling.getValue().getMainValue().getName();
+            else
+                return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
+        }
+
+        // Clearly above or just (max. 10%) above highest named value
+        if (this.foundCeiling == null) {
+            if (Double.isInfinite(this.foundRelativeDistance))
+                return ">" + this.foundFloor.getValue().getMainValue().getName();
+            else
+                return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
+        }
+
+        // Exact match
+        if (this.mainValues.containsKey(Double.valueOf(value)))
+            return this.mainValues.get(Double.valueOf(value)).getMainValue().getName();
+
+        // Near (10%) one of the borders of a zone interval, or clearly within a zone
+        if (this.foundRelativeDistance <= 0.001)
+            return this.foundFloor.getValue().getMainValue().getName();
+        else if (this.foundRelativeDistance <= 0.1)
+            return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
+        else if (this.foundRelativeDistance >= 0.9)
+            return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
+        else
+            return this.foundFloor.getValue().getMainValue().getName() + "-" + this.foundCeiling.getValue().getMainValue().getName();
+    }
+
+    /**
+     * Searches the main value zone for a value, and returns the zone name for an exact match, the nomatchReturn otherwise
+     */
+    public String findExactZoneName(final double value, final String noMatchReturn) {
+        this.findValue(value);
+        if ((this.foundFloor != null) && (this.foundFloor.getKey() == this.foundCeiling.getKey()))
+            return this.foundFloor.getValue().getMainValue().getName();
+        else
+            return noMatchReturn;
+    }
+
+    /**
+     * Searches the interval of a main value and its relative distance from the lower value
+     */
+    public boolean findValue(final double value) {
+        this.foundFloor = null;
+        this.foundCeiling = null;
+        this.foundRelativeDistance = Double.NaN;
+        if (!this.isValid())
+            return false;
+        if (Double.isNaN(value))
+            return false;
+
+        // Clearly below or just (max. 10%) below lowest named value
+        this.foundFloor = this.mainValues.floorEntry(Double.valueOf(value));
+        if (this.foundFloor == null) {
+            this.foundCeiling = this.mainValues.firstEntry();
+            if (value >= this.mainValues.firstKey().doubleValue() * 0.9) {
+                this.foundRelativeDistance = 0.9;
+                return true;
+            }
+            else {
+                this.foundRelativeDistance = Double.NEGATIVE_INFINITY;
+                return false;
+            }
+        }
+
+        // Clearly above or just (max. 10%) above highest named value
+        this.foundCeiling = this.mainValues.ceilingEntry(Double.valueOf(value));
+        if (this.foundCeiling == null) {
+            if (value <= this.mainValues.lastKey().doubleValue() * 1.1) {
+                this.foundRelativeDistance = 0.1;
+                return true;
+            }
+            else {
+                this.foundRelativeDistance = Double.POSITIVE_INFINITY;
+                return false;
+            }
+        }
+
+        // Exact match or within an interval
+        if (this.foundCeiling.getKey() == this.foundFloor.getKey())
+            this.foundRelativeDistance = 0.0;
+        else
+            this.foundRelativeDistance = (value - this.foundFloor.getKey().doubleValue())
+            / (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue());
+        return true;
+    }
+
+    /**
+     * Floor value of the last findValue
+     */
+    public MainValue getFoundFloorValue() {
+        if (this.foundFloor != null)
+            return this.foundFloor.getValue();
+        else
+            return null;
+    }
+
+    /**
+     * Ceiling value of the last findValue
+     */
+    public MainValue getFoundCeilingValue() {
+        if (this.foundCeiling != null)
+            return this.foundCeiling.getValue();
+        else
+            return null;
+    }
+
+    /**
+     * Relative distance of the last findValue
+     */
+    public double getFoundRelativeDistance() {
+        return this.getFoundRelativeDistance();
+    }
+
+    /**
+     * Searches a pair of zone names and return the a value within the interval by a relative distance, or NaN
+     */
+    public double findValue(final String floorZone, final String ceilingZone, final double relativeDistance) {
+        this.foundFloor = null;
+        this.foundCeiling = null;
+        this.foundRelativeDistance = relativeDistance;
+        for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
+            if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(floorZone)) {
+                this.foundFloor = mainValue;
+                break;
+            }
+        }
+        if (this.foundFloor == null)
+            return Double.NaN;
+        if (floorZone.equalsIgnoreCase(ceilingZone))
+            return this.foundFloor.getKey().doubleValue();
+        for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
+            if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(ceilingZone)) {
+                this.foundCeiling = mainValue;
+                break;
+            }
+        }
+        if (this.foundCeiling == null)
+            return Double.NaN;
+        else
+            return (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue()) * this.foundRelativeDistance
+                    + this.foundFloor.getKey().doubleValue();
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueNameFinder.java	Sun Jul 01 15:29:40 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
- * Software engineering by
- *  Björnsen Beratende Ingenieure GmbH
- *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-package org.dive4elements.river.artifacts.sinfo.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 = "\u2248";// "ca.";
-
-    /***** 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/SInfoI18NStrings.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoI18NStrings.java	Mon Jul 02 07:33:53 2018 +0200
@@ -75,7 +75,9 @@
 
     String PREFIX_TKH_KIND = "sinfo.export.tkh.soilkind.";
 
-    String CSV_INFRASTRUCTURE_HEIGHT_HEADER = "sinfo.export.csv.header.infrastructure.height";
+    String CSV_INFRASTRUCTURE_HEIGHT_HEADER = "sinfo.export.flood_duration.csv.header.infrastructure.height";
+
+    String PDF_INFRASTRUCTURE_HEIGHT_HEADER = "sinfo.export.flood_duration.pdf.header.infrastructure.height";
 
     String CSV_COLLISION_COUNT_HEADER = "sinfo.export.csv.header.collision.count";
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultFacet.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultFacet.java	Mon Jul 02 07:33:53 2018 +0200
@@ -20,7 +20,7 @@
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
 /**
- * Facet of a FlowDepth curve.
+ * Facet of one of the S-Info curves.
  */
 public final class SInfoResultFacet extends DataFacet {
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Mon Jul 02 07:33:53 2018 +0200
@@ -87,8 +87,40 @@
         }
     };
 
-    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") {
+    public static final SInfoResultType mainValue1Duration = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.1.duration",
+            "sinfo.flood_duration.header.pdf.mainvalue.1.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
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getIntegerFormatter(context);
+        }
+    };
+
+    public static final SInfoResultType mainValue2Duration = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.2.duration",
+            "sinfo.flood_duration.header.pdf.mainvalue.2.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
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getIntegerFormatter(context);
+        }
+    };
+
+    public static final SInfoResultType mainValue3Duration = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.3.duration",
+            "sinfo.flood_duration.header.pdf.mainvalue.3.duration") {
         private static final long serialVersionUID = 1L;
 
         @Override
@@ -121,6 +153,54 @@
         }
     };
 
+    public static final SInfoResultType waterlevel1 = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.1.w",
+            "sinfo.flood_duration.header.pdf.mainvalue.1.w") {
+        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
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getFlowDepth(context);
+        }
+    };
+
+    public static final SInfoResultType waterlevel2 = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.2.w",
+            "sinfo.flood_duration.header.pdf.mainvalue.2.w") {
+        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
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getFlowDepth(context);
+        }
+    };
+
+    public static final SInfoResultType waterlevel3 = new SInfoResultType(null, "sinfo.flood_duration.header.mainvalue.3.w",
+            "sinfo.flood_duration.header.pdf.mainvalue.3.w") {
+        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
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getFlowDepth(context);
+        }
+    };
+
     public static final SInfoResultType waterlevelLabel = new SInfoResultType(I18NStrings.UNIT_NONE, SInfoI18NStrings.CSV_LABEL_HEADER) {
         private static final long serialVersionUID = 1L;
 
@@ -166,6 +246,57 @@
         }
     };
 
+    public static final SInfoResultType discharge1 = new SInfoResultType(I18NStrings.UNIT_CUBIC_M, "sinfo.flood_duration.header.mainvalue.1.q",
+            "sinfo.flood_duration.header.pdf.mainvalue.1.q") {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String exportValue(final CallContext context, final Object value) {
+            final double doubleValue = asDouble(value);
+            final double roundedDischarge = RiverUtils.roundQ(doubleValue);
+            return exportDoubleValue(context, roundedDischarge);
+        }
+
+        @Override
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getWaterlevelQ(context);
+        }
+    };
+
+    public static final SInfoResultType discharge2 = new SInfoResultType(I18NStrings.UNIT_CUBIC_M, "sinfo.flood_duration.header.mainvalue.2.q",
+            "sinfo.flood_duration.header.pdf.mainvalue.2.q") {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String exportValue(final CallContext context, final Object value) {
+            final double doubleValue = asDouble(value);
+            final double roundedDischarge = RiverUtils.roundQ(doubleValue);
+            return exportDoubleValue(context, roundedDischarge);
+        }
+
+        @Override
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getWaterlevelQ(context);
+        }
+    };
+
+    public static final SInfoResultType discharge3 = new SInfoResultType(I18NStrings.UNIT_CUBIC_M, "sinfo.flood_duration.header.mainvalue.3.q",
+            "sinfo.flood_duration.header.pdf.mainvalue.3.q") {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public String exportValue(final CallContext context, final Object value) {
+            final double doubleValue = asDouble(value);
+            final double roundedDischarge = RiverUtils.roundQ(doubleValue);
+            return exportDoubleValue(context, roundedDischarge);
+        }
+
+        @Override
+        protected NumberFormat createFormatter(final CallContext context) {
+            return Formatter.getWaterlevelQ(context);
+        }
+    };
+
     public static final SInfoResultType meanBedHeight = new SInfoResultType(null, SInfoI18NStrings.CSV_MEAN_BED_HEIGHT_HEADER,
             SInfoI18NStrings.CSV_MEAN_BED_HEIGHT_HEADER_SHORT) {
         private static final long serialVersionUID = 1L;
@@ -545,23 +676,8 @@
         }
     };
 
-    public static final SInfoResultType infrastructureHeightFloodDur = new SInfoResultType(I18NStrings.UNIT_M,
-            "sinfo.export.flood_duration.csv.header.infrastructure.height", "sinfo.export.flood_duration.pdf.header.infrastructure.height") {
-        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
-        protected NumberFormat createFormatter(final CallContext context) {
-            return Formatter.getInfrastructureHeight(context);
-        }
-    };
-
-    public static final SInfoResultType infrastructureHeight = new SInfoResultType(I18NStrings.UNIT_M, SInfoI18NStrings.CSV_INFRASTRUCTURE_HEIGHT_HEADER) {
+    public static final SInfoResultType infrastructureHeight = new SInfoResultType(I18NStrings.UNIT_M, SInfoI18NStrings.CSV_INFRASTRUCTURE_HEIGHT_HEADER,
+            SInfoI18NStrings.PDF_INFRASTRUCTURE_HEIGHT_HEADER) {
         private static final long serialVersionUID = 1L;
 
         @Override
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Mon Jul 02 07:33:53 2018 +0200
@@ -11,13 +11,16 @@
 
 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.RiverInfo;
 import org.dive4elements.river.model.River;
 import org.dive4elements.river.model.sinfo.Infrastructure;
 
@@ -41,6 +44,7 @@
         /* 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();
 
         final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
@@ -51,8 +55,20 @@
 
         final Calculation problems = new Calculation();
 
-        final FloodDurationCalculationResults results = calculateResult(calcModeLabel, infrasType, riverside, calcRange, infoProvider,
-                RiversideChoiceKey.fromKey(access.getRiverside()), user, problems);
+        // Calculate the selected main values, if any
+        /* misuse winfo-artifact to calculate waterlevels in the same way */
+        final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
+        int mainValueCount = 0;
+        if (winfo.isW())
+            mainValueCount = winfo.getWs().length;
+        else if (winfo.isQ())
+            mainValueCount = winfo.getQs().length;
+
+        final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange, mainValueCount);
+
+        final FloodDurationCalculationResult result = calculateResult(infrasType, riverside, calcRange, infoProvider,
+                RiversideChoiceKey.fromKey(access.getRiverside()), problems, winfo);
+        results.addResult(result, problems);
 
         return new CalculationResult(results, problems);
     }
@@ -60,12 +76,11 @@
     /**
      * Calculates the flood durations of the infrastructures of a km range of a river
      */
-    private FloodDurationCalculationResults 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) {
+    private FloodDurationCalculationResult calculateResult(final String infrastructureType, final String riverside, final DoubleRange calcRange,
+            final RiverInfoProvider riverInfoProvider, final RiversideChoiceKey riversideKey, final Calculation problems, final WINFOArtifact winfo) {
 
         final FloodDurationCalculator calculator = new FloodDurationCalculator(this.context, riverInfoProvider);
         final String label = infrastructureType + ", " + riverside;
-        return calculator.execute(problems, label, calcModeLabel, calcRange, riversideKey, user);
+        return calculator.execute(problems, label, calcRange, riversideKey, winfo);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Mon Jul 02 07:33:53 2018 +0200
@@ -11,38 +11,108 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
 import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
 import org.dive4elements.river.artifacts.common.ExportContextCSV;
 import org.dive4elements.river.artifacts.common.ExportContextPDF;
 import org.dive4elements.river.artifacts.common.GeneralResultType;
 import org.dive4elements.river.artifacts.common.IExportContext;
+import org.dive4elements.river.artifacts.common.IResultType;
 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 import org.dive4elements.river.artifacts.common.ResultRow;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoI18NStrings;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 
+import gnu.trove.TDoubleArrayList;
+
 /**
  * Contains the result of a {@link FloodDurationCalculation}.
  *
  * @author Gernot Belger
  */
-final class FloodDurationCalculationResult extends AbstractCalculationExportableResult {
+public final class FloodDurationCalculationResult extends AbstractCalculationExportableResult {
 
     private static final long serialVersionUID = 1L;
 
     private static final String JASPER_FILE = "/jasper/templates/sinfo.floodduration.jrxml";
 
-    public FloodDurationCalculationResult(final String label, final Collection<ResultRow> rows) {
+    private final String[] mainvalueLabels;
+
+    public FloodDurationCalculationResult(final String label, final String[] mainvalueLabels, final Collection<ResultRow> rows) {
         super(label, rows);
+        this.mainvalueLabels = mainvalueLabels;
+    }
+
+    /**
+     * The label of one of the optional main values, or null
+     */
+    public String getMainValueLabel(final int index) {
+        if (index <= this.mainvalueLabels.length - 1)
+            return this.mainvalueLabels[index];
+        else
+            return null;
+    }
+
+    /**
+     * Collection of the result rows containing only the rows describing an infrastructure
+     */
+    @Override
+    public Collection<ResultRow> getRows() {
+        final List<ResultRow> infrasOnlyRows = new ArrayList<>();
+        for (final ResultRow row : this.rows)
+            if (row.getValue(SInfoResultType.infrastructuretype) != null)
+                infrasOnlyRows.add(row);
+        return Collections.unmodifiableCollection(infrasOnlyRows);
+    }
+
+    /**
+     * Fetches the km-longitudinal section of the infrastructures and one of their result fields
+     */
+    public final double[][] fetchInfrastructurePoints(final IResultType type) {
+        final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size());
+        final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size());
+        for (final ResultRow row : this.rows) {
+            if (row.getValue(SInfoResultType.infrastructuretype) != null) {
+                xPoints.add(row.getDoubleValue(GeneralResultType.station));
+                yPoints.add(row.getDoubleValue(type));
+            }
+        }
+        return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() };
+    }
+
+    /**
+     * Fetches the km-longitudinal section of a main value
+     */
+    public final double[][] fetchMainValuePoints(final IResultType type) {
+        final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size());
+        final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size());
+        // final IResultType check = new IResultType[] { SInfoResultType.mainValue1Duration, SInfoResultType.mainValue2Duration,
+        // SInfoResultType.mainValue3Duration }[index];
+        for (final ResultRow row : this.rows) {
+            // if (!Double.isNaN(row.getDoubleValue(check))) {
+            xPoints.add(row.getDoubleValue(GeneralResultType.station));
+            yPoints.add(row.getDoubleValue(type));
+            // }
+        }
+        return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() };
     }
 
     @Override
     protected void writeCSVResultMetadata(final ExportContextCSV exportContextCSV) {
-        // TODO Metadaten der Wasserspiegellage(n) falls gewählt
-        // exportContextCSV.writeCSVWaterlevelMetadata(this.wstInfo);
-        // exportContextCSV.writeBlankLine();
-        // writer.writeNext(new String[] { "" }); // break line
+        if (this.mainvalueLabels.length >= 1) {
+            // "##METADATEN WASSERSPIEGELLAGE"
+            exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL);
+            for (int i = 1; i <= this.mainvalueLabels.length; i++) {
+                // "# Bezeichnung der Wasserspiegellage: "
+                final String label = this.getMainValueLabel(i - 1);
+                exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_NAME, String.format("%d: %s", i, label));
+            }
+            // "# Bezugspegel: "
+            exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_GAUGE, "TODO: gauge");
+        }
     }
 
     @Override
@@ -62,7 +132,21 @@
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructureHeight));
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructuretype));
 
-        // TODO Wasserspiegellage(n) und Dauerzahlen falls gewählt
+        if (this.getMainValueLabel(0) != null) {
+            lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.waterlevel1));
+            lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.mainValue1Duration));
+            lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.discharge1));
+            if (this.getMainValueLabel(1) != null) {
+                lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.waterlevel2));
+                lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.mainValue2Duration));
+                lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.discharge2));
+                if (this.getMainValueLabel(2) != null) {
+                    lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.waterlevel3));
+                    lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.mainValue3Duration));
+                    lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.discharge3));
+                }
+            }
+        }
 
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.gaugeLabel));
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.location));
@@ -82,7 +166,21 @@
         header.add(exportContextCSV.msgUnitCSV(SInfoResultType.infrastructureHeight, SInfoResultType.infrastructureHeight.getUnit()));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.infrastructuretype));
 
-        // TODO Je vier Spalten der bis zu drei Wasserspiegellagen
+        if (this.getMainValueLabel(0) != null) {
+            header.add(exportContextCSV.msgUnitCSV(SInfoResultType.waterlevel1, SInfoResultType.waterlevel.getUnit()));
+            header.add(exportContextCSV.formatCsvHeader(SInfoResultType.mainValue1Duration));
+            header.add(exportContextCSV.msgUnitCSV(SInfoResultType.discharge1, SInfoResultType.discharge.getUnit()));
+            if (this.getMainValueLabel(1) != null) {
+                header.add(exportContextCSV.msgUnitCSV(SInfoResultType.waterlevel2, SInfoResultType.waterlevel2.getUnit()));
+                header.add(exportContextCSV.formatCsvHeader(SInfoResultType.mainValue2Duration));
+                header.add(exportContextCSV.msgUnitCSV(SInfoResultType.discharge2, SInfoResultType.discharge2.getUnit()));
+                if (this.getMainValueLabel(2) != null) {
+                    header.add(exportContextCSV.msgUnitCSV(SInfoResultType.waterlevel3, SInfoResultType.waterlevel3.getUnit()));
+                    header.add(exportContextCSV.formatCsvHeader(SInfoResultType.mainValue3Duration));
+                    header.add(exportContextCSV.msgUnitCSV(SInfoResultType.discharge3, SInfoResultType.discharge3.getUnit()));
+                }
+            }
+        }
 
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.gaugeLabel));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.location));
@@ -111,10 +209,25 @@
         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_height_header", SInfoResultType.infrastructureHeight);
         exportContextPDF.addJRMetadata(source, "infrastructure_type_header", SInfoResultType.infrastructuretype);
 
-        // TODO Je vier Spalten der bis zu drei Wasserspiegellagen
+        // TODO Feldnamen ergaenzen und aktivieren wenn Report fertig
+        // if (this.getMainValueLabel(0) != null) {
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.waterlevel);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.mainValue1Duration);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.discharge);
+        // if (this.getMainValueLabel(1) != null) {
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.waterlevel2);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.mainValue2Duration);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.discharge2);
+        // if (this.getMainValueLabel(2) != null) {
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.waterlevel3);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.mainValue3Duration);
+        // exportContextPDF.addJRMetadata(source, "?", SInfoResultType.discharge3);
+        // }
+        // }
+        // }
 
         exportContextPDF.addJRMetadata(source, "gauge_header", SInfoResultType.gaugeLabel);
         exportContextPDF.addJRMetadata(source, "location_header", SInfoResultType.location);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResults.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResults.java	Mon Jul 02 07:33:53 2018 +0200
@@ -20,7 +20,15 @@
 
     private static final long serialVersionUID = 1L;
 
-    public FloodDurationCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange) {
+    private final int mainValueCount;
+
+    public FloodDurationCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange,
+            final int mainValueCount) {
         super(calcModeLabel, user, river, calcRange);
+        this.mainValueCount = mainValueCount;
+    }
+
+    public int getMainValueCount() {
+        return this.mainValueCount;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jul 02 07:33:53 2018 +0200
@@ -12,23 +12,33 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.commons.lang.math.DoubleRange;
 import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.WINFOArtifact;
+import org.dive4elements.river.artifacts.access.ComputationRangeAccess;
 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.model.Calculation.Problem;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WQKms;
 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
+import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder;
 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.model.Attribute.AttributeKey;
 import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
 import org.dive4elements.river.model.sinfo.InfrastructureValue;
 
+import gnu.trove.TDoubleArrayList;
+
 /**
  * Calculation of the result rows of the flood duration of the infrastructures in a river km range
  * and selected main value durations
@@ -50,60 +60,207 @@
     }
 
     /**
-     * Calculate the result rows
-     *
-     * @return a result collection with one result
+     * Calculate the infrastructures flood duration result rows
      */
-    public FloodDurationCalculationResults execute(final Calculation problems, final String label, final String calcModeLabel,
-            final DoubleRange calcRange, final RiversideChoiceKey riverside, final String user) {
+    public FloodDurationCalculationResult execute(final Calculation problems, final String label, final DoubleRange calcRange,
+            final RiversideChoiceKey riverside, final WINFOArtifact winfo) {
 
-        final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
-                calcRange.getMaximumDouble(), problems);
+        // Find all gauges of the calc range, and create the duration finders
         final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>();
+        Gauge firstGauge = null;
         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);
+            if (firstGauge == null)
+                firstGauge = gauge;
         }
 
-        final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, this.rows);
+        // Find all infrastructures within the calc range
+        final AttributeKey bankKey = riverside.getAttributeKey();
+        final List<InfrastructureValue> infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
+                calcRange.getMaximumDouble(), bankKey);
 
-        final RiverInfo riverInfo = new RiverInfo(this.riverInfoProvider.getRiver());
+        // Merge all stations (range/step, borders of gauge ranges, infrastructures)
+        final Map<Double, InfrastructureValue> allStations = new HashMap<>();
+        final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option
+        addRangeStations(allStations, winfo);
+        addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
+        addInfrastructures(allStations, secondBank, infras);
+        final double[] stationsSorted = sortStations(allStations.keySet());
 
-        final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange);
-        results.addResult(result, problems);
-        return results;
+        // Calculate W and Q for all stations and the selected discharge states
+        final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
+
+        // Determine discharge state labels of the main values
+        final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), firstGauge, problems);
+
+        // Create a finder for Q in the {river}.wst km-w-q table
+        final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
+                calcRange.getMaximumDouble(), problems);
+
+        // Calculate the durations and create the result rows
+        for (int i = 0; i <= stationsSorted.length - 1; i++) {
+            final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
+            final ResultRow row = createRow(stationsSorted[i], gauge, wqkmsArray, durFinders.get(gauge), i);
+            if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
+                calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders);
+            this.rows.add(row);
+            if (secondBank.containsKey(stationsSorted[i])) {
+                final ResultRow row2 = ResultRow.create(row);
+                calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders);
+                this.rows.add(row2);
+            }
+        }
+
+        return new FloodDurationCalculationResult(label, mainValueLabels, this.rows);
     }
 
     /**
-     * Calculate the result row for one infrastructure
+     * Adds to a stations map all stations corresponding to the active range and step
      */
-    private void calculateResultRow(final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder,
-            final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
+    private void addRangeStations(final Map<Double, InfrastructureValue> allStations, final WINFOArtifact winfo) {
+        for (final double station : new ComputationRangeAccess(winfo).getKms())
+            allStations.put(Double.valueOf(station), null);
+    }
+
+    /**
+     * Adds to a stations map all range limits of the gauges within the calc range
+     */
+    private void addGaugeLimits(final Map<Double, InfrastructureValue> allStations, final Set<Gauge> gauges, final double fromKm, final double toKm) {
+        for (final Gauge gauge : gauges) {
+            final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue());
+            final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue());
+            if (kmA > fromKm - 0.0001)
+                allStations.put(kmA, null);
+            if (kmB < toKm + 0.0001)
+                allStations.put(kmB, null);
+        }
+    }
+
+    /**
+     * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map
+     */
+    private void addInfrastructures(final Map<Double, InfrastructureValue> allStations, final Map<Double, InfrastructureValue> secondBank,
+            final List<InfrastructureValue> infrastructures) {
+        for (final InfrastructureValue infrastructure : infrastructures) {
+            final Double station = infrastructure.getStation();
+            if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue))
+                allStations.put(station, infrastructure);
+            else
+                secondBank.put(station, infrastructure);
+        }
+    }
+
+    /**
+     * Returns a double array with a sorted stations set
+     */
+    private double[] sortStations(final Set<Double> stations) {
+        final TDoubleArrayList sorted = new TDoubleArrayList();
+        for (final Double station : stations)
+            sorted.add(station.doubleValue());
+        sorted.sort();
+        return sorted.toNativeArray();
+    }
+
+    /**
+     * Calculates an array of w-q-longitudinal sections for all artifact W/Q options
+     */
+    private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) {
+        // REMARK aus TkhCalculation - move to WinfoArtifactWrapper?
+        // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen)
+        // (So funktioniert computeWaterlevelData wohl:
+        // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.)
+        // und interpoliert für diese horizontale Tabellenposition jeweils die vertikale Tabellenposition der station;
+        // das ergibt das W einer station für einen Abflusszustand;
+        // bei Vorgabe eines Pegel-W wird vorher anhand der W-Q-Tabelle des Pegels ({gauge}.at in DischargeTable) das Q
+        // interpoliert;
+        // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt.
+        final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations);
+
+        /* copy all problems */
+        final Calculation winfoProblems = waterlevelData.getReport();
+        final List<Problem> problems2 = winfoProblems.getProblems();
+        if (problems2 != null) {
+            for (final Problem problem : problems2) {
+                problems.addProblem(problem);
+            }
+        }
+        return (WQKms[]) waterlevelData.getData();
+    }
+
+    /**
+     * Determines the discharge state labels for the selected Q or W values
+     */
+    private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final Gauge gauge, final Calculation problems) {
+        final String[] mainValueLabels = new String[wqkmsArray.length];
+        if (wqkmsArray.length >= 1) {
+            // Labels like Q=123 or W=123
+            for (int i = 0; i <= wqkmsArray.length - 1; i++)
+                mainValueLabels[i] = wqkmsArray[i].getName();
+            // Replace labels for named main Q values
+            final GaugeMainValueFinder zoneFinder = GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, gauge, problems);
+            if ((zoneFinder != null) && (qs != null)) {
+                for (int i = 0; i <= qs.length - 1; i++)
+                    mainValueLabels[i] = zoneFinder.findExactZoneName(qs[i], mainValueLabels[i]);
+            }
+        }
+        return mainValueLabels;
+    }
+
+    /**
+     * Create a result row for a station and its gauge, and add w-q-values as selected
+     */
+    private ResultRow createRow(final Double station, final Gauge gauge, final WQKms[] wqkmsArray,
+            final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
 
         final ResultRow row = ResultRow.create();
+        row.putValue(GeneralResultType.station, station);
+        row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure
+        row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure
+        row.putValue(SInfoResultType.gaugeLabel, gauge.getName());
+        final String location = this.riverInfoProvider.getLocation(station);
+        row.putValue(SInfoResultType.location, location);
 
-        final Gauge gauge = this.riverInfoProvider.getGauge(infrastructure.getStation(), true);
+        if (wqkmsArray.length >= 1) {
+            assert (wqkmsArray[0].getKm(kmIndex) == station.doubleValue());
+            row.putValue(SInfoResultType.waterlevel1, wqkmsArray[0].getW(kmIndex));
+            row.putValue(SInfoResultType.discharge1, wqkmsArray[0].getQ(kmIndex));
+            row.putValue(SInfoResultType.mainValue1Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[0].getQ(kmIndex))));
+            if (wqkmsArray.length >= 2) {
+                assert (wqkmsArray[1].getKm(kmIndex) == station.doubleValue());
+                row.putValue(SInfoResultType.waterlevel2, wqkmsArray[1].getW(kmIndex));
+                row.putValue(SInfoResultType.discharge2, wqkmsArray[1].getQ(kmIndex));
+                row.putValue(SInfoResultType.mainValue2Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[1].getQ(kmIndex))));
+                if (wqkmsArray.length >= 3) {
+                    assert (wqkmsArray[2].getKm(kmIndex) == station.doubleValue());
+                    row.putValue(SInfoResultType.waterlevel3, wqkmsArray[2].getW(kmIndex));
+                    row.putValue(SInfoResultType.discharge3, wqkmsArray[2].getQ(kmIndex));
+                    row.putValue(SInfoResultType.mainValue3Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[2].getQ(kmIndex))));
+                }
+            }
+        }
+        return row;
+    }
+
+    /**
+     * Calculate the result row fields for one infrastructure
+     */
+    private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure,
+            final WQBaseTableFinder wqFinder, final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
+
         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(infrastructure.getStation());
-        row.putValue(GeneralResultType.station, infrastructure.getStation());
+        final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q));
         row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n
-        row.putValue(SInfoResultType.floodDuration, 365 - durFinders.get(gauge).getDuration(q));
+        row.putValue(SInfoResultType.floodDuration, dur);
         row.putValue(SInfoResultType.floodDischarge, qOut);
         row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
         row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
-
-        // TODO Berechne W, Überflutungsdauer, Q und Bezeichnung von bis zu drei WSPL
-        // row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps);
+    }
 
-        row.putValue(SInfoResultType.gaugeLabel, gauge.getName());
-        row.putValue(SInfoResultType.location, location);
-
-        this.rows.add(row);
+    /**
+     * Translates underflow duration into overflow duration
+     */
+    private double underflowDaysToOverflowDays(final double underflowDays) {
+        return 365 - underflowDays;
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Mon Jul 02 07:33:53 2018 +0200
@@ -15,7 +15,7 @@
 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 
 /**
- * Generates different output formats (csv, pdf) of data that resulted from a flow depths min/max computation.
+ * Generates different output formats (csv, pdf) of data that resulted from a flood duration computation.
  *
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  * @author Gernot Belger
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Mon Jul 02 07:33:53 2018 +0200
@@ -22,6 +22,7 @@
 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.sinfo.common.FloodHeightProcessor;
 import org.dive4elements.river.artifacts.states.DefaultState;
 
 /**
@@ -79,7 +80,15 @@
         for (final FloodDurationCalculationResult result : resultList) {
 
             facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, index));
-            facets.add(FloodDurationProcessor.createMainValueDurationFacet(context, hash, this.id, result, index));
+            for (int j = 0; j <= 2; j++) {
+                if (result.getMainValueLabel(j) != null)
+                    facets.add(FloodDurationProcessor.createMainValueDurationFacet(context, hash, this.id, result, j));
+            }
+            facets.add(FloodHeightProcessor.createFloodHeightFacet(context, hash, this.id, result, index));
+            for (int j = 0; j <= 2; j++) {
+                if (result.getMainValueLabel(j) != null)
+                    facets.add(FloodHeightProcessor.createMainValueHeightFacet(context, hash, this.id, result, j));
+            }
 
             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));
--- a/artifacts/src/main/resources/messages.properties	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties	Mon Jul 02 07:33:53 2018 +0200
@@ -920,6 +920,7 @@
 sinfo.export.flow_depth.csv.header.mean_bed_height.short = Mittlere Sohl-
 sinfo.export.flow_depth.csv.header.sounding = Peilung/Epoche
 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.duration = \u00dcberflutungsdauer [d/a]
@@ -1004,10 +1005,10 @@
 sinfo.flood_duration.header.pdf.bezeichnung_index = Bezeich-nung{0}
 sinfo.flood_duration.header.fd_per_year_index = \u00dcberflutungsdauer WSPL{0} [d/a]
 sinfo.flood_duration.header.pdf.fd_per_year_index = \u00dcberflu-tungs-dauer WSPL{0} [d/a]
-sinfo.flood_duration.header.pdf.fd_per_year_index = \u00dcberflu-tungs-dauer WSPL{0} [d/a]
 sinfo.flood_duration.header.w_index = Wasser-stand/Wasser-spiegel-lage{0}
 sinfo.flood_duration.header.pdf.w_index = Wasser-stand/ Wasser-spiegel-lage{0}
-sinfo.flood_duration.header.q_index =  Q{0} [m\u00b3/sec]
+sinfo.flood_duration.header.q_index =  Q{0}
+sinfo.flood_duration.header.pdf.q_index =  Q{0} [m\u00b3/s]
 
 sinfo.export.flow_depth_minmax.csv.header.min = Minimale Flie\u00dftiefe
 sinfo.export.flow_depth_minmax.csv.header.max = Maximale Flie\u00dftiefe
@@ -1135,8 +1136,35 @@
 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.1.duration = \u00dcberflutungsdauer-1
 mainvalue.duration.description = \u00dcberflutungsdauer ({0})
+mainvalue.2.duration = \u00dcberflutungsdauer-2
+mainvalue.3.duration = \u00dcberflutungsdauer-3
+mainvalue.1.w = W-1
+mainvalue.w.description = W ({0})
+mainvalue.2.w = W-2
+mainvalue.3.w = W-3
+sinfo.flood_duration.header.mainvalue.1.duration = \u00dcberflutungsdauer WSPL-1 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.1.duration = \u00dcberflu-tungs-dauer WSPL-1 [d/a]
+sinfo.flood_duration.header.mainvalue.1.w = Wasserstand/Wasserspiegel-lage-1
+sinfo.flood_duration.header.pdf.mainvalue.1.w = Wasser-stand/Wasser-spiegel-lage-1
+sinfo.flood_duration.header.mainvalue.1.q =  Q-1
+sinfo.flood_duration.header.pdf.mainvalue.1.q =  Q-1 [m\u00b3/s]
+sinfo.flood_duration.header.mainvalue.2.duration = \u00dcberflutungsdauer WSPL-2 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.2.duration = \u00dcberflu-tungs-dauer WSPL-2 [d/a]
+sinfo.flood_duration.header.mainvalue.2.w = Wasserstand/Wasserspiegel-lage-2
+sinfo.flood_duration.header.pdf.mainvalue.2.w = Wasser-stand/Wasser-spiegel-lage-2
+sinfo.flood_duration.header.mainvalue.2.q =  Q-2
+sinfo.flood_duration.header.pdf.mainvalue.2.q =  Q-2 [m\u00b3/s]
+sinfo.flood_duration.header.mainvalue.3.duration = \u00dcberflutungsdauer WSPL-3 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.3.duration = \u00dcberflu-tungs-dauer WSPL-3 [d/a]
+sinfo.flood_duration.header.mainvalue.3.w = Wasserstand/Wasserspiegel-lage-3
+sinfo.flood_duration.header.pdf.mainvalue.3.w = Wasser-stand/Wasser-spiegel-lage-3
+sinfo.flood_duration.header.mainvalue.3.q =  Q-3
+sinfo.flood_duration.header.pdf.mainvalue.3.q =  Q-3 [m\u00b3/s]
+sinfo.chart.flood_duration.height.section.title = H\u00f6he Infrastrukturen BWaStr
+sinfo_facet_flood_height = Geod\u00e4tische H\u00f6he Infrastrukturen BWaStr
+sinfo_facet_flood_height.description = Geod\u00e4tische H\u00f6he Infrastrukturen BWaStr ({0})
 
 bundu_bezugswst = Bezugswasserst\u00e4nde
 bundu_analysis = Fixinganalysis
--- a/artifacts/src/main/resources/messages_de.properties	Sun Jul 01 15:29:40 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Mon Jul 02 07:33:53 2018 +0200
@@ -920,6 +920,7 @@
 sinfo.export.flow_depth.csv.header.mean_bed_height.short = Mittlere Sohl-
 sinfo.export.flow_depth.csv.header.sounding = Peilung/Epoche
 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.duration = \u00dcberflutungsdauer [d/a]
@@ -1002,12 +1003,13 @@
 
 sinfo.flood_duration.header.bezeichnung_index = Bezeichnung{0}
 sinfo.flood_duration.header.pdf.bezeichnung_index = Bezeich-nung{0}
+sinfo.flood_duration.header.duration_index = \u00dcberflutungsdauer WSPL{0} [d/a]
 sinfo.flood_duration.header.fd_per_year_index = \u00dcberflutungsdauer WSPL{0} [d/a]
 sinfo.flood_duration.header.pdf.fd_per_year_index = \u00dcberflu-tungs-dauer WSPL{0} [d/a]
-sinfo.flood_duration.header.pdf.fd_per_year_index = \u00dcberflu-tungs-dauer WSPL{0} [d/a]
 sinfo.flood_duration.header.w_index = Wasser-stand/Wasser-spiegel-lage{0}
 sinfo.flood_duration.header.pdf.w_index = Wasser-stand/ Wasser-spiegel-lage{0}
-sinfo.flood_duration.header.q_index =  Q{0} [m\u00b3/sec]
+sinfo.flood_duration.header.q_index =  Q{0}
+sinfo.flood_duration.header.pdf.q_index =  Q{0} [m\u00b3/s]
 
 sinfo.export.flow_depth_minmax.csv.header.min = Minimale Flie\u00dftiefe
 sinfo.export.flow_depth_minmax.csv.header.max = Maximale Flie\u00dftiefe
@@ -1135,8 +1137,35 @@
 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.1.duration = \u00dcberflutungsdauer-1
 mainvalue.duration.description = \u00dcberflutungsdauer ({0})
+mainvalue.2.duration = \u00dcberflutungsdauer-2
+mainvalue.3.duration = \u00dcberflutungsdauer-3
+mainvalue.1.w = W-1
+mainvalue.w.description = W ({0})
+mainvalue.2.w = W-2
+mainvalue.3.w = W-3
+sinfo.flood_duration.header.mainvalue.1.duration = \u00dcberflutungsdauer WSPL-1 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.1.duration = \u00dcberflu-tungs-dauer WSPL-1 [d/a]
+sinfo.flood_duration.header.mainvalue.1.w = Wasserstand/Wasserspiegel-lage-1
+sinfo.flood_duration.header.pdf.mainvalue.1.w = Wasser-stand/Wasser-spiegel-lage-1
+sinfo.flood_duration.header.mainvalue.1.q =  Q-1
+sinfo.flood_duration.header.pdf.mainvalue.1.q =  Q-1 [m\u00b3/s]
+sinfo.flood_duration.header.mainvalue.2.duration = \u00dcberflutungsdauer WSPL-2 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.2.duration = \u00dcberflu-tungs-dauer WSPL-2 [d/a]
+sinfo.flood_duration.header.mainvalue.2.w = Wasserstand/Wasserspiegel-lage-2
+sinfo.flood_duration.header.pdf.mainvalue.2.w = Wasser-stand/Wasser-spiegel-lage-2
+sinfo.flood_duration.header.mainvalue.2.q =  Q-2
+sinfo.flood_duration.header.pdf.mainvalue.2.q =  Q-2 [m\u00b3/s]
+sinfo.flood_duration.header.mainvalue.3.duration = \u00dcberflutungsdauer WSPL-3 [d/a]
+sinfo.flood_duration.header.pdf.mainvalue.3.duration = \u00dcberflu-tungs-dauer WSPL-3 [d/a]
+sinfo.flood_duration.header.mainvalue.3.w = Wasserstand/Wasserspiegel-lage-3
+sinfo.flood_duration.header.pdf.mainvalue.3.w = Wasser-stand/Wasser-spiegel-lage-3
+sinfo.flood_duration.header.mainvalue.3.q =  Q-3
+sinfo.flood_duration.header.pdf.mainvalue.3.q =  Q-3 [m\u00b3/s]
+sinfo.chart.flood_duration.height.section.title = H\u00f6he Infrastrukturen BWaStr
+sinfo_facet_flood_height = Geod\u00e4tische H\u00f6he Infrastrukturen BWaStr
+sinfo_facet_flood_height.description = Geod\u00e4tische H\u00f6he Infrastrukturen BWaStr ({0})
 
 bundu_bezugswst = Bezugswasserst\u00e4nde
 bundu_analysis = Fixierungsanalyse
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Sun Jul 01 15:29:40 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Mon Jul 02 07:33:53 2018 +0200
@@ -1542,4 +1542,6 @@
     String sinfo_flood_duration();
 
     String sinfo_collision_export();
+
+    String sinfo_flood_height();
 }
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Sun Jul 01 15:29:40 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Mon Jul 02 07:33:53 2018 +0200
@@ -812,6 +812,7 @@
 sinfo_collision = Grundber\u00fchrungen
 
 sinfo_flood_duration = \u00dcberflutungsdauer Infrastruktur BWaStr
+sinfo_flood_height = H\u00f6he Infrastruktur BWaStr
 
 uinfo = U-INFO
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Sun Jul 01 15:29:40 2018 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Mon Jul 02 07:33:53 2018 +0200
@@ -812,6 +812,7 @@
 sinfo_collision = Grundber\u00fchrungen
 
 sinfo_flood_duration = \u00dcberflutungsdauer Infrastruktur BWaStr
+sinfo_flood_height = H\u00f6he Infrastruktur BWaStr
 
 uinfo = U-INFO
 uinfo_inundation_duration_export = \u00dcberflutungsdauern Export

http://dive4elements.wald.intevation.org