changeset 9207:559775e2f53c

Merge
author mschaefer
date Mon, 02 Jul 2018 19:12:54 +0200
parents 3dae6b78e1da (diff) b38be7ea53e2 (current diff)
children 53cc5b496692
files
diffstat 24 files changed, 451 insertions(+), 450 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/sinfo.xml	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/doc/conf/artifacts/sinfo.xml	Mon Jul 02 19:12:54 2018 +0200
@@ -360,17 +360,14 @@
         <outputmode name="sinfo_flood_duration" description="output.sinfo_flood_duration" mime-type="image/png" type="chart">
           <facets>
             <facet name="sinfo_facet_flood_duration" description="flood duration of the heights of the infrastructures (points)"/>
-            <facet name="mainvalue.1.duration" description="flood duration of first W main value"/>
-            <facet name="mainvalue.2.duration" description="flood duration of second W main value"/>
-            <facet name="mainvalue.3.duration" description="flood duration of third W main value"/>
+            <facet name="mainvalue.duration" description="flood duration of W main value"/>
           </facets>
         </outputmode>
         <outputmode name="sinfo_flood_height" description="output.sinfo_flood_height" mime-type="image/png" type="chart">
           <facets>
             <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"/>
+            <facet name="mainvalue.w" description="W of main value"/>
+       
           </facets>
         </outputmode>
         <outputmode name="sinfo_flood_duration_curve" description="output.sinfo_duration_curve" mime-type="image/png" type="chart">
--- a/artifacts/doc/conf/themes.xml	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/doc/conf/themes.xml	Mon Jul 02 19:12:54 2018 +0200
@@ -446,13 +446,10 @@
         <mapping from="sinfo_facet_collision_calc_count.3" to="SInfoCollisionCount3" />
         
         <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="mainvalue.duration" to="SInfoMainValues" /> 
         <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="mainvalue.w" to="SInfoMainValues" />
+      
         
         <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	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/doc/conf/themes/default.xml	Mon Jul 02 19:12:54 2018 +0200
@@ -3100,7 +3100,7 @@
             <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
         </fields>
     </theme>
-    <theme name="SInfoMainValues1">
+    <theme name="SInfoMainValues">
         <inherits>
             <inherit from="MainValuesW" />
         </inherits>
@@ -3108,20 +3108,6 @@
             <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/src/main/java/org/dive4elements/river/artifacts/common/AbstractCalculationResult.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractCalculationResult.java	Mon Jul 02 19:12:54 2018 +0200
@@ -41,10 +41,6 @@
         return this.rows.isEmpty();
     }
 
-    // public final void addRow(final ResultRow resultRow) {
-    // this.rows.add(resultRow);
-    // }
-
     public Collection<ResultRow> getRows() {
         return Collections.unmodifiableCollection(this.rows);
     }
@@ -66,10 +62,6 @@
         return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() };
     }
 
-    public double[][] getStationPoints(final IResultType type, final int index) {
-        return getStationPoints(type);
-    }
-
     protected final <TYPE> List<TYPE> getValues(final IResultType type) {
 
         final List<TYPE> values = new ArrayList<>();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractExportContext.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/common/AbstractExportContext.java	Mon Jul 02 19:12:54 2018 +0200
@@ -74,7 +74,7 @@
         return Resources.getMsg(this.context.getMeta(), key, key);
     }
 
