# HG changeset patch # User Sascha L. Teichmann # Date 1310652689 0 # Node ID bd3683453928a0d94b5f42fb69e66fa111b14063 # Parent 9e813e9137a580de049a412e4f71e19f3b0da2a9 Debugged the water fill algorithm. flys-artifacts/trunk@2330 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 9e813e9137a5 -r bd3683453928 flys-artifacts/ChangeLog --- 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 + + * 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 * src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java, diff -r 9e813e9137a5 -r bd3683453928 flys-artifacts/src/main/java/de/intevation/flys/artifacts/charts/CrossSectionApp.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 COL_POS_CMP = new Comparator() { @@ -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 ps = line.getPoints(); - List points = - new ArrayList(ps.size()); + if (ps.isEmpty()) { + return dataset; + } + + Collections.sort(ps, COL_POS_CMP); + + List points = new ArrayList(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 points, + double waterlevel + ) { + List lines = Lines.fillWater(points, waterlevel); + + TDoubleArrayList lxs = new TDoubleArrayList(); + TDoubleArrayList lys = new TDoubleArrayList(); + + for (Iterator 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; } diff -r 9e813e9137a5 -r bd3683453928 flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java --- 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 - { - protected Point2D point; - protected int index; - - public StableIndex(Point2D point, int index) { - this.point = point; - this.index = index; - } + public static List fillWater(List 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 fillWater(List points, double waterLevel) { List 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,