changeset 633:d08f77e7f7e8

WST value table: Qs are now stored in ranges for each column. flys-artifacts/trunk@2006 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 25 May 2011 15:31:25 +0000
parents 07640ab913fd
children d8c47520c726
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java
diffstat 5 files changed, 386 insertions(+), 596 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Tue May 24 14:46:45 2011 +0000
+++ b/flys-artifacts/ChangeLog	Wed May 25 15:31:25 2011 +0000
@@ -1,3 +1,20 @@
+2011-05-25	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Qs are now stored in ranges for each column.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java:
+	  Deleted.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  Was WstValueTableFactory2.
+
+	* src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: Fixed
+	  node linking bug. Removed dead code.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Q values are now stored in range trees by each column. The qs of the rows
+	  are removed and the calculations are adjusted. Removed dead code.
+
 2011-05-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java	Tue May 24 14:46:45 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java	Wed May 25 15:31:25 2011 +0000
@@ -2,16 +2,15 @@
 
 import java.io.Serializable;
 
-import java.math.BigDecimal;
-
 import java.util.List;
 
-import de.intevation.flys.model.WstQRange;
-import de.intevation.flys.model.Range;
+import org.apache.log4j.Logger;
 
 public class QRangeTree
 implements   Serializable
 {
+    private static Logger log = Logger.getLogger(QRangeTree.class);
+
     public static class Node
     implements          Serializable
     {
@@ -75,6 +74,7 @@
         }
 
         public double findQ(double pos) {
+
             Node current = this;
             for (;;) {
                 if (pos < current.a) {
@@ -82,7 +82,7 @@
                         current = current.left;
                         continue;
                     }
-                    return current.left != null
+                    return current.prev != null
                         ? current.interpolatePrev(pos)
                         : Double.NaN;
                 }
@@ -91,7 +91,7 @@
                         current = current.right;
                         continue;
                     }
-                    return current.right != null
+                    return current.next != null
                         ? current.interpolateNext(pos)
                         : Double.NaN;
                 }
@@ -106,27 +106,6 @@
     }
 
     /** wstQRanges need to be sorted by range.a */
