changeset 8951:322b0e6298ea

Work on SINFO FlowDepth-Development
author gernotbelger
date Fri, 16 Mar 2018 18:08:38 +0100
parents b0aeed4c97c1
children 1a8f19f3b776
files artifacts/doc/conf/artifacts/sinfo.xml artifacts/doc/conf/generators/generators.xml artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/SInfoI18NStrings.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/DatacagePairSelectState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthPairSelectState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthUtils.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentAccess.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculationResult.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculationResults.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentExporter.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/WaterlevelSoundingCurrentPairSelectState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/WaterlevelSoundingHistoricalPairSelectState.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties
diffstat 23 files changed, 1049 insertions(+), 86 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/artifacts/sinfo.xml	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/doc/conf/artifacts/sinfo.xml	Fri Mar 16 18:08:38 2018 +0100
@@ -130,6 +130,67 @@
       </outputmodes>
     </state>
 
+    <!-- Calculation Mode: Fließtiefenentwicklung -->
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.sinfo.calculation_mode"/>
+      <to state="state.sinfo.distance_only"/>
+      <condition data="calculation_mode" value="sinfo_calc_flow_depth_development" operator="equal"/>
+    </transition>
+
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.sinfo.distance_only"/>
+      <to state="state.sinfo.flowdepthdevlopment_current_select"/>
+      <condition data="calculation_mode" value="sinfo_calc_flow_depth_development" operator="equal"/>
+    </transition>
+
+    <state id="state.sinfo.flowdepthdevlopment_current_select" description="state.sinfo.flowdepthdevlopment_current_select" state="org.dive4elements.river.artifacts.sinfo.flowdepthdev.WaterlevelSoundingCurrentPairSelectState" helpText="help.state.sinfo.flowdepthdevlopment_current_select">
+      <data name="diffid_current" type="String"/>
+    </state>
+
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.sinfo.flowdepthdevlopment_current_select"/>
+      <to state="state.sinfo.flowdepthdevlopment_historical_select"/>
+      <condition data="calculation_mode" value="sinfo_calc_flow_depth_development" operator="equal"/>
+    </transition>
+
+    <state id="state.sinfo.flowdepthdevlopment_historical_select" description="state.sinfo.flowdepthdevlopment_historical_select" state="org.dive4elements.river.artifacts.sinfo.flowdepthdev.WaterlevelSoundingHistoricalPairSelectState" helpText="help.state.sinfo.flowdepthdevlopment_historical_select">
+      <data name="diffid_historical" type="String"/>
+    </state>
+
+    <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
+      <from state="state.sinfo.flowdepthdevlopment_historical_select"/>
+      <to state="state.sinfo.flow_depth_development"/>
+      <condition data="calculation_mode" value="sinfo_calc_flow_depth_development" operator="equal"/>
+    </transition>
+
+    <state id="state.sinfo.flow_depth_development" description="state.sinfo.flow_depth_development" state="org.dive4elements.river.artifacts.sinfo.flowdepthdev.FlowDepthDevelopmentState" helpText="help.state.sinfo.flow_depth_development">
+      <outputmodes>
+        <outputmode name="sinfo_flow_depth_development" description="output.flow_depth_development" mime-type="image/png" type="chart">
+          <facets>
+          <!-- 
+            <facet name="sinfo_facet_flow_depth_min.filtered" description="min flow depth"/>
+            <facet name="sinfo_facet_flow_depth_max.filtered" description="max flow depth"/>
+           -->
+
+            <facet name="longitudinal_section.annotations" description="facet.longitudinal_section.annotations"/>
+          </facets>
+        </outputmode>
+
+        <outputmode name="sinfo_flowdepthdevelopment_export" description="output.sinfo_flowdepthdevelopment_export" mime-type="text/plain" type="export">
+          <facets>
+            <facet name="csv" description="facet.sinfo_flowdepthdevelopment_export.csv"/>
+            <facet name="pdf" description="facet.sinfo_flowdepthdevelopment_export.pdf"/>
+          </facets>
+        </outputmode>
+
+        <outputmode name="sinfo_flowdepthdevelopment_report" description="output.sinfo_flowdepthdevelopment_report" mime-type="text/xml" type="report">
+          <facets>
+            <facet name="report" description="facet.sinfo_flowdepthdevelopment_report"/>
+          </facets>
+        </outputmode>
+      </outputmodes>
+    </state>
+
     <!-- Calculation Mode: Transportkörperhöhen -->
     <transition transition="org.dive4elements.river.artifacts.transitions.ValueCompareTransition">
       <from state="state.sinfo.calculation_mode"/>
--- a/artifacts/doc/conf/generators/generators.xml	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/doc/conf/generators/generators.xml	Fri Mar 16 18:08:38 2018 +0100
@@ -64,6 +64,8 @@
     <output-generator names="sinfo_flowdepth_report" class="org.dive4elements.river.exports.ReportGenerator"/>
     <output-generator names="sinfo_flowdepthminmax_export" class="org.dive4elements.river.artifacts.sinfo.flowdepthminmax.FlowDepthMinMaxExporter"/>
     <output-generator names="sinfo_flowdepthminmax_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="sinfo_flowdepthdevelopment_export" class="org.dive4elements.river.artifacts.sinfo.flowdepthdev.FlowDepthDevelopmentExporter"/>
+    <output-generator names="sinfo_flowdepthdevelopment_report" class="org.dive4elements.river.exports.ReportGenerator"/>
     <output-generator names="sinfo_tkh_export" class="org.dive4elements.river.artifacts.sinfo.tkhstate.TkhExporter"/>
     <output-generator names="sinfo_tkh_report" class="org.dive4elements.river.exports.ReportGenerator"/>
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/SInfoI18NStrings.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/SInfoI18NStrings.java	Fri Mar 16 18:08:38 2018 +0100
@@ -68,6 +68,18 @@
 
     String CSV_TKHKIND_HEADER = "sinfo.export.tkh.csv.header.tkhkind";
 
+    String CSV_FLOWDEPTH_DEVELOPMENT_HEADER = "sinfo.export.csv.header.flowdepth.development";
+
+    String CSV_FLOWDEPTH_DEVELOPMENT_PER_YEAR_HEADER = "sinfo.export.csv.header.flowdepth.development.per.year";
+
+    String CSV_WATERLEVEL_DIFFERENCE_HEADER = "sinfo.export.csv.header.waterlevel.difference";
+
+    String CSV_MEAN_BED_HEIGHT_DIFFERENCE_HEADER = "sinfo.export.csv.header.mean_bed_height.difference";
+
+    String CSV_FLOWDEPTH_CURRENT_HEADER = "sinfo.export.csv.header.flowdepth.current";
+
+    String CSV_FLOWDEPTH_HISTORICAL_HEADER = "sinfo.export.csv.header.flowdepth.historical";
+
     String CSV_META_HEADER_WATERLEVEL = "sinfo.export.flow_depth.csv.meta.header.waterlevel";
 
     String CSV_META_HEADER_WATERLEVEL_NAME = "sinfo.export.flow_depth.csv.meta.header.waterlevel.name";
@@ -92,5 +104,7 @@
 
     String UNIT_CM = "cm";
 
