changeset 9229:0dcd1cd41915

Different themes/facets for left bank and right bank infrastructures in S-Info flood durations, some fixmes done
author mschaefer
date Thu, 05 Jul 2018 16:49:42 +0200
parents dba14da43f23
children 6a1580b38e7d
files artifacts/doc/conf/artifacts/sinfo.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/sinfo/common/AbstractSInfoProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/RiverInfoProvider.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodHeightProcessor.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties
diffstat 16 files changed, 270 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/sinfo.xml	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/doc/conf/artifacts/sinfo.xml	Thu Jul 05 16:49:42 2018 +0200
@@ -359,13 +359,15 @@
       <outputmodes>
         <outputmode name="sinfo_flood_duration" description="output.sinfo_flood_duration" mime-type="image/png" type="chart">
           <facets>
-            <facet name="sinfo_facet_flood_duration" description="flood duration of the heights of the infrastructures (points)"/>
+            <facet name="sinfo_facet_flood_duration.left" description="flood duration of the heights of the left bank infrastructures (points)"/>
+            <facet name="sinfo_facet_flood_duration.right" description="flood duration of the heights of the right bank infrastructures (points)"/>
             <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="sinfo_facet_flood_height.left" description="flood heights of the left bank infrastructures (points)"/>
+            <facet name="sinfo_facet_flood_height.right" description="flood heights of the right bank infrastructures (points)"/>
             <facet name="mainvalue.w" description="W of main value"/>
        
           </facets>
--- a/artifacts/doc/conf/themes.xml	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/doc/conf/themes.xml	Thu Jul 05 16:49:42 2018 +0200
@@ -445,9 +445,12 @@
         <mapping from="sinfo_facet_collision_calc_count.2" to="SInfoCollisionCount2" />
         <mapping from="sinfo_facet_collision_calc_count.3" to="SInfoCollisionCount3" />
         
-        <mapping from="sinfo_facet_flood_duration" to="SInfoFloodDuration" />
+        <mapping from="sinfo_facet_flood_duration.left" to="SInfoFloodDurationLeft" />
+        <mapping from="sinfo_facet_flood_duration.right" to="SInfoFloodDurationRight" />
         <mapping from="mainvalue.duration" to="SInfoMainValues" /> 
         <mapping from="sinfo_facet_flood_height" to="SInfoInfrastructureHeight" />
+        <mapping from="sinfo_facet_flood_height.left" to="SInfoInfrastructureHeightLeft" />
+        <mapping from="sinfo_facet_flood_height.right" to="SInfoInfrastructureHeightRight" />
         <mapping from="mainvalue.w" to="SInfoMainValues" />
       
         
--- a/artifacts/doc/conf/themes/default.xml	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/doc/conf/themes/default.xml	Thu Jul 05 16:49:42 2018 +0200
@@ -2812,6 +2812,24 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="0, 192, 0" />
     </fields>
   </theme>
+  <theme name="SInfoInfrastructureHeightLeft">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
+    </fields>
+  </theme>
+  <theme name="SInfoInfrastructureHeightRight">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="192, 0, 0" />
+    </fields>
+  </theme>
   <theme name="SInfoCollisionCount">
     <inherits>
       <inherit from="LongitudinalSectionPoints" />
@@ -2839,7 +2857,7 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="48, 96, 255" />
     </fields>
   </theme>
-  <theme name="SInfoFloodDuration">
+  <theme name="SInfoFloodDurationLeft">
     <inherits>
       <inherit from="LongitudinalSectionPoints" />
     </inherits>
@@ -2848,6 +2866,15 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
     </fields>
   </theme>
+  <theme name="SInfoFloodDurationRight">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="192, 0, 0" />
+    </fields>
+  </theme>
   <theme name="SInfoMainValues">
     <inherits>
       <inherit from="MainValuesW" />
--- a/artifacts/doc/conf/themes/second.xml	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/doc/conf/themes/second.xml	Thu Jul 05 16:49:42 2018 +0200
@@ -2800,6 +2800,24 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="0, 192, 0" />
     </fields>
   </theme>
