changeset 9620:26e113e8224f

Nachtrag Pos. 20: flood duration calculation for multiple infrastructure groups/types, local class FloodDurationCalculationResult.Infrastructure renamed and extracted into own class
author mschaefer
date Thu, 10 Oct 2019 17:11:54 +0200
parents 63bbd5e45839
children 387822d7d5e8
files 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/FloodDurationCurveProcessor.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationMainValuesQFacet.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/java/org/dive4elements/river/artifacts/sinfo/flood_duration/InfrastructureResultFacet.java backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java
diffstat 11 files changed, 114 insertions(+), 164 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculation.java	Thu Oct 10 17:11:54 2019 +0200
@@ -20,11 +20,11 @@
 import org.dive4elements.river.artifacts.model.river.RiverInfoProvider;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.sinfo.tkhstate.WinfoArtifactWrapper;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.sinfo.Infrastructure;
 
 /**
  * Calculation of the flood durations of the infrastructures of the km range of a river
@@ -76,10 +76,11 @@
 
         final FloodDurationCalculator calculator = new FloodDurationCalculator(this.context, riverInfoProvider);
 
-        // FIXME: fetch from access; maybe we need database for that... whatever
-        final Set<Infrastructure> infrastructureKeys = null;
+        // FIXME: fetch from access (fetchInfrastructureTypes only for test purposes)
+        final Set<String> infrastructureChoices = Infrastructure.fetchInfrastructureTypes(riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
+                calcRange.getMaximumDouble(), access.getRiverside().getAttributeKey());
 
-        calculator.execute(problems, label, calcRange, access.getRiverside(), infrastructureKeys, access.getIsWspl(), winfo, results);
+        calculator.execute(problems, label, calcRange, access.getRiverside().getAttributeKey(), infrastructureChoices, access.getIsWspl(), winfo, results);
     }
 
     /**
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculationResult.java	Thu Oct 10 17:11:54 2019 +0200
@@ -9,7 +9,6 @@
  */
 package org.dive4elements.river.artifacts.sinfo.flood_duration;
 
-import java.io.Serializable;
 import java.text.NumberFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -19,9 +18,6 @@
 import java.util.Set;
 
 import org.apache.commons.collections.Predicate;
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.common.AbstractCalculationExportableResult;
 import org.dive4elements.river.artifacts.common.AbstractExportContext;
 import org.dive4elements.river.artifacts.common.ExportContextCSV;
@@ -30,10 +26,8 @@
 import org.dive4elements.river.artifacts.common.IResultType;
 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 import org.dive4elements.river.artifacts.common.ResultRow;
-import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
-import org.dive4elements.river.model.Attribute.AttributeKey;
 
 import gnu.trove.TDoubleArrayList;
 