+    String UNIT_CM_A = "cm/a";
+
     String UNIT_CUBIC_M = "m³/s";
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/DatacagePairSelectState.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,118 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.sinfo.common;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.artifactdatabase.ProtocolUtils;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.artifacts.states.WaterlevelPairSelectState;
+import org.w3c.dom.Element;
+
+/**
+ * Copy of {@link WaterlevelPairSelectState}. We must copy instead of crate an abstraction, else we break the
+ * serialization of {@link WaterlevelPairSelectState}.
+ * State in which the user selects 1 to n pairs of Waterlevels and alikes.
+ */
+public abstract class DatacagePairSelectState extends DefaultState implements FacetTypes {
+
+    private static final long serialVersionUID = 1L;
+
+    /** The log that is used in this state. */
+    private static Logger log = Logger.getLogger(DatacagePairSelectState.class);
+
+    private final String dataId;
+
+    private final String uiProvider;
+
+    /**
+     * Name of the state data this state is responsible for.
+     */
+    public DatacagePairSelectState(final String uiProvider, final String dataId) {
+        this.uiProvider = uiProvider;
+        this.dataId = dataId;
+    }
+
+    /** Specify to display a datacage_twin_panel. */
+    @Override
+    protected final String getUIProvider() {
+        return this.uiProvider;
+    }
+
+    /**
+     * Overridden to do nothing.
+     */
+    @Override
+    public final Object computeAdvance(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) {
+        // Get data and do stuff, do not calculate
+        return "";
+    }
+
+    /**
+     * Create elements for document (prepopulated with data, if any).
+     *
+     * @param artifact
+     *            D4EArtifact to get data from.
+     * @param name
+     *            DataName, expceted to be dataId given in constructor.
+     */
+    @Override
+    protected final Element[] createItems(final ElementCreator cr, final Artifact artifact, final String name, final CallContext context) {
+        log.debug("createItems: " + name);
+
+        if (name.equals(this.dataId)) {
+            final Element item = ProtocolUtils.createArtNode(cr, "item", null, null);
+            final Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+            final Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+            final D4EArtifact flys = (D4EArtifact) artifact;
+
+            final String s = flys.getDataAsString(name);
+            value.setTextContent(s);
+            item.appendChild(label);
+            item.appendChild(value);
+            return new Element[] { item };
+        }
+
+        return new Element[] {};
+    }
+
+    /**
+     * Creats the data element used for the static part of DESCRIBE document.
+     */
+    @Override
+    protected final Element createStaticData(final D4EArtifact flys, final ElementCreator creator, final CallContext cc, final String name, final String value,
+            final String type) {
+        final Element dataElement = creator.create("data");
+        creator.addAttr(dataElement, "name", name, true);
+        creator.addAttr(dataElement, "type", type, true);
+
+        final Element itemElement = creator.create("item");
+        creator.addAttr(itemElement, "value", value, true);
+
+        final String[] labels = WaterlevelPairSelectState.getLabels(cc, value);
+        final Object[] obj = new Object[] { labels[0] };
+
+        // TODO own i18n
+        final String attrValue = Resources.getMsg(cc.getMeta(), "wsp.selected.string", "wsp.selected.string", obj);
+
+        creator.addAttr(itemElement, "label", attrValue, true);
+        dataElement.appendChild(itemElement);
+
+        return dataElement;
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/SInfoResultType.java	Fri Mar 16 18:08:38 2018 +0100
@@ -280,6 +280,78 @@
         protected NumberFormat createFormatter(final CallContext context) {
             return Formatter.getFlowDepth(context);
         }
+    },
+    flowdepthDevelopment(SInfoI18NStrings.UNIT_CM, SInfoI18NStrings.CSV_FLOWDEPTH_DEVELOPMENT_HEADER) {
+        @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.getTkh(context);
+        }
+    },
+    flowdepthDevelopmentPerYear(SInfoI18NStrings.UNIT_CM_A, SInfoI18NStrings.CSV_FLOWDEPTH_DEVELOPMENT_PER_YEAR_HEADER) {
+        @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.getFlowDepthDevelopmentPerYear(context);
+        }
+    },
+    waterlevelDifference(SInfoI18NStrings.UNIT_CM, SInfoI18NStrings.CSV_WATERLEVEL_DIFFERENCE_HEADER) {
+        @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.getTkh(context);
+        }
+    },
+    bedHeightDifference(SInfoI18NStrings.UNIT_CM, SInfoI18NStrings.CSV_MEAN_BED_HEIGHT_DIFFERENCE_HEADER) {
+        @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.getTkh(context);
+        }
+    },
+    flowdepthCurrent(SInfoI18NStrings.UNIT_M, SInfoI18NStrings.CSV_FLOWDEPTH_CURRENT_HEADER) {
+        @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);
+        }
+    },
+    flowdepthHistorical(SInfoI18NStrings.UNIT_M, SInfoI18NStrings.CSV_FLOWDEPTH_HISTORICAL_HEADER) {
+        @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);
+        }
     };
 
     /* Cache for formatters because Formatter will always create new formats (which is very expensive) */
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java	Fri Mar 16 18:08:38 2018 +0100
@@ -100,7 +100,7 @@
         final String soundingLabel = bedHeight.getInfo().getDescription();
         final String label = String.format("%s - %s", wspLabel, soundingLabel);
 
-        FlowDepthUtils.checkYearDifference(label, waterlevel, bedHeight.getInfo().getYear(), problems);
+        FlowDepthUtils.checkYearDifference(label, waterlevel.getYear(), bedHeight.getInfo().getYear(), problems);
         checkWaterlevelDiscretisation(wstKms, calcRange, problems);
         // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthPairSelectState.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthPairSelectState.java	Fri Mar 16 18:08:38 2018 +0100
@@ -9,7 +9,7 @@
  */
 package org.dive4elements.river.artifacts.sinfo.flowdepth;
 
-import org.dive4elements.river.artifacts.states.WaterlevelPairSelectState;
+import org.dive4elements.river.artifacts.sinfo.common.DatacagePairSelectState;
 
 /**
  * @author Gernot Belger
@@ -17,13 +17,11 @@
  */
 // FIXME: very ugly; but probably we will break the serialization of WaterlevelPairSelectState if we introduce an
 // abstraction