-    protected final String msg(final String key, final Object... args) {
+    public final String msg(final String key, final Object... args) {
         return Resources.getMsg(this.context.getMeta(), key, key, args);
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcFacet.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcFacet.java	Mon Jul 02 19:12:54 2018 +0200
@@ -23,6 +23,7 @@
  *
  * @author Matthias Schäfer
  */
+// FIXME: remove
 public class CollisionCalcFacet extends DataFacet {
 
     private static final long serialVersionUID = 1;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcOverviewResult.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalcOverviewResult.java	Mon Jul 02 19:12:54 2018 +0200
@@ -46,15 +46,14 @@
         return this.singleYears;
     }
 
-    @Override
-    public double[][] getStationPoints(final IResultType type, final int index) {
+    public double[][] getStationPointsByYear(final IResultType type, final int year) {
 
         final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size());
         final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size());
 
         for (final ResultRow row : this.rows) {
 
-            if ((this.singleYears == null) || (Integer.valueOf(row.getValue(SInfoResultType.years).toString()) == this.singleYears[index])) {
+            if ((this.singleYears == null) || (Integer.valueOf(row.getValue(SInfoResultType.years).toString()) == year)) {
 
                 final double station = row.getDoubleValue(GeneralResultType.station);
                 final double value = row.getDoubleValue(type);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionState.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionState.java	Mon Jul 02 19:12:54 2018 +0200
@@ -78,17 +78,18 @@
 
         /* add themes for chart, for each result */
         final List<AbstractCalculationExportableResult> resultList = results.getResults();
-        int j = -1;
+        int themeCount = 0;
         for (int index = 0; index < resultList.size(); index++) {
             if (resultList.get(index) instanceof CollisionCalcOverviewResult) {
                 final CollisionCalcOverviewResult result = (CollisionCalcOverviewResult) resultList.get(index);
-                if (result.getSingleYears() == null)
-                    facets.add(CollisionCalcProcessor.createFacet(context, hash, this.id, result, index));
+
+                final int[] singleYears = result.getSingleYears();
+
+                if (singleYears == null)
+                    facets.add(CollisionCalcProcessor.createFacet(context, hash, this.id, result, index, -1, themeCount++));
                 else {
-                    for (int i = 0; i <= result.getSingleYears().length - 1; i++) {
-                        j++;
-                        facets.add(CollisionCalcProcessor.createFacet(context, hash, this.id, result, j));
-                    }
+                    for (final int singleYear : singleYears)
+                        facets.add(CollisionCalcProcessor.createFacet(context, hash, this.id, result, index, singleYear, themeCount++));
                 }
             }
         }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoProcessor.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoProcessor.java	Mon Jul 02 19:12:54 2018 +0200
@@ -68,6 +68,23 @@
         }
     }
 
+    protected static final AbstractCalculationResult getResult(final DiagramGenerator generator, final ArtifactAndFacet bundle) {
+        final CallContext context = generator.getContext();
+        final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
+        if (data == null) {
+            // Check has been here before so we keep it for security reasons
+            // this should never happen though.
+            final String facetName = bundle.getFacetName();
+            throw new IllegalStateException("Data is null for facet: " + facetName);
+        }
+
+        return data;
+    }
+
+    protected static final int getDataIndex(final ArtifactAndFacet bundle) {
+        return ((SInfoResultFacet) bundle.getFacet()).getDataIndex();
+    }
+
     /**
      * @return The axis label
      */
@@ -75,6 +92,16 @@
 
     protected final String buildSeriesForType(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible,
             final IResultType resultType, final Double gapDistance) {
+
+        final AbstractCalculationResult data = getResult(generator, bundle);
+
+        final double[][] points = data.getStationPoints(resultType);
+
+        return buildSeriesForType(points, generator, bundle, theme, visible, gapDistance);
+    }
+
+    protected final String buildSeriesForType(final double[][] points, final DiagramGenerator generator, final ArtifactAndFacet bundle,
+            final ThemeDocument theme, final boolean visible, final Double gapDistance) {
         final CallContext context = generator.getContext();
         final Map<String, String> metaData = bundle.getFacet().getMetaData();
 
@@ -85,68 +112,55 @@
 
         final String facetName = bundle.getFacetName();
 
-        final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
-        if (data == null) {
-            // Check has been here before so we keep it for security reasons
-            // this should never happen though.
-            throw new IllegalStateException("Data is null for facet: " + facetName);
-        }
-
-        final double[][] points = generatePoints(context, artifact, data, facetName, resultType);
+        final double[][] filteredPoints = filterPoints(points, context, artifact, facetName);
 
         if (gapDistance == null)
-            StyledSeriesBuilder.addPoints(series, points, true);
+            StyledSeriesBuilder.addPoints(series, filteredPoints, true);
         else
-            StyledSeriesBuilder.addPoints(series, points, true, gapDistance);
+            StyledSeriesBuilder.addPoints(series, filteredPoints, true, gapDistance);
 
         generator.addAxisSeries(series, getAxisName(), visible);
 
         return metaData.get("Y");
     }
 
-    protected final String buildSeriesForType1(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme,
-            final boolean visible,
-            final IResultType resultType, final Double gapDistance) {
-        final CallContext context = generator.getContext();
-        final Map<String, String> metaData = bundle.getFacet().getMetaData();
-
-        final Artifact artifact = bundle.getArtifact();
-
-        final StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
-        series.putMetaData(metaData, artifact, context);
-
-        final String facetName = bundle.getFacetName();
-
-        final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
-        if (data == null) {
-            // Check has been here before so we keep it for security reasons
-            // this should never happen though.
-            throw new IllegalStateException("Data is null for facet: " + facetName);
-        }
-
-        final double[][] points = generatePoints(context, artifact, data, facetName, resultType, bundle.getFacet().getIndex());
-
-        if (gapDistance == null)
-            StyledSeriesBuilder.addPoints(series, points, true);
-        else
-            StyledSeriesBuilder.addPoints(series, points, true, gapDistance);
-
-        generator.addAxisSeries(series, getAxisName(), visible);
-
-        return metaData.get("Y");
-    }
+    // protected final String buildSeriesForType1(final DiagramGenerator generator, final ArtifactAndFacet bundle, final
+    // ThemeDocument theme,
+    // final boolean visible, final IResultType resultType, final Double gapDistance) {
+    // final CallContext context = generator.getContext();
+    // final Map<String, String> metaData = bundle.getFacet().getMetaData();
+    //
+    // final Artifact artifact = bundle.getArtifact();
+    //
+    // final StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+    // series.putMetaData(metaData, artifact, context);
+    //
+    // final String facetName = bundle.getFacetName();
+    //
+    // final AbstractCalculationResult data = (AbstractCalculationResult) bundle.getData(context);
+    // if (data == null) {
+    // // Check has been here before so we keep it for security reasons
+    // // this should never happen though.
+    // throw new IllegalStateException("Data is null for facet: " + facetName);
+    // }
+    //
+    // final double[][] points = generatePoints(context, artifact, data, facetName, resultType,
+    // bundle.getFacet().getIndex());
+    //
+    // if (gapDistance == null)
+    // StyledSeriesBuilder.addPoints(series, points, true);
+    // else
+    // StyledSeriesBuilder.addPoints(series, points, true, gapDistance);
+    //
+    // generator.addAxisSeries(series, getAxisName(), visible);
+    //
+    // return metaData.get("Y");
+    // }
 
     protected final String buildSeriesForTkh(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme,
             final boolean visible) {
-        final CallContext context = generator.getContext();
 
-        final String facetName = bundle.getFacetName();
-        final AbstractTkhCalculationResult data = (AbstractTkhCalculationResult) bundle.getData(context);
-        if (data == null) {
-            // Check has been here before so we keep it for security reasons
-            // this should never happen though.
-            throw new IllegalStateException("Data is null for facet: " + facetName);
-        }
+        final AbstractTkhCalculationResult data = (AbstractTkhCalculationResult) getResult(generator, bundle);
 
         final StyledXYSeries seriesUp = new StyledXYSeries(bundle.getFacetDescription(), theme);
         final double[][] pointsUp = data.getTkhUpPoints();
@@ -182,10 +196,8 @@
         return scales.getRadius(river, start, end);
     }
 
-    private double[][] generatePoints(final CallContext context, final Artifact artifact, final AbstractCalculationResult data, final String facetName,
-            final IResultType resultType) {
+    private double[][] filterPoints(final double[][] points, final CallContext context, final Artifact artifact, final String facetName) {
 
-        final double[][] points = data.getStationPoints(resultType);
         if (facetName.endsWith(".filtered")) {
             final Double radius = findRadius(context, artifact);
             return movingAverage(radius, points);
@@ -194,17 +206,18 @@
         return points;
     }
 
-    private double[][] generatePoints(final CallContext context, final Artifact artifact, final AbstractCalculationResult data, final String facetName,
-            final IResultType resultType, final int index) {
-
-        final double[][] points = data.getStationPoints(resultType, index);
-        if (facetName.endsWith(".filtered")) {
-            final Double radius = findRadius(context, artifact);
-            return movingAverage(radius, points);
-        }
-
-        return points;
-    }
+    // private double[][] generatePoints(final CallContext context, final Artifact artifact, final AbstractCalculationResult
+    // data, final String facetName,
+    // final IResultType resultType, final int index) {
+    //
+    // final double[][] points = data.getStationPoints(resultType, index);
+    // if (facetName.endsWith(".filtered")) {
+    // final Double radius = findRadius(context, artifact);
+    // return movingAverage(radius, points);
+    // }
+    //
+    // return points;
+    // }
 
     private double[][] movingAverage(final Double radius, final double[][] points) {
 
@@ -231,11 +244,14 @@
         return generator.msg(this.i18n_axis_label, "MISSING");
     }
 
-    // Moved from SInfoLineProcessor:
     protected static final Facet createFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int index, final String axisLabel, final String facetId, final String description) {
-        final String facetFlowDepthFilteredDescription = Resources.getMsg(context.getMeta(), description, description, result.getLabel());
-        return new SInfoResultFacet(index, facetId, facetFlowDepthFilteredDescription, axisLabel, ComputeType.ADVANCE, id, hash);
+            final int resultIndex, final String axisLabel, final String facetId, final String description) {
+        return createFacet(context, hash, id, result, resultIndex, -1, axisLabel, facetId, description);
     }
 
+    protected static final Facet createFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
+            final int resultIndex, final int dataIndex, final String axisLabel, final String facetId, final String description) {
+        final String facetFlowDepthFilteredDescription = Resources.getMsg(context.getMeta(), description, description, result.getLabel());
+        return new SInfoResultFacet(resultIndex, dataIndex, facetId, facetFlowDepthFilteredDescription, axisLabel, ComputeType.ADVANCE, id, hash);
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/CollisionCalcProcessor.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/CollisionCalcProcessor.java	Mon Jul 02 19:12:54 2018 +0200
@@ -18,7 +18,6 @@
 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.collision.CollisionCalcFacet;
 import org.dive4elements.river.artifacts.sinfo.collision.CollisionCalcOverviewResult;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
@@ -38,6 +37,8 @@
 
     public static final String FACET_COLLISION_CALC_COUNT_3 = "sinfo_facet_collision_calc_count.3";
 
+    private static final String[] COLLISION_FACETS = new String[] { FACET_COLLISION_CALC_COUNT, FACET_COLLISION_CALC_COUNT_2, FACET_COLLISION_CALC_COUNT_3 };
+
     private static final String I18N_AXIS_LABEL = "chart.collision_count.section.yaxis.label";
 
     private static final String I18N_SERIES_NAME_PATTERN = "collision.count.title";
@@ -57,24 +58,30 @@
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        return buildSeriesForType1(generator, bundle, theme, visible, SInfoResultType.collisionCount, null);
+        final int year = getDataIndex(bundle);
+
+        final CollisionCalcOverviewResult result = (CollisionCalcOverviewResult) getResult(generator, bundle);
+
+        final double[][] points = result.getStationPointsByYear(SInfoResultType.collisionCount, year);
+
+        return buildSeriesForType(points, generator, bundle, theme, visible, null);
     }
 
     public static final Facet createFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int index) {
+            final int resultIndex, final int year, final int themeCount) {
+
+        // REMARK: we are using themeCount as facet index, because we get several lines (with the same facet name) for the same
+        // result index; however name and index are used on client side as unique id.
 
         final CollisionCalcOverviewResult ccoResult = (CollisionCalcOverviewResult) result;
-        String facetDescription;
-        String facetName;
         if (ccoResult.getSingleYears() == null) {
-            facetDescription = Resources.getMsg(context.getMeta(), I18N_SERIES_NAME_PATTERN, I18N_SERIES_NAME_PATTERN, result.getLabel());
-            facetName = FACET_COLLISION_CALC_COUNT;
+            final String facetDescription = Resources.getMsg(context.getMeta(), I18N_SERIES_NAME_PATTERN, I18N_SERIES_NAME_PATTERN, result.getLabel());
+            return new SInfoResultFacet(themeCount, resultIndex, -1, FACET_COLLISION_CALC_COUNT, facetDescription, I18N_AXIS_LABEL, ComputeType.ADVANCE, id,
+                    hash);
+        } else {
+            final String facetDescription = Resources.getMsg(context.getMeta(), I18N_SERIES_NAME_PATTERN, I18N_SERIES_NAME_PATTERN, Integer.toString(year));
+            return new SInfoResultFacet(themeCount, resultIndex, year, COLLISION_FACETS[themeCount % 3], facetDescription, I18N_AXIS_LABEL, ComputeType.ADVANCE,
+                    id, hash);
         }
-        else {
-            facetDescription = Resources.getMsg(context.getMeta(), I18N_SERIES_NAME_PATTERN, I18N_SERIES_NAME_PATTERN,
-                    Integer.toString(ccoResult.getSingleYears()[index]));
-            facetName = new String[] { FACET_COLLISION_CALC_COUNT, FACET_COLLISION_CALC_COUNT_2, FACET_COLLISION_CALC_COUNT_3 }[index % 3].toString();
-        }
-        return new CollisionCalcFacet(index, facetName, facetDescription, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodDurationProcessor.java	Mon Jul 02 19:12:54 2018 +0200
@@ -10,19 +10,27 @@
 
 package org.dive4elements.river.artifacts.sinfo.common;
 
+import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
+import org.dive4elements.river.artifacts.common.GeneralResultType;
+import org.dive4elements.river.artifacts.common.ResultRow;
 import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.DurationWaterlevel;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.themes.ThemeDocument;
 
+import gnu.trove.TDoubleArrayList;
+
 /**
  * Processor to generate the facet and data series of infrastructure flood durations
  *
@@ -31,17 +39,13 @@
  */
 public final class FloodDurationProcessor extends AbstractSInfoProcessor {
 
-    public static final String FACET_FLOOD_DURATION = "sinfo_facet_flood_duration";
-
-    public static final String FACET_FLOOD_DURATION_DESCRIPTION = "sinfo_facet_flood_duration.description";
+    private static final String FACET_FLOOD_DURATION = "sinfo_facet_flood_duration";
 
-    public static final String FACET_MAIN_VALUE_1_DURATION = "mainvalue.1.duration";
+    private static final String FACET_FLOOD_DURATION_DESCRIPTION = "sinfo_facet_flood_duration.description";
 
-    public static final String FACET_MAIN_VALUE_2_DURATION = "mainvalue.2.duration";
+    private static final String FACET_MAIN_VALUE_DURATION = "mainvalue.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 FACET_MAIN_VALUE_DURATION_DESCRIPTION = "mainvalue.duration.description";
 
     private static final String I18N_AXIS_LABEL = "sinfo.chart.flood_duration.section.yaxis.label";
 
@@ -49,72 +53,83 @@
 
     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);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_DURATION);
+
     }
 
     public FloodDurationProcessor() {
         super(I18N_AXIS_LABEL, HANDLED_FACET_TYPES);
     }
 
-    // @Override TODO: REMOVE (has been replaced by doGetType
-    // protected double[][] doGetPoints(final AbstractCalculationResult data, final String facetName) {
-    //
-    // if (FACET_FLOOD_DURATION.contentEquals(facetName))
-    // return ((FloodDurationCalculationResult) data).fetchInfrastructurePoints(SInfoResultType.floodDuration);
-    //
-    // if (FACET_MAIN_VALUE_1_DURATION.contentEquals(facetName))
-    // 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);
-    // }
-
     public static Facet createFloodDurationFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int index) {
-
-        return AbstractSInfoProcessor.createFacet(context, hash, id, result, index, I18N_AXIS_LABEL, FACET_FLOOD_DURATION, FACET_FLOOD_DURATION_DESCRIPTION);
-    }
-
-    private SInfoResultType doGetType(final String facetName) {
-
-        if (FACET_FLOOD_DURATION.contentEquals(facetName))
-            return SInfoResultType.floodDuration;
+            final int resultIndex) {
 
-        if (FACET_MAIN_VALUE_1_DURATION.contentEquals(facetName))
-            return SInfoResultType.mainValue1Duration;
-
-        if (FACET_MAIN_VALUE_2_DURATION.contentEquals(facetName))
-            return SInfoResultType.mainValue2Duration;
-
-        if (FACET_MAIN_VALUE_3_DURATION.contentEquals(facetName))
-            return SInfoResultType.mainValue3Duration;
-
-        final String error = String.format("Unknown facet name: %s", facetName);
-        throw new UnsupportedOperationException(error);
-
+        return AbstractSInfoProcessor.createFacet(context, hash, id, result, resultIndex, -1, I18N_AXIS_LABEL, FACET_FLOOD_DURATION,
+                FACET_FLOOD_DURATION_DESCRIPTION);
     }
 
     public static Facet createMainValueDurationFacet(final CallContext context, final String hash, final String id, final FloodDurationCalculationResult result,
-            final int index) {
+            final int facetIndex, final int resultIndex, final int dataIndex) {
 
         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);
+                result.getMainValueLabel(resultIndex));
+
+        return new SInfoResultFacet(facetIndex, resultIndex, dataIndex, FACET_MAIN_VALUE_DURATION, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
     }
 
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
+        final String facetName = bundle.getFacetName();
 