+  <theme name="SInfoInfrastructureHeightLeft">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
+    </fields>
+  </theme>
+  <theme name="SInfoInfrastructureHeightRight">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="192, 0, 0" />
+    </fields>
+  </theme>
   <theme name="SInfoCollisionCount">
     <inherits>
       <inherit from="LongitudinalSectionPoints" />
@@ -2827,7 +2845,7 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="48, 96, 255" />
     </fields>
   </theme>
-  <theme name="SInfoFloodDuration">
+  <theme name="SInfoFloodDurationLeft">
     <inherits>
       <inherit from="LongitudinalSectionPoints" />
     </inherits>
@@ -2836,6 +2854,15 @@
       <field name="pointcolor" type="Color" display="Punktfarbe" default="68, 216, 40" />
     </fields>
   </theme>
+  <theme name="SInfoFloodDurationRight">
+    <inherits>
+      <inherit from="LongitudinalSectionPoints" />
+    </inherits>
+    <fields>
+      <field name="pointsize" type="int" display="Punktdicke" default="3" />
+      <field name="pointcolor" type="Color" display="Punktfarbe" default="192, 0, 0" />
+    </fields>
+  </theme>
   <theme name="SInfoMainValues">
     <inherits>
       <inherit from="MainValuesW" />
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoProcessor.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/AbstractSInfoProcessor.java	Thu Jul 05 16:49:42 2018 +0200
@@ -24,6 +24,7 @@
 import org.dive4elements.river.artifacts.common.IResultType;
 import org.dive4elements.river.artifacts.context.RiverContext;
 import org.dive4elements.river.artifacts.math.MovingAverage;
+import org.dive4elements.river.artifacts.model.WQKms;
 import org.dive4elements.river.artifacts.model.ZoomScale;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
@@ -120,38 +121,29 @@
         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 buildStepLineSeriesForType(final double[][] points, final DiagramGenerator generator, final ArtifactAndFacet bundle,
+            final ThemeDocument theme, final boolean visible) {
+
+        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();
+
+        // Create WQKms to use the step points method
+        // REMARK: must have any values in w array; not sure whether the name is needed
+        final WQKms wqkms = new WQKms(points[0], points[1], points[1], facetName);
+
+        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
+
+        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) {
@@ -202,19 +194,6 @@
         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) {
 
         if (radius == null)
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/RiverInfoProvider.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/RiverInfoProvider.java	Thu Jul 05 16:49:42 2018 +0200
@@ -93,6 +93,13 @@
         return gauge == null ? this.notinrange : gauge.getName();
     }
 
+    public String findGauge(final double km, final boolean allGauges) {
+        // REMARK: access the gauge once only during calculation
+        final Gauge gauge = getGauge(km, allGauges);
+
+        return gauge == null ? this.notinrange : gauge.getName();
+    }
+
     private Gauge getGauge(final double km) {
 
         // REMARK: using same logic as in WaterlevelExporter here
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Thu Jul 05 16:49:42 2018 +0200
@@ -17,6 +17,7 @@
 import org.dive4elements.river.artifacts.common.I18NStrings;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.tkhcalculation.SoilKind;
+import org.dive4elements.river.model.Attribute.AttributeKey;
 import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.RiverUtils;
 
@@ -328,7 +329,7 @@
 
         @Override
         public String exportValue(final CallContext context, final Object value) {
-            return exportStringValue(value);
+            return localizeRiverside(context, (AttributeKey) value);
         }
 
         @Override
@@ -337,6 +338,19 @@
         }
     };
 
