changeset 9286:bcbae86ce7b3

Improved flood duration curve calculation as specified by BfG
author mschaefer
date Mon, 23 Jul 2018 19:20:06 +0200
parents 9b16f58c62a7
children 6c88ad449c83
files artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java
diffstat 2 files changed, 454 insertions(+), 435 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java	Mon Jul 23 19:18:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java	Mon Jul 23 19:20:06 2018 +0200
@@ -8,31 +8,25 @@
 
 package org.dive4elements.river.artifacts.model;
 
-import java.io.Serializable;
+import static org.dive4elements.river.backend.utils.EpsilonComparator.CMP;
 
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.apache.commons.math.exception.MathIllegalArgumentException;
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.math.Function;
 import org.dive4elements.river.artifacts.math.Linear;
-import org.dive4elements.river.artifacts.math.Function;
 import org.dive4elements.river.utils.DoubleUtil;
 
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Collections;
-
-import org.apache.log4j.Logger;
-
-import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
-
-import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
-
-import org.apache.commons.math.ArgumentOutsideDomainException;
-
-import org.apache.commons.math.exception.MathIllegalArgumentException;
-
 import gnu.trove.TDoubleArrayList;
 
-import static org.dive4elements.river.backend.utils.EpsilonComparator.CMP;
-
 /**
  * W, Q and km data from database 'wsts' spiced with interpolation algorithms.
  */
@@ -60,23 +54,23 @@
         public Column() {
         }
 