-        return buildSeriesForType(generator, bundle, theme, visible, doGetType(bundle.getFacetName()), GAP_DISTANCE);
+        if (FACET_FLOOD_DURATION.contentEquals(facetName))
+            return buildSeriesForType(generator, bundle, theme, visible, SInfoResultType.floodDuration, null);
+
+        if (FACET_MAIN_VALUE_DURATION.contentEquals(facetName)) {
+
+            final Function<DurationWaterlevel, Double> valueGetter = new Function<DurationWaterlevel, Double>() {
+                @Override
+                public Double apply(final DurationWaterlevel waterlevel) {
+                    return (double) waterlevel.getFloodDurDaysPerYear();
+                }
+            };
+
+            // FIXME: instead (see CollisionXXProcessor) delegate to result
+            final double[][] points = getMainValueDurationPoints(generator, bundle, valueGetter);
+
+            return buildSeriesForType(points, generator, bundle, theme, visible, null);
+        }
+
+        final String error = String.format("Unknown facet name: %s", facetName);
+        throw new UnsupportedOperationException(error);
+    }
+
+    public static final double[][] getMainValueDurationPoints(final DiagramGenerator generator, final ArtifactAndFacet bundle,
+            final Function<DurationWaterlevel, Double> valueGetter) {
+
+        final AbstractCalculationResult data = getResult(generator, bundle);
+
+        final int index = getDataIndex(bundle);
+
+        ((SInfoResultFacet) bundle.getFacet()).getDataIndex();
+
+        final Collection<ResultRow> rows = data.getRows();
+
+        final TDoubleArrayList xPoints = new TDoubleArrayList(rows.size());
+        final TDoubleArrayList yPoints = new TDoubleArrayList(rows.size());
+
+        for (final ResultRow row : rows) {
+
+            final double station = row.getDoubleValue(GeneralResultType.station);
+
+            final List<DurationWaterlevel> waterlevels = (List<DurationWaterlevel>) row.getValue(SInfoResultType.customMultiRowColWaterlevel);
+            final DurationWaterlevel waterlevel = waterlevels.get(index);
+
+            final Double value = valueGetter.apply(waterlevel);
+
+            xPoints.add(station);
+            yPoints.add(value);
+        }
+
+        return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() };
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodHeightProcessor.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/FloodHeightProcessor.java	Mon Jul 02 19:12:54 2018 +0200
@@ -12,12 +12,14 @@
 
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Function;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 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.DurationWaterlevel;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
@@ -32,17 +34,13 @@
  */
 public final class FloodHeightProcessor extends AbstractSInfoProcessor {
 
-    public static final String FACET_FLOOD_HEIGHT = "sinfo_facet_flood_height";
-
-    public static final String FACET_FLOOD_HEIGHT_DESCRIPTION = "sinfo_facet_flood_height.description";
+    private static final String FACET_FLOOD_HEIGHT = "sinfo_facet_flood_height";
 
-    public static final String FACET_MAIN_VALUE_1_HEIGHT = "mainvalue.1.w";
+    private static final String FACET_FLOOD_HEIGHT_DESCRIPTION = "sinfo_facet_flood_height.description";
 
-    public static final String FACET_MAIN_VALUE_2_HEIGHT = "mainvalue.2.w";
+    private static final String FACET_MAIN_VALUE_HEIGHT = "mainvalue.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 FACET_MAIN_VALUE_HEIGHT_DESCRIPTION = "mainvalue.w.description";
 
     private static final String I18N_AXIS_LABEL = LongitudinalSectionGenerator.I18N_YAXIS_LABEL;
 
@@ -50,49 +48,45 @@
 
     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);