+    /**
+     * Returns the localized text of a riverside
+     * FIXME: Find a better place for this common method
+     */
+    public static final String localizeRiverside(final CallContext context, final AttributeKey riverside) {
+        if (riverside == AttributeKey.LEFT)
+            return Resources.getMsg(context.getMeta(), "riverside.left");
+        else if (riverside == AttributeKey.RIGHT)
+            return Resources.getMsg(context.getMeta(), "riverside.right");
+        else
+            return "?";
+    }
+
     public static final SInfoResultType gaugeLabel = new SInfoResultType(I18NStrings.UNIT_NONE, SInfoI18NStrings.CSV_GAUGE_HEADER) {
         private static final long serialVersionUID = 1L;
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationAccess.java	Thu Jul 05 16:49:42 2018 +0200
@@ -14,6 +14,7 @@
 import org.dive4elements.river.artifacts.access.RangeAccess;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
 import org.dive4elements.river.artifacts.sinfo.SinfoCalcMode;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
 
 /**
  * Access to the flood duration calculation type specific SInfo artifact data.
@@ -40,7 +41,7 @@
         return super.getStep();
     }
 
-    public String getRiverside() {
-        return super.getString("riverside");
+    public RiversideChoiceKey getRiverside() {
+        return RiversideChoiceKey.fromKey(super.getString("riverside"));
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Thu Jul 05 16:49:42 2018 +0200
@@ -49,20 +49,20 @@
 
         final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
         final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
-        final String riverside = Resources.getMsg(this.context.getMeta(), access.getRiverside());
         final Infrastructure infrasSeries = Infrastructure.getSeries(river);
         final String infrasType = (infrasSeries != null) ? infrasSeries.getType().getName() : "?";
+        final String label = infrasType + ", " + Resources.getMsg(this.context.getMeta(), access.getRiverside().getKey());
 
         final Calculation problems = new Calculation();
 
         // Calculate the selected main values, if any
         /* misuse winfo-artifact to calculate waterlevels in the same way */
         final WINFOArtifact winfo = new WinfoArtifactWrapper(sinfo);
+        // TODO Aktivieren wenn Step-Eingabe im Workflow weg: winfo.addStringData("ld_step", "100");
 
         final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange);
 
-        final FloodDurationCalculationResult result = calculateResult(infrasType, riverside, calcRange, infoProvider,
-                RiversideChoiceKey.fromKey(access.getRiverside()), problems, winfo);
+        final FloodDurationCalculationResult result = calculateResult(label, calcRange, infoProvider, access.getRiverside(), problems, winfo);
         results.addResult(result, problems);
 
         return new CalculationResult(results, problems);
@@ -71,11 +71,10 @@
     /**
      * Calculates the flood durations of the infrastructures of a km range of a river
      */
-    private FloodDurationCalculationResult calculateResult(final String infrastructureType, final String riverside, final DoubleRange calcRange,
-            final RiverInfoProvider riverInfoProvider, final RiversideChoiceKey riversideKey, final Calculation problems, final WINFOArtifact winfo) {
+    private FloodDurationCalculationResult calculateResult(final String label, final DoubleRange calcRange, final RiverInfoProvider riverInfoProvider,
+            final RiversideChoiceKey riverside, final Calculation problems, final WINFOArtifact winfo) {
 
         final FloodDurationCalculator calculator = new FloodDurationCalculator(this.context, riverInfoProvider);
-        final String label = infrastructureType + ", " + riverside;
-        return calculator.execute(problems, label, calcRange, riversideKey, winfo);
+        return calculator.execute(problems, label, calcRange, riverside, winfo);
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Thu Jul 05 16:49:42 2018 +0200
@@ -27,6 +27,7 @@
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.model.Attribute.AttributeKey;
 
 import gnu.trove.TDoubleArrayList;
 
@@ -41,8 +42,8 @@
 
     private final String[] mainvalueLabels;
 
-    private final int waterlevelCount;
     // private final WstInfo wstInfo;
+
     private final int maxWaterlevelPdf = 3;
 
     public interface ValueGetter {
@@ -56,20 +57,9 @@
     public FloodDurationCalculationResult(final String label, final String[] mainvalueLabels, final Collection<ResultRow> rows) {
         super(label, rows);
         this.mainvalueLabels = mainvalueLabels;
-        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;
-    // }
-
-    /**
      * Collection of the result rows containing only the rows describing an infrastructure
      */
     @Override
@@ -81,38 +71,6 @@
         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) {
         if (this.mainvalueLabels.length >= 1) {
@@ -125,14 +83,17 @@
                 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");
+            for (final ResultRow row : this.rows) {
+                exportContextCSV.writeCSVMetaEntry(SInfoI18NStrings.CSV_META_HEADER_WATERLEVEL_GAUGE, row.getValue(SInfoResultType.gaugeLabel));
+                break;
+            }
         }
         exportContextCSV.writeBlankLine();
     }
 
     @Override
     protected String getJasperFile() {
-        if (this.waterlevelCount <= 1)
+        if (this.getWaterlevelCount() <= 1)
             return "/jasper/templates/sinfo.floodduration.jrxml";
         else
             return "/jasper/templates/sinfo.floodduration2.jrxml";
@@ -189,14 +150,11 @@
         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.getHeaderWCsv(), new Object[] { appendIndex, "results.getRiver().getWstUnit()" }));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderFloodDurPerYearCsv(), args));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderQ(), args));
