changeset 938:bd3683453928

Debugged the water fill algorithm. flys-artifacts/trunk@2330 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 14 Jul 2011 14:11:29 +0000
parents 9e813e9137a5
children 32d0a543e3e8
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java
diffstat 3 files changed, 226 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Thu Jul 14 11:27:01 2011 +0000
+++ b/flys-artifacts/ChangeLog	Thu Jul 14 14:11:29 2011 +0000
@@ -1,3 +1,11 @@
+2011-07-14  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/geom/Lines.java:
+	  Debugged the water fill algorithm. Added a lot of logging.
+
+	* src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java:
+	  Added a text field to give a water level to fill in.
+
 2011-07-14  Ingo Weinzierl <ingo@intevation.de>
 
 	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java	Thu Jul 14 11:27:01 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.java	Thu Jul 14 14:11:29 2011 +0000
@@ -9,6 +9,7 @@
 import javax.swing.JPanel;
 import javax.swing.JButton;
 import javax.swing.JComboBox;
+import javax.swing.JTextField;
 import javax.swing.DefaultComboBoxModel;
 
 import java.awt.event.ItemListener;
@@ -16,10 +17,14 @@
 import java.awt.event.ActionListener;
 import java.awt.event.ActionEvent;
 
+import java.awt.geom.Point2D;
+import java.awt.geom.Line2D;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Comparator;
 import java.util.Collections;
+import java.util.Iterator;
 
 import java.io.File;
 import java.io.IOException;
@@ -32,11 +37,14 @@
 import org.jfree.chart.ChartFactory;
 
 import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
 
 import org.jfree.chart.ChartUtilities;
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.ChartPanel;
 
+import org.jfree.chart.axis.NumberAxis;
+
 import org.jfree.data.xy.XYDataset;
 import org.jfree.data.xy.DefaultXYDataset;
 
@@ -44,11 +52,14 @@
 import de.intevation.flys.model.CrossSectionLine;
 import de.intevation.flys.model.CrossSectionPoint;
 
+import de.intevation.flys.geom.Lines;
+
 import de.intevation.flys.backend.SessionFactoryProvider;
 
 import org.hibernate.Session;
 import org.hibernate.Query;
 
+import gnu.trove.TDoubleArrayList;
 
 public class CrossSectionApp
 extends      ApplicationFrame
@@ -58,7 +69,7 @@
     public static final double EPSILON   = 1e-4;
 
     public static final double TOO_SMALL = 0.2;
-    public static final double TOO_BIG   = 4000;
+    public static final double TOO_BIG   = 500;
 
     public static final Comparator<CrossSectionPoint> COL_POS_CMP =
         new Comparator<CrossSectionPoint>() {
@@ -83,9 +94,12 @@
 
     protected JComboBox crossSectionsCB;
     protected JComboBox crossSectionLinesCB;
+    protected JTextField waterlevelTF;
 
     protected ChartPanel chartPanel;
 
+    protected Double lastWaterLevel;
+
 
     public static class CrossSectionItem {
 
@@ -175,16 +189,21 @@
         crossSectionLinesCB.addItemListener(new ItemListener() {
             public void itemStateChanged(ItemEvent ie) {
                 if (ie.getStateChange() == ItemEvent.SELECTED) {
-                    updateCrossSectionLine(((CrossSectionLineItem)ie.getItem()).line);
+                    updateChart();
                 }
             }
         });
 
-        panel.add(nav, BorderLayout.SOUTH);
 
-        chartPanel = createChartPanel();
+        waterlevelTF = new JTextField(5);
 
-        panel.add(chartPanel, BorderLayout.CENTER);
+        waterlevelTF.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent ae) {
+                waterLevelChanged();
+            }
+        });
+
+        nav.add(waterlevelTF);
 
         JButton dump = new JButton("dump");
 
@@ -196,30 +215,51 @@
 
         nav.add(dump);
 
+        panel.add(nav, BorderLayout.SOUTH);
+
+        chartPanel = createChartPanel();
+
+        panel.add(chartPanel, BorderLayout.CENTER);
+
         return panel;
     }
 