+        HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_HEIGHT);
     }
 
     public FloodHeightProcessor() {
         super(I18N_AXIS_LABEL, HANDLED_FACET_TYPES);
     }
 
-    protected SInfoResultType doGetType(final String facetName) {
-        if (FACET_FLOOD_HEIGHT.contentEquals(facetName))
-            return SInfoResultType.infrastructureHeight;
-
-        if (FACET_MAIN_VALUE_1_HEIGHT.contentEquals(facetName))
-            return SInfoResultType.waterlevel1;
-
-        if (FACET_MAIN_VALUE_2_HEIGHT.contentEquals(facetName))
-            return SInfoResultType.waterlevel2;
-
-        if (FACET_MAIN_VALUE_3_HEIGHT.contentEquals(facetName))
-            return 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 AbstractSInfoProcessor.createFacet(context, hash, id, result, index, I18N_AXIS_LABEL, FACET_FLOOD_HEIGHT, FACET_FLOOD_HEIGHT_DESCRIPTION);
+        return AbstractSInfoProcessor.createFacet(context, hash, id, result, index, -1, 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 int facetIndex, final int resultIndex, final int dataIndex) {
         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);
+                result.getMainValueLabel(dataIndex));
+
+        return new SInfoResultFacet(facetIndex, resultIndex, dataIndex, FACET_MAIN_VALUE_HEIGHT, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
     }
 
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
 