@@ -44,18 +38,18 @@
  */
 public final class FloodDurationCalculationResult extends AbstractCalculationExportableResult {
 
-    private final static class InfrastructurePredicate implements Predicate {
+    private final static class IsInfrastructureChoicePredicate implements Predicate {
 
-        private final Infrastructure m_infrastructure;
+        private final FloodDurationInfrastructureChoice m_infrastructure;
 
-        public InfrastructurePredicate(final Infrastructure infrastructure) {
+        public IsInfrastructureChoicePredicate(final FloodDurationInfrastructureChoice infrastructure) {
             this.m_infrastructure = infrastructure;
         }
 
         @Override
         public boolean evaluate(final Object object) {
             final ResultRow row = (ResultRow) object;
-            final Infrastructure test = new Infrastructure(row);
+            final FloodDurationInfrastructureChoice test = new FloodDurationInfrastructureChoice(row);
             return this.m_infrastructure.equals(test);
         }
     }
@@ -70,69 +64,6 @@
         }
     }
 
-    public static final class Infrastructure implements Serializable {
-
-        private static final long serialVersionUID = 1L;
-
-        private final String m_group;
-
-        private final String m_type;
-
-        private final AttributeKey m_riverside;
-
-        private static final String FACET_FLOOD_DURATION_DESCRIPTION = "sinfo_facet_flood_duration";
-
-        private static final String FACET_ABSOLUTE_HEIGHT = "sinfo.flood_duration.absolute.height";
-
-        public Infrastructure(final ResultRow row) {
-            this.m_group = String.valueOf(row.getValue(SInfoResultType.infrastructuregroup));
-            this.m_type = String.valueOf(row.getValue(SInfoResultType.infrastructuretype));
-            final String riversideStr = String.valueOf(row.getValue(SInfoResultType.riverside));
-            this.m_riverside = riversideStr.equals("null") ? AttributeKey.NONE : AttributeKey.valueOf(riversideStr);
-        }
-
-        public AttributeKey getRiverside() {
-            return this.m_riverside;
-        }
-
-        @Override
-        public int hashCode() {
-            return new HashCodeBuilder() //
-                    .append(this.m_group)//
-                    .append(this.m_type)//
-                    .append(this.m_riverside)//
-                    .toHashCode();
-        }
-
-        @Override
-        public boolean equals(final Object obj) {
-
-            if (obj == null)
-                return false;
-            if (obj == this)
-                return true;
-            if (obj.getClass() != getClass())
-                return false;
-
-            final Infrastructure other = (Infrastructure) obj;
-            return new EqualsBuilder() //
-                    .append(this.m_group, other.m_group) //
-                    .append(this.m_type, other.m_type) //
-                    .append(this.m_riverside, other.m_riverside) //
-                    .isEquals();
-        }
-
-        public String getFloodHeightLabel(final CallContext context) {
-            return Resources.getMsg(context.getMeta(), FACET_ABSOLUTE_HEIGHT, FACET_ABSOLUTE_HEIGHT)
-                    + " " + SInfoResultType.getInfrastructureLabel(context, this.m_group, this.m_type, this.m_riverside);
-        }
-
-        public String getFloodDurationLabel(final CallContext context) {
-            return Resources.getMsg(context.getMeta(), FACET_FLOOD_DURATION_DESCRIPTION, FACET_FLOOD_DURATION_DESCRIPTION)
-                    + " " + SInfoResultType.getInfrastructureLabel(context, this.m_group, this.m_type, this.m_riverside);
-        }
-    }
-
     private static final long serialVersionUID = 1L;
 
     private final boolean isUseWspl;
@@ -141,7 +72,7 @@
 
     private final int maxWaterlevelPdf = 3;
 
-    private final Set<Infrastructure> m_infrastructures;
+    private final Set<FloodDurationInfrastructureChoice> m_infrastructures;
 
     public interface ValueGetter {
         double getValue(DurationWaterlevel waterlevel);
@@ -152,14 +83,14 @@
     }
 
     public FloodDurationCalculationResult(final String label, final String[] mainvalueLabels, final Collection<ResultRow> rows, final boolean isUseWspl,
-            final Set<Infrastructure> infrastructures) {
+            final Set<FloodDurationInfrastructureChoice> infrastructures) {
         super(label, rows);
         this.waterlevelLabels = mainvalueLabels;
         this.isUseWspl = isUseWspl;
         this.m_infrastructures = infrastructures;
     }
 
-    public Set<Infrastructure> getInfrastructureMap() {
+    public Set<FloodDurationInfrastructureChoice> getInfrastructureMap() {
         return this.m_infrastructures;
     }
 
@@ -346,9 +277,9 @@
     /**
      * Gets the longitudinal section of a result value type for one river side
      */
-    public final double[][] getInfrastructurePoints(final IResultType type, final Infrastructure infrastructure) {
+    public final double[][] getInfrastructurePoints(final IResultType type, final FloodDurationInfrastructureChoice infrastructure) {
 
-        return getPoints(GeneralResultType.station, type, new InfrastructurePredicate(infrastructure));
+        return getPoints(GeneralResultType.station, type, new IsInfrastructureChoicePredicate(infrastructure));
     }
 
     /**
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Thu Oct 10 17:11:54 2019 +0200
@@ -33,8 +33,6 @@
 import org.dive4elements.river.artifacts.model.river.RiverInfoProvider;
 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
-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;
@@ -54,6 +52,7 @@
 
     private final RiverInfoProvider riverInfoProvider;
     private RiverInfoProvider riverInfoProvider2;
+    private final Map<Double, List<InfrastructureValue>> stationInfras;
 
     private final CallContext context;
 
@@ -61,18 +60,14 @@
         this.context = context;
         this.riverInfoProvider = riverInfoProvider;
         this.riverInfoProvider2 = null;
+        this.stationInfras = new HashMap<>();
     }
 
     /**
      * Calculate the infrastructures flood duration result rows
      */
-    public void execute(final Calculation problems, final String label, final DoubleRange calcRange, final RiversideChoiceKey riverside,
-            final Set<Infrastructure> infrastruktureKey, final boolean withWspl, final WINFOArtifact winfo, final FloodDurationCalculationResults results) {
-
-        // FIXME Schäfer:
-        // filter by infrastructureKey
-        // FIXME: Tironi: api gscheit benennen;
-        // Lösung finden für: Infrastructure enthält riverside, wird hier aber nicht benötigt (weil schon im choice)
+    public void execute(final Calculation problems, final String label, final DoubleRange calcRange, final AttributeKey riverside,
+            final Set<String> infrastructureChoices, final boolean withWspl, final WINFOArtifact winfo, final FloodDurationCalculationResults results) {
 
         // Find all gauges of the calc range, and create the duration finders
         final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>();
@@ -81,17 +76,18 @@
         }
 
         // Find all infrastructures within the calc range
-        final AttributeKey bankKey = riverside.getAttributeKey();
         final List<InfrastructureValue> infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
-                calcRange.getMaximumDouble(), bankKey);
+                calcRange.getMaximumDouble(), riverside, infrastructureChoices);
 
         // Merge all stations (range/step, borders of gauge ranges, infrastructures)
-        final Map<Double, InfrastructureValue> allStations = new HashMap<>();
-        final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option
-        addRangeStations(allStations, winfo);
-        addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
-        addInfrastructures(allStations, secondBank, infras);
-        final double[] stationsSorted = sortStations(allStations.keySet());
+        // final Map<Double, InfrastructureValue> allStations = new HashMap<>();
+        this.stationInfras.clear();
+        // final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of
+        // both-banks-option
+        addRangeStations(winfo);
+        addGaugeLimits(durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
+        addInfrastructures(infras);
+        final double[] stationsSorted = sortStations(this.stationInfras.keySet());
 
         // Calculate W and Q for all stations and the selected discharge states/waterlevels
         final WQKms[] wqkmsArray = calculateWsts(winfo, withWspl, stationsSorted, problems);
@@ -111,22 +107,18 @@
         // (should be in cache since already used in calculateWaterlevels (winfo.computeWaterlevelData)
         final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider2.getRiver());
 
-        final Set<FloodDurationCalculationResult.Infrastructure> infrastructures = new HashSet<>();
+        final Set<FloodDurationInfrastructureChoice> infrastructures = new HashSet<>();
 
         // Create the result rows, and calculate and add the flood durations etc.
         for (int i = 0; i <= stationsSorted.length - 1; i++) {
             final Gauge gauge = this.riverInfoProvider2.getGauge(stationsSorted[i], true);
-            final ResultRow row = createRow(stationsSorted[i], wqkmsArray, gaugeWstDurations.get(gauge), i);
-            if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
-                calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wst, durFinders, infrastructures);
-
-            this.rows.add(row);
-            if (secondBank.containsKey(stationsSorted[i])) {
-                final ResultRow row2 = ResultRow.create(row);
-                calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wst, durFinders, infrastructures);
-                this.rows.add(row2);
+            if (this.stationInfras.containsKey(stationsSorted[i])) {
+                for (final InfrastructureValue infra : this.stationInfras.get(stationsSorted[i])) {
+                    final ResultRow row = createRow(stationsSorted[i], wqkmsArray, gaugeWstDurations.get(gauge), i);
+                    calculateInfrastructure(row, gauge, infra, wst, durFinders, infrastructures);
+                    this.rows.add(row);
+                }
             }
-
         }
 
         // Get the labels of the selected waterlevels
@@ -158,36 +150,33 @@
     /**
      * Adds to a stations map all stations corresponding to the active range and step
      */
-    private void addRangeStations(final Map<Double, InfrastructureValue> allStations, final WINFOArtifact winfo) {
+    private void addRangeStations(final WINFOArtifact winfo) {
         for (final double station : new ComputationRangeAccess(winfo).getKms())
-            allStations.put(Double.valueOf(station), null);
+            this.stationInfras.put(Double.valueOf(station), new ArrayList<InfrastructureValue>());
     }
 
     /**
      * Adds to a stations map all range limits of the gauges within the calc range
      */
-    private void addGaugeLimits(final Map<Double, InfrastructureValue> allStations, final Set<Gauge> gauges, final double fromKm, final double toKm) {
+    private void addGaugeLimits(final Set<Gauge> gauges, final double fromKm, final double toKm) {
         for (final Gauge gauge : gauges) {
             final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue());
             final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue());
             if (kmA > fromKm - 0.0001)
-                allStations.put(kmA, null);
+                this.stationInfras.put(kmA, new ArrayList<InfrastructureValue>());
             if (kmB < toKm + 0.0001)
-                allStations.put(kmB, null);
+                this.stationInfras.put(kmB, new ArrayList<InfrastructureValue>());
         }
     }
 
     /**
-     * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map
+     * Adds all infrastructures of a station to the station map
      */
-    private void addInfrastructures(final Map<Double, InfrastructureValue> allStations, final Map<Double, InfrastructureValue> secondBank,
-            final List<InfrastructureValue> infrastructures) {
+    private void addInfrastructures(final List<InfrastructureValue> infrastructures) {
         for (final InfrastructureValue infrastructure : infrastructures) {
             final Double station = infrastructure.getStation();
-            if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue))
-                allStations.put(station, infrastructure);
-            else
-                secondBank.put(station, infrastructure);
+            if (this.stationInfras.containsKey(station))
+                this.stationInfras.get(station).add(infrastructure);
         }
     }
 