-            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderBezeichnCsv(), args));
+            final String appendIndex = "_" + Integer.toString(i + 1);
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderWCsv(), appendIndex, "results.getRiver().getWstUnit()"));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderFloodDurPerYearCsv(), appendIndex));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderQ(), appendIndex));
+            header.add(exportContextCSV.msg(DurationWaterlevel.getHeaderBezeichnCsv(), appendIndex));
         }
 
         header.add(exportContextCSV.formatCsvHeader(SInfoResultType.gaugeLabel));
@@ -229,39 +187,61 @@
         exportContextPDF.addJRMetadata(source, "infrastructure_height_header", SInfoResultType.infrastructureHeight);
         exportContextPDF.addJRMetadata(source, "infrastructure_type_header", SInfoResultType.infrastructuretype);
 
-        for (int i = 0; i < this.waterlevelCount; i++) {
-            final int naturalIndex = i + 1;
+        for (int i = 1; i <= this.getWaterlevelCount(); i++) {
 
-            final Object[] args = new String[] { new StringBuilder().append("_").append(naturalIndex).toString() };
-            exportContextPDF.addJRMetadata(source, getPdfHeader("w", naturalIndex), exportContextPDF.msg(DurationWaterlevel.getHeaderWPdf(), args));
-            exportContextPDF.addJRMetadata(source, getPdfHeader("duration", naturalIndex),
-                    exportContextPDF.msg(DurationWaterlevel.getHeaderFloodDurPerYearPdf(), args));
-            exportContextPDF.addJRMetadata(source, getPdfHeader("q", naturalIndex), exportContextPDF.msg(DurationWaterlevel.getHeaderQ(), args));
-            exportContextPDF.addJRMetadata(source, getPdfHeader("bezeichnung", naturalIndex),
-                    exportContextPDF.msg(DurationWaterlevel.getHeaderBezeichnPdf(), args));
+            final String appendIndex = "_" + Integer.toString(i);
+            exportContextPDF.addJRMetadata(source, getPdfHeader("w", i), exportContextPDF.msg(DurationWaterlevel.getHeaderWPdf(), appendIndex));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("duration", i),
+                    exportContextPDF.msg(DurationWaterlevel.getHeaderFloodDurPerYearPdf(), appendIndex));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("q", i), exportContextPDF.msg(DurationWaterlevel.getHeaderQ(), appendIndex));
+            exportContextPDF.addJRMetadata(source, getPdfHeader("bezeichnung", i),
+                    exportContextPDF.msg(DurationWaterlevel.getHeaderBezeichnPdf(), appendIndex));
         }
 
         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 final int getWaterlevelCount() {
+        return (this.mainvalueLabels != null) ? this.mainvalueLabels.length : 0;
+    }
+
     public String getMainValueLabel(final int j) {
-
         if (this.mainvalueLabels != null && j < this.mainvalueLabels.length)
             return this.mainvalueLabels[j];
         return "";
     }
 