-        return buildSeriesForType(generator, bundle, theme, visible, doGetType(bundle.getFacetName()), null);
+        final String facetName = bundle.getFacetName();
+
+        if (FACET_FLOOD_HEIGHT.contentEquals(facetName))
+            return buildSeriesForType(generator, bundle, theme, visible, SInfoResultType.infrastructureHeight, null);
+
+        if (FACET_MAIN_VALUE_HEIGHT.contentEquals(facetName)) {
+            final double[][] heightPoints = FloodDurationProcessor.getMainValueDurationPoints(generator, bundle, new Function<DurationWaterlevel, Double>() {
+                @Override
+                public Double apply(final DurationWaterlevel waterlevel) {
+                    return waterlevel.getWaterlevel();
+                }
+            });
+            return buildSeriesForType(heightPoints, generator, bundle, theme, visible, null);
+        }
+
+        final String error = String.format("Unknown facet name: %s", facetName);
+        throw new UnsupportedOperationException(error);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultFacet.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultFacet.java	Mon Jul 02 19:12:54 2018 +0200
@@ -28,17 +28,42 @@
 
     private static Logger log = Logger.getLogger(SInfoResultFacet.class);
 
+    private int dataIndex;
+
+    private int resultIndex;
+
     public SInfoResultFacet() {
         // required for clone operation deepCopy()
     }
 
-    public SInfoResultFacet(final int idx, final String name, final String description, final String yAxisLabelKey, final ComputeType type,
+    public SInfoResultFacet(final int resultIndex, final String name, final String description, final String yAxisLabelKey, final ComputeType type,
             final String stateId, final String hash) {
-        super(idx, name, description, type, hash, stateId);
+        // REMARK:
+        this(resultIndex, resultIndex, -1, name, description, yAxisLabelKey, type, stateId, hash);
+    }
+
+    public SInfoResultFacet(final int resultIndex, final int dataIndex, final String name, final String description, final String yAxisLabelKey,
+            final ComputeType type, final String stateId, final String hash) {
+        // REMARK: in some cases, we have several
+        this(resultIndex, resultIndex, dataIndex, name, description, yAxisLabelKey, type, stateId, hash);
+    }
+
+    public SInfoResultFacet(final int facetIndex, final int resultIndex, final int dataIndex, final String name, final String description,
+            // REMARK: in some cases, we have several data-lines for the same result (which normally determines the facet index) and
+            // facet name. But index and name are used by the client side as unique keys for the chart themes...
+            // So we might have different facet index and result index.
+            final String yAxisLabelKey, final ComputeType type, final String stateId, final String hash) {
+        super(facetIndex, name, description, type, hash, stateId);
+        this.resultIndex = resultIndex;
+        this.dataIndex = dataIndex;
         this.metaData.put("X", "sinfo.chart.km.xaxis.label");
         this.metaData.put("Y", yAxisLabelKey);
     }
 
+    public int getDataIndex() {
+        return this.dataIndex;
+    }
+
     @Override
     public Object getData(final Artifact artifact, final CallContext context) {
         log.debug("Get data for result at index: " + this.index);
@@ -49,7 +74,7 @@
 
         final AbstractCalculationResults<AbstractCalculationResult> data = (AbstractCalculationResults<AbstractCalculationResult>) res.getData();
 
-        return data.getResults().get(this.index);
+        return data.getResults().get(this.resultIndex);
     }
 
     /** Copy deeply. */
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Mon Jul 02 19:12:54 2018 +0200
@@ -43,14 +43,12 @@
 
         @Override
         public String exportValue(final CallContext context, final Object value) {
-            // TODO Auto-generated method stub
-            return null;
+            throw new UnsupportedOperationException();
         }
 
         @Override
         protected NumberFormat createFormatter(final CallContext context) {
-            // TODO Auto-generated method stub
-            return null;
+            throw new UnsupportedOperationException();
         }
 
     };
@@ -87,54 +85,6 @@
         }
     };
 
-    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
-        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 waterlevel = new SInfoResultType(null, SInfoI18NStrings.CSV_WATERLEVEL_HEADER,
             "sinfo.export.flow_depth.pdf.header.waterlevel") {
         private static final long serialVersionUID = 1L;
@@ -185,21 +135,22 @@
         }
     };
 
-    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 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;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/DurationWaterlevel.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/DurationWaterlevel.java	Mon Jul 02 19:12:54 2018 +0200
@@ -32,6 +32,14 @@
         this.floodDurDaysPerYear = floodDurDaysPerYear;
     }
 