@@ -312,7 +301,7 @@
      * Calculate the result row fields for one infrastructure
      */
     private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, final WstValueTable wst,
-            final Map<Gauge, GaugeDurationValuesFinder> durFinders, final Set<Infrastructure> infrastructures) {
+            final Map<Gauge, GaugeDurationValuesFinder> durFinders, final Set<FloodDurationInfrastructureChoice> infrastructures) {
 
         // Interpolate the infrastructure height in the wst table to get the corresponding Q
         final Calculation problems = new Calculation();
@@ -328,16 +317,15 @@
 
         // Determine the relative column position of the Q of the infrastructure height
         final QPosition qPos = wst.getQPosition(infrastructure.getStation().doubleValue(), q);
-        if (qPos == null)
-            return;
-        // Get the Q for the found column position for the station of the gauge
-        final double qGauge = wst.getQ(qPos, gauge.getStation().doubleValue());
-        // Interpolate the Q-D-table of the gauge
-        final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(qGauge));
-        // Set D in the result row
-        row.putValue(SInfoResultType.floodDuration, dur);
-
-        final FloodDurationCalculationResult.Infrastructure groupType = new FloodDurationCalculationResult.Infrastructure(row);
+        if (qPos != null) {
+            // Get the Q for the found column position for the station of the gauge
+            final double qGauge = wst.getQ(qPos, gauge.getStation().doubleValue());
+            // Interpolate the Q-D-table of the gauge
+            final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(qGauge));
+            // Set D in the result row
+            row.putValue(SInfoResultType.floodDuration, dur);
+        }
+        final FloodDurationInfrastructureChoice groupType = new FloodDurationInfrastructureChoice(row);
         infrastructures.add(groupType);
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCurveProcessor.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCurveProcessor.java	Thu Oct 10 17:11:54 2019 +0200
@@ -18,7 +18,6 @@
 import org.dive4elements.river.artifacts.common.AbstractCalculationResult;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.resources.Resources;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.exports.DurationCurveGenerator;
 import org.dive4elements.river.exports.process.DefaultProcessor;