+    /**
+     * Gets the longitudinal section of a result value type for one river side
+     */
+    public final double[][] getInfrastructurePoints(final IResultType type, final AttributeKey riverside) {
+
+        final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size());
+        final TDoubleArrayList yPoints = new TDoubleArrayList(this.rows.size());
+
+        for (final ResultRow row : this.rows) {
+
+            final double station = row.getDoubleValue(GeneralResultType.station);
+            final double value = row.getDoubleValue(type);
+            if (row.getValue(SInfoResultType.riverside) == riverside) {
+                xPoints.add(station);
+                yPoints.add(value);
+            }
+        }
+
+        return new double[][] { xPoints.toNativeArray(), yPoints.toNativeArray() };
+    }
+
+    /**
+     * Gets a longitudinal section of W, Q, or flood duration of one of the waterlevels
+     */
     public final double[][] getMainValueDurationPoints(final DiagramGenerator generator, final ValueGetter valuegetter, final int dataIndex) {
 
         final TDoubleArrayList xPoints = new TDoubleArrayList(this.rows.size());
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Thu Jul 05 16:49:42 2018 +0200
@@ -27,7 +27,6 @@
 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;
@@ -35,7 +34,6 @@
 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;
 import org.dive4elements.river.model.sinfo.InfrastructureValue;
 
 import gnu.trove.TDoubleArrayList;
@@ -82,17 +80,19 @@
         // 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
-        // FIXME: check, do we really need all stations? compare with tkh...
         addRangeStations(allStations, winfo);
         addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
         addInfrastructures(allStations, secondBank, infras);
         final double[] stationsSorted = sortStations(allStations.keySet());
 
         // Calculate W and Q for all stations and the selected discharge states
+        // TODO Laut Herrn Reiß: Q und D jeweils konstant für jedes Pegel-Intervall, Q-Änderungen (Zuflüsse etc.) aus .wst
+        // ignorieren
         final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
 
         // Determine discharge state labels of the main values
-        final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), firstGauge, problems);
+        final WaterlevelDescriptionBuilder wdescBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
+        final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), wdescBuilder, problems);
 
         // Create a finder for Q in the {river}.wst km-w-q table
         final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
@@ -103,7 +103,7 @@
         // 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(descBuilder, stationsSorted[i], gauge, wqkmsArray, durFinders.get(gauge), i);
+            final ResultRow row = createRow(descBuilder, stationsSorted[i], gauge, firstGauge, 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);
@@ -171,12 +171,13 @@
         // 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;
+        // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.),
+        // interpoliert die horizontale Tabellenposition (Q) und dann 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.
+        // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt, um das Q zu bekommen.
+
         final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations);
 
         /* copy all problems */
@@ -191,31 +192,14 @@
     }
 
     /**
-     * Determines the discharge state labels for the selected Q or W values
+     * Determines the waterlevel/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) {
+    private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final WaterlevelDescriptionBuilder descBuilder,
+            final Calculation problems) {
 
         final String[] mainValueLabels = new String[wqkmsArray.length];
-        if (wqkmsArray.length >= 1) {
-
-            // FIXME
-            // WaterlevelDescriptionBuilder builder = new WaterlevelDescriptionBuilder(artifact, context);
-
-            // Labels like Q=123 or W=123
-            for (int i = 0; i <= wqkmsArray.length - 1; i++) {
-                // FIXME
-                // String label = builder.getDesc(wqkmsArray[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]);
-            }
-        }
+        for (int i = 0; i <= wqkmsArray.length - 1; i++)
+            mainValueLabels[i] = descBuilder.getDesc(wqkmsArray[i]);
         return mainValueLabels;
     }
 
@@ -224,16 +208,15 @@
      *
      * @param descBuilder
      */