+    public double getWaterlevel() {
+        return this.w;
+    }
+
+    public int getFloodDurDaysPerYear() {
+        return this.floodDurDaysPerYear;
+    }
+
     public String getFloodDurDaysPerYearFormatted() {
         return String.valueOf(this.floodDurDaysPerYear);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Mon Jul 02 19:12:54 2018 +0200
@@ -58,6 +58,7 @@
         // Calculate the selected main values, if any
         /* misuse winfo-artifact to calculate waterlevels in the same way */
         final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
+        // FIXME: check! wenn mainValueCount nicht mehr benutzt --> weg!
         int mainValueCount = 0;
         if (winfo.isW())
             mainValueCount = winfo.getWs().length;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Mon Jul 02 19:12:54 2018 +0200
@@ -9,16 +9,17 @@
  */
 package org.dive4elements.river.artifacts.sinfo.flood_duration;
 
+import java.text.NumberFormat;
 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.AbstractExportContext;
 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;
@@ -37,24 +38,31 @@
 
     private static final long serialVersionUID = 1L;
 
-    private static final String JASPER_FILE = "/jasper/templates/sinfo.floodduration.jrxml";
+    private final String[] mainvalueLabels;
 
-    private final String[] mainvalueLabels;
+    private final int waterlevelCount;
+    // private final WstInfo wstInfo;
+    private final int maxWaterlevelPdf = 3;
+
+    private enum ExportMode {
+        pdf, csv
+    }
 
     public FloodDurationCalculationResult(final String label, final String[] mainvalueLabels, final Collection<ResultRow> rows) {
         super(label, rows);
-        this.mainvalueLabels = mainvalueLabels;
+        this.mainvalueLabels = mainvalueLabels; // TODO: löschen (?)
+        this.waterlevelCount = mainvalueLabels.length;
     }
 
     /**
      * 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;
-    }
+    // 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
@@ -105,7 +113,8 @@
         if (this.mainvalueLabels.length >= 1) {
             // "##METADATEN WASSERSPIEGELLAGE"
             exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL);
-            for (int i = 1; i <= this.mainvalueLabels.length; i++) {
+            for (int i = 1; i <= getWaterlevelCount(); // 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));
@@ -113,15 +122,18 @@
             // "# Bezugspegel: "
             exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_GAUGE, "TODO: gauge");
         }
+        exportContextCSV.writeBlankLine();
     }
 
     @Override
     protected String getJasperFile() {
-        // TODO Variante mit Wasserspiegellage(n)
-        return JASPER_FILE;
+        if (this.waterlevelCount <= 1)
+            return "/jasper/templates/sinfo.floodduration.jrxml";
+        else
+            return "/jasper/templates/sinfo.floodduration2.jrxml";
     }
 
-    protected String[] formatRow(final IExportContext exportContextCSV, final ResultRow row) {
+    protected String[] formatRow(final AbstractExportContext exportContextCSV, final ResultRow row, final ExportMode mode) {
 
         final Collection<String> lines = new ArrayList<>(10);
 
@@ -132,22 +144,31 @@
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructureHeight));
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.infrastructuretype));
 
-        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));
-                }
+        final List<DurationWaterlevel> waterlevelList = (List<DurationWaterlevel>) row.getValue(SInfoResultType.customMultiRowColWaterlevel);
+        if (waterlevelList != null) {
+            final NumberFormat wFormatter = exportContextCSV.getFlowDepthFormatter();
+            final NumberFormat qFormatter = exportContextCSV.getQFormatter();
+
+            for (int i = 0; i < waterlevelList.size(); i++) {
+
+                if (i == this.maxWaterlevelPdf && mode == ExportMode.pdf)
+                    break;
+
+                final DurationWaterlevel item = waterlevelList.get(i);
+                lines.add(item.getWFormatted(wFormatter));
+                lines.add(item.getFloodDurDaysPerYearFormatted());
+                lines.add(item.getQFormatted(qFormatter));
+                lines.add(item.getBezeichnung());
             }
         }
 
+        if ((this.waterlevelCount == 0 || this.waterlevelCount == 2) && mode == ExportMode.pdf) {
+            // lines.add("dummy1");
+            // lines.add("dummy2");
+            // lines.add("dummy3");
+            // lines.add("dummy4");
+        }
+
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.gaugeLabel));
         lines.add(exportContextCSV.formatRowValue(row, SInfoResultType.location));
 
@@ -166,20 +187,18 @@
         header.add(exportContextCSV.msgUnitCSV(SInfoResultType.infrastructureHeight, SInfoResultType.infrastructureHeight.getUnit()));
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.infrastructuretype));
 
-        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()));
-                }
-            }
+        // add dynamic headers
+        final int waterlevelCount = // results.
+                getWaterlevelCount();
+        for (int i = 0; i < waterlevelCount; i++) {
+            final int naturalIndex = i + 1;
+            final String appendIndex = new StringBuilder().append("_").append(naturalIndex).toString();
+            final Object[] args = new Object[] { appendIndex };
+            // new StringBuilder().append('\u2081').toString(); // schlechter UTF-8-Support für subscript ints
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderW(), new Object[] { appendIndex, "results.getRiver().getWstUnit()" }));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderFloodDurPerYear(), args));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderQ(), args));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderBezeichn(), args));
         }
 
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.gaugeLabel));
@@ -192,13 +211,13 @@
     @Override
     protected String[] formatCSVRow(final ExportContextCSV exportContextCSV, final ResultRow row) {
 
-        return this.formatRow(exportContextCSV, row);
+        return this.formatRow(exportContextCSV, row, ExportMode.csv);
     }
 
     @Override
     protected String[] formatPDFRow(final ExportContextPDF exportContextPDF, final ResultRow row) {
 
-        return this.formatRow(exportContextPDF, row);
+        return this.formatRow(exportContextPDF, row, ExportMode.pdf);
     }
 
     @Override
@@ -212,24 +231,45 @@
         exportContextPDF.addJRMetadata(source, "infrastructure_height_header", SInfoResultType.infrastructureHeight);
         exportContextPDF.addJRMetadata(source, "infrastructure_type_header", SInfoResultType.infrastructuretype);
 
-        // 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);
-        // }
-        // }
-        // }
+        // add dynamic headers
+
+        if (this.waterlevelCount == 0 || this.waterlevelCount == 2) {
+            // source.addMetaData("dummy1", "dummy1");
+            // source.addMetaData("dummy2", "dummy2");
+            // source.addMetaData("dummy3", "dummy3");
+            // source.addMetaData("dummy4", "dummy4");
+        }
+
+        for (int i = 0; i < this.waterlevelCount; i++) {
+            final int naturalIndex = i + 1;
+
+            final Object[] args = new String[] { new StringBuilder().append("_").append(naturalIndex).toString() };
+            exportContextPDF.addJRMetadata(source, getPdfHeader("w", naturalIndex), exportContextPDF.msg(DurationWaterlevel.getHeaderW(), args));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("duration", naturalIndex),
+                    exportContextPDF.msg(DurationWaterlevel.getHeaderFloodDurPerYear(), args));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("q", naturalIndex), exportContextPDF.msg(DurationWaterlevel.getHeaderQ(), args));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("bezeichnung", naturalIndex),
+                    exportContextPDF.msg(DurationWaterlevel.getHeaderBezeichn(), args));
+        }
 
         exportContextPDF.addJRMetadata(source, "gauge_header", SInfoResultType.gaugeLabel);
         exportContextPDF.addJRMetadata(source, "location_header", SInfoResultType.location);
     }
+
+    public final int getWaterlevelCount() {
+        return this.waterlevelCount;
+    }
+
+    private final String getPdfHeader(final String rootStr, final int index) {
+        final String hd = "_header";
+        final StringBuilder builder = new StringBuilder();
+        return builder.append(rootStr).append("_").append(index).append(hd).toString();
+    }
+
+    public String getMainValueLabel(final int j) {
+
+        if (this.mainvalueLabels != null && j < this.mainvalueLabels.length)
+            return this.mainvalueLabels[j];
+        return "";
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jul 02 19:12:54 2018 +0200
@@ -32,6 +32,7 @@
 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.exports.WaterlevelDescriptionBuilder;
 import org.dive4elements.river.model.Attribute.AttributeKey;
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
@@ -53,7 +54,6 @@
 
     private final CallContext context;
 
-
     public FloodDurationCalculator(final CallContext context, final RiverInfoProvider riverInfoProvider) {
         this.context = context;
         this.riverInfoProvider = riverInfoProvider;
@@ -97,10 +97,12 @@
         final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
                 calcRange.getMaximumDouble(), problems);
 
+        final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
+
         // 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);
+            final ResultRow row = createRow(descBuilder, 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);
@@ -190,7 +192,9 @@
     /**
      * Determines the discharge state labels for the selected Q or W values
      */
+    // FIXME: use WaterlevelDescriptionBuilder instead!
     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
@@ -208,44 +212,46 @@
 
     /**
      * Create a result row for a station and its gauge, and add w-q-values as selected
+     *
+     * @param descBuilder
      */
-    private ResultRow createRow(final Double station, final Gauge gauge, final WQKms[] wqkmsArray,
+    private ResultRow createRow(final WaterlevelDescriptionBuilder descBuilder, 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());
+
+        // row.putValue(SInfoResultType.gaugeLabel, gauge.getName());
+        final String gaugeLabel = this.riverInfoProvider.findGauge(station);
+        row.putValue(SInfoResultType.gaugeLabel, gaugeLabel);
+
         final String location = this.riverInfoProvider.getLocation(station);
         row.putValue(SInfoResultType.location, location);
 
-        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))));
-                }
-            }
+        final List<DurationWaterlevel> waterlevels = new ArrayList<>(wqkmsArray.length);
+
+        for (final WQKms wqKm : wqkmsArray) {
+            assert (wqKm.getKm(kmIndex) == station.doubleValue());
+
+            final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKm.getQ(kmIndex))));
+
+            final String waterlevelLabel = descBuilder.getDesc(wqKm);
+
+            final DurationWaterlevel dw = new DurationWaterlevel(wqKm.getW(kmIndex), overflowDays, wqKm.getQ(kmIndex), waterlevelLabel);
+            waterlevels.add(dw);
         }