+    protected void waterLevelChanged() {
+        String value = waterlevelTF.getText();
+        try {
+            lastWaterLevel = Double.parseDouble(value);
+        }
+        catch (NumberFormatException nfe) {
+            waterlevelTF.setText(
+                lastWaterLevel != null ? lastWaterLevel.toString() : "");
+            return;
+        }
+        updateChart();
+    }
+
+    protected void updateChart() {
+
+        CrossSectionLineItem csli =
+            (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
+
+        JFreeChart chart = createChart(csli == null
+            ? new DefaultXYDataset()
+            : generateDataset(csli.line, lastWaterLevel));
+
+        chartPanel.setChart(chart);
+    }
+
     protected ChartPanel createChartPanel() {
         CrossSectionLineItem csli =
             (CrossSectionLineItem)crossSectionLinesCB.getSelectedItem();
 
-        if (csli == null) {
-            return new ChartPanel(null);
-        }
-
-        XYDataset dataset = crossSectionPoints(csli.line);
-
-        JFreeChart chart = createChart(dataset);
+        JFreeChart chart = createChart(csli == null
+            ? new DefaultXYDataset()
+            : generateDataset(csli.line, lastWaterLevel));
 
         return new ChartPanel(chart);
     }
 
-    protected void updateCrossSectionLine(CrossSectionLine line) {
-        XYDataset dataset = crossSectionPoints(line);
-        JFreeChart chart = createChart(dataset);
-        chartPanel.setChart(chart);
-    }
-
     protected void dumpData() {
 
         CrossSectionLineItem csli =
@@ -272,18 +312,27 @@
         }
     }
 
-    protected XYDataset crossSectionPoints(CrossSectionLine line) {
+    protected XYDataset generateDataset(
+        CrossSectionLine line,
+        Double           waterlevel
+    ) {
         DefaultXYDataset dataset = new DefaultXYDataset();
 
         List<CrossSectionPoint> ps = line.getPoints();
 
-        List<CrossSectionPoint> points =
-            new ArrayList<CrossSectionPoint>(ps.size());
+        if (ps.isEmpty()) {
+            return dataset;
+        }
+
+        Collections.sort(ps, COL_POS_CMP);
+
+        List<Point2D> points = new ArrayList<Point2D>(ps.size());
 
         for (CrossSectionPoint p: ps) {
-            if (isValid(p.getX().doubleValue())
-            &&  isValid(p.getY().doubleValue())) {
-                points.add(p);
+            double x = p.getX().doubleValue();
+            double y = p.getY().doubleValue();
+            if (isValid(x) && isValid(y)) {
+                points.add(new Point2D.Double(x, y));
             }
         }
 
@@ -291,21 +340,16 @@
             return dataset;
         }
 
-        Collections.sort(points, COL_POS_CMP);
-
         double [] xs = new double[points.size()];
         double [] ys = new double[xs.length];
 
-        double x = points.get(0).getX().doubleValue();
-        double y = points.get(0).getY().doubleValue();
-
-        xs[0] = x;
-        ys[0] = y;
+        xs[0] = points.get(0).getX();
+        ys[0] = points.get(0).getY();
 
         for (int i = 1; i < xs.length; ++i) {
-            CrossSectionPoint p = points.get(i);
-            x = p.getX().doubleValue();
-            y = p.getY().doubleValue();
+            Point2D p = points.get(i);
+            double x = p.getX();
+            double y = p.getY();
 
             if (x <= xs[i-1]) {
                 x = xs[i-1] + EPSILON;
@@ -314,6 +358,11 @@
             ys[i] = y;
         }
 
+        if (waterlevel != null) {
+            double [][] data = createWaterLines(points, waterlevel);
+            dataset.addSeries(String.valueOf(waterlevel), data);
+        }
+
         CrossSection cs = line.getCrossSection();
 
         String legend = (cs != null ? cs.getDescription() : "???")
@@ -324,6 +373,32 @@
         return dataset;
     }
 
+    protected static double [][] createWaterLines(
+        List<Point2D> points,
+        double        waterlevel
+    ) {
+        List<Line2D> lines = Lines.fillWater(points, waterlevel);
+
+        TDoubleArrayList lxs = new TDoubleArrayList();
+        TDoubleArrayList lys = new TDoubleArrayList();
+
+        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
+            Line2D  l  = iter.next();
+            Point2D p1 = l.getP1();
+            Point2D p2 = l.getP2();
+            lxs.add(p1.getX());
+            lys.add(p1.getY());
+            lxs.add(p2.getX());
+            lys.add(p2.getY());
+            if (iter.hasNext()) {
+                lxs.add(Double.NaN);
+                lys.add(Double.NaN);
+            }
+        }
+
+        return new double [][] { lxs.toNativeArray(), lys.toNativeArray() };
+    }
+
     protected void updateCrossSection(CrossSection crossSection) {
         Object [] cslis = createCrossSectionLineItems(crossSection);
         DefaultComboBoxModel dcbm = new DefaultComboBoxModel(cslis);
@@ -334,8 +409,8 @@
         if (cslis.length > 0) {
             CrossSectionLine line =
                 ((CrossSectionLineItem)cslis[0]).line;
-            updateCrossSectionLine(line);
         }
+        updateChart();
     }
 
     protected Object [] createCrossSectionLineItems(CrossSection cs) {
@@ -367,6 +442,11 @@
             true,
             true,
             false);
+
+        XYPlot plot = chart.getXYPlot();
+        NumberAxis yAxis = (NumberAxis)plot.getRangeAxis();
+        yAxis.setAutoRangeIncludesZero(false);
+
         ChartUtilities.applyCurrentTheme(chart);
         return chart;
     }
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java	Thu Jul 14 11:27:01 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java	Thu Jul 14 14:11:29 2011 +0000
@@ -1,16 +1,19 @@
-package de.intevation.flys.geom.Lines;
+package de.intevation.flys.geom;
 
 import java.util.List;
 import java.util.ArrayList;