-    private ResultRow createRow(final WaterlevelDescriptionBuilder descBuilder, final Double station, final Gauge gauge, final WQKms[] wqkmsArray,
-            final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
+    private ResultRow createRow(final WaterlevelDescriptionBuilder descBuilder, final Double station, final Gauge gauge, final Gauge firstGauge,
+            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 gaugeLabel = this.riverInfoProvider.findGauge(station);
+        final String gaugeLabel = this.riverInfoProvider.findGauge(station, (gauge == firstGauge));
         row.putValue(SInfoResultType.gaugeLabel, gaugeLabel);
 
         final String location = this.riverInfoProvider.getLocation(station);
@@ -265,7 +248,7 @@
         final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight());
         final double qOut = Double.isInfinite(q) ? Double.NaN : q;
         final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q));
-        row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n
+        row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey());
         row.putValue(SInfoResultType.floodDuration, dur);
         row.putValue(SInfoResultType.floodDischarge, qOut);
         row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationProcessor.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationProcessor.java	Thu Jul 05 16:49:42 2018 +0200
@@ -19,10 +19,12 @@
 import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoProcessor;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultFacet;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.ValueGetter;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.model.Attribute.AttributeKey;
 import org.dive4elements.river.themes.ThemeDocument;
 
 /**
@@ -33,7 +35,9 @@
  */
 public final class FloodDurationProcessor extends AbstractSInfoProcessor {
 
-    private static final String FACET_FLOOD_DURATION = "sinfo_facet_flood_duration";
+    private static final String FACET_FLOOD_DURATION_LEFT = "sinfo_facet_flood_duration.left";
+
+    private static final String FACET_FLOOD_DURATION_RIGHT = "sinfo_facet_flood_duration.right";
 
     private static final String FACET_FLOOD_DURATION_DESCRIPTION = "sinfo_facet_flood_duration.description";
 
@@ -46,9 +50,9 @@
     private static final Set<String> HANDLED_FACET_TYPES = new HashSet<>();
 
     static {
-        HANDLED_FACET_TYPES.add(FACET_FLOOD_DURATION);
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_DURATION_LEFT);
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_DURATION_RIGHT);
         HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_DURATION);
-
     }
 
     public FloodDurationProcessor() {
@@ -56,10 +60,18 @@
     }
 
     public static Facet createFloodDurationFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int resultIndex) {
+            final int facetIndex, final int resultIndex) {
 
-        return AbstractSInfoProcessor.createFacet(context, hash, id, result, resultIndex, I18N_AXIS_LABEL, FACET_FLOOD_DURATION,
-                FACET_FLOOD_DURATION_DESCRIPTION);
+        if (facetIndex == 0) {
+            final String description = Resources.getMsg(context.getMeta(), FACET_FLOOD_DURATION_DESCRIPTION, FACET_FLOOD_DURATION_DESCRIPTION,
+                    SInfoResultType.localizeRiverside(context, AttributeKey.LEFT));
+            return new SInfoResultFacet(facetIndex, resultIndex, FACET_FLOOD_DURATION_LEFT, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
+        }
+        else {
+            final String description = Resources.getMsg(context.getMeta(), FACET_FLOOD_DURATION_DESCRIPTION, FACET_FLOOD_DURATION_DESCRIPTION,
+                    SInfoResultType.localizeRiverside(context, AttributeKey.RIGHT));
+            return new SInfoResultFacet(facetIndex, resultIndex, FACET_FLOOD_DURATION_RIGHT, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
+        }
     }
 
     public static Facet createMainValueDurationFacet(final CallContext context, final String hash, final String id, final FloodDurationCalculationResult result,
@@ -74,10 +86,15 @@
 
     @Override
     protected String generateSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible) {
+
         final String facetName = bundle.getFacetName();
 
-        if (FACET_FLOOD_DURATION.contentEquals(facetName))
-            return buildSeriesForType(generator, bundle, theme, visible, SInfoResultType.floodDuration, null);
+        if (FACET_FLOOD_DURATION_LEFT.contentEquals(facetName)) {
+            return buildInfrastructureSeries(generator, bundle, theme, visible, AttributeKey.LEFT);
+        }
+
+        if (FACET_FLOOD_DURATION_RIGHT.contentEquals(facetName))
+            return buildInfrastructureSeries(generator, bundle, theme, visible, AttributeKey.RIGHT);
 
         if (FACET_MAIN_VALUE_DURATION.contentEquals(facetName)) {
 
@@ -94,11 +111,21 @@
                     }
                 };
                 final double[][] points = ((FloodDurationCalculationResult) data).getMainValueDurationPoints(generator, valuegetter, index);
-                return buildSeriesForType(points, generator, bundle, theme, visible, null);
+                return buildStepLineSeriesForType(points, generator, bundle, theme, visible);
             }
         }
 
         final String error = String.format("Unknown facet name: %s", facetName);
         throw new UnsupportedOperationException(error);
     }