-        public Column(String name) {
+        public Column(final String name) {
             this.name = name;
         }
 
         public String getName() {
-            return name;
+            return this.name;
         }
 
-        public void setName(String name) {
+        public void setName(final String name) {
             this.name = name;
         }
 
         public QRangeTree getQRangeTree() {
-            return qRangeTree;
+            return this.qRangeTree;
         }
 
-        public void setQRangeTree(QRangeTree qRangeTree) {
+        public void setQRangeTree(final QRangeTree qRangeTree) {
             this.qRangeTree = qRangeTree;
         }
     } // class Column
@@ -92,12 +86,12 @@
         public QPosition() {
         }
 
-        public QPosition(int index, double weight) {
+        public QPosition(final int index, final double weight) {
             this.index  = index;
             this.weight = weight;
         }
 
-        public QPosition set(int index, double weight) {
+        public QPosition set(final int index, final double weight) {
             this.index  = index;
             this.weight = weight;
             return this;
@@ -112,38 +106,38 @@
         public double []                splineWs;
 
         public SplineFunction(
-            PolynomialSplineFunction spline,
-            double []                splineQs,
-            double []                splineWs
-        ) {
+                final PolynomialSplineFunction spline,
+                final double []                splineQs,
+                final double []                splineWs
+                ) {
             this.spline   = spline;
             this.splineQs = splineQs;
             this.splineWs = splineWs;
         }
 
         public double [][] sample(
-            int         numSamples,
-            double      km,
-            Calculation errors
-        ) {
-            double minQ = getQMin();
-            double maxQ = getQMax();
+                final int         numSamples,
+                final double      km,
+                final Calculation errors
+                ) {
+            final double minQ = getQMin();
+            final double maxQ = getQMax();
 
-            double [] outWs = new double[numSamples];
-            double [] outQs = new double[numSamples];
+            final double [] outWs = new double[numSamples];
+            final double [] outQs = new double[numSamples];
 
             Arrays.fill(outWs, Double.NaN);
             Arrays.fill(outQs, Double.NaN);
 
-            double stepWidth = (maxQ - minQ)/numSamples;
+            final double stepWidth = (maxQ - minQ)/numSamples;
 
             try {
                 double q = minQ;
                 for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
-                    outWs[i] = spline.value(outQs[i] = q);
+                    outWs[i] = this.spline.value(outQs[i] = q);
                 }
             }
-            catch (ArgumentOutsideDomainException aode) {
+            catch (final ArgumentOutsideDomainException aode) {
                 if (errors != null) {
                     errors.addProblem(km, "spline.interpolation.failed");
                 }
@@ -154,26 +148,26 @@
         }
 
         public double getQMin() {
-            return Math.min(splineQs[0], splineQs[splineQs.length-1]);
+            return Math.min(this.splineQs[0], this.splineQs[this.splineQs.length-1]);
         }
 
         public double getQMax() {
-            return Math.max(splineQs[0], splineQs[splineQs.length-1]);
+            return Math.max(this.splineQs[0], this.splineQs[this.splineQs.length-1]);
         }
 
         /** Constructs a continues index between the columns to Qs. */
         public PolynomialSplineFunction createIndexQRelation() {
 
-            double [] indices = new double[splineQs.length];
+            final double [] indices = new double[this.splineQs.length];
             for (int i = 0; i < indices.length; ++i) {
                 indices[i] = i;
             }
 
             try {
-                SplineInterpolator interpolator = new SplineInterpolator();
-                return interpolator.interpolate(indices, splineQs);
+                final SplineInterpolator interpolator = new SplineInterpolator();
+                return interpolator.interpolate(indices, this.splineQs);
             }
-            catch (MathIllegalArgumentException miae) {
+            catch (final MathIllegalArgumentException miae) {
                 // Ignore me!
             }
             return null;
@@ -192,11 +186,11 @@
         public Row() {
         }
 
-        public Row(double km) {
+        public Row(final double km) {
             this.km = km;
         }
 
-        public Row(double km, double [] ws) {
+        public Row(final double km, final double [] ws) {
             this(km);
             this.ws = ws;
         }
@@ -205,26 +199,26 @@
          * Sort Qs and Ws for this Row over Q.
          */
         private double[][] getSortedWQ(
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            int W = ws.length;
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final int W = this.ws.length;
 
             if (W < 1) {
                 if (errors != null) {
-                    errors.addProblem(km, "no.ws.found");
+                    errors.addProblem(this.km, "no.ws.found");
                 }
                 return new double[][] {{Double.NaN}, {Double.NaN}};
             }
 
-            double [] sortedWs = ws;
-            double [] sortedQs = new double[W];
+            final double [] sortedWs = this.ws;
+            final double [] sortedQs = new double[W];
 
             for (int i=0; i < W; ++i) {
-                double q = table.getQIndex(i, km);
+                final double q = table.getQIndex(i, this.km);
                 if (Double.isNaN(q) && errors != null) {
                     errors.addProblem(
-                        km, "no.q.found.in.column", i+1);
+                            this.km, "no.q.found.in.column", i+1);
                 }
                 sortedQs[i] = q;
             }
@@ -239,12 +233,12 @@
          * this Row and another, sorted over Q.
          */
         private double[][] getSortedWQ(
-            Row           other,
-            double        km,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            int W = Math.min(ws.length, other.ws.length);
+                final Row           other,
+                final double        km,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final int W = Math.min(this.ws.length, other.ws.length);
 
             if (W < 1) {
                 if (errors != null) {
@@ -253,14 +247,14 @@
                 return new double[][] {{Double.NaN}, {Double.NaN}};
             }
 
-            double factor = Linear.factor(km, this.km, other.km);
+            final double factor = Linear.factor(km, this.km, other.km);
 
-            double [] sortedQs = new double[W];
-            double [] sortedWs = new double[W];
+            final double [] sortedQs = new double[W];
+            final double [] sortedWs = new double[W];
 
             for (int i = 0; i < W; ++i) {
-                double wws = Linear.weight(factor, ws[i], other.ws[i]);
-                double wqs = table.getQIndex(i, km);
+                final double wws = Linear.weight(factor, this.ws[i], other.ws[i]);
+                final double wqs = table.getQIndex(i, km);
 
                 if (Double.isNaN(wws) || Double.isNaN(wqs)) {
                     if (errors != null) {
@@ -280,34 +274,34 @@
         /**
          * Find Qs matching w in an array of Qs and Ws sorted over Q.
          */
-        private double[] findQsForW(double w, double[][] sortedWQ) {
-            int W = sortedWQ[0].length;
+        private double[] findQsForW(final double w, final double[][] sortedWQ) {
+            final int W = sortedWQ[0].length;
 
-            double[] sortedQs = sortedWQ[1];
-            double[] sortedWs = sortedWQ[0];
+            final double[] sortedQs = sortedWQ[1];
+            final double[] sortedWs = sortedWQ[0];
 
-            TDoubleArrayList qs = new TDoubleArrayList();
+            final TDoubleArrayList qs = new TDoubleArrayList();
 
             if (W > 0 && Math.abs(sortedWs[0]-w) < W_EPSILON) {
-                double q = sortedQs[0];
+                final double q = sortedQs[0];
                 if (!Double.isNaN(q)) {
                     qs.add(q);
                 }
             }
 
             for (int i = 1; i < W; ++i) {
-                double w2 = sortedWs[i];
+                final double w2 = sortedWs[i];
                 if (Double.isNaN(w2)) {
                     continue;
                 }
                 if (Math.abs(w2-w) < W_EPSILON) {
-                    double q = sortedQs[i];
+                    final double q = sortedQs[i];
                     if (!Double.isNaN(q)) {
                         qs.add(q);
                     }
                     continue;
                 }
-                double w1 = sortedWs[i-1];
+                final double w1 = sortedWs[i-1];
                 if (Double.isNaN(w1)) {
                     continue;
                 }
@@ -316,13 +310,13 @@
                     continue;
                 }
 
-                double q1 = sortedQs[i-1];
-                double q2 = sortedQs[i];
+                final double q1 = sortedQs[i-1];
+                final double q2 = sortedQs[i];
                 if (Double.isNaN(q1) || Double.isNaN(q2)) {
                     continue;
                 }
 
-                double q = Linear.linear(w, w1, w2, q1, q2);
+                final double q = Linear.linear(w, w1, w2, q1, q2);
                 qs.add(q);
             }
 
@@ -332,8 +326,9 @@
         /**
          * Compare according to place of measurement (km).
          */
-        public int compareTo(Row other) {
-            return CMP.compare(km, other.km);
+        @Override
+        public int compareTo(final Row other) {
+            return CMP.compare(this.km, other.km);
         }
 
         /**
@@ -344,25 +339,25 @@
          * @param table Table of which to use data for interpolation.
          */
         public void interpolateW(
-            Row           other,
-            double        km,
-            double []     iqs,
-            double []     ows,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            double kmWeight = Linear.factor(km, this.km, other.km);
+                final Row           other,
+                final double        km,
+                final double []     iqs,
+                final double []     ows,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final double kmWeight = Linear.factor(km, this.km, other.km);
 
-            QPosition qPosition = new QPosition();
+            final QPosition qPosition = new QPosition();
 
             for (int i = 0; i < iqs.length; ++i) {
                 if (table.getQPosition(km, iqs[i], qPosition) != null) {
-                    double wt =       getW(qPosition);
-                    double wo = other.getW(qPosition);
+                    final double wt =       getW(qPosition);
+                    final double wo = other.getW(qPosition);
                     if (Double.isNaN(wt) || Double.isNaN(wo)) {
                         if (errors != null) {
                             errors.addProblem(
-                                km, "cannot.find.w.for.q", iqs[i]);
+                                    km, "cannot.find.w.for.q", iqs[i]);
                         }
                         ows[i] = Double.NaN;
                     }
@@ -381,21 +376,21 @@
 
 
         public SplineFunction createSpline(
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            double[][] sortedWQ = getSortedWQ(table, errors);
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final double[][] sortedWQ = getSortedWQ(table, errors);
 
             try {
-                SplineInterpolator interpolator = new SplineInterpolator();
-                PolynomialSplineFunction spline =
-                    interpolator.interpolate(sortedWQ[1], sortedWQ[0]);
+                final SplineInterpolator interpolator = new SplineInterpolator();
+                final PolynomialSplineFunction spline =
+                        interpolator.interpolate(sortedWQ[1], sortedWQ[0]);
 
-                return new SplineFunction(spline, sortedWQ[1], ws);
+                return new SplineFunction(spline, sortedWQ[1], this.ws);
             }
-            catch (MathIllegalArgumentException miae) {
+            catch (final MathIllegalArgumentException miae) {
                 if (errors != null) {
-                    errors.addProblem(km, "spline.creation.failed");
+                    errors.addProblem(this.km, "spline.creation.failed");
                 }
                 log.error("spline creation failed", miae);
             }
@@ -403,22 +398,22 @@
         }
 
         public SplineFunction createSpline(
-            Row           other,
-            double        km,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            double[][] sortedWQ = getSortedWQ(other, km, table, errors);
+                final Row           other,
+                final double        km,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final double[][] sortedWQ = getSortedWQ(other, km, table, errors);
 
-            SplineInterpolator interpolator = new SplineInterpolator();
+            final SplineInterpolator interpolator = new SplineInterpolator();
 
             try {
-                PolynomialSplineFunction spline =
-                    interpolator.interpolate(sortedWQ[1], sortedWQ[0]);
+                final PolynomialSplineFunction spline =
+                        interpolator.interpolate(sortedWQ[1], sortedWQ[0]);
 
                 return new SplineFunction(spline, sortedWQ[1], sortedWQ[0]);
             }
-            catch (MathIllegalArgumentException miae) {
+            catch (final MathIllegalArgumentException miae) {
                 if (errors != null) {
                     errors.addProblem(km, "spline.creation.failed");
                 }
@@ -429,60 +424,60 @@
         }
 
         public double [][] interpolateWQ(
-            Row           other,
-            double        km,
-            int           steps,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            SplineFunction sf = createSpline(other, km, table, errors);
+                final Row           other,
+                final double        km,
+                final int           steps,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final SplineFunction sf = createSpline(other, km, table, errors);
 
             return sf != null
-                ? sf.sample(steps, km, errors)
-                : new double[2][0];
+                    ? sf.sample(steps, km, errors)
+                            : new double[2][0];
         }
 
 
         public double [][] interpolateWQ(
-            int           steps,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            SplineFunction sf = createSpline(table, errors);
+                final int           steps,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            final SplineFunction sf = createSpline(table, errors);
 
             return sf != null
-                ? sf.sample(steps, km, errors)
-                : new double[2][0];
+                    ? sf.sample(steps, this.km, errors)
+                            : new double[2][0];
         }
 
 
-        public double getW(QPosition qPosition) {
-            int    index  = qPosition.index;
-            double weight = qPosition.weight;
+        public double getW(final QPosition qPosition) {
+            final int    index  = qPosition.index;
+            final double weight = qPosition.weight;
 
             return weight == 1.0
-                ? ws[index]
-                : Linear.weight(weight, ws[index-1], ws[index]);
+                    ? this.ws[index]
+                            : Linear.weight(weight, this.ws[index-1], this.ws[index]);
         }
 
         public double getW(
-            Row       other,
-            double    km,
-            QPosition qPosition
-        ) {
-            double kmWeight = Linear.factor(km, this.km, other.km);
+                final Row       other,
+                final double    km,
+                final QPosition qPosition
+                ) {
+            final double kmWeight = Linear.factor(km, this.km, other.km);
 
-            int    index  = qPosition.index;
-            double weight = qPosition.weight;
+            final int    index  = qPosition.index;
+            final double weight = qPosition.weight;
 
             double tw, ow;
 
             if (weight == 1.0) {
-                tw = ws[index];
+                tw = this.ws[index];
                 ow = other.ws[index];
             }
             else {
-                tw = Linear.weight(weight, ws[index-1], ws[index]);
+                tw = Linear.weight(weight, this.ws[index-1], this.ws[index]);
                 ow = Linear.weight(weight, other.ws[index-1], other.ws[index]);
             }
 
@@ -490,30 +485,30 @@
         }
 
         public double [] findQsForW(
-            double        w,
-            WstValueTable table,
-            Calculation   errors
-        ) {
-            log.debug("Find Qs for given W at tabulated km " + km);
+                final double        w,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
+            log.debug("Find Qs for given W at tabulated km " + this.km);
             return findQsForW(w, getSortedWQ(table, errors));
         }
 
         public double [] findQsForW(
-            Row           other,
-            double        w,
-            double        km,
-            WstValueTable table,
-            Calculation   errors
-        ) {
+                final Row           other,
+                final double        w,
+                final double        km,
+                final WstValueTable table,
+                final Calculation   errors
+                ) {
             log.debug("Find Qs for given W at non-tabulated km " + km);
             return findQsForW(w, getSortedWQ(other, km, table, errors));
         }
 
-        public double [] getMinMaxW(double [] result) {
+        public double [] getMinMaxW(final double [] result) {
             double minW =  Double.MAX_VALUE;
             double maxW = -Double.MAX_VALUE;
-            for (int i = 0; i < ws.length; ++i) {
-                double w = ws[i];
+            for (int i = 0; i < this.ws.length; ++i) {
+                final double w = this.ws[i];
                 if (w < minW) minW = w;
                 if (w > maxW) maxW = w;
             }
@@ -522,10 +517,10 @@
             return result;
         }
 
-        public double [] getMinMaxW(Row other, double km, double [] result) {
-            double [] m1 = this .getMinMaxW(new double [2]);
-            double [] m2 = other.getMinMaxW(new double [2]);
-            double factor = Linear.factor(km, this.km, other.km);
+        public double [] getMinMaxW(final Row other, final double km, final double [] result) {
+            final double [] m1 = this .getMinMaxW(new double [2]);
+            final double [] m2 = other.getMinMaxW(new double [2]);
+            final double factor = Linear.factor(km, this.km, other.km);
             result[0] = Linear.weight(factor, m1[0], m2[0]);
             result[1] = Linear.weight(factor, m1[1], m2[1]);
             return result;
@@ -539,10 +534,10 @@
     protected Column [] columns;
 
     public WstValueTable() {
-        rows = new ArrayList<Row>();
+        this.rows = new ArrayList<>();
     }
 
-    public WstValueTable(Column [] columns) {
+    public WstValueTable(final Column [] columns) {
         this();
         this.columns = columns;
     }
@@ -551,13 +546,13 @@
      * @param columns The WST-columns.
      * @param rows A list of Rows that must be sorted by km.
      */
-    public WstValueTable(Column [] columns, List<Row> rows) {
+    public WstValueTable(final Column [] columns, final List<Row> rows) {
         this.columns = columns;
         this.rows    = rows;
     }
 
     public Column [] getColumns() {
-        return columns;
+        return this.columns;
     }
 
     /**
@@ -565,7 +560,7 @@
      * @param qs Given Q values.
      * @param ws output parameter.
      */
-    public double [] interpolateW(double km, double [] qs, double [] ws) {
+    public double [] interpolateW(final double km, final double [] qs, final double [] ws) {
         return interpolateW(km, qs, ws, null);
     }
 
@@ -575,17 +570,17 @@
      * @return output parameter ws.
      */
     public double [] interpolateW(
-        double      km,
-        double []   qs,
-        double []   ws,
-        Calculation errors
-    ) {
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+            final double      km,
+            final double []   qs,
+            final double []   ws,
+            final Calculation errors
+            ) {
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
-        QPosition qPosition = new QPosition();
+        final QPosition qPosition = new QPosition();
 
         if (rowIndex >= 0) { // direct row match
-            Row row = rows.get(rowIndex);
+            final Row row = this.rows.get(rowIndex);
             for (int i = 0; i < qs.length; ++i) {
                 if (getQPosition(km, qs[i], qPosition) == null) {
                     if (errors != null) {
@@ -595,9 +590,9 @@
                 }
                 else {
                     if (Double.isNaN(ws[i] = row.getW(qPosition))
-                    && errors != null) {
+                            && errors != null) {
                         errors.addProblem(
-                            km, "cannot.find.w.for.q", qs[i]);
+                                km, "cannot.find.w.for.q", qs[i]);
                     }
                 }
             }
@@ -605,7 +600,7 @@
         else { // needs bilinear interpolation
             rowIndex = -rowIndex -1;
 
-            if (rowIndex < 1 || rowIndex >= rows.size()) {
+            if (rowIndex < 1 || rowIndex >= this.rows.size()) {
                 // do not extrapolate
                 Arrays.fill(ws, Double.NaN);
                 if (errors != null) {
@@ -613,8 +608,8 @@
                 }
             }
             else {
-                Row r1 = rows.get(rowIndex-1);
-                Row r2 = rows.get(rowIndex);
+                final Row r1 = this.rows.get(rowIndex-1);
+                final Row r2 = this.rows.get(rowIndex);
                 r1.interpolateW(r2, km, qs, ws, this, errors);
             }
         }
@@ -622,16 +617,46 @@
         return ws;
     }
 
-    public double [] getMinMaxQ(double km) {
+    /**
+     * Interpolates a W for a km using a Q column position
+     */
+    public double interpolateW(final double km, final QPosition qPosition, final Calculation errors) {
+
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
+
+        if (rowIndex >= 0) { // direct row match
+            final Row row = this.rows.get(rowIndex);
+            return row.getW(qPosition);
+        }
+        else { // needs bilinear interpolation
+            rowIndex = -rowIndex - 1;
+            if ((rowIndex <= 0) || (rowIndex >= this.rows.size()))
+                return Double.NaN;
+            else {
+                final Row r1 = this.rows.get(rowIndex - 1);
+                final Row r2 = this.rows.get(rowIndex);
+                final double w1 = r1.getW(qPosition);
+                final double w2 = r2.getW(qPosition);
+                if (Double.isNaN(w1) || Double.isNaN(w2))
+                    return Double.NaN;
+                else {
+                    final double kmWeight = Linear.factor(km, r1.km, r2.km);
+                    return Linear.weight(kmWeight, w1, w2);
+                }
+            }
+        }
+    }
+
+    public double [] getMinMaxQ(final double km) {
         return getMinMaxQ(km, new double [2]);
     }
 
-    public double [] getMinMaxQ(double km, double [] result) {
+    public double [] getMinMaxQ(final double km, final double [] result) {
         double minQ =  Double.MAX_VALUE;
         double maxQ = -Double.MAX_VALUE;
 
-        for (int i = 0; i < columns.length; ++i) {
-            double q = columns[i].getQRangeTree().findQ(km);
+        for (int i = 0; i < this.columns.length; ++i) {
+            final double q = this.columns[i].getQRangeTree().findQ(km);
             if (!Double.isNaN(q)) {
                 if (q < minQ) minQ = q;
                 if (q > maxQ) maxQ = q;
@@ -648,13 +673,13 @@
     }
 
     public double [] getMinMaxQ(double from, double to, double step) {
-        double [] result = new double[2];
+        final double [] result = new double[2];
 
         double minQ =  Double.MAX_VALUE;
         double maxQ = -Double.MAX_VALUE;
 
         if (from > to) {
-            double tmp = from;
+            final double tmp = from;
             from = to;
             to = tmp;
         }
@@ -677,42 +702,42 @@
         }
 
         return minQ < Double.MAX_VALUE
-            ? new double [] { minQ, maxQ }
-            : null;
+                ? new double [] { minQ, maxQ }
+        : null;
     }
 
-    public double [] getMinMaxW(double km) {
+    public double [] getMinMaxW(final double km) {
         return getMinMaxW(km, new double [2]);
 
     }
-    public double [] getMinMaxW(double km, double [] result) {
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+    public double [] getMinMaxW(final double km, final double [] result) {
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) {
-            return rows.get(rowIndex).getMinMaxW(result);
+            return this.rows.get(rowIndex).getMinMaxW(result);
         }
 
         rowIndex = -rowIndex -1;
 
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
+        if (rowIndex < 1 || rowIndex >= this.rows.size()) {
             // do not extrapolate
             return null;
         }
 
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
+        final Row r1 = this.rows.get(rowIndex-1);
+        final Row r2 = this.rows.get(rowIndex);
 
         return r1.getMinMaxW(r2, km, result);
     }
 
     public double [] getMinMaxW(double from, double to, double step) {
-        double [] result = new double[2];
+        final double [] result = new double[2];
 
         double minW =  Double.MAX_VALUE;
         double maxW = -Double.MAX_VALUE;
 
         if (from > to) {
-            double tmp = from;
+            final double tmp = from;
             from = to;
             to = tmp;
         }
@@ -735,14 +760,14 @@
         }
 
         return minW < Double.MAX_VALUE
-            ? new double [] { minW, maxW }
-            : null;
+                ? new double [] { minW, maxW }
+        : null;
     }
 
     /**
      * Interpolate W and Q values at a given km.
      */
-    public double [][] interpolateWQ(double km) {
+    public double [][] interpolateWQ(final double km) {
         return interpolateWQ(km, null);
     }
 
@@ -753,7 +778,7 @@
      *
      * @return double double array, first index Ws, second Qs.
      */
-    public double [][] interpolateWQ(double km, Calculation errors) {
+    public double [][] interpolateWQ(final double km, final Calculation errors) {
         return interpolateWQ(km, DEFAULT_Q_STEPS, errors);
     }
 
@@ -762,20 +787,20 @@
      * Interpolate W and Q values at a given km.
      */
     public double [][] interpolateWQ(
-        double km,
-        int steps,
-        Calculation errors
-    ) {
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+            final double km,
+            final int steps,
+            final Calculation errors
+            ) {
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) { // direct row match
-            Row row = rows.get(rowIndex);
+            final Row row = this.rows.get(rowIndex);
             return row.interpolateWQ(steps, this, errors);
         }
 
         rowIndex = -rowIndex -1;
 
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
+        if (rowIndex < 1 || rowIndex >= this.rows.size()) {
             // do not extrapolate
             if (errors != null) {
                 errors.addProblem(km, "km.not.found");
@@ -783,19 +808,19 @@
             return new double[2][0];
         }
 
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
+        final Row r1 = this.rows.get(rowIndex-1);
+        final Row r2 = this.rows.get(rowIndex);
 
         return r1.interpolateWQ(r2, km, steps, this, errors);
     }
 
     public boolean interpolate(
-        double    km,
-        double [] out,
-        QPosition qPosition,
-        Function  qFunction
-    ) {
-        int R1 = rows.size()-1;
+            final double    km,
+            final double [] out,
+            final QPosition qPosition,
+            final Function  qFunction
+            ) {
+        final int R1 = this.rows.size()-1;
 
         out[1] = qFunction.value(getQ(qPosition, km));
 
@@ -803,16 +828,16 @@
             return false;
         }
 
-        QPosition nPosition = new QPosition();
+        final QPosition nPosition = new QPosition();
         if (getQPosition(km, out[1], nPosition) == null) {
             return false;
         }
 
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) {
             // direct row match
-            out[0] = rows.get(rowIndex).getW(nPosition);
+            out[0] = this.rows.get(rowIndex).getW(nPosition);
             return !Double.isNaN(out[0]);
         }
 
@@ -823,8 +848,8 @@
             return false;
         }
 
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
+        final Row r1 = this.rows.get(rowIndex-1);
+        final Row r2 = this.rows.get(rowIndex);
         out[0] = r1.getW(r2, km, nPosition);
 
         return !Double.isNaN(out[0]);
@@ -842,15 +867,15 @@
      * @param errors      calculation object to store errors.
      */
     public QPosition interpolate(
-        double      q,
-        double      referenceKm,
-        double []   kms,
-        double []   ws,
-        double []   qs,
-        Calculation errors
-    ) {
+            final double      q,
+            final double      referenceKm,
+            final double []   kms,
+            final double []   ws,
+            final double []   qs,
+            final Calculation errors
+            ) {
         return interpolate(
-            q, referenceKm, kms, ws, qs, 0, kms.length, errors);
+                q, referenceKm, kms, ws, qs, 0, kms.length, errors);
     }
 
     /**
@@ -860,16 +885,16 @@
      * @param qs [out] looked up qs for kms.
      */
     public QPosition interpolate(
-        double      q,
-        double      referenceKm,
-        double []   kms,
-        double []   ws,
-        double []   qs,
-        int         startIndex,
-        int         length,
-        Calculation errors
-    ) {
-        QPosition qPosition = getQPosition(referenceKm, q);
+            final double      q,
+            final double      referenceKm,
+            final double []   kms,
+            final double []   ws,
+            final double []   qs,
+            final int         startIndex,
+            final int         length,
+            final Calculation errors
+            ) {
+        final QPosition qPosition = getQPosition(referenceKm, q);
 
         if (qPosition == null) {
             // we cannot locate q at km
@@ -881,9 +906,9 @@
             return null;
         }
 
-        Row kmKey = new Row();
+        final Row kmKey = new Row();
 
-        int R1 = rows.size()-1;
+        final int R1 = this.rows.size()-1;
 
         for (int i = startIndex, end = startIndex+length; i < end; ++i) {
 
@@ -896,12 +921,12 @@
             }
 
             kmKey.km = kms[i];
-            int rowIndex = Collections.binarySearch(rows, kmKey);
+            int rowIndex = Collections.binarySearch(this.rows, kmKey);
 
             if (rowIndex >= 0) {
                 // direct row match
-                if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition))
-                && errors != null) {
+                if (Double.isNaN(ws[i] = this.rows.get(rowIndex).getW(qPosition))
+                        && errors != null) {
                     errors.addProblem(kms[i], "cannot.find.w.for.q", q);
                 }
                 continue;
@@ -917,11 +942,11 @@
                 ws[i] = Double.NaN;
                 continue;
             }
-            Row r1 = rows.get(rowIndex-1);
-            Row r2 = rows.get(rowIndex);
+            final Row r1 = this.rows.get(rowIndex-1);
+            final Row r2 = this.rows.get(rowIndex);
 
             if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition))
-            && errors != null) {
+                    && errors != null) {
                 errors.addProblem(kms[i], "cannot.find.w.for.q", q);
             }
         }
@@ -939,14 +964,14 @@
      *
      * @return Linearly interpolated w, NaN if one of the given rows was null.
      */
-    public static double linearW(double km, Row row1, Row row2, int col) {
+    public static double linearW(final double km, final Row row1, final Row row2, final int col) {
         if (row1 == null || row2 == null) {
             return Double.NaN;
         }
 
         return Linear.linear(km,
-            row1.km, row2.km,
-            row1.ws[col], row2.ws[col]);
+                row1.km, row2.km,
+                row1.ws[col], row2.ws[col]);
     }
 
     /**
@@ -955,27 +980,27 @@
      * @param km position (km) at which to interpolate/lookup.
      * @return [[q0, q1, .. qx] , [w0, w1, .. wx]] (can contain NaNs)
      */
-    public double [][] interpolateWQColumnwise(double km) {
+    public double [][] interpolateWQColumnwise(final double km) {
         log.debug("WstValueTable.interpolateWQColumnwise");
-        double [] qs = new double[columns.length];
-        double [] ws = new double[columns.length];
+        final double [] qs = new double[this.columns.length];
+        final double [] ws = new double[this.columns.length];
 
         // Find out row from where we will start searching.
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex < 0) {
             rowIndex = -rowIndex -1;
         }
 
         // TODO Beyond definition, we could stop more clever.
-        if (rowIndex >= rows.size()) {
-            rowIndex = rows.size() -1;
+        if (rowIndex >= this.rows.size()) {
+            rowIndex = this.rows.size() -1;
         }
 
-        Row startRow = rows.get(rowIndex);
+        final Row startRow = this.rows.get(rowIndex);
 
-        for (int col = 0; col < columns.length; col++) {
-            qs[col] = columns[col].getQRangeTree().findQ(km);
+        for (int col = 0; col < this.columns.length; col++) {
+            qs[col] = this.columns[col].getQRangeTree().findQ(km);
             if (startRow.km == km && !Double.isNaN(startRow.ws[col])) {
                 // Great. W is defined at km.
                 ws[col] = startRow.ws[col];
@@ -986,18 +1011,18 @@
             Row rowBefore = null;
             Row rowAfter  = null;
             for (int before = rowIndex -1; before >= 0; before--) {
-                if (!Double.isNaN(rows.get(before).ws[col])) {
-                    rowBefore = rows.get(before);
+                if (!Double.isNaN(this.rows.get(before).ws[col])) {
+                    rowBefore = this.rows.get(before);
                     break;
                 }
             }
             if (rowBefore != null) {
-                for (int after = rowIndex, R = rows.size();
-                     after < R;
-                     after++
-                ) {
-                    if (!Double.isNaN(rows.get(after).ws[col])) {
-                        rowAfter = rows.get(after);
+                for (int after = rowIndex, R = this.rows.size();
+                        after < R;
+                        after++
+                        ) {
+                    if (!Double.isNaN(this.rows.get(after).ws[col])) {
+                        rowAfter = this.rows.get(after);
                         break;
                     }
                 }
@@ -1009,34 +1034,34 @@
         return new double [][] {qs, ws};
     }
 
-    public double [] findQsForW(double km, double w, Calculation errors) {
+    public double [] findQsForW(final double km, final double w, final Calculation errors) {
 
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) {
-            return rows.get(rowIndex).findQsForW(w, this, errors);
+            return this.rows.get(rowIndex).findQsForW(w, this, errors);
         }
 
         rowIndex = -rowIndex - 1;
 
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
+        if (rowIndex < 1 || rowIndex >= this.rows.size()) {
             // Do not extrapolate.
             return new double[0];
         }
 
         // Needs bilinear interpolation.
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
+        final Row r1 = this.rows.get(rowIndex-1);
+        final Row r2 = this.rows.get(rowIndex);
 
         return r1.findQsForW(r2, w, km, this, errors);
     }
 
-    protected SplineFunction createSpline(double km, Calculation errors) {
+    protected SplineFunction createSpline(final double km, final Calculation errors) {
 
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) {
-            SplineFunction sf = rows.get(rowIndex).createSpline(this, errors);
+            final SplineFunction sf = this.rows.get(rowIndex).createSpline(this, errors);
             if (sf == null && errors != null) {
                 errors.addProblem(km, "cannot.create.wq.relation");
             }
@@ -1045,7 +1070,7 @@
 
         rowIndex = -rowIndex - 1;
 
-        if (rowIndex < 1 || rowIndex >= rows.size()) {
+        if (rowIndex < 1 || rowIndex >= this.rows.size()) {
             // Do not extrapolate.
             if (errors != null) {
                 errors.addProblem(km, "km.not.found");
@@ -1054,10 +1079,10 @@
         }
 
         // Needs bilinear interpolation.
-        Row r1 = rows.get(rowIndex-1);
-        Row r2 = rows.get(rowIndex);
+        final Row r1 = this.rows.get(rowIndex-1);
+        final Row r2 = this.rows.get(rowIndex);
 
-        SplineFunction sf = r1.createSpline(r2, km, this, errors);
+        final SplineFunction sf = r1.createSpline(r2, km, this, errors);
         if (sf == null && errors != null) {
             errors.addProblem(km, "cannot.create.wq.relation");
         }
@@ -1067,10 +1092,10 @@
 
     /** 'Bezugslinienverfahren' */
     public double [][] relateWs(
-        double      km1,
-        double      km2,
-        Calculation errors
-    ) {
+            final double      km1,
+            final double      km2,
+            final Calculation errors
+            ) {
         return relateWs(km1, km2, RELATE_WS_SAMPLES, errors);
     }
 
@@ -1079,14 +1104,14 @@
         boolean     hasErrors;
         Calculation errors;
 
-        ErrorHandler(Calculation errors) {
+        ErrorHandler(final Calculation errors) {
             this.errors = errors;
         }
 
-        void error(double km, String key, Object ... args) {
-            if (errors != null && !hasErrors) {
-                hasErrors = true;
-                errors.addProblem(km, key, args);
+        void error(final double km, final String key, final Object ... args) {
+            if (this.errors != null && !this.hasErrors) {
+                this.hasErrors = true;
+                this.errors.addProblem(km, key, args);
             }
         }
     } // class ErrorHandler
@@ -1097,22 +1122,22 @@
      *       the start km is always the same.
      */
     public double [][] relateWs(
-        double      km1,
-        double      km2,
-        int         numSamples,
-        Calculation errors
-    ) {
-        SplineFunction sf1 = createSpline(km1, errors);
+            final double      km1,
+            final double      km2,
+            final int         numSamples,
+            final Calculation errors
+            ) {
+        final SplineFunction sf1 = createSpline(km1, errors);
         if (sf1 == null) {
             return new double[2][0];
         }
 
-        SplineFunction sf2 = createSpline(km2, errors);
+        final SplineFunction sf2 = createSpline(km2, errors);
         if (sf2 == null) {
             return new double[2][0];
         }
 
-        PolynomialSplineFunction iQ1 = sf1.createIndexQRelation();
+        final PolynomialSplineFunction iQ1 = sf1.createIndexQRelation();
         if (iQ1 == null) {
             if (errors != null) {
                 errors.addProblem(km1, "cannot.create.index.q.relation");
@@ -1120,7 +1145,7 @@
             return new double[2][0];
         }
 
-        PolynomialSplineFunction iQ2 = sf2.createIndexQRelation();
+        final PolynomialSplineFunction iQ2 = sf2.createIndexQRelation();
         if (iQ2 == null) {
             if (errors != null) {
                 errors.addProblem(km2, "cannot.create.index.q.relation");
@@ -1128,18 +1153,18 @@
             return new double[2][0];
         }
 
-        int N = Math.min(sf1.splineQs.length, sf2.splineQs.length);
-        double stepWidth = N/(double)numSamples;
-
-        PolynomialSplineFunction qW1 = sf1.spline;
-        PolynomialSplineFunction qW2 = sf2.spline;
+        final int N = Math.min(sf1.splineQs.length, sf2.splineQs.length);
+        final double stepWidth = N/(double)numSamples;
 
-        TDoubleArrayList ws1 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList ws2 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList qs1 = new TDoubleArrayList(numSamples);
-        TDoubleArrayList qs2 = new TDoubleArrayList(numSamples);
+        final PolynomialSplineFunction qW1 = sf1.spline;
+        final PolynomialSplineFunction qW2 = sf2.spline;
 
-        ErrorHandler err = new ErrorHandler(errors);
+        final TDoubleArrayList ws1 = new TDoubleArrayList(numSamples);
+        final TDoubleArrayList ws2 = new TDoubleArrayList(numSamples);
+        final TDoubleArrayList qs1 = new TDoubleArrayList(numSamples);
+        final TDoubleArrayList qs2 = new TDoubleArrayList(numSamples);
+
+        final ErrorHandler err = new ErrorHandler(errors);
 
         int i = 0;
         for (double p = 0d; p <= N-1; p += stepWidth, ++i) {
@@ -1148,7 +1173,7 @@
             try {
                 q1 = iQ1.value(p);
             }
-            catch (ArgumentOutsideDomainException aode) {
+            catch (final ArgumentOutsideDomainException aode) {
                 err.error(km1, "w.w.qkm1.failed", p);
                 continue;
             }
@@ -1157,7 +1182,7 @@
             try {
                 w1 = qW1.value(q1);
             }
-            catch (ArgumentOutsideDomainException aode) {
+            catch (final ArgumentOutsideDomainException aode) {
                 err.error(km1, "w.w.wkm1.failed", q1, p);
                 continue;
             }
@@ -1166,7 +1191,7 @@
             try {
                 q2 = iQ2.value(p);
             }
-            catch (ArgumentOutsideDomainException aode) {
+            catch (final ArgumentOutsideDomainException aode) {
                 err.error(km2, "w.w.qkm2.failed", p);
                 continue;
             }
@@ -1175,7 +1200,7 @@
             try {
                 w2 = qW2.value(q2);
             }
-            catch (ArgumentOutsideDomainException aode) {
+            catch (final ArgumentOutsideDomainException aode) {
                 err.error(km2, "w.w.wkm2.failed", q2, p);
                 continue;
             }
@@ -1193,24 +1218,24 @@
             qs2.toNativeArray() };
     }
 
-    public QPosition getQPosition(double km, double q) {
+    public QPosition getQPosition(final double km, final double q) {
         return getQPosition(km, q, new QPosition());
     }
 
-    public QPosition getQPosition(double km, double q, QPosition qPosition) {
+    public QPosition getQPosition(final double km, final double q, final QPosition qPosition) {
 
-        if (columns.length == 0) {
+        if (this.columns.length == 0) {
             return null;
         }
 
-        double qLast = columns[0].getQRangeTree().findQ(km);
+        double qLast = this.columns[0].getQRangeTree().findQ(km);
 
         if (Math.abs(qLast - q) < 0.00001) {
             return qPosition.set(0, 1d);
         }
 
-        for (int i = 1; i < columns.length; ++i) {
-            double qCurrent = columns[i].getQRangeTree().findQ(km);
+        for (int i = 1; i < this.columns.length; ++i) {
+            final double qCurrent = this.columns[i].getQRangeTree().findQ(km);
             if (Math.abs(qCurrent - q) < 0.00001) {
                 return qPosition.set(i, 1d);
             }
@@ -1220,7 +1245,7 @@
             else                  { qMin = qCurrent; qMax = qLast; }
 
             if (q > qMin && q < qMax) {
-                double weight = Linear.factor(q, qLast, qCurrent);
+                final double weight = Linear.factor(q, qLast, qCurrent);
                 return qPosition.set(i, weight);
             }
             qLast = qCurrent;
@@ -1229,53 +1254,53 @@
         return null;
     }
 
-    public double getQIndex(int index, double km) {
-        return columns[index].getQRangeTree().findQ(km);
+    public double getQIndex(final int index, final double km) {
+        return this.columns[index].getQRangeTree().findQ(km);
     }
 
-    public double getQ(QPosition qPosition, double km) {
-        int    index  = qPosition.index;
-        double weight = qPosition.weight;
+    public double getQ(final QPosition qPosition, final double km) {
+        final int    index  = qPosition.index;
+        final double weight = qPosition.weight;
 
         if (weight == 1d) {
-            return columns[index].getQRangeTree().findQ(km);
+            return this.columns[index].getQRangeTree().findQ(km);
         }
-        double q1 = columns[index-1].getQRangeTree().findQ(km);
-        double q2 = columns[index  ].getQRangeTree().findQ(km);
+        final double q1 = this.columns[index-1].getQRangeTree().findQ(km);
+        final double q2 = this.columns[index  ].getQRangeTree().findQ(km);
         return Linear.weight(weight, q1, q2);
     }
 
-    public double [][] interpolateTabulated(double km) {
-        return interpolateTabulated(km, new double[2][columns.length]);
+    public double [][] interpolateTabulated(final double km) {
+        return interpolateTabulated(km, new double[2][this.columns.length]);
     }
 
-    public double [][] interpolateTabulated(double km, double [][] result) {
+    public double [][] interpolateTabulated(final double km, final double [][] result) {
 
-        int rowIndex = Collections.binarySearch(rows, new Row(km));
+        int rowIndex = Collections.binarySearch(this.rows, new Row(km));
 
         if (rowIndex >= 0) {
             // Direct hit -> copy ws.
-            Row row = rows.get(rowIndex);
+            final Row row = this.rows.get(rowIndex);
             System.arraycopy(
-                row.ws, 0, result[0], 0,
-                Math.min(row.ws.length, result[0].length));
+                    row.ws, 0, result[0], 0,
+                    Math.min(row.ws.length, result[0].length));
         }
         else {
             rowIndex = -rowIndex -1;
-            if (rowIndex < 1 || rowIndex >= rows.size()) {
+            if (rowIndex < 1 || rowIndex >= this.rows.size()) {
                 // Out of bounds.
                 return null;
             }
             // Interpolate ws.
-            Row r1 = rows.get(rowIndex-1);
-            Row r2 = rows.get(rowIndex);
-            double factor = Linear.factor(km, r1.km, r2.km);
+            final Row r1 = this.rows.get(rowIndex-1);
+            final Row r2 = this.rows.get(rowIndex);
+            final double factor = Linear.factor(km, r1.km, r2.km);
             Linear.weight(factor, r1.ws, r2.ws, result[0]);
         }
 
-        double [] qs = result[1];
-        for (int i = Math.min(qs.length, columns.length)-1; i >= 0; --i) {
-            qs[i] = columns[i].getQRangeTree().findQ(km);
+        final double [] qs = result[1];
+        for (int i = Math.min(qs.length, this.columns.length)-1; i >= 0; --i) {
+            qs[i] = this.columns[i].getQRangeTree().findQ(km);
         }
         return result;
     }
@@ -1283,7 +1308,7 @@
 
     /** True if no QRange is given or Q equals zero. */
     public boolean hasEmptyQ() {
-        for (Column column: columns) {
+        for (final Column column: this.columns) {
             if (column.getQRangeTree() == null) {
                 return true;
             }
@@ -1294,7 +1319,7 @@
             }
         }
 
-        if (columns.length == 0) {
+        if (this.columns.length == 0) {
             log.warn("No columns in WstValueTable.");
         }
 
@@ -1303,10 +1328,10 @@
 
 
     /** Find ranges that are between km1 and km2 (inclusive?) */
-    public List<Range> findSegments(double km1, double km2) {
-        return columns.length != 0
-            ? columns[columns.length-1].getQRangeTree().findSegments(km1, km2)
-            : Collections.<Range>emptyList();
+    public List<Range> findSegments(final double km1, final double km2) {
+        return this.columns.length != 0
+                ? this.columns[this.columns.length-1].getQRangeTree().findSegments(km1, km2)
+                        : Collections.<Range>emptyList();
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jul 23 19:18:11 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java	Mon Jul 23 19:20:06 2018 +0200
@@ -66,8 +66,8 @@
     /**
      * Calculate the infrastructures flood duration result rows
      */
-    public void execute(final Calculation problems, final String label, final DoubleRange calcRange,
-            final RiversideChoiceKey riverside, final WINFOArtifact winfo, final FloodDurationCalculationResults results) {
+    public void execute(final Calculation problems, final String label, final DoubleRange calcRange, final RiversideChoiceKey riverside,
+            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<>();
@@ -91,20 +91,23 @@
         addInfrastructures(allStations, secondBank, infras);
         final double[] stationsSorted = sortStations(allStations.keySet());
 
-        // Calculate W and Q for all stations and the selected discharge states
-        // TODO Geht das schneller, wenn man WstValueTable statt WINFOArtifact.computeWaterlevelData nutzt?
-        final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
+        // Calculate W and Q for all stations and the selected discharge states/waterlevels
+        final WQKms[] wqkmsArray = calculateWsts(winfo, stationsSorted, problems);
 
-        // Determine discharge state labels of the main values
-        updateMainValueLabels(wqkmsArray, winfo, problems);
+        // Determine discharge state labels of the waterlevels
+        updateWstLabels(wqkmsArray, winfo, problems);
 
-        // Load base wst table (river).wst - first run takes long time, then it's cached
+        final Map<Gauge, List<Double>> gaugeWstDurations = new HashMap<>();
+        calcGaugeWstDurations(winfo, new ArrayList<>(durFinders.keySet()), gaugeWstDurations, durFinders);
+
+        // Load base wst table (river).wst
+        // (should be in cache since already used in calculateWaterlevels (winfo.computeWaterlevelData)
         final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider.getRiver());
 
-        // Calculate the durations and create the result rows
+        // 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.riverInfoProvider.getGauge(stationsSorted[i], true);
-            final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i);
+            final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, gaugeWstDurations.get(gauge), i);
             if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
                 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wst, durFinders);
             this.rows.add(row);
@@ -115,50 +118,39 @@
             }
         }
 
-        //// Create a finder for Q in the {river}.wst km-w-q table
-        // final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(),
-        //// calcRange.getMinimumDouble(),
-        // calcRange.getMaximumDouble(), problems);
-        //
-        //// Calculate the durations and create the result rows
-        // for (int i = 0; i <= stationsSorted.length - 1; i++) {
-        // final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
-        // final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i);
-        // if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
-        // calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders);
-        // this.rows.add(row);
-        // if (secondBank.containsKey(stationsSorted[i])) {
-        // final ResultRow row2 = ResultRow.create(row);
-        // calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders);
-        // this.rows.add(row2);
-        // }
-        // }
+        // Get the labels of the selected waterlevels
+        final String[] wstLabels = new String[wqkmsArray.length];
+        for (int i = 0; i <= wqkmsArray.length - 1; i++)
+            wstLabels[i] = wqkmsArray[i].getName();
 
-        final String[] mainValueLabels = new String[wqkmsArray.length];
-        for (int i = 0; i <= wqkmsArray.length - 1; i++)
-            mainValueLabels[i] = wqkmsArray[i].getName();
-        results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems);
+        results.addResult(new FloodDurationCalculationResult(label, wstLabels, this.rows), problems);
     }
 
     /**
      * Calculates the duration curve for a station
+     * (other than the version 3.2.1 W-Info Dauerlinie the wst column positions
+     * are taken from the Q values of the gauge's Q-D-table)
      */
     public WQDay calcWQDays(final Calculation problems, final double station, final WINFOArtifact winfo) {
 
-        // Same processing as in W-Info DurationCurveState
-        winfo.addStringData("ld_locations", Double.toString(station));
-        final CalculationResult res = winfo.getDurationCurveData();
-        final WQDay underflow = (WQDay) res.getData();
-        // Transform underflow days into overflow days and re-sort
-        final int[] days = new int[underflow.getWs().length];
-        final double[] ws = new double[days.length];
-        final double[] qs = new double[days.length];
-        for (int i = 0, j = days.length - 1; i <= days.length - 1; i++, j--) {
-            days[j] = 365 - underflow.getDay(i);
-            ws[j] = underflow.getW(i);
-            qs[j] = underflow.getQ(i);
+        final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider.getRiver());
+        final Gauge gauge = this.riverInfoProvider.getRiver().determineGaugeByPosition(station);
+        final Object[] obj = gauge.fetchDurationCurveData();
+        final int[] udays = (int[]) obj[0];
+        final double[] qs = (double[]) obj[1];
+        final int[] odays = new int[udays.length];
+        final double[] oqs = new double[udays.length];
+        final double[] ows = new double[udays.length];
+        for (int i = 0, j = udays.length - 1; i <= udays.length - 1; i++, j--) {
+            odays[j] = 365 - udays[i];
+            oqs[j] = qs[i];
+            final QPosition qpos = wst.getQPosition(gauge.getStation().doubleValue(), qs[i]);
+            if (qpos != null)
+                ows[j] = wst.interpolateW(station, qpos, problems);
+            else
+                ows[j] = Double.NaN;
         }
-        return new WQDay(days, ws, qs);
+        return new WQDay(odays, ows, oqs);
     }
 
     /**
@@ -341,9 +333,8 @@
     /**
      * Calculates an array of w-q-longitudinal sections for all artifact W/Q options
      */
-    private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) {
-        // REMARK aus TkhCalculation - move to WinfoArtifactWrapper?
-        // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen)
+    private WQKms[] calculateWsts(final WINFOArtifact winfo, final double[] stations, final Calculation problems) {
+        // First run may take long, further runs are faster since WstValueTable is in cache then
         // (So funktioniert computeWaterlevelData wohl:
         // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.),
         // interpoliert die horizontale Tabellenposition (Q) und dann die vertikale Tabellenposition der station;
@@ -352,36 +343,35 @@
         // interpoliert;
         // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt, um das Q zu bekommen.
 
-        final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations);
+        final CalculationResult wstsData = winfo.computeWaterlevelData(stations);
 
         /* copy all problems */
-        final Calculation winfoProblems = waterlevelData.getReport();
+        final Calculation winfoProblems = wstsData.getReport();
         final List<Problem> problems2 = winfoProblems.getProblems();
         if (problems2 != null) {
             for (final Problem problem : problems2) {
                 problems.addProblem(problem);
             }
         }
-        return (WQKms[]) waterlevelData.getData();
+        return (WQKms[]) wstsData.getData();
     }
 
     /**
      * Determines the waterlevel/discharge state labels for the selected Q or W values and sets them in the WQKms array
      */
-    private void updateMainValueLabels(final WQKms[] wqkmsArray, final WINFOArtifact winfo, final Calculation problems) {
+    private void updateWstLabels(final WQKms[] wqkmsArray, final WINFOArtifact winfo, final Calculation problems) {
 
         for (int i = 0; i <= wqkmsArray.length - 1; i++)
             wqkmsArray[i].setName(buildWQDescription(wqkmsArray[i], winfo));
     }
 
     /**
-     *
-     * @param wqkms
-     * @param descBuilder
-     * @return
+     * Builds the description label of a waterlevel
      */
     private String buildWQDescription(final WQKms wqkms, final WINFOArtifact winfo) {
+
         final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
+        // TODO Zwischen numerischem Q-Wert und Dauerzahl-Hauptwert (0..364) unterscheiden
         final String description = descBuilder.getDesc(wqkms);
         if (!description.isEmpty() && Character.isDigit(description.charAt(0))) {
             if (winfo.isQ())
@@ -394,10 +384,32 @@
     }
 
     /**
+     * Calculates the flood durations of the Qs of the waterlevels/discharge states for a map of gauges
+     */
+    private void calcGaugeWstDurations(final WINFOArtifact winfo, final List<Gauge> gauges, final Map<Gauge, List<Double>> gaugeWstDurations,
+            final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
+
+        final double[] gaugeKms = new double[gauges.size()];
+        for (int i = 0; i <= gauges.size() - 1; i++) {
+            gaugeKms[i] = gauges.get(i).getStation().doubleValue();
+            gaugeWstDurations.put(gauges.get(i), new ArrayList<Double>());
+        }
+        final CalculationResult wstsData = winfo.computeWaterlevelData(gaugeKms);
+        final WQKms[] wsts = (WQKms[]) wstsData.getData();
+        for (int i = 0; i <= gauges.size() - 1; i++) {
+            final GaugeDurationValuesFinder durFinder = durFinders.get(gauges.get(i));
+            for (int j = 0; j <= wsts.length - 1; j++) {
+                final double d = durFinder.getDuration(wsts[j].getQ(i));
+                gaugeWstDurations.get(gauges.get(i)).add(Double.valueOf(underflowDaysToOverflowDays(d)));
+            }
+        }
+    }
+
+    /**
      * Create a result row for a station and its gauge, and add w-q-values as selected
      */
     private ResultRow createRow(final Double station, final Gauge gauge, final Gauge firstGauge, final WQKms[] wqkmsArray,
-            final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
+            final List<Double> gaugeDurations, final int kmIndex) {
 
         final ResultRow row = ResultRow.create();
         row.putValue(GeneralResultType.station, station);
@@ -410,39 +422,22 @@
         final String location = this.riverInfoProvider.getLocation(station);
         row.putValue(SInfoResultType.location, location);
 
-        final List<DurationWaterlevel> waterlevels = new ArrayList<>(wqkmsArray.length);
-
-        for (final WQKms wqKms : wqkmsArray) {
-            assert (wqKms.getKm(kmIndex) == station.doubleValue());
+        final List<DurationWaterlevel> wsts = new ArrayList<>(wqkmsArray.length);
 
-            final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKms.getQ(kmIndex))));
+        for (int i = 0; i <= wqkmsArray.length - 1; i++) {
+            assert (wqkmsArray[i].getKm(kmIndex) == station.doubleValue());
 
-            final DurationWaterlevel dw = new DurationWaterlevel(wqKms.getW(kmIndex), overflowDays, wqKms.getQ(kmIndex), wqKms.getName());
-            waterlevels.add(dw);
+            final int overflowDays = (int) Math.round(gaugeDurations.get(i));
+
+            final DurationWaterlevel dw = new DurationWaterlevel(wqkmsArray[i].getW(kmIndex), overflowDays, wqkmsArray[i].getQ(kmIndex),
+                    wqkmsArray[i].getName());
+            wsts.add(dw);
         }
-        row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels);
+        row.putValue(SInfoResultType.customMultiRowColWaterlevel, wsts);
 
         return row;
     }
 
-    /// **
-    // * Calculate the result row fields for one infrastructure - gives wrong duration numbers where Q changes within the
-    /// gauge range
-    // */
-    // private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue
-    /// infrastructure, final WQBaseTableFinder wqFinder,
-    // final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
-    //
-    // final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight());
-    // final double qOut = Double.isInfinite(q) ? Double.NaN : q;
-    // final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q));
-    // row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey());
-    // row.putValue(SInfoResultType.floodDuration, dur);
-    // row.putValue(SInfoResultType.floodDischarge, qOut);
-    // row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
-    // row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
-    // }
-
     /**
      * Calculate the result row fields for one infrastructure
      */
@@ -454,17 +449,16 @@
         final double[] qs = wst.findQsForW(infrastructure.getStation().doubleValue(), infrastructure.getHeight().doubleValue(), problems);
         // TODO Fehlerbehandlung (kein Q gefunden)
         final double q = (qs.length >= 1) ? qs[0] : Double.NaN;
-        final double qOut = Double.isInfinite(q) ? Double.NaN : q;
-        // Determine the relative column position of the Q
+        // Determine the relative column position of the Q of the infrastructure height
         final QPosition qPos = wst.getQPosition(infrastructure.getStation().doubleValue(), q);
         // 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 result row
+        // Set the result row
         row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey());
         row.putValue(SInfoResultType.floodDuration, dur);
-        row.putValue(SInfoResultType.floodDischarge, qOut);
+        row.putValue(SInfoResultType.floodDischarge, q);
         row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
         row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
     }

http://dive4elements.wald.intevation.org