-    public QRangeTree(List<WstQRange> wstQRanges) {
-
-        if (wstQRanges.isEmpty()) {
-            return;
-        }
-
-        Node [] nodes = new Node[wstQRanges.size()];
-        for (int i = 0; i < nodes.length; ++i) {
-            WstQRange wstQRange = wstQRanges.get(i);
-            Range     range     = wstQRange.getRange();
-            BigDecimal a = range.getA();
-            BigDecimal b = range.getB();
-            nodes[i] = new Node(
-                a != null ? a.doubleValue() : -Double.MAX_VALUE,
-                b != null ? b.doubleValue() :  Double.MAX_VALUE,
-                wstQRange.getQ().doubleValue());
-        }
-
-        root = wireTree(nodes);
-    }
-
     public QRangeTree(List<Object []> qRanges, int start, int stop) {
 
         if (stop <= start) {
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java	Tue May 24 14:46:45 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java	Wed May 25 15:31:25 2011 +0000
@@ -25,8 +25,8 @@
 
     public static final int DEFAULT_Q_STEPS = 500;
 
-    public static class Column
-    implements          Serializable
+    public static final class Column
+    implements                Serializable
     {
         protected String name;
 
@@ -54,32 +54,34 @@
         public void setQRangeTree(QRangeTree qRangeTree) {
             this.qRangeTree = qRangeTree;
         }
-    }
-    // class Column
+    } // class Column
 
-    public static class QPosition {
+    public static final class QPosition {
 
-        protected double q;
         protected int    index;
         protected double weight;
 
         public QPosition() {
         }
 
-        public QPosition(double q, int index, double weight) {
+        public QPosition(int index, double weight) {
             this.index  = index;
             this.weight = weight;
         }
 
+        public QPosition set(int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+            return this;
+        }
+
     } // class Position
 
-    public static class Row
-    implements          Serializable, Comparable<Row>
+    public static final class Row
+    implements                Serializable, Comparable<Row>
     {
         double    km;
         double [] ws;
-        double [] qs;
-        boolean   qSorted;
 
         public Row() {
         }
@@ -88,110 +90,9 @@
             this.km = km;
         }
 
-        public Row(double km, double [] ws, double [] qs) {
+        public Row(double km, double [] ws) {
             this(km);
             this.ws = ws;
-            this.qs = qs;
-        }
-
-        public static final double linear(
-            double x,
-            double x1, double x2,
-            double y1, double y2
-        ) {
-            // y1 = m*x1 + b
-            // y2 = m*x2 + b
-            // y2 - y1 = m*(x2 - x1)
-            // m = (y2 - y1)/(x2 - x1) # x2 != x1
-            // b = y1 - m*x1
-
-            if (x1 == x2) {
-                return 0.5*(y1 + y2);
-            }
-            double m = (y2 - y1)/(x2 - x1);
-            double b = y1 - m*x1;
-            return x*m + b;
-        }
-
-        public static final double factor(double x, double p1, double p2) {
-            // 0 = m*p1 + b <=> b = -m*p1
-            // 1 = m*p2 + b
-            // 1 = m*(p2 - p1)
-            // m = 1/(p2 - p1) # p1 != p2
-            // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1)
-
-            return p1 == p2 ? 0.0 : (x-p1)/(p2-p1);
-        }
-
-        public static final double weight(double factor, double a, double b) {
-            return (1.0-factor)*a + factor*b;
-        }
-
-        public void interpolateW(
-            Row       other,
-            double    km,
-            double [] input,
-            double [] output
-        ) {
-            double factor = factor(km, this.km, other.km);
-
-            for (int i = 0; i < input.length; ++i) {
-                double q = input[i];
-                int idx1 =       getQIndex(q);
-                int idx2 = other.getQIndex(q);
-
-                double w1 = idx1 >= 0
-                    ? ws[idx1]
-                    : interpolateW(-idx1-1, q);
-
-                double w2 = idx2 >= 0
-                    ? other.ws[idx2]
-                    : other.interpolateW(-idx2-1, q);
-
-                output[i] = weight(factor, w1, w2);
-            }
-        }
-
-        public double interpolateW(int idx, double q) {
-            return idx < 1 || idx >= qs.length
-                ? Double.NaN // do not extrapolate
-                : linear(q, qs[idx-1], qs[idx], ws[idx-1], ws[idx]);
-        }
-
-        public double interpolateW(double q) {
-            if (Double.isNaN(q)) return Double.NaN;
-            int index = getQIndex(q);
-            return index >= 0 ? ws[index] : interpolateW(-index -1, q);
-        }
-
-        public int getQIndex(double q) {
-            return qSorted ? binaryQIndex(q) : linearQIndex(q);
-        }
-
-        public int binaryQIndex(double q) {
-            return Arrays.binarySearch(qs, q);
-        }
-
-        public int linearQIndex(double q) {
-            switch (qs.length) {
-                case 0: break;
-                case 1:
-                    if (qs[0] == q) return 0;
-                    if (qs[0] <  q) return -(1+1);
-                    return -(0+1);
-                default:
-                    for (int i = 1; i < qs.length; ++i) {
-                        double qa = qs[i-1];
-                        double qb = qs[i];
-                        if (qa == q) return i-1;
-                        if (qb == q) return i;
-                        if (qa > qb) { double t = qa; qa = qb; qb = t; }
-                        if (q > qa && q < qb) return -(i+1);
-                    }
-                    return -qs.length;
-            }
-
-            return -1;
         }
 
         public int compareTo(Row other) {
@@ -201,8 +102,35 @@
             return 0;
         }
 
-        public double [][] interpolateWQ(Row other, double km, int steps) {
+        public void interpolateW(
+            Row           other,
+            double        km,
+            double []     iqs,
+            double []     ows,
+            WstValueTable table
+        ) {
+            double kmWeight = factor(km, this.km, other.km);
 
+            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);
+                    ows[i] = weight(kmWeight, wt, wo);
+                }
+                else {
+                    ows[i] = Double.NaN;
+                }
+            }
+        }
+
+        public double [][] interpolateWQ(
+            Row           other,
+            double        km,
+            int           steps,
+            WstValueTable table
+        ) {
             int W = Math.min(ws.length, other.ws.length);
 
             if (W < 1) {
@@ -219,7 +147,11 @@
 
             for (int i = 0; i < W; ++i) {
                 double wws = weight(factor, ws[i], other.ws[i]);
-                double wqs = weight(factor, qs[i], other.qs[i]);
+                double wqs = weight(
+                    factor,
+                    table.getQIndex(i, km),
+                    table.getQIndex(i, other.km));
+
                 splineW[i] = wws;
                 splineQ[i] = wqs;
                 if (wqs < minQ) minQ = wqs;
@@ -248,7 +180,7 @@
             return new double [][] { outWs, outQs };
         }
 
-        public double [][] interpolateWQ(int steps) {
+        public double [][] interpolateWQ(int steps, WstValueTable table) {
 
             int W = ws.length;
 
@@ -256,17 +188,17 @@
                 return new double[2][0];
             }
 
-            double [] splineW = new double[W];
             double [] splineQ = new double[W];
 
             double minQ =  Double.MAX_VALUE; 
             double maxQ = -Double.MAX_VALUE; 
 
+            QPosition qPosition = new QPosition();
+
             for (int i = 0; i < W; ++i) {
-                splineW[i] = ws[i];
-                splineQ[i] = qs[i];
-                if (qs[i] < minQ) minQ = qs[i];
-                if (qs[i] > maxQ) maxQ = qs[i];
+                splineQ[i] = table.getQIndex(i, km);
+                if (splineQ[i] < minQ) minQ = splineQ[i];
+                if (splineQ[i] > maxQ) maxQ = splineQ[i];
             }
 
             double stepWidth = (maxQ - minQ)/steps;
@@ -274,7 +206,7 @@
             SplineInterpolator interpolator = new SplineInterpolator();
 
             PolynomialSplineFunction spline =
-                interpolator.interpolate(splineQ, splineW);
+                interpolator.interpolate(splineQ, ws);
 
             double [] outWs = new double[steps];
             double [] outQs = new double[steps];
@@ -292,153 +224,40 @@
             return new double [][] { outWs, outQs };
         }
 
-        public QPosition getQPosition(double q) {
-            int qIndex = getQIndex(q);
-
-            if (qIndex >= 0) {
-                // direct column match
-                return new QPosition(q, qIndex, 1.0);
-            }
 
-            qIndex = -qIndex -1;
+        public double getW(QPosition qPosition) {
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
 
-            if (qIndex < 0 || qIndex >= qs.length-1) {
-                // do not extrapolate
-                return null;
-            }
-
-            return new QPosition(
-                q, qIndex, factor(q, qs[qIndex-1], qs[qIndex]));
+            return weight == 1.0
+                ? ws[index]
+                : weight(weight, ws[index-1], ws[index]);
         }
 
-        public QPosition getQPosition(Row other, double km, double q) {
-
-            QPosition qpt = getQPosition(q);
-            QPosition qpo = other.getQPosition(q);
-
-            if (qpt == null || qpo == null) {
-                return null;
-            }
-
-            double kmWeight = factor(km, this.km, other.km);
-
-            // XXX: Index interpolation is a bit sticky here!
-
-            int index = (int)Math.round(
-                weight(kmWeight, qpt.index, qpo.index));
-
-            double weight = weight(kmWeight, qpt.weight, qpo.weight);
-
-            return new QPosition(q, index, weight);
-        }
-
-        public void storeWQ(
-            QPosition qPosition,
-            double [] ows,
-            double [] oqs,
-            int       index
-        ) {
-            int    qIdx    = qPosition.index;
-            double qWeight = qPosition.weight;
-
-            if (qWeight == 1.0) {
-                oqs[index] = qs[qIdx];
-                ows[index] = ws[qIdx];
-            }
-            else {
-                oqs[index] = weight(qWeight, qs[qIdx-1], qs[qIdx]); 
-                ows[index] = weight(qWeight, ws[qIdx-1], ws[qIdx]); 
-            }
-        }
-
-        public void storeWQ(
+        public double getW(
             Row       other,
             double    km,
-            QPosition qPosition,
-            double [] ows,
-            double [] oqs,
-            int       index
+            QPosition qPosition
         ) {
             double kmWeight = factor(km, this.km, other.km);
 
-            double tq, tw;
-            double oq, ow;
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
 
-            int    qIdx    = qPosition.index;
-            double qWeight = qPosition.weight;
+            double tw, ow;
 
-            if (qWeight == 1.0) {
-                tq = qs[qIdx];
-                tw = ws[qIdx];
-                oq = other.qs[qIdx];
-                ow = other.ws[qIdx];
+            if (weight == 1.0) {
+                tw = ws[index];
+                ow = other.ws[index];
             }
             else {
-                tq = weight(qWeight, qs[qIdx-1], qs[qIdx]); 
-                tw = weight(qWeight, ws[qIdx-1], ws[qIdx]); 
-                oq = weight(qWeight, other.qs[qIdx-1], other.qs[qIdx]); 
-                ow = weight(qWeight, other.ws[qIdx-1], other.ws[qIdx]); 
+                tw = weight(weight, ws[index-1], ws[index]); 
+                ow = weight(weight, other.ws[index-1], other.ws[index]); 
             }
 
-            oqs[index] = weight(kmWeight, tq, oq);
-            ows[index] = weight(kmWeight, tw, ow);
-        }
-
-        public void storeWQ(
-            QPosition   qPosition,
-            LinearRemap remap,
-            double []   ows,
-            double []   oqs,
-            int         index
-        ) {
-            int    qIdx    = qPosition.index;
-            double qWeight = qPosition.weight;
-
-            oqs[index] = remap.eval(km, qWeight == 1.0
-                ? qs[qIdx]
-                : weight(qWeight, qs[qIdx-1], qs[qIdx])); 
-
-            ows[index] = interpolateW(oqs[index]);
+            return weight(kmWeight, tw, ow);
         }
-
-        public void storeWQ(
-            Row         other,
-            double      km,
-            QPosition   qPosition,
-            LinearRemap remap,
-            double []   ows,
-            double []   oqs,
-            int         index
-        ) {
-            double kmWeight = factor(km, this.km, other.km);
-
-            double tq, tw;
-            double oq, ow;
-
-            int    qIdx    = qPosition.index;
-            double qWeight = qPosition.weight;
-
-            if (qWeight == 1.0) {
-                tq = remap.eval(this.km,  qs[qIdx]);
-                oq = remap.eval(other.km, other.qs[qIdx]);
-            }
-            else {
-                tq = remap.eval(
-                    this.km,
-                    weight(qWeight, qs[qIdx-1], qs[qIdx]));
-                oq = remap.eval(
-                    other.km,
-                    weight(qWeight, other.qs[qIdx-1], other.qs[qIdx])); 
-            }
-
-            tw = interpolateW(tq);
-            ow = other.interpolateW(oq);
-
-            oqs[index] = weight(kmWeight, tq, oq);
-            ows[index] = weight(kmWeight, tw, ow);
-        }
-    }
-    // class Row
+    } // class Row
 
     protected List<Row> rows;
 
@@ -453,23 +272,27 @@
         this.columns = columns;
     }
 
+    public WstValueTable(Column [] columns, List<Row> rows) {
+        this.columns = columns;
+        this.rows    = rows;
+    }
+
     public void sortRows() {
         Collections.sort(rows);
     }
 
-
-    public double [] interpolateW(double km, double [] qs) {
-        return interpolateW(km, qs, new double[qs.length]);
-    }
-
     public double [] interpolateW(double km, double [] qs, double [] ws) {
 
         int rowIndex = Collections.binarySearch(rows, new Row(km));
 
+        QPosition qPosition = new QPosition();
+
         if (rowIndex >= 0) { // direct row match
             Row row = rows.get(rowIndex);
             for (int i = 0; i < qs.length; ++i) {
-                ws[i] = row.interpolateW(qs[i]);
+                ws[i] = getQPosition(km, qs[i], qPosition) != null
+                    ? row.getW(qPosition)
+                    : Double.NaN;
             }
         }
         else { // needs bilinear interpolation
@@ -480,16 +303,15 @@
                 Arrays.fill(ws, Double.NaN);
             }
             else {
-                rows.get(rowIndex-1).interpolateW(
-                    rows.get(rowIndex),
-                    km, qs, ws);
+                Row r1 = rows.get(rowIndex-1);
+                Row r2 = rows.get(rowIndex);
+                r1.interpolateW(r2, km, qs, ws, this);
             }
         }
 
         return ws;
     }
 
-
     public double [][] interpolateWQ(double km) {
         return interpolateWQ(km, DEFAULT_Q_STEPS);
     }
@@ -500,7 +322,7 @@
 
         if (rowIndex >= 0) { // direct row match
             Row row = rows.get(rowIndex);
-            return row.interpolateWQ(steps);
+            return row.interpolateWQ(steps, this);
         }
 
         rowIndex = -rowIndex -1;
@@ -513,7 +335,7 @@
         Row r1 = rows.get(rowIndex-1);
         Row r2 = rows.get(rowIndex);
 
-        return r1.interpolateWQ(r2, km, steps);
+        return r1.interpolateWQ(r2, km, steps, this);
     }
 
     public void interpolate(
@@ -526,13 +348,24 @@
         int R1 = rows.size()-1;
 
         Row kmKey = new Row();
+
+        QPosition nPosition = new QPosition();
+
         for (int i = 0; i < kms.length; ++i) {
             kmKey.km = kms[i];
+
+            qs[i] = remap.eval(kms[i], getQ(qPosition, kms[i]));
+
+            if (getQPosition(kms[i], qs[i], nPosition) == null) {
+                ws[i] = Double.NaN;
+                continue;
+            }
+
             int rowIndex = Collections.binarySearch(rows, kmKey);
 
             if (rowIndex >= 0) {
                 // direct row match
-                rows.get(rowIndex).storeWQ(qPosition, remap, ws, qs, i);
+                ws[i] = rows.get(rowIndex).getW(nPosition);
                 continue;
             }
 
@@ -540,12 +373,12 @@
 
             if (rowIndex < 1 || rowIndex >= R1) {
                 // do not extrapolate
-                ws[i] = qs[i] = Double.NaN;
+                ws[i] = Double.NaN;
                 continue;
             }
             Row r1 = rows.get(rowIndex-1);
             Row r2 = rows.get(rowIndex);
-            r1.storeWQ(r2, kms[i], qPosition, remap, ws, qs, i);
+            ws[i] = r1.getW(r2, kms[i], nPosition);
         }
     }
 
@@ -562,28 +395,10 @@
 
         int R1 = rows.size()-1;
 
-        QPosition qPosition = null;
-
-        if (rowIndex >= 0) { // direct row match
-            qPosition = rows.get(rowIndex).getQPosition(q);
-        }
-        else {
-            rowIndex = -rowIndex -1;
-
-            if (rowIndex < 1 || rowIndex >= R1) {
-                // do not extrapolate
-                return null;
-            }
-
-            // interpolation case
-            Row r1 = rows.get(rowIndex-1);
-            Row r2 = rows.get(rowIndex);
-
-            qPosition = r1.getQPosition(r2, kms[referenceIndex], q);
-        }
+        QPosition qPosition = getQPosition(kms[referenceIndex], q);
 
         if (qPosition == null) {
-            // we cannot locate q in reference row
+            // we cannot locate q at km
             return null;
         }
 
@@ -592,9 +407,11 @@
 
             rowIndex = Collections.binarySearch(rows, kmKey);
 
+            qs[i] = getQ(qPosition, kms[i]);
+
             if (rowIndex >= 0) {
                 // direct row match
-                rows.get(rowIndex).storeWQ(qPosition, ws, qs, i);
+                ws[i] = rows.get(rowIndex).getW(qPosition);
                 continue;
             }
 
@@ -602,15 +419,102 @@
 
             if (rowIndex < 1 || rowIndex >= R1) {
                 // do not extrapolate
-                ws[i] = qs[i] = Double.NaN;
+                ws[i] =  Double.NaN;
                 continue;
             }
             Row r1 = rows.get(rowIndex-1);
             Row r2 = rows.get(rowIndex);
-            r1.storeWQ(r2, kms[i], qPosition, ws, qs, i);
+
+            ws[i] = r1.getW(r2, kms[i], qPosition);
         }
 
         return qPosition;
     }