-import java.util.Arrays;
 
 import java.awt.geom.Point2D;
 import java.awt.geom.Line2D;
 
 import de.intevation.flys.artifacts.math.Linear;
 
+import org.apache.log4j.Logger;
+
 public class Lines
 {
+    private static Logger log = Logger.getLogger(Lines.class);
+
     public static final double EPSILON = 1e-4;
 
     public static enum Mode { UNDEF, WET, DRY };
@@ -18,28 +21,14 @@
     protected Lines() {
     }
 
-    public static final class StableIndex
-    implements                Comparable<StableIndex>
-    {
-        protected Point2D point;
-        protected int     index;
-
-        public StableIndex(Point2D point, int index) {
-            this.point = point;
-            this.index = index;
-        }
+    public static List<Line2D> fillWater(List<Point2D> points, double waterLevel) {
 
-        public int compareTo(StableIndex other) {
-            double diff = point.getX() - other.point.getX();
-            if (diff  < -EPSILON   ) return -1;
-            if (diff  > +EPSILON   ) return +1;
-            if (index < other.index) return -1;
-            if (index > other.index) return +1;
-            return 0;
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("fillWater");
+            log.debug("----------------------------");
         }
-    } // class StableIndex
-
-    public static List<Line2D> fillWater(List<Point2D> points, double waterLevel) {
 
         List<Line2D> result = new ArrayList();
 
@@ -67,10 +56,7 @@
 
         // To ensure for sequences of equals x's that
         // the original index order is preserved.
-        StableIndex [] ps = new StableIndex[N];
-        for (int i = 0; i < N; ++i) {
-            Point2D p = points.get(i);
-            ps[i] = new StableIndex(p, i);
+        for (Point2D p: points) {
             double x = p.getX(), y = p.getY();
             if (x < minX) minX = x;
             if (x > maxX) maxX = x;
@@ -79,32 +65,43 @@
         }
 
         if (minY > waterLevel) { // profile completely over water level
+            log.debug("complete over water");
             return result;
         }
 
         if (waterLevel > maxY) { // water floods profile
+            log.debug("complete under water");
             result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel));
             return result;
         }
 
-        Arrays.sort(ps);
-
         Mode mode = Mode.UNDEF;
 
-        double  startX   = minX;
+        double startX = minX;
 
         for (int i = 1; i < N; ++i) {
-            Point2D p1 = ps[i-1].point;
-            Point2D p2 = ps[i  ].point;
+            Point2D p1 = points.get(i-1);
+            Point2D p2 = points.get(i);
 
-            if (p1.getX() < waterLevel && p2.getX() < waterLevel) {
+            if (p1.getY() < waterLevel && p2.getY() < waterLevel) {
                 // completely under water
+                if (debug) {
+                    log.debug("under water: " + p1 + " " + p2);
+                }
+                if (mode != Mode.WET) {
+                    startX = p1.getX();
+                    mode = Mode.WET;
+                }
                 continue;
             }
 
-            if (p1.getX() > waterLevel && p2.getX() > waterLevel) {
+            if (p1.getY() > waterLevel && p2.getY() > waterLevel) {
+                if (debug) {
+                    log.debug("over water: " + p1 + " " + p2);
+                }
                 // completely over water
                 if (mode == Mode.WET) {
+                    log.debug("over/wet");
                     result.add(new Line2D.Double(
                         startX, waterLevel,
                         p1.getX(), waterLevel));
@@ -117,16 +114,19 @@
                 // vertical line
                 switch (mode) {
                     case WET:
+                        log.debug("vertical/wet");
                         mode = Mode.DRY;
                         result.add(new Line2D.Double(
                             startX, waterLevel,
                             p1.getX(), waterLevel));
                         break;
                     case DRY:
+                        log.debug("vertical/dry");
                         mode = Mode.WET;
                         startX = p2.getX();
                         break;
                     default: // UNDEF
+                        log.debug("vertical/undef");
                         if (p2.getY() < waterLevel) {
                             mode = Mode.WET;
                             startX = p2.getX();
@@ -138,14 +138,79 @@
                 continue;
             }
 
+            // check if waterlevel directly hits the vertices;
+
+            boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON;
+            boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON;
+
+            if (p1W || p2W) {
+                if (debug) {
+                    log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode);
+                }
+                if (p1W && p2W) { // parallel to water -> dry
+                    log.debug("water hits both vertices");
+                    if (mode == Mode.WET) {
+                        result.add(new Line2D.Double(
+                            startX, waterLevel,
+                            p1.getX(), waterLevel));
+                    }
+                    mode = Mode.DRY;
+                }
+                else if (p1W) { // p1 == waterlevel
+                    log.debug("water hits first vertex");
+                    if (p2.getY() > waterLevel) { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p1.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                    }
+                    else { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p1.getX();
+                            mode = Mode.WET;
+                        }
+                    }
+                }
+                else { // p2 == waterlevel
+                    log.debug("water hits second vertex");
+                    if (p1.getY() > waterLevel) { // --> wet
+                        if (mode != Mode.WET) {
+                            startX = p2.getX();
+                            mode = Mode.WET;
+                        }
+                    }
+                    else { // --> dry
+                        if (mode == Mode.WET) {
+                            result.add(new Line2D.Double(
+                                startX, waterLevel,
+                                p2.getX(), waterLevel));
+                        }
+                        mode = Mode.DRY;
+                    }
+                }
+                if (debug) {
+                    log.debug("mode is now: " + mode);
+                }
+                continue;
+            }
+
             // intersection case
             double x = Linear.linear(
                 waterLevel,
-                p1.getY(), p1.getX(),
-                p2.getY(), p2.getX());
+                p1.getY(), p2.getY(),
+                p1.getX(), p2.getX());
+
+            if (debug) {
+                log.debug("intersection p1:" + p1);
+                log.debug("intersection p2:" + p2);
+                log.debug("intersection at x: " + x);
+            }
 
             switch (mode) {
                 case WET:
+                    log.debug("intersect/wet");
                     mode = Mode.DRY;
                     result.add(new Line2D.Double(
                         startX, waterLevel,
@@ -153,12 +218,15 @@
                     break;
 
                 case DRY:
+                    log.debug("intersect/dry");
                     mode   = Mode.WET;
                     startX = x;
                     break;
 
                 default: // UNDEF
+                    log.debug("intersect/undef");
                     if (p2.getY() > waterLevel) {
+                        log.debug("intersect/undef/over");
                         mode = Mode.DRY;
                         result.add(new Line2D.Double(
                             p1.getX(), waterLevel,

http://dive4elements.wald.intevation.org