+        row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels);
+
         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) {
+    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;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationExporter.java	Mon Jul 02 19:12:54 2018 +0200
@@ -36,11 +36,14 @@
 
     @Override
     protected void configureDesign(final FloodDurationCalculationResult result, final JasperDesigner design) {
-        if (result instanceof FloodDurationCalculationResult) { // redundant, but type might change
-            design.removeColumn("wOpt");
-            design.removeColumn("qOpt");
-            design.removeColumn("bezOpt");
-            design.removeColumn("durOpt");
+        if (result instanceof FloodDurationCalculationResult) {
+            final int wlCount = result.getWaterlevelCount();
+            if (wlCount == 0 || wlCount == 2) {
+                design.removeColumn("wOpt");
+                design.removeColumn("qOpt");
+                design.removeColumn("bezOpt");
+                design.removeColumn("durOpt");
+            }
         }
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Mon Jul 02 19:12:54 2018 +0200
@@ -76,24 +76,31 @@
 
         final FloodDurationCalculationResults results = (FloodDurationCalculationResults) res.getData();
         final List<FloodDurationCalculationResult> resultList = results.getResults();
-        int index = 0;
+        int resultIndex = 0;
+        int themeCount = 0;
         for (final FloodDurationCalculationResult result : resultList) {
 
-            facets.add(FloodDurationProcessor.createFloodDurationFacet(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(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, resultIndex));
+
+            final int waterlevelCount = result.getWaterlevelCount();
+
+            facets.add(FloodHeightProcessor.createFloodHeightFacet(context, hash, this.id, result, resultIndex));
+
+            for (int j = 0; j < waterlevelCount; j++) {
+
+                final String waterlevelLabel = result.getMainValueLabel(j);
+                // FIXME: use label as label for theme
+
+                facets.add(FloodDurationProcessor.createMainValueDurationFacet(context, hash, this.id, result, themeCount, resultIndex, j));
+                facets.add(FloodHeightProcessor.createMainValueHeightFacet(context, hash, this.id, result, themeCount, resultIndex, j));
+
+                themeCount++;
             }
 
             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));
 
-            index++;
+            resultIndex++;
         }
 
         final Calculation report = res.getReport();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java	Mon Jul 02 19:12:54 2018 +0200
@@ -96,6 +96,7 @@
         final WKms wstKms = waterlevel.getWkms();
 
         final String wspLabel = wstKms.getName();
+
         final String soundingLabel = bedHeight.getInfo().getDescription();
         final String label = String.format("%s - %s", wspLabel, soundingLabel);
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/TkhCalculation.java	Mon Jul 02 19:12:54 2018 +0200
@@ -156,13 +156,12 @@
 
         final RiverInfoProvider riverInfoProvider = riverInfo.forWaterlevel(waterlevel);
 
-        // FIXME: check with winfo how the name is generated
-        final String wstLabel = waterlevel.getName();
+        final String waterlevelLabel = descBuilder.getDesc(wkms);
 
-        final WstInfo wstInfo = new WstInfo(wstLabel, wspYear, riverInfoProvider.getReferenceGauge());
+        final WstInfo wstInfo = new WstInfo(waterlevelLabel, wspYear, riverInfoProvider.getReferenceGauge());
 
         /* build tkh calculators per bedheight */
-        final Map<NumberRange, TkhCalculator> calculatorsByRanges = buildCalculators(calcRange, wkms, bedHeights, problems, riverInfoProvider, wstLabel);
+        final Map<NumberRange, TkhCalculator> calculatorsByRanges = buildCalculators(calcRange, wkms, bedHeights, problems, riverInfoProvider, waterlevelLabel);
         if (calculatorsByRanges.isEmpty()) {
             /* there should already be some problems, so just abort */
             return null;
@@ -170,8 +169,6 @@
 
         final Collection<ResultRow> rows = new ArrayList<>();
 
-        final String waterlevelLabel = descBuilder.getDesc(wkms);
-
         for (final Double stationDbl : allStations) {
 
             final double station = stationDbl;
@@ -191,7 +188,7 @@
                 rows.add(row);
         }
 
-        return new TkhCalculationResult(wstLabel, wstInfo, true, rows);
+        return new TkhCalculationResult(waterlevelLabel, wstInfo, true, rows);
     }
 
     private TkhCalculator findCalculator(final Map<NumberRange, TkhCalculator> calculators, final double station) {
--- a/artifacts/src/main/resources/messages.properties	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties	Mon Jul 02 19:12:54 2018 +0200
@@ -1003,6 +1003,7 @@
 
 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.w_index = Wasser-stand/Wasser-spiegel-lage{0}
@@ -1078,6 +1079,10 @@
 state.uinfo.river = Gew\u00e4sser
 state.uinfo.calculation_mode=Berechnungsart
 
+mainvalue.w = Wasserstand/Wasserspiegellage
+mainvalue.w.description = Wasserstand/Wasserspiegel-lage ({0})
+mainvalue.duration = \u00dcberflutungsdauer
+mainvalue.duration.description = \u00dcberflutungsdauer ({0})
 uinfo.export.salix_line.csv.header.salix_line = Salix-Linie [m]
 uinfo.export.salix_line.csv.header.delta_mw =  (MW-MNW)x(-1) [m]
 uinfo.export.salix_line.csv.header.scen =  Salix-Linie [m] Szenario dMW={0} cm
@@ -1136,32 +1141,6 @@
 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.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})
--- a/artifacts/src/main/resources/messages_de.properties	Mon Jul 02 19:02:24 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Mon Jul 02 19:12:54 2018 +0200
@@ -1079,6 +1079,10 @@
 state.uinfo.river = Gew\u00e4sser
 state.uinfo.calculation_mode=Berechnungsart
 
+mainvalue.w = Wasserstand/Wasserspiegellage
+mainvalue.w.description = Wasserstand/Wasserspiegel-lage ({0})
+mainvalue.duration = \u00dcberflutungsdauer
+mainvalue.duration.description = \u00dcberflutungsdauer ({0})
 uinfo.export.salix_line.csv.header.salix_line = Salix-Linie [m]
 uinfo.export.salix_line.csv.header.delta_mw =  (MW-MNW)x(-1) [m]
 uinfo.export.salix_line.csv.header.scen =  Salix-Linie [m] Szenario dMW={0} cm
@@ -1090,17 +1094,17 @@
 uinfo.export.csv.meta.header.salix.regionalextended = ##\u00dcberregional wirkende Eingriffe
 uinfo.export.csv.meta.header.salix.szenariotyp = # Szenariotyp
 uinfo.export.csv.meta.header.salix.teilabschnitt = # Teilabschnitt
-uinfo.export.csv.meta.header.salix.mwspiegellaenderung = # MittelwasserSpiegellagen\ufffdnderung
+uinfo.export.csv.meta.header.salix.mwspiegellaenderung = # MittelwasserSpiegellagen\u00e4nderung
 uinfo.export.csv.meta.header.salix.historical = ##Historische Betrachtung
 uinfo.export.csv.meta.header.salix.historical.zeitpunkt = # Historischer Zeitpunkt
-uinfo.export.csv.meta.header.salix.historical.zeitart = # Art des Zeitraums
+uinfo.export.csv.meta.header.salix.historical.zeitart = # Art des Zeitraums 
 uinfo.export.csv.meta.header.veg.name = Vegetationszonen
 uinfo.export.csv.meta.header.veg.dauervon = \u00dcberflutungsdauer von [d/a]
 uinfo.export.csv.meta.header.veg.dauerbis = \u00dcberflutungsdauer bis [d/a]
 uinfo.export.url.inundationduration.inundationduration = \u00dcberflutungsdauer ({0})
 uinfo.export.url.inundationduration.vegetation = Vegetationszonen ({0})
 uinfo.export.url.inundationduration.vegetation_scenario= Vegetationszonen Szenario ({0}, {1}cm)
-uinfo.export.url.inundationduration.scenario = \u00dcberflutungsdauer Szenario ({0}, {1}cm)
+uinfo.export.url.inundationduration.scenario = \u00dcberflutungsdauer Szenario ({0}, {1}cm) 
 
 predefineddepthevol.total.title = Gesamt: {0}
 predefineddepthevol.peryear.title = J\u00e4hrlich: {0}
@@ -1137,32 +1141,6 @@
 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.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})

http://dive4elements.wald.intevation.org