+
+    private String buildInfrastructureSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible,
+            final AttributeKey riverside) {
+
+        final FloodDurationCalculationResult data = (FloodDurationCalculationResult) getResult(generator, bundle);
+
+        final double[][] points = data.getInfrastructurePoints(SInfoResultType.floodDuration, riverside);
+
+        return buildSeriesForType(points, generator, bundle, theme, visible, null);
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Thu Jul 05 16:49:42 2018 +0200
@@ -21,6 +21,7 @@
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.ReportFacet;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
 import org.dive4elements.river.artifacts.states.DefaultState;
 
 /**
@@ -78,11 +79,18 @@
         int themeCount = 0;
         for (final FloodDurationCalculationResult result : resultList) {
 
-            facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, resultIndex));
+            final FloodDurationAccess access = new FloodDurationAccess(sinfo);
+            if ((access.getRiverside() == RiversideChoiceKey.LEFT) || (access.getRiverside() == RiversideChoiceKey.BOTH))
+                facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, 0, resultIndex));
+            if ((access.getRiverside() == RiversideChoiceKey.RIGHT) || (access.getRiverside() == RiversideChoiceKey.BOTH))
+                facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, 1, resultIndex));
 
             final int waterlevelCount = result.getWaterlevelCount();
 
-            facets.add(FloodHeightProcessor.createFloodHeightFacet(context, hash, this.id, result, resultIndex));
+            if ((access.getRiverside() == RiversideChoiceKey.LEFT) || (access.getRiverside() == RiversideChoiceKey.BOTH))
+                facets.add(FloodHeightProcessor.createFloodHeightFacet(context, hash, this.id, result, 0, resultIndex));
+            if ((access.getRiverside() == RiversideChoiceKey.RIGHT) || (access.getRiverside() == RiversideChoiceKey.BOTH))
+                facets.add(FloodHeightProcessor.createFloodHeightFacet(context, hash, this.id, result, 1, resultIndex));
 
             for (int j = 0; j < waterlevelCount; j++) {
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodHeightProcessor.java	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodHeightProcessor.java	Thu Jul 05 16:49:42 2018 +0200
@@ -19,11 +19,13 @@
 import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoProcessor;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultFacet;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.ValueGetter;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.exports.LongitudinalSectionGenerator;
+import org.dive4elements.river.model.Attribute.AttributeKey;
 import org.dive4elements.river.themes.ThemeDocument;
 
 /**
@@ -34,7 +36,9 @@
  */
 public final class FloodHeightProcessor extends AbstractSInfoProcessor {
 
-    private static final String FACET_FLOOD_HEIGHT = "sinfo_facet_flood_height";
+    private static final String FACET_FLOOD_HEIGHT_LEFT = "sinfo_facet_flood_height.left";
+
+    private static final String FACET_FLOOD_HEIGHT_RIGHT = "sinfo_facet_flood_height.right";
 
     private static final String FACET_FLOOD_HEIGHT_DESCRIPTION = "sinfo_facet_flood_height.description";
 
@@ -47,7 +51,8 @@
     private static final Set<String> HANDLED_FACET_TYPES = new HashSet<>();
 
     static {
-        HANDLED_FACET_TYPES.add(FACET_FLOOD_HEIGHT);
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_HEIGHT_LEFT);
+        HANDLED_FACET_TYPES.add(FACET_FLOOD_HEIGHT_RIGHT);
         HANDLED_FACET_TYPES.add(FACET_MAIN_VALUE_HEIGHT);
     }
 
@@ -56,8 +61,18 @@
     }
 
     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);