+
+    public QPosition getQPosition(double km, double q) {
+        return getQPosition(km, q, new QPosition());
+    }
+
+    public QPosition getQPosition(double km, double q, QPosition qPosition) {
+
+        if (columns.length == 0) {
+            return null;
+        }
+
+        double qLast = 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);
+            if (Math.abs(qCurrent - q) < 0.00001) {
+                return qPosition.set(i, 1d);
+            }
+
+            double qMin, qMax;
+            if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; }
+            else                  { qMin = qCurrent; qMax = qLast; }
+
+            if (q > qMin && q < qMax) {
+                double weight = factor(q, qLast, qCurrent);
+                return qPosition.set(i, weight);
+            }
+            qLast = qCurrent;
+        }
+
+        return null;
+    }
+
+    public double getQIndex(int index, double km) {
+        return columns[index].getQRangeTree().findQ(km);
+    }
+
+    public double getQ(QPosition qPosition, double km) {
+        int    index  = qPosition.index;
+        double weight = qPosition.weight;
+
+        if (weight == 1d) {
+            return columns[index].getQRangeTree().findQ(km);
+        }
+        double q1 = columns[index-1].getQRangeTree().findQ(km);
+        double q2 = columns[index  ].getQRangeTree().findQ(km);
+        return weight(weight, q1, q2);
+    }
+
+    public static final double linear(
+        double x,
+        double x1, double x2,
+        double y1, double y2
+    ) {
+        // y1 = m*x1 + b
+        // y2 = m*x2 + b
+        // y2 - y1 = m*(x2 - x1)
+        // m = (y2 - y1)/(x2 - x1) # x2 != x1
+        // b = y1 - m*x1
+
+        if (x1 == x2) {
+            return 0.5*(y1 + y2);
+        }
+        double m = (y2 - y1)/(x2 - x1);
+        double b = y1 - m*x1;
+        return x*m + b;
+    }
+
+    public static final double factor(double x, double p1, double p2) {
+        // 0 = m*p1 + b <=> b = -m*p1
+        // 1 = m*p2 + b
+        // 1 = m*(p2 - p1)
+        // m = 1/(p2 - p1) # p1 != p2
+        // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1)
+
+        return p1 == p2 ? 0.0 : (x-p1)/(p2-p1);
+    }
+
+    public static final double weight(double factor, double a, double b) {
+        //return (1.0-factor)*a + factor*b;
+        return a + factor*(b-a);
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java	Tue May 24 14:46:45 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java	Wed May 25 15:31:25 2011 +0000
@@ -1,9 +1,19 @@
 package de.intevation.flys.artifacts.model;
 
+import java.util.List;
 import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Collections;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
 
 import org.hibernate.Session;
 import org.hibernate.Query;
@@ -11,34 +21,34 @@
 
 import org.hibernate.type.StandardBasicTypes;
 
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-import de.intevation.flys.model.WstColumn;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
 public class WstValueTableFactory
 {
     private static Logger log = Logger.getLogger(WstValueTableFactory.class);
 
+    public static final int DEFAULT_KIND = 0;
+
     // TODO: put this into a property file
-    public static final String SQL_POS_WQ = 
-        "SELECT position, w, q, column_pos" +
-        "    FROM wst_value_table"          +
-        "    WHERE wst_id = :wst_id";
+
+    public static final String HQL_WST =
+        "from Wst where river=:river and kind=:kind";
+
+    public static final String SQL_SELECT_NAMES_POS =
+        "SELECT position, name FROM wst_columns " +
+        "WHERE wst_id = :wst_id ORDER BY position";
+
+    public static final String SQL_SELECT_QS =
+        "SELECT column_pos, q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id";
+
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w, column_pos FROM wst_w_values " +
+        "WHERE wst_id = :wst_id";
 
     private WstValueTableFactory() {
     }
 
     public static WstValueTable getTable(River river) {
-        return getTable(river, 0);
+        return getTable(river, DEFAULT_KIND);
     }
 
     public static WstValueTable getTable(River river, int kind) {
@@ -70,99 +80,165 @@
         return valueTable;
     }
 
+    public static WstValueTable getTableUncached(River river) {
+        return getTableUncached(river, DEFAULT_KIND);
+    }
+
     public static WstValueTable getTableUncached(River river, int kind) {
 
         Session session = SessionHolder.HOLDER.get();
 
-        Query query = session.createQuery(
-            "from Wst where river=:river and kind=:kind");
+        Wst wst = loadWst(session, river, kind);
+
+        if (wst == null) {
+            return null;
+        }
+
+        WstValueTable.Column [] columns = loadColumns(session, wst);
+
+        loadQRanges(session, columns, wst);
+
+        List<WstValueTable.Row> rows = loadRows(session, wst, columns.length);
+
+        return new WstValueTable(columns, rows);
+    }
+
+    protected static Wst loadWst(Session session, River river, int kind) {
+        Query query = session.createQuery(HQL_WST);
         query.setParameter("river", river);
         query.setInteger("kind",    kind);
 
         List<Wst> wsts = query.list();
 
-        if (wsts.isEmpty()) {
-            return null;
-        }
-
-        Wst wst = wsts.get(0);
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
 
-        // TODO: Do this sorting at database level
-        List<WstColumn> wstColumns = new ArrayList(wst.getColumns());
-        Collections.sort(wstColumns, new Comparator<WstColumn>() {
-            public int compare(WstColumn a, WstColumn b) {
-                int pa = a.getPosition();
-                int pb = b.getPosition();
-                if (pa < pb) return -1;
-                if (pa > pb) return +1;
-                return 0;
-            }
-        });
-
-        WstValueTable.Column [] columns =
-            new WstValueTable.Column[wstColumns.size()];
-
-        for (int i = 0; i < columns.length; ++i) {
-            columns[i] = new WstValueTable.Column(wstColumns.get(i).getName());
-        }
-
-        // using native SQL here to avoid myriad of small objects.
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_POS_WQ)
-            .addScalar("position",   StandardBasicTypes.DOUBLE)
+    protected static List<WstValueTable.Row> loadRows(
+        Session session, 
+        Wst     wst,
+        int     numColumns
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km",         StandardBasicTypes.DOUBLE)
             .addScalar("w",          StandardBasicTypes.DOUBLE)
-            .addScalar("q",          StandardBasicTypes.DOUBLE)
             .addScalar("column_pos", StandardBasicTypes.INTEGER);
 
         sqlQuery.setInteger("wst_id", wst.getId());
 
-        WstValueTable valueTable = new WstValueTable(columns);
-
-        int lastColumnNo      = -1;
-        WstValueTable.Row row = null;
-
-        Double lastQ     = -Double.MAX_VALUE;
-        boolean qSorted  = true;
-
-        for (Object  r: sqlQuery.list()) {
-            Object[] result = (Object[]) r;
+        List<Object []> results = sqlQuery.list();
 
-            double km    = (Double) result[0];
-            Double w     = (Double) result[1];
-            Double q     = (Double) result[2];
-            int columnNo = (Integer)result[3];
+        int lastColumn = Integer.MAX_VALUE;
+        double [] ws = null;
 
-            if (columnNo > lastColumnNo) { // new row
-                if (row != null) {
-                    row.qSorted = qSorted;
-                    valueTable.rows.add(row);
-                }
-                row = new WstValueTable.Row(
-                    km,
-                    new double[columnNo+1],
-                    new double[columnNo+1]);
-                lastQ = -Double.MAX_VALUE;
-                qSorted = true;
+        ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
+
+        for (Object [] result: results) {
+            int column = (Integer)result[2];
+            if (column < lastColumn) {
+                ws = new double[numColumns];
+                WstValueTable.Row row =
+                    new WstValueTable.Row((Double)result[0], ws);
+                rows.add(row);
+            }
+            Double w = (Double)result[1];
+            ws[column] = w != null ? w : Double.NaN;
+            lastColumn = column;
+        }
+
+        rows.trimToSize();
+        return rows;
+    }
+
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        Wst     wst
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
+            .addScalar("position",   StandardBasicTypes.INTEGER)
+            .addScalar("name",       StandardBasicTypes.STRING);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        List<Object []> columnNames = sqlQuery.list();
+
+        WstValueTable.Column [] columns =
+            new WstValueTable.Column[columnNames.size()];
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new WstValueTable.Column(
+                (String)columnNames.get(i)[1]);
+        }
+        return columns;
+    }
+
+    protected static void loadQRanges(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        Wst                     wst
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER)
+            .addScalar("q",          StandardBasicTypes.DOUBLE)
+            .addScalar("a",          StandardBasicTypes.DOUBLE)
+            .addScalar("b",          StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int     start      = -1;
+        int     Q          = qRanges.size();
+        Integer lastColumn = null;
+
+        for (int i = 0; i < Q; ++i) {
+            Object [] qRange = qRanges.get(i);
+            Integer columnId = (Integer)qRange[0];
+            if (lastColumn == null) {
+                lastColumn = columnId;
+                start = i;
+            }
+            else if (!lastColumn.equals(columnId)) {
+                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
+                columns[lastColumn].setQRangeTree(qRangeTree);
+                lastColumn = columnId;
+                start = i;
+            }
+        }
+
+        if (start != -1) {
+            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
+            columns[lastColumn].setQRangeTree(qRangeTree);
+        }
+
+        /* This is debug code to visualize the q ranges trees 
+
+        java.io.PrintWriter out = null;
+        try {
+            out = new java.io.PrintWriter(
+                new java.io.FileWriter(
+                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
+
+            out.println("graph \"Q ranges trees\" {");
+
+            for (int i = 0; i < columns.length; ++i) {
+                QRangeTree tree = columns[i].getQRangeTree();
+                out.println(tree.toGraph());
             }
 
-            row.ws[columnNo] = w != null ? w : Double.NaN;
-            row.qs[columnNo] = q != null ? q : Double.NaN;
+            out.println("}");
 
-            if (qSorted && (q == null || lastQ > q)) {
-                qSorted = false;
-            }
-            lastQ = q;
-
-            lastColumnNo = columnNo;
+            out.flush();
         }
-
-        if (row != null) {
-            valueTable.rows.add(row);
+        catch (java.io.IOException ioe) {
+            log.error(ioe);
         }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+        */
+    }
 
-        // XXX: should not be necessary
-        valueTable.sortRows();
-
-        return valueTable;
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java	Tue May 24 14:46:45 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-package de.intevation.flys.artifacts.model;
-
-import java.util.List;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.flys.model.River;
-import de.intevation.flys.model.Wst;
-
-import org.hibernate.Session;
-import org.hibernate.Query;
-import org.hibernate.SQLQuery;
-
-import org.hibernate.type.StandardBasicTypes;
-
-public class WstValueTableFactory2
-{
-    private static Logger log = Logger.getLogger(WstValueTableFactory2.class);
-
-    public static final int DEFAULT_KIND = 0;
-
-    // TODO: put this into a property file
-
-    public static final String HQL_WST =
-        "from Wst where river=:river and kind=:kind";
-
-    public static final String SQL_SELECT_NAMES_POS =
-        "SELECT position, name FROM wst_columns " +
-        "WHERE wst_id = :wst_id ORDER BY position";
-
-    public static final String SQL_SELECT_QS =
-        "SELECT column_pos, q, a, b FROM wst_q_values " +
-        "WHERE wst_id = :wst_id";
-
-    public static final String SQL_SELECT_WS =
-        "SELECT km, w, column_pos FROM wst_w_values " +
-        "WHERE wst_id = :wst_id";
-
-    private WstValueTableFactory2() {
-    }
-
-    public static WstValueTable getTable(River river) {
-        return getTable(river, DEFAULT_KIND);
-    }
-
-    public static WstValueTable getTable(River river, int kind) {
-
-        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
-
-        WstValueTableCacheKey cacheKey;
-
-        if (cache != null) {
-            cacheKey = new WstValueTableCacheKey(river.getId(), kind);
-            Element element = cache.get(cacheKey);
-            if (element != null) {
-                log.debug("got wst value table from cache");
-                return (WstValueTable)element.getValue();
-            }
-        }
-        else {
-            cacheKey = null;
-        }
-
-        WstValueTable valueTable = getTableUncached(river, kind);
-
-        if (valueTable != null && cacheKey != null) {
-            log.debug("store wst value table in cache");
-            Element element = new Element(cacheKey, valueTable);
-            cache.put(element);
-        }
-
-        return valueTable;
-    }
-
-    public static WstValueTable getTableUncached(River river) {
-        return getTableUncached(river, DEFAULT_KIND);
-    }
-
-    public static WstValueTable getTableUncached(River river, int kind) {
-
-        Session session = SessionHolder.HOLDER.get();
-
-        Query query = session.createQuery(HQL_WST);
-        query.setParameter("river", river);
-        query.setInteger("kind",    kind);
-
-        List<Wst> wsts = query.list();
-
-        if (wsts.isEmpty()) {
-            return null;
-        }
-
-        Wst wst = wsts.get(0);
-
-        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
-            .addScalar("position",   StandardBasicTypes.INTEGER)
-            .addScalar("name",       StandardBasicTypes.STRING);
-
-        sqlQuery.setInteger("wst_id", wst.getId());
-
-        List<Object []> columnNames = sqlQuery.list();
-
-        WstValueTable.Column [] columns =
-            new WstValueTable.Column[columnNames.size()];
-
-        for (int i = 0; i < columns.length; ++i) {
-            columns[i] = new WstValueTable.Column(
-                (String)columnNames.get(i)[1]);
-        }
-
-        sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
-            .addScalar("column_pos", StandardBasicTypes.INTEGER)
-            .addScalar("q",          StandardBasicTypes.DOUBLE)
-            .addScalar("a",          StandardBasicTypes.DOUBLE)
-            .addScalar("b",          StandardBasicTypes.DOUBLE);
-
-        sqlQuery.setInteger("wst_id", wst.getId());
-
-        Integer lastColumn = null;
-
-        List<Object []> qRanges = sqlQuery.list();
-        int start = -1;
-
-        int Q = qRanges.size();
-
-        for (int i = 0; i < Q; ++i) {
-            Object [] qRange = qRanges.get(i);
-            Integer columnId = (Integer)qRange[0];
-            if (lastColumn == null) {
-                lastColumn = columnId;
-                start = i;
-            }
-            else if (!lastColumn.equals(columnId)) {
-                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
-                columns[lastColumn].setQRangeTree(qRangeTree);
-                lastColumn = columnId;
-                start = i;
-            }
-        }
-
-        if (start != -1) {
-            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
-            columns[lastColumn].setQRangeTree(qRangeTree);
-        }
-
-        /* This is debug code to visualize the q ranges trees 
-
-        java.io.PrintWriter out = null;
-        try {
-            out = new java.io.PrintWriter(
-                new java.io.FileWriter(
-                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
-
-            out.println("graph \"Q ranges trees\" {");
-
-            for (int i = 0; i < columns.length; ++i) {
-                QRangeTree tree = columns[i].getQRangeTree();
-                out.println(tree.toGraph());
-            }
-
-            out.println("}");
-
-            out.flush();
-        }
-        catch (java.io.IOException ioe) {
-            log.error(ioe);
-        }
-        finally {
-            if (out != null) {
-                out.close();
-            }
-        }
-        */
-        // TODO: Implement me!
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org