@@ -85,7 +84,7 @@
     }
 
     public static Facet createMainValuesQFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int facetIndex, final int resultIndex, final String description, final Infrastructure infrastructure) {
+            final int facetIndex, final int resultIndex, final String description, final FloodDurationInfrastructureChoice infrastructure) {
 
         return new FloodDurationMainValuesQFacet(FACET_FLOOD_DURATION_MAINVALUES_Q, description, facetIndex, infrastructure);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationMainValuesQFacet.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationMainValuesQFacet.java	Thu Oct 10 17:11:54 2019 +0200
@@ -19,7 +19,6 @@
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.Calculation;
 import org.dive4elements.river.artifacts.model.CalculationResult;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.fixings.FixChartGenerator;
 import org.dive4elements.river.jfree.RiverAnnotation;
@@ -35,10 +34,11 @@
     /** Own log. */
     private static Logger log = Logger.getLogger(FloodDurationMainValuesQFacet.class);
 
-    private final Infrastructure m_infrastructure;
+    private final FloodDurationInfrastructureChoice m_infrastructure;
 
     /** Trivial Constructor. */
-    public FloodDurationMainValuesQFacet(final String name, final String description, final int facetIndex, final Infrastructure infrastructure) {
+    public FloodDurationMainValuesQFacet(final String name, final String description, final int facetIndex,
+            final FloodDurationInfrastructureChoice infrastructure) {
         this.description = description;
         this.name = name;
         this.index = facetIndex;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationProcessor.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationProcessor.java	Thu Oct 10 17:11:54 2019 +0200
@@ -20,7 +20,6 @@
 import org.dive4elements.river.artifacts.common.AbstractProcessor;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.ValueGetter;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
@@ -58,7 +57,7 @@
     }
 
     public static Facet createFloodDurationFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int facetIndex, final int resultIndex, final Infrastructure infrastructure) {
+            final int facetIndex, final int resultIndex, final FloodDurationInfrastructureChoice infrastructure) {
 
         final String description = infrastructure.getFloodDurationLabel(context);
         final String facetName = infrastructure.getRiverside() == AttributeKey.LEFT ? FACET_FLOOD_DURATION_LEFT : FACET_FLOOD_DURATION_RIGHT;
@@ -110,7 +109,7 @@
             final boolean visible) {
 
         final InfrastructureResultFacet infFacet = (InfrastructureResultFacet) bundle.getFacet();
-        final Infrastructure infrastructure = infFacet.getInfrastructure();
+        final FloodDurationInfrastructureChoice infrastructure = infFacet.getInfrastructure();
 
         final FloodDurationCalculationResult data = (FloodDurationCalculationResult) getResult(generator, bundle);
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationState.java	Thu Oct 10 17:11:54 2019 +0200
@@ -27,7 +27,6 @@
 import org.dive4elements.river.artifacts.model.ReportFacet;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.states.DefaultState;
 import org.dive4elements.river.model.Attribute.AttributeKey;
 
@@ -128,8 +127,8 @@
                 facets.add(FloodDurationCurveProcessor.createInfrastructureFacet(context, hash, this.id, result, themeCount++, resultIndex, false, riversideC));
             }
 
-            final Set<Infrastructure> infrastructures = result.getInfrastructureMap();
-            for (final Infrastructure entry : infrastructures) {
+            final Set<FloodDurationInfrastructureChoice> infrastructures = result.getInfrastructureMap();
+            for (final FloodDurationInfrastructureChoice entry : infrastructures) {
 
                 facets.add(FloodDurationProcessor.createFloodDurationFacet(context, hash, this.id, result, themeCount++, resultIndex, entry));
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodHeightProcessor.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodHeightProcessor.java	Thu Oct 10 17:11:54 2019 +0200
@@ -20,7 +20,6 @@
 import org.dive4elements.river.artifacts.common.AbstractProcessor;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.ValueGetter;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 import org.dive4elements.river.exports.DiagramGenerator;
@@ -58,7 +57,7 @@
     }
 
     public static Facet createFloodHeightFacet(final CallContext context, final String hash, final String id, final AbstractCalculationResult result,
-            final int facetIndex, final int resultIndex, final Infrastructure infrastructure) {
+            final int facetIndex, final int resultIndex, final FloodDurationInfrastructureChoice infrastructure) {
 
         final String facetName = infrastructure.getRiverside().equals(AttributeKey.LEFT) ? FACET_FLOOD_HEIGHT_LEFT : FACET_FLOOD_HEIGHT_RIGHT;
         final String description = infrastructure.getFloodHeightLabel(context);
@@ -108,7 +107,7 @@
             final boolean visible) {
 
         final InfrastructureResultFacet infFacet = (InfrastructureResultFacet) bundle.getFacet();
-        final Infrastructure infrastructure = infFacet.getInfrastructure();
+        final FloodDurationInfrastructureChoice infrastructure = infFacet.getInfrastructure();
 
         final FloodDurationCalculationResult data = (FloodDurationCalculationResult) getResult(generator, bundle);
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/InfrastructureResultFacet.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/InfrastructureResultFacet.java	Thu Oct 10 17:11:54 2019 +0200
@@ -10,7 +10,6 @@
 package org.dive4elements.river.artifacts.sinfo.flood_duration;
 
 import org.dive4elements.river.artifacts.common.ResultFacet;
-import org.dive4elements.river.artifacts.sinfo.flood_duration.FloodDurationCalculationResult.Infrastructure;
 import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
 
 /**
@@ -21,15 +20,15 @@
 
     private static final long serialVersionUID = 1L;
 
-    private final Infrastructure m_infrastructure;
+    private final FloodDurationInfrastructureChoice m_infrastructure;
 
     public InfrastructureResultFacet(final int facetIndex, final int resultIndex, final String name, final String description, final String axisLabel,
-            final String id, final String hash, final Infrastructure infrastructure) {
+            final String id, final String hash, final FloodDurationInfrastructureChoice infrastructure) {
         super(facetIndex, resultIndex, name, description, axisLabel, ComputeType.ADVANCE, id, hash);
         this.m_infrastructure = infrastructure;
     }
 
-    public Infrastructure getInfrastructure() {
+    public FloodDurationInfrastructureChoice getInfrastructure() {
         return this.m_infrastructure;
     }
 }
--- a/backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/sinfo/Infrastructure.java	Thu Oct 10 17:11:54 2019 +0200
@@ -12,7 +12,9 @@
 
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -228,10 +230,22 @@
     }
 
     /**
+     * Fetches from the database the infrastructure groups and types of a river's km range and river side(s)
+     * as tab-separated strings ordered by the infrastructure group, then the type
+     */
+    public static Set<String> fetchInfrastructureTypes(final River river, final double kmLo, final double kmHi,
+            final AttributeKey riverside) {
+        final Set<String> groupTypes = new HashSet<>();
+        for (final Infrastructure ifs : fetchInfrastructureTypeList(river, kmLo, kmHi, riverside))
+            groupTypes.add(ifs.getGroup().getName() + "\t" + ifs.getType().getName());
+        return groupTypes;
+    }
+
+    /**
      * Fetches from the database the list of infrastructure types of a river's km range and river side(s)
      * ordered by type group
      */
-    public static List<Infrastructure> fetchInfrastructureTypes(final River river, final double kmLo, final double kmHi,
+    private static List<Infrastructure> fetchInfrastructureTypeList(final River river, final double kmLo, final double kmHi,
             final AttributeKey riverside) {
 
         final Session session = SessionHolder.HOLDER.get();
@@ -240,7 +254,7 @@
                 + " WHERE (river=:river)"
                 + " AND (id IN (SELECT v.infrastructure.id FROM InfrastructureValue v"
                 + "  WHERE (v.station BETWEEN (:kmLo - 0.0001) AND (:kmHi + 0.0001))"
-                + InfrastructureValue.getRiversideClause(riverside, "v", "attr_id")
+                + InfrastructureValue.getRiversideClause(riverside, "v.", "attr_id")
                 + "))"
                 + " ORDER BY group, type");
         query.setParameter("river", river);
--- a/backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java	Thu Oct 10 16:08:47 2019 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/sinfo/InfrastructureValue.java	Thu Oct 10 17:11:54 2019 +0200
@@ -12,6 +12,7 @@
 
 import java.io.Serializable;
 import java.util.List;
+import java.util.Set;
 
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -148,29 +149,49 @@
     }
 
     /**
-     * Selects from the database the infrastructure values of a km range of a river and a river side
+     * Selects from the database the infrastructure values of a km range of a river and a group/type/river-side selection
      */
-    public static List<InfrastructureValue> getValues(final River river, final double kmLo, final double kmHi, final AttributeKey riverside) {
+    public static List<InfrastructureValue> getValues(final River river, final double kmLo, final double kmHi, final AttributeKey riverside,
+            final Set<String> groupTypes) {
         final Session session = SessionHolder.HOLDER.get();
-        final Query query = session.createQuery("FROM InfrastructureValue v"
-                + " WHERE (v.infrastructure.river=:river)"
-                + " AND (v.station BETWEEN :kmLo - 0.0001 AND :kmHi + 0.0001)"
-                + getRiversideClause(riverside, "v", "attr_id")
-                + " ORDER BY v.station, v.attribute.id");
+        final Query query = session.createQuery("FROM InfrastructureValue"
+                + " WHERE (infrastructure.river=:river)"
+                + " AND (station BETWEEN :kmLo - 0.0001 AND :kmHi + 0.0001)"
+                + getRiversideClause(riverside, "", "attr_id")
+                + getGroupTypeClause(groupTypes, "")
+                + " ORDER BY station, attribute.id");
         query.setParameter("river", river);
         query.setParameter("kmLo", new Double(kmLo));
         query.setParameter("kmHi", new Double(kmHi));
-        if (!getRiversideClause(riverside, "v", "attr_id").isEmpty())
+        if (!getRiversideClause(riverside, "", "attr_id").isEmpty())
             query.setParameter("attr_id", riverside.getId());
         return query.list();
     }
 
     /**
-     * Gets a query's and-where-clause for a riverside key
+     * Gets a query's and-where-clause for a set of infrastructure group-type-pairs (as tab-separated strings)
      */
-    public static String getRiversideClause(final AttributeKey riverside, final String tablealias, final String variable) {
+    private static String getGroupTypeClause(final Set<String> groupTypes, final String tableprefix) {
+        if (groupTypes.size() == 0)
+            return "";
+        String clause = " AND (";
+        String sep = "";
+        for (final String groupType : groupTypes) {
+            clause += sep + "(" + tableprefix + "infrastructure.group.name='" + groupType.split("\t")[0] + "'"
+                    + " AND " + tableprefix + "infrastructure.type.name='" + groupType.split("\t")[1] + "')";
+            sep = " OR ";
+        }
+        if (sep.length() >= 1)
+            clause += ")";
+        return clause;
+    }
+
+    /**
+     * Gets a query's and-where-clause for a riverside key (tableprefix empty or starting with a dot)
+     */
+    public static String getRiversideClause(final AttributeKey riverside, final String tableprefix, final String variable) {
         if ((riverside == AttributeKey.LEFT) || (riverside == AttributeKey.RIGHT))
-            return " AND (" + tablealias + ".attribute.id=:" + variable + ")";
+            return " AND (" + tableprefix + "attribute.id=:" + variable + ")";
         else
             return "";
     }

http://dive4elements.wald.intevation.org