-public final class FlowDepthPairSelectState extends WaterlevelPairSelectState {
+public final class FlowDepthPairSelectState extends DatacagePairSelectState {
 
     private static final long serialVersionUID = 1L;
 
-    /** Specify to display a datacage_twin_panel. */
-    @Override
-    protected String getUIProvider() {
-        return "sinfo_flowdepth_twin_panel";
+    public FlowDepthPairSelectState() {
+        super("sinfo_flowdepth_twin_panel", "diffids");
     }
 }
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthUtils.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthUtils.java	Fri Mar 16 18:08:38 2018 +0100
@@ -10,7 +10,6 @@
 package org.dive4elements.river.artifacts.sinfo.flowdepth;
 
 import org.dive4elements.river.artifacts.model.Calculation;
-import org.dive4elements.river.artifacts.states.WaterlevelData;
 
 /**
  * @author Gernot Belger
@@ -30,9 +29,8 @@
      * 1918 ≤ X < 1958 ± 12
      * X < 1918 ± 25
      */
-    public static void checkYearDifference(final String label, final WaterlevelData waterlevel, final int soundingYear, final Calculation problems) {
+    public static void checkYearDifference(final String label, final int wstYear, final int soundingYear, final Calculation problems) {
 
-        final int wstYear = waterlevel.getYear();
         if (wstYear < 0)
             return;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentAccess.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,67 @@
+/* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import java.util.List;
+
+import org.apache.commons.lang.math.DoubleRange;
+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.flowdepth.WstSoundingIdPair;
+
+/**
+ * Access to the flow depth calculation type specific SInfo artifact data.
+ * REMARK: this class is NOT intended to be hold in the results (or anywhere else), in order to avoid a permanent
+ * reference to the artifact instance.
+ * Hence we do NOT cache any data.
+ *
+ * @author Gernot Belger
+ */
+final class FlowDepthDevelopmentAccess extends RangeAccess {
+
+    static final String FIELD_DIFFID_CURRENT = "diffid_current";
+    static final String FIELD_DIFFID_HIST = "diffid_historical";
+
+    public FlowDepthDevelopmentAccess(final SINFOArtifact artifact) {
+        super(artifact);
+
+        /* assert calculation mode */
+        final SinfoCalcMode calculationMode = artifact.getCalculationMode();
+        assert (calculationMode == SinfoCalcMode.sinfo_calc_flow_depth_minmax);
+    }
+
+    public DoubleRange getRange() {
+        final double from = getFrom();
+        final double to = getTo();
+        return new DoubleRange(from, to);
+    }
+
+    public WstSoundingIdPair getCurrentPair() {
+        return getPair(FIELD_DIFFID_CURRENT);
+    }
+
+    public WstSoundingIdPair getHistoricalPair() {
+        return getPair(FIELD_DIFFID_HIST);
+    }
+
+    private WstSoundingIdPair getPair(final String field) {
+        final String diffids = getString(field);
+
+        /* fetch the raw configured pairs */
+        final List<WstSoundingIdPair> diffPairs = WstSoundingIdPair.parsePairs(diffids);
+
+        if (diffPairs.size() != 1)
+            return null;
+
+        return diffPairs.get(0);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculation.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,197 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultRow;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
+import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthUtils;
+import org.dive4elements.river.artifacts.sinfo.flowdepth.WstSoundingIdPair;
+import org.dive4elements.river.artifacts.sinfo.tkhcalculation.WaterlevelValuesFinder;
+import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder;
+import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
+import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
+import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
+import org.dive4elements.river.artifacts.states.WaterlevelData;
+import org.dive4elements.river.artifacts.states.WaterlevelFetcher;
+import org.dive4elements.river.model.River;
+
+/**
+ * @author Gernot Belger
+ */
+final class FlowDepthDevelopmentCalculation {
+
+    private final CallContext context;
+
+    public FlowDepthDevelopmentCalculation(final CallContext context) {
+        this.context = context;
+    }
+
+    public CalculationResult calculate(final SINFOArtifact sinfo) {
+
+        final String user = CalculationUtils.findArtifactUser(this.context, sinfo);
+
+        /* access input data */
+        final FlowDepthDevelopmentAccess access = new FlowDepthDevelopmentAccess(sinfo);
+        final River river = access.getRiver();
+        final RiverInfo riverInfo = new RiverInfo(river);
+
+        final WstSoundingIdPair currentPair = access.getCurrentPair();
+        final WstSoundingIdPair histPair = access.getHistoricalPair();
+
+        final DoubleRange calcRange = access.getRange();
+
+        /* calculate results for each diff pair */
+        final Calculation problems = new Calculation();
+
+        final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange);
+
+        final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
+
+        final FlowDepthDevelopmentCalculationResults results = new FlowDepthDevelopmentCalculationResults(calcModeLabel, user, riverInfo, calcRange);
+
+        final FlowDepthDevelopmentCalculationResult result = calculateResult(calcRange, currentPair, histPair, problems, infoProvider);
+        if (result != null)
+            results.addResult(result);
+
+        return new CalculationResult(results, problems);
+    }
+
+    private FlowDepthDevelopmentCalculationResult calculateResult(final DoubleRange calcRange, final WstSoundingIdPair currentPair,
+            final WstSoundingIdPair histPair, final Calculation problems, final RiverInfoProvider infoProvider) {
+
+        /* access real input data from database */
+        final WaterlevelData currentWaterlevel = loadWaterlevel(currentPair, problems);
+        if (currentWaterlevel == null)
+            return null;
+
+        final WaterlevelData historicalWaterlevel = loadWaterlevel(histPair, problems);
+        if (historicalWaterlevel == null)
+            return null;
+
+        final BedHeightsFinder currentSounding = loadBedHeight(currentPair, calcRange, problems);
+        if (currentSounding == null)
+            return null;
+
+        final BedHeightsFinder historicalSounding = loadBedHeight(histPair, calcRange, problems);
+        if (historicalSounding == null)
+            return null;
+
+        // FIXME: check current/hist wst have same discharge...
+
+        final BedHeightInfo currentSoundingInfo = currentSounding.getInfo();
+        final BedHeightInfo historicalSoundingInfo = historicalSounding.getInfo();
+
+        // final String label = createLabel(waterlevel, minBedHeight, maxBedHeight);
+
+        // final WKms wstKms = waterlevel.getWkms();
+        final int currentWstYear = currentWaterlevel.getYear();
+        final int historicalWstYear = historicalWaterlevel.getYear();
+        final int currentSoundingYear = currentSoundingInfo.getYear();
+        final int historicalSoundingYear = historicalSoundingInfo.getYear();
+
+        // FIXME: distinguish error messages
+        FlowDepthUtils.checkYearDifference("", currentWstYear, currentSoundingYear, problems);
+        FlowDepthUtils.checkYearDifference("", historicalWstYear, historicalSoundingYear, problems);
+
+        // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
+        // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
+
+        /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */
+        final RiverInfoProvider currentRiverInfoProvider = infoProvider.forWaterlevel(currentWaterlevel);
+        final RiverInfoProvider histRiverInfoProvider = infoProvider.forWaterlevel(historicalWaterlevel);
+
+        final WstInfo currentWstInfo = new WstInfo(currentWaterlevel.getName(), currentWstYear, currentRiverInfoProvider.getReferenceGauge());
+        final WstInfo historicalWstInfo = new WstInfo(historicalWaterlevel.getName(), historicalWstYear, histRiverInfoProvider.getReferenceGauge());
+
+        final WKms currentWkms = currentWaterlevel.getWkms();
+        final WaterlevelValuesFinder currentWstProvider = WaterlevelValuesFinder.fromKms(currentWkms);
+        // final DischargeValuesFinder currentDischargeProvider = DischargeValuesFinder.fromKms(currentWkms);
+
+        final WKms historicalWkms = historicalWaterlevel.getWkms();
+        final WaterlevelValuesFinder historicalWstProvider = WaterlevelValuesFinder.fromKms(historicalWkms);
+        // final DischargeValuesFinder historicalDischargeProvider = DischargeValuesFinder.fromKms(historicalWkms);
+
+        final int currentMeanYear = (currentWstYear + currentSoundingYear) / 2;
+        final int historcialMeanYear = (historicalWstYear + historicalSoundingYear) / 2;
+
+        // final String waterlevelLabel = waterlevel.getName();
+        // final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight);
+
+        final double diffYear = currentMeanYear - historcialMeanYear;
+
+        /* real calculation loop */
+        final Collection<SInfoResultRow> rows = new ArrayList<>();
+
+        // FIXME: determine what is the spatial discretisation that we will use...
+        final double[] allKms = currentWkms.allKms().toNativeArray();
+        for (final double station : allKms) {
+            if (calcRange.containsDouble(station)) {
+
+                final double currentWst = currentWstProvider.getWaterlevel(station);
+                // final double currentDischarge = currentDischargeProvider.getDischarge(station);
+                final double currentBedHeight = currentSounding.getMeanBedHeight(station);
+
+                final double historicalWst = historicalWstProvider.getWaterlevel(station);
+                // final double historicalDischarge = historicalDischargeProvider.getDischarge(station);
+                final double historicalBedHeight = historicalSounding.getMeanBedHeight(station);
+
+                final double diffWst = (currentWst - historicalWst) * 100;
+                final double diffBedHeight = (currentBedHeight - historicalBedHeight) * 100;
+
+                final double flowDepthDevelopment = diffWst - diffBedHeight;
+
+                final double flowDepthDevelopmentPerYear = flowDepthDevelopment / diffYear;
+
+                final double currentFlowDepth = currentWst - currentBedHeight;
+                final double historicalFlowDepth = historicalWst - historicalBedHeight;
+
+                // REMARK: access the location once only during calculation
+                final String location = currentRiverInfoProvider.getLocation(station);
+
+                final SInfoResultRow row = SInfoResultRow.create().//
+                        putValue(SInfoResultType.station, station). //
+                        putValue(SInfoResultType.flowdepthDevelopment, flowDepthDevelopment). //
+                        putValue(SInfoResultType.flowdepthDevelopmentPerYear, flowDepthDevelopmentPerYear). //
+                        putValue(SInfoResultType.waterlevelDifference, diffWst). //
+                        putValue(SInfoResultType.bedHeightDifference, diffBedHeight). //
+                        putValue(SInfoResultType.flowdepthCurrent, currentFlowDepth). //
+                        putValue(SInfoResultType.flowdepthHistorical, historicalFlowDepth). //
+                        putValue(SInfoResultType.location, location);
+                rows.add(row);
+            }
+        }
+
+        return new FlowDepthDevelopmentCalculationResult("", currentWstInfo, historicalWstInfo, currentSoundingInfo, historicalSoundingInfo,
+                rows);
+    }
+
+    /* REMARK: fetch ALL wst kms, because we need to determine the original reference gauge */
+    private WaterlevelData loadWaterlevel(final WstSoundingIdPair pair, final Calculation problems) {
+        final String wstId = pair.getWstId();
+        return new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems);
+    }
+
+    private BedHeightsFinder loadBedHeight(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) {
+        final String soundingId = pair.getSoundingId();
+        return BedHeightsFinder.forId(this.context, soundingId, calcRange, problems);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculationResult.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,63 @@
+/* Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import java.util.Collection;
+
+import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoCalculationResult;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultRow;
+import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo;
+import org.dive4elements.river.artifacts.sinfo.util.WstInfo;
+
+/**
+ * Contains the results of a {@link FlowDepthCalculation}.
+ *
+ * @author Gernot Belger
+ */
+final class FlowDepthDevelopmentCalculationResult extends AbstractSInfoCalculationResult {
+
+    private static final long serialVersionUID = 1L;
+
+    private final BedHeightInfo currentSounding;
+
+    private final BedHeightInfo historicalSounding;
+
+    private final WstInfo historicalWst;
+
+    private final WstInfo currentWst;
+
+    public FlowDepthDevelopmentCalculationResult(final String label, final WstInfo currentWst, final WstInfo historicalWst, final BedHeightInfo currentSounding,
+            final BedHeightInfo historicalSounding, final Collection<SInfoResultRow> rows) {
+        // FIXME: bad abstraction if we give null here...
+        super(label, null, rows);
+
+        this.currentWst = currentWst;
+        this.historicalWst = historicalWst;
+
+        this.currentSounding = currentSounding;
+        this.historicalSounding = historicalSounding;
+    }
+
+    public WstInfo getCurrentWst() {
+        return this.currentWst;
+    }
+
+    public WstInfo getHistoricalWst() {
+        return this.historicalWst;
+    }
+
+    public BedHeightInfo getCurrentSounding() {
+        return this.currentSounding;
+    }
+
+    public BedHeightInfo getHistoricalSounding() {
+        return this.historicalSounding;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentCalculationResults.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,26 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import org.apache.commons.lang.math.DoubleRange;
+import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoCalculationResults;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
+
+/**
+ * @author Gernot Belger
+ */
+final class FlowDepthDevelopmentCalculationResults extends AbstractSInfoCalculationResults<FlowDepthDevelopmentCalculationResult> {
+
+    private static final long serialVersionUID = 1L;
+
+    public FlowDepthDevelopmentCalculationResults(final String calcModeLabel, final String user, final RiverInfo river, final DoubleRange calcRange) {
+        super(calcModeLabel, user, river, calcRange);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentExporter.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,161 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.sinfo.common.AbstractSInfoExporter;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultRow;
+import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
+import org.dive4elements.river.artifacts.sinfo.util.MetaAndTableJRDataSource;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+/**
+ * Generates different output formats (csv, pdf) of data that resulted from a flow depths computation.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ * @author Gernot Belger
+ */
+// REMARK: must be public because its registered in generators.xml
+public class FlowDepthDevelopmentExporter extends AbstractSInfoExporter<FlowDepthDevelopmentCalculationResult, FlowDepthDevelopmentCalculationResults> {
+
+    /** The log used in this exporter. */
+    private static Logger log = Logger.getLogger(FlowDepthDevelopmentExporter.class);
+
+    private static final String JASPER_FILE = "/jasper/sinfo.flowdepthminmax.jasper";
+
+    @Override
+    protected Logger getLog() {
+        return log;
+    }
+
+    @Override
+    protected void writeCSVResultMetadata(final CSVWriter writer, final FlowDepthDevelopmentCalculationResults results,
+            final FlowDepthDevelopmentCalculationResult result) {
+
+        // FIXME: distinguish header labels
+        writeCSVSoundingMetadata(writer, result.getCurrentSounding());
+        writeCSVWaterlevelMetadata(writer, result.getCurrentWst());
+
+        // FIXME: distinguish header labels
+        writeCSVSoundingMetadata(writer, result.getHistoricalSounding());
+        writeCSVWaterlevelMetadata(writer, result.getHistoricalWst());
+    }
+
+    @Override
+    protected void writeCSVGlobalMetadata(final CSVWriter writer, final FlowDepthDevelopmentCalculationResults results) {
+
+        super.writeCSVGlobalMetadataDefaults(writer, results);
+
+        writer.writeNext(new String[] { "" });
+    }
+
+    /**
+     * Write the header, with different headings depending on whether at a
+     * gauge or at a location.
+     *
+     * @param river
+     * @param useTkh
+     */
+    @Override
+    protected void writeCSVHeader(final CSVWriter writer, final FlowDepthDevelopmentCalculationResults results, final RiverInfo river) {
+        log.info("FlowDepthExporter.writeCSVHeader");
+
+        final Collection<String> header = new ArrayList<>(11);
+
+        header.add(msg(SInfoResultType.station.getCsvHeader()));
+        header.add(msgUnit(SInfoResultType.flowdepthDevelopment.getCsvHeader(), SInfoResultType.flowdepthDevelopment.getUnit()));
+        header.add(msgUnit(SInfoResultType.flowdepthDevelopmentPerYear.getCsvHeader(), SInfoResultType.flowdepthDevelopmentPerYear.getUnit()));
+
+        // FIXME: add data-labels in header
+        header.add(msgUnit(SInfoResultType.waterlevelDifference.getCsvHeader(), SInfoResultType.waterlevelDifference.getUnit()));
+        header.add(msgUnit(SInfoResultType.bedHeightDifference.getCsvHeader(), SInfoResultType.bedHeightDifference.getUnit()));
+
+        header.add(msgUnit(SInfoResultType.flowdepthCurrent.getCsvHeader(), SInfoResultType.flowdepthCurrent.getUnit()));
+        header.add(msgUnit(SInfoResultType.flowdepthHistorical.getCsvHeader(), SInfoResultType.flowdepthHistorical.getUnit()));
+
+        header.add(msg(SInfoResultType.location.getCsvHeader()));
+
+        writer.writeNext(header.toArray(new String[header.size()]));
+    }
+
+    @Override
+    protected String[] formatCSVRow(final FlowDepthDevelopmentCalculationResults results, final FlowDepthDevelopmentCalculationResult result,
+            final SInfoResultRow row) {
+        return formatRow(result, row);
+    }
+
+    /**
+     * Format a row of a flow depth result into an array of string, both used by csv and pdf
+     *
+     * @param result
+     *
+     * @param useTkh
+     */
+    private String[] formatRow(final FlowDepthDevelopmentCalculationResult result, final SInfoResultRow row) {
+
+        final Collection<String> lines = new ArrayList<>(10);
+
+        lines.add(row.exportValue(this.context, SInfoResultType.station));
+
+        // FIXME
+
+        // REMARK: null check as pdf will call this with null and in that case we show all columns (to avoid multiple jasper
+        // FIXME: does not work like this: we may have several pairs of min/max; so we need to look at all of them?
+        // templates)
+        // if (result == null || result.getMinSounding() != null)
+        lines.add(row.exportValue(this.context, SInfoResultType.flowdepthmin));
+        // if (result == null || result.getMaxSounding() != null)
+        lines.add(row.exportValue(this.context, SInfoResultType.flowdepthmax));
+
+        lines.add(row.exportValue(this.context, SInfoResultType.waterlevel));
+        lines.add(row.exportValue(this.context, SInfoResultType.discharge));
+        lines.add(row.exportValue(this.context, SInfoResultType.waterlevelLabel));
+        lines.add(row.exportValue(this.context, SInfoResultType.gaugeLabel));
+        lines.add(row.exportValue(this.context, SInfoResultType.meanBedHeight));
+        lines.add(row.exportValue(this.context, SInfoResultType.soundingLabel));
+        lines.add(row.exportValue(this.context, SInfoResultType.location));
+
+        return lines.toArray(new String[lines.size()]);
+    }
+
+    @Override
+    protected final String getJasperFile() {
+        return JASPER_FILE;
+    }
+
+    @Override
+    protected final void addJRMetaData(final MetaAndTableJRDataSource source, final FlowDepthDevelopmentCalculationResults results) {
+
+        /* general metadata */
+        super.addJRMetaDataDefaults(source, results);
+
+        /* column headings */
+        // FIXME
+        source.addMetaData("station_header", SInfoResultType.station.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("flowdepthmin_header", SInfoResultType.flowdepthmin.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("flowdepthmax_header", SInfoResultType.flowdepthmax.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("waterlevel_header", SInfoResultType.waterlevel.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("discharge_header", SInfoResultType.discharge.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("waterlevel_name_header", SInfoResultType.waterlevelLabel.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("gauge_header", SInfoResultType.gaugeLabel.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("bedheight_header", SInfoResultType.meanBedHeight.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("sounding_name_header", SInfoResultType.soundingLabel.getPdfHeader(this.context.getMeta()));
+        source.addMetaData("location_header", SInfoResultType.location.getPdfHeader(this.context.getMeta()));
+    }
+
+    @Override
+    protected String[] formatPDFRow(final FlowDepthDevelopmentCalculationResults results, final SInfoResultRow row) {
+        return formatRow(null, row);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/FlowDepthDevelopmentState.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,113 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import java.util.List;
+
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.ChartArtifact;
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.EmptyFacet;
+import org.dive4elements.river.artifacts.sinfo.SINFOArtifact;
+import org.dive4elements.river.artifacts.states.DefaultState;
+
+/**
+ * @author Gernot Belger
+ */
+public class FlowDepthDevelopmentState extends DefaultState {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * From this state can only be continued trivially.
+     */
+    @Override
+    protected String getUIProvider() {
+        return "continue";
+    }
+
+    @Override
+    public Object computeFeed(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) {
+        // FIXME: why is this necessary?
+        if (artifact instanceof ChartArtifact) {
+            facets.add(new EmptyFacet());
+            return null;
+        }
+
+        return compute((SINFOArtifact) artifact, context, hash, facets, old);
+    }
+
+    @Override
+    public Object computeAdvance(final D4EArtifact artifact, final String hash, final CallContext context, final List<Facet> facets, final Object old) {
+        if (artifact instanceof ChartArtifact) {
+            facets.add(new EmptyFacet());
+            return null;
+        }
+        return compute((SINFOArtifact) artifact, context, hash, facets, old);
+    }
+
+    /**
+     * Compute result or returned object from cache, create facets.
+     *
+     * @param old
+     *            Object that was cached.
+     */
+    private Object compute(final SINFOArtifact sinfo, final CallContext context, final String hash, final List<Facet> facets, final Object old) {
+
+        final CalculationResult res = doCompute(sinfo, context, old);
+
+        if (facets == null)
+            return res;
+
+        // final FlowDepthCalculationResults results = (FlowDepthCalculationResults) res.getData();
+        //
+        // /* add themes for chart, for each result */
+        // final List<FlowDepthCalculationResult> resultList = results.getResults();
+        // for (int index = 0; index < resultList.size(); index++) {
+        //
+        // final FlowDepthCalculationResult result = resultList.get(index);
+        //
+        // /* filtered (zoom dependent mean) flow depth */
+        // facets.add(FlowDepthProcessor.createFlowDepthFacet(context, hash, this.id, result, index));
+        //
+        // if (results.isUseTkh()) {
+        // /* filtered (zoom dependent mean) flow depth including tkh */
+        // facets.add(FlowDepthProcessor.createFlowDepthTkhFacet(context, hash, this.id, result, index));
+        //
+        // facets.add(TkhProcessor.createTkhFacet(context, hash, this.id, result, index));
+        // }
+        // }
+        //
+        // if (!resultList.isEmpty()) {
+        // final Facet csv = new DataFacet(FacetTypes.CSV, "CSV data", ComputeType.ADVANCE, hash, this.id);
+        // final Facet pdf = new DataFacet(FacetTypes.PDF, "PDF data", ComputeType.ADVANCE, hash, this.id);
+        //
+        // facets.add(csv);
+        // facets.add(pdf);
+        // }
+        //
+        // final Calculation report = res.getReport();
+        //
+        // if (report.hasProblems()) {
+        // facets.add(new ReportFacet(ComputeType.ADVANCE, hash, this.id));
+        // }
+
+        return res;
+    }
+
+    private CalculationResult doCompute(final SINFOArtifact sinfo, final CallContext context, final Object old) {
+        if (old instanceof CalculationResult)
+            return (CalculationResult) old;
+
+        return new FlowDepthDevelopmentCalculation(context).calculate(sinfo);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/WaterlevelSoundingCurrentPairSelectState.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,27 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import org.dive4elements.river.artifacts.sinfo.common.DatacagePairSelectState;
+
+/**
+ * @author Gernot Belger
+ *
+ */
+// FIXME: very ugly; but probably we will break the serialization of WaterlevelPairSelectState if we introduce an
+// abstraction
+public final class WaterlevelSoundingCurrentPairSelectState extends DatacagePairSelectState {
+
+    private static final long serialVersionUID = 1L;
+
+    public WaterlevelSoundingCurrentPairSelectState() {
+        super("sinfo_flowdepth_twin_panel", FlowDepthDevelopmentAccess.FIELD_DIFFID_CURRENT);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthdev/WaterlevelSoundingHistoricalPairSelectState.java	Fri Mar 16 18:08:38 2018 +0100
@@ -0,0 +1,27 @@
+/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
+ * Software engineering by
+ *  Björnsen Beratende Ingenieure GmbH
+ *  Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.sinfo.flowdepthdev;
+
+import org.dive4elements.river.artifacts.sinfo.common.DatacagePairSelectState;
+
+/**
+ * @author Gernot Belger
+ *
+ */
+// FIXME: very ugly; but probably we will break the serialization of WaterlevelPairSelectState if we introduce an
+// abstraction
+public final class WaterlevelSoundingHistoricalPairSelectState extends DatacagePairSelectState {
+
+    private static final long serialVersionUID = 1L;
+
+    public WaterlevelSoundingHistoricalPairSelectState() {
+        super("sinfo_flowdepth_twin_panel", FlowDepthDevelopmentAccess.FIELD_DIFFID_HIST);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java	Fri Mar 16 18:08:38 2018 +0100
@@ -105,7 +105,7 @@
         final WKms wstKms = waterlevel.getWkms();
 
         final int soundingYear = checkSoundingYear(minBedHeight, maxBedHeight, problems);
-        FlowDepthUtils.checkYearDifference(label, waterlevel, soundingYear, problems);
+        FlowDepthUtils.checkYearDifference(label, waterlevel.getYear(), soundingYear, problems);
         // FIXME
         // checkWaterlevelDiscretisation(wstKms, calcRange, problems);
         // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden
--- a/artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java	Fri Mar 16 18:08:38 2018 +0100
@@ -9,9 +9,9 @@
 package org.dive4elements.river.utils;
 
 import java.text.DateFormat;
+import java.text.DecimalFormat;
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
-import java.text.DecimalFormat;
 import java.util.Locale;
 
 import org.dive4elements.artifacts.CallContext;
@@ -111,9 +111,9 @@
      * @return A NumberFormat. Use #format(NUMBER) to get String representation
      *         of NUMBER.
      */
-    public static NumberFormat getFormatter(CallMeta m, int min, int max){
-        Locale       locale = Resources.getLocale(m);
-        NumberFormat nf     = NumberFormat.getInstance(locale);
+    public static NumberFormat getFormatter(final CallMeta m, final int min, final int max){
+        final Locale       locale = Resources.getLocale(m);
+        final NumberFormat nf     = NumberFormat.getInstance(locale);
 
         nf.setMaximumFractionDigits(max);
         nf.setMinimumFractionDigits(min);
@@ -121,7 +121,7 @@
         return nf;
     }
 
-    public static NumberFormat getFormatter(CallContext c, int min, int max){
+    public static NumberFormat getFormatter(final CallContext c, final int min, final int max){
         return getFormatter(c.getMeta(), min, max);
     }
 
@@ -133,18 +133,18 @@
      *
      * @return a number formatter.
      */
-    public static NumberFormat getRawFormatter(CallContext c) {
-        Locale locale = Resources.getLocale(c.getMeta());
+    public static NumberFormat getRawFormatter(final CallContext c) {
+        final Locale locale = Resources.getLocale(c.getMeta());
         return NumberFormat.getInstance(locale);
     }
 
     /**
      * Returns a formatter in engineering notation.
      */
-    public static NumberFormat getEngFormatter(CallContext c) {
-        NumberFormat nf = getRawFormatter(c);
+    public static NumberFormat getEngFormatter(final CallContext c) {
+        final NumberFormat nf = getRawFormatter(c);
         if (nf instanceof DecimalFormat) {
-            DecimalFormat df = (DecimalFormat)nf;
+            final DecimalFormat df = (DecimalFormat)nf;
             df.applyPattern("##0.#####E0");
         }
         return nf;
@@ -154,13 +154,13 @@
      * Returns a number formatter that uses an exponent after max digits.
      */
     public static NumberFormat getScientificFormater(
-        CallContext c,
-        int min,
-        int max
-    ) {
-        NumberFormat nf = getRawFormatter(c);
+            final CallContext c,
+            final int min,
+            final int max
+            ) {
+        final NumberFormat nf = getRawFormatter(c);
         if (nf instanceof DecimalFormat) {
-            DecimalFormat df = (DecimalFormat)nf;
+            final DecimalFormat df = (DecimalFormat)nf;
             df.applyPattern("0.0E0");
             df.setMaximumFractionDigits(max);
             df.setMinimumFractionDigits(min);
@@ -172,8 +172,8 @@
     /**
      * Returns a date formatter with SHORT style.
      */
-    public static DateFormat getShortDateFormat(CallContext cc) {
-        Locale locale = Resources.getLocale(cc.getMeta());
+    public static DateFormat getShortDateFormat(final CallContext cc) {
+        final Locale locale = Resources.getLocale(cc.getMeta());
         return DateFormat.getDateInstance(DateFormat.SHORT, locale);
     }
 
@@ -181,8 +181,8 @@
     /**
      * Returns a date formatter with MEDIUM style.
      */
-    public static DateFormat getMediumDateFormat(CallContext cc) {
-        Locale locale = Resources.getLocale(cc.getMeta());
+    public static DateFormat getMediumDateFormat(final CallContext cc) {
+        final Locale locale = Resources.getLocale(cc.getMeta());
         return DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
     }
 
@@ -192,7 +192,7 @@
      *
      * @return the number formatter for kilometer values.
      */
-    public static NumberFormat getWaterlevelKM(CallContext context) {
+    public static NumberFormat getWaterlevelKM(final CallContext context) {
         return getFormatter(
                 context,
                 WATERLEVEL_KM_MIN_DIGITS,
@@ -205,14 +205,14 @@
      *
      * @return the number formatter for csv data from diagra.
      */
-    public static NumberFormat getCSVFormatter(CallContext context) {
+    public static NumberFormat getCSVFormatter(final CallContext context) {
         return getFormatter(
                 context,
                 CSV_DIAGRAM_DATA_MIN_DIGITS,
                 CSV_DIAGRAM_DATA_MAX_DIGITS);
     }
 
-    public static NumberFormat getWaterlevelW(CallMeta meta) {
+    public static NumberFormat getWaterlevelW(final CallMeta meta) {
         return getFormatter(
                 meta,
                 WATERLEVEL_W_MIN_DIGITS,
@@ -225,7 +225,7 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getWaterlevelW(CallContext context) {
+    public static NumberFormat getWaterlevelW(final CallContext context) {
         return getFormatter(
                 context,
                 WATERLEVEL_W_MIN_DIGITS,
@@ -238,7 +238,7 @@
      *
      * @return the number formatter for Q values.
      */
-    public static NumberFormat getWaterlevelQ(CallContext context) {
+    public static NumberFormat getWaterlevelQ(final CallContext context) {
         return getFormatter(
                 context,
                 WATERLEVEL_Q_MIN_DIGITS,
@@ -246,7 +246,7 @@
     }
 
 
-    public static NumberFormat getWaterlevelQ(CallMeta meta) {
+    public static NumberFormat getWaterlevelQ(final CallMeta meta) {
         return getFormatter(
                 meta,
                 WATERLEVEL_Q_MIN_DIGITS,
@@ -259,7 +259,7 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getComputedDischargeW(CallContext context) {
+    public static NumberFormat getComputedDischargeW(final CallContext context) {
         return getFormatter(
                 context,
                 COMPUTED_DISCHARGE_W_MIN_DIGITS,
@@ -273,7 +273,7 @@
      *
      * @return the number formatter for Q values.
      */
-    public static NumberFormat getComputedDischargeQ(CallContext context) {
+    public static NumberFormat getComputedDischargeQ(final CallContext context) {
         return getFormatter(
                 context,
                 COMPUTED_DISCHARGE_Q_MIN_DIGITS,
@@ -287,7 +287,7 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getHistoricalDischargeW(CallContext context) {
+    public static NumberFormat getHistoricalDischargeW(final CallContext context) {
         return getFormatter(
                 context,
                 HISTORICAL_DISCHARGE_W_MIN_DIGITS,
@@ -301,7 +301,7 @@
      *
      * @return the number formatter for Q values.
      */
-    public static NumberFormat getHistoricalDischargeQ(CallContext context) {
+    public static NumberFormat getHistoricalDischargeQ(final CallContext context) {
         return getFormatter(
                 context,
                 HISTORICAL_DISCHARGE_Q_MIN_DIGITS,
@@ -314,7 +314,7 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getDurationW(CallContext context) {
+    public static NumberFormat getDurationW(final CallContext context) {
         return getFormatter(
                 context,
                 DURATION_W_MIN_DIGITS,
@@ -327,7 +327,7 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getDurationQ(CallContext context) {
+    public static NumberFormat getDurationQ(final CallContext context) {
         return getFormatter(
                 context,
                 DURATION_Q_MIN_DIGITS,
@@ -340,14 +340,14 @@
      *
      * @return the number formatter for W values.
      */
-    public static NumberFormat getDurationD(CallContext context) {
+    public static NumberFormat getDurationD(final CallContext context) {
         return getFormatter(
                 context,
                 DURATION_D_MIN_DIGITS,
                 DURATION_D_MAX_DIGITS);
     }
 
-    public static NumberFormat getCalculationKm(CallMeta meta) {
+    public static NumberFormat getCalculationKm(final CallMeta meta) {
         return getFormatter(
                 meta,
                 CALCULATION_REPORT_KM_MIN_DIGITS,
@@ -355,7 +355,7 @@
     }
 
 
-    public static NumberFormat getFlowVelocityKM(CallContext context) {
+    public static NumberFormat getFlowVelocityKM(final CallContext context) {
         return getFormatter(
                 context,
                 FLOW_VELOCITY_KM_MIN_DIGITS,
@@ -363,7 +363,7 @@
     }
 
 
-    public static NumberFormat getFlowVelocityValues(CallContext context) {
+    public static NumberFormat getFlowVelocityValues(final CallContext context) {
         return getFormatter(
                 context,
                 FLOW_VELOCITY_VALUES_MIN_DIGITS,
@@ -371,7 +371,7 @@
     }
 
 
-    public static NumberFormat getFlowVelocityQ(CallContext context) {
+    public static NumberFormat getFlowVelocityQ(final CallContext context) {
         return getFormatter(
                 context,
                 FLOW_VELOCITY_Q_MIN_DIGITS,
@@ -379,7 +379,7 @@
     }
 
 
-    public static NumberFormat getMiddleBedHeightKM(CallContext context) {
+    public static NumberFormat getMiddleBedHeightKM(final CallContext context) {
         return getFormatter(
                 context,
                 MIDDLE_BED_HEIGHT_KM_MIN_DIGITS,
@@ -387,7 +387,7 @@
     }
 
 
-    public static NumberFormat getMiddleBedHeightHeight(CallContext context) {
+    public static NumberFormat getMiddleBedHeightHeight(final CallContext context) {
         return getFormatter(
                 context,
                 MIDDLE_BED_HEIGHT_HEIGHT_MIN_DIGITS,
@@ -395,7 +395,7 @@
     }
 
 
-    public static NumberFormat getMiddleBedHeightUncert(CallContext context) {
+    public static NumberFormat getMiddleBedHeightUncert(final CallContext context) {
         return getFormatter(
                 context,
                 MIDDLE_BED_HEIGHT_UNCERT_MIN_DIGITS,
@@ -403,7 +403,7 @@
     }
 
 
-    public static NumberFormat getMiddleBedHeightDataGap(CallContext context) {
+    public static NumberFormat getMiddleBedHeightDataGap(final CallContext context) {
         return getFormatter(
                 context,
                 MIDDLE_BED_HEIGHT_DATAGAP_MIN_DIGITS,
@@ -412,8 +412,8 @@
 
 
     public static NumberFormat getMiddleBedHeightSounding(
-        CallContext context
-    ) {
+            final CallContext context
+            ) {
         return getFormatter(
                 context,
                 MIDDLE_BED_HEIGHT_SOUNDING_WIDTH_MIN_DIGITS,
@@ -421,63 +421,63 @@
     }
 
 
-    public static NumberFormat getFixDeltaWKM(CallContext context) {
+    public static NumberFormat getFixDeltaWKM(final CallContext context) {
         return getFormatter(
                 context,
                 FIX_DELTA_W_KM_MIN_DIGITS,
                 FIX_DELTA_W_KM_MAX_DIGITS);
     }
 
-    public static NumberFormat getFixDeltaWDeltaW(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
-                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWQ(CallContext context) {
-        return getFormatter(
-                context,
-                FIX_DELTA_W_DELTA_Q_MIN_DIGITS,
-                FIX_DELTA_W_DELTA_Q_MAX_DIGITS);
-    }
-
-    public static NumberFormat getFixDeltaWW(CallContext context) {
+    public static NumberFormat getFixDeltaWDeltaW(final CallContext context) {
         return getFormatter(
                 context,
                 FIX_DELTA_W_DELTA_W_MIN_DIGITS,
                 FIX_DELTA_W_DELTA_W_MAX_DIGITS);
     }
 
-    public static NumberFormat getVariance(CallContext context) {
+    public static NumberFormat getFixDeltaWQ(final CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_DELTA_Q_MIN_DIGITS,
+                FIX_DELTA_W_DELTA_Q_MAX_DIGITS);
+    }
+
+    public static NumberFormat getFixDeltaWW(final CallContext context) {
+        return getFormatter(
+                context,
+                FIX_DELTA_W_DELTA_W_MIN_DIGITS,
+                FIX_DELTA_W_DELTA_W_MAX_DIGITS);
+    }
+
+    public static NumberFormat getVariance(final CallContext context) {
         return getFormatter(
                 context,
                 VARIANCE_MIN_DIGITS,
                 VARIANCE_MAX_DIGITS);
     }
 
-    public static NumberFormat getSQRelationA(CallContext context) {
+    public static NumberFormat getSQRelationA(final CallContext context) {
         return getScientificFormater(
                 context,
                 SQ_RELATION_A_MIN_DIGITS,
                 SQ_RELATION_A_MAX_DIGITS);
     }
 
-    public static NumberFormat getSQRelationB(CallContext context) {
+    public static NumberFormat getSQRelationB(final CallContext context) {
         return getFormatter(
                 context,
                 SQ_RELATION_B_MIN_DIGITS,
                 SQ_RELATION_B_MAX_DIGITS);
     }
 
-    public static NumberFormat getSQRelationKM(CallContext context) {
+    public static NumberFormat getSQRelationKM(final CallContext context) {
         return getFormatter(
                 context,
                 SQ_RELATION_KM_MIN_DIGITS,
                 SQ_RELATION_KM_MAX_DIGITS);
     }
 
-    public static NumberFormat getMeterFormat(CallContext context) {
+    public static NumberFormat getMeterFormat(final CallContext context) {
         return getFormatter(
                 context,
                 0,
@@ -485,32 +485,35 @@
 
     }
 
-    public static DateFormat getDateFormatter(CallMeta m, String pattern) {
-        Locale locale = Resources.getLocale(m);
+    public static DateFormat getDateFormatter(final CallMeta m, final String pattern) {
+        final Locale locale = Resources.getLocale(m);
         return new SimpleDateFormat(pattern, locale);
     }
 
-    public static NumberFormat getMeanBedHeight(CallContext context) {
+    public static NumberFormat getMeanBedHeight(final CallContext context) {
         return Formatter.getFormatter(context, 2, 2);
     }
 
-    public static NumberFormat getTkh(CallContext context) {
+    public static NumberFormat getTkh(final CallContext context) {
         return Formatter.getFormatter(context, 1, 1);
     }
 
-    public static NumberFormat getFlowDepth(CallContext context) {
+    public static NumberFormat getFlowDepth(final CallContext context) {
         return Formatter.getFormatter(context, 2, 2);
     }
 
-    public static NumberFormat getW(CallContext context) {
+    public static NumberFormat getW(final CallContext context) {
         return Formatter.getFormatter(context, 2, 2);
     }
 
     /**
      * Another waterlevel formatter with fixed digits (always 2)
      */
-    public static NumberFormat getWaterlevelW2(CallMeta meta) {
+    public static NumberFormat getWaterlevelW2(final CallMeta meta) {
         return getFormatter( meta, 2, 2);
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
+
+    public static NumberFormat getFlowDepthDevelopmentPerYear(final CallContext context) {
+        return getFormatter(context.getMeta(), 2, 2);
+    }
+}
\ No newline at end of file
--- a/artifacts/src/main/resources/messages.properties	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/resources/messages.properties	Fri Mar 16 18:08:38 2018 +0100
@@ -821,6 +821,12 @@
 useTransportBodies.active = Activ
 useTransportBodies.inactive = Inactiv
 
+state.sinfo.flowdepthdevlopment_current_select = Aktuelles Differenzenpaar
+help.state.sinfo.flowdepthdevlopment_current_select = ${help.url}/OnlineHilfe/SINFO#help.state.sinfo.flowdepthdevlopment_current_select
+
+state.sinfo.flowdepthdevlopment_historical_select = Historisches Differenzenpaar
+help.state.sinfo.flowdepthdevlopment_historical_select = ${help.url}/OnlineHilfe/SINFO#help.state.sinfo.flowdepthdevlopment_historical_select
+
 sinfo.export.flow_depth.csv.meta.header.result = ## {0} - {1} - {2}
 sinfo.export.flow_depth.csv.meta.header.result.label = Calculation Output
 sinfo.export.flow_depth.csv.meta.version = # {0}: {1}
--- a/artifacts/src/main/resources/messages_de.properties	Thu Mar 15 17:22:28 2018 +0100
+++ b/artifacts/src/main/resources/messages_de.properties	Fri Mar 16 18:08:38 2018 +0100
@@ -821,6 +821,12 @@
 useTransportBodies.active = Aktiv
 useTransportBodies.inactive = Inaktiv
 
+state.sinfo.flowdepthdevlopment_current_select = Aktuelles Differenzenpaar
+help.state.sinfo.flowdepthdevlopment_current_select = ${help.url}/OnlineHilfe/SINFO#help.state.sinfo.flowdepthdevlopment_current_select
+
+state.sinfo.flowdepthdevlopment_historical_select = Historisches Differenzenpaar
+help.state.sinfo.flowdepthdevlopment_historical_select = ${help.url}/OnlineHilfe/SINFO#help.state.sinfo.flowdepthdevlopment_historical_select
+
 sinfo.export.flow_depth.csv.meta.header.result = ## {0} - {1} - {2}
 sinfo.export.flow_depth.csv.meta.header.result.label = Ergebnisausgabe
 sinfo.export.flow_depth.csv.meta.version = # {0}: {1}
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Thu Mar 15 17:22:28 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Fri Mar 16 18:08:38 2018 +0100
@@ -1457,5 +1457,7 @@
     String sinfo_d50s();
 
     String sinfo_flowdepthminmax_export();
+
+    String sinfo_flowdepthdevelopment_export();
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Thu Mar 15 17:22:28 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Fri Mar 16 18:08:38 2018 +0100
@@ -774,4 +774,5 @@
 sinfo_bedqualities = Sohlbeschaffenheit  
 sinfo_d50s = Korndurchmesser
 
-sinfo_flowdepthminmax_export = min/max Flie\u00dftiefen Export
\ No newline at end of file
+sinfo_flowdepthminmax_export = min/max Flie\u00dftiefen Export
+sinfo_flowdepthdevelopment_export = Flie\u00dftiefenentwicklung Export
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Thu Mar 15 17:22:28 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Fri Mar 16 18:08:38 2018 +0100
@@ -775,4 +775,5 @@
 sinfo_bedqualities = Sohlbeschaffenheit  
 sinfo_d50s = Korndurchmesser
 
-sinfo_flowdepthminmax_export = min/max Flie\u00dftiefen Export
\ No newline at end of file
+sinfo_flowdepthminmax_export = min/max Flie\u00dftiefen Export
+sinfo_flowdepthdevelopment_export = Flie\u00dftiefenentwicklung Export
\ No newline at end of file

http://dive4elements.wald.intevation.org