+            final int facetIndex, final int resultIndex) {
+
+        if (facetIndex == 0) {
+            final String description = Resources.getMsg(context.getMeta(), FACET_FLOOD_HEIGHT_DESCRIPTION, FACET_FLOOD_HEIGHT_DESCRIPTION,
+                    SInfoResultType.localizeRiverside(context, AttributeKey.LEFT));
+            return new SInfoResultFacet(facetIndex, resultIndex, FACET_FLOOD_HEIGHT_LEFT, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
+        }
+        else {
+            final String description = Resources.getMsg(context.getMeta(), FACET_FLOOD_HEIGHT_DESCRIPTION, FACET_FLOOD_HEIGHT_DESCRIPTION,
+                    SInfoResultType.localizeRiverside(context, AttributeKey.RIGHT));
+            return new SInfoResultFacet(facetIndex, resultIndex, FACET_FLOOD_HEIGHT_RIGHT, description, I18N_AXIS_LABEL, ComputeType.ADVANCE, id, hash);
+        }
     }
 
     public static Facet createMainValueHeightFacet(final CallContext context, final String hash, final String id, final FloodDurationCalculationResult result,
@@ -73,8 +88,12 @@
 
         final String facetName = bundle.getFacetName();
 
-        if (FACET_FLOOD_HEIGHT.contentEquals(facetName))
-            return buildSeriesForType(generator, bundle, theme, visible, SInfoResultType.infrastructureHeight, null);
+        if (FACET_FLOOD_HEIGHT_LEFT.contentEquals(facetName)) {
+            return buildInfrastructureSeries(generator, bundle, theme, visible, AttributeKey.LEFT);
+        }
+
+        if (FACET_FLOOD_HEIGHT_RIGHT.contentEquals(facetName))
+            return buildInfrastructureSeries(generator, bundle, theme, visible, AttributeKey.RIGHT);
 
         if (FACET_MAIN_VALUE_HEIGHT.contentEquals(facetName)) {
 
@@ -98,4 +117,14 @@
         final String error = String.format("Unknown facet name: %s", facetName);
         throw new UnsupportedOperationException(error);
     }
+
+    private String buildInfrastructureSeries(final DiagramGenerator generator, final ArtifactAndFacet bundle, final ThemeDocument theme, final boolean visible,
+            final AttributeKey riverside) {
+
+        final FloodDurationCalculationResult data = (FloodDurationCalculationResult) getResult(generator, bundle);
+
+        final double[][] points = data.getInfrastructurePoints(SInfoResultType.infrastructureHeight, riverside);
+
+        return buildSeriesForType(points, generator, bundle, theme, visible, null);
+    }
 }
\ No newline at end of file
--- a/artifacts/src/main/resources/messages.properties	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/resources/messages.properties	Thu Jul 05 16:49:42 2018 +0200
@@ -95,6 +95,8 @@
 state.sinfo.riverside.left = Linkes Ufer
 state.sinfo.riverside.right = Rechtes Ufer
 state.sinfo.riverside.both = Beide Ufer
+riverside.left = left
+riverside.right = right
 
 year=Year
 epoch=Epoch
--- a/artifacts/src/main/resources/messages_de.properties	Thu Jul 05 13:46:36 2018 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Thu Jul 05 16:49:42 2018 +0200
@@ -95,6 +95,8 @@
 state.sinfo.riverside.left = Linkes Ufer
 state.sinfo.riverside.right = Rechtes Ufer
 state.sinfo.riverside.both = Beide Ufer
+riverside.left = links
+riverside.right = rechts
 
 year=Jahr
 epoch=Epoche

http://dive4elements.wald.intevation.org