changeset 447:92b7ccbf6163

Improved generation of iso lines in vertical cross section. gnv-artifacts/trunk@495 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 01 Jan 2010 21:52:41 +0000
parents f5a041000357
children 3cb2bea50456
files gnv-artifacts/ChangeLog gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonDataset.java gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonSeries.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/verticalcrosssection/VerticalCrossSectionOutputState.java
diffstat 8 files changed, 155 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/gnv-artifacts/ChangeLog	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/ChangeLog	Fri Jan 01 21:52:41 2010 +0000
@@ -1,3 +1,37 @@
+2010-01-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/gnv/state/profile/verticalcrosssection/VerticalCrossSectionOutputState.java:
+	  Generate iso lines by dividing palette ranges in two
+	  parts each and traces them. This shows that the ideas described
+	  by K. Jancke in gnv-issues/issue108 are possible not the
+	  right way to go. Applying this strategy there would be
+	  locally to many isolines if there is a steep gradient. 
+	  On the other side large areas are splitted in too less sections
+	  by too less iso lines. A better way may be a splitting with
+	  a look at the areas and shapes of regions. Large, round areas need
+	  more splits. Long, thin areas need less splits.
+
+	* src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java:
+	  Stabilized iso line hashing a bit. Added line width to
+	  give iso lines a more appealing presentation in the chart.
+	  The according value is stored in "line.width".
+
+	* src/main/java/de/intevation/gnv/raster/Vectorizer.java: Fixed silly
+	  bug when simplifying lines. This prevented iso line
+
+	* src/main/java/de/intevation/gnv/raster/Palette.java: Fixed index issue
+	  when generating a divided palette.
+
+	* src/main/java/de/intevation/gnv/jfreechart/PolygonSeries.java:
+	  Do not crash with NPE any more when not having any series.
+
+	* src/main/java/de/intevation/gnv/jfreechart/PolygonDataset.java:
+	  Added method to add a whole collection of series. Useful to add
+	  iso lines to dataset.
+
+	* src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java:
+	  Set the "line.width" attribute when rendering lines.
+
 2010-01-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	* doc/conf/conf.xml: Deactivated gauss filter because
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonDataset.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonDataset.java	Fri Jan 01 21:52:41 2010 +0000
@@ -41,6 +41,9 @@
         data.add(series);
     }
 
+    public void addAllSeries(Collection<PolygonSeries> series) {
+        data.addAll(series);
+    }
 
     public Range getDomainBounds() {
         double lower       = Double.POSITIVE_INFINITY;
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java	Fri Jan 01 21:52:41 2010 +0000
@@ -4,17 +4,24 @@
 import java.awt.Graphics2D;
 import java.awt.Paint;
 import java.awt.Shape;
+import java.awt.BasicStroke;
+
 import java.awt.geom.GeneralPath;
 import java.awt.geom.Rectangle2D;
 import java.awt.geom.Rectangle2D.Double;
 
 import org.jfree.data.Range;
 
+import org.apache.log4j.Logger;
+
 /**
  * @author Ingo Weinzierl <ingo.weinzierl@intevation.de>
  */
 public class PolygonRenderer
 {
+	private static Logger log = Logger.getLogger(
+		PolygonRenderer.class);
+
     public static final int AREA           = 1;
     public static final int LINES          = 2;
     public static final int AREA_AND_LINES = AREA | LINES;
@@ -73,6 +80,10 @@
                 graphics.fill(constructShape(series, true));
             }
             else {
+                Number lineWidth = (Number)series.getAttribute("line.width");
+                BasicStroke stroke = new BasicStroke(
+                    lineWidth != null ? lineWidth.floatValue() : 1f);
+                graphics.setStroke(stroke);
                 graphics.setPaint(Color.black);
                 graphics.draw(constructShape(series, false));
             }
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonSeries.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonSeries.java	Fri Jan 01 21:52:41 2010 +0000
@@ -97,7 +97,7 @@
 
 
     public int getItemCount() {
-        return rings.length;
+        return rings != null ? rings.length : 0;
     }
 
 
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java	Fri Jan 01 21:52:41 2010 +0000
@@ -6,9 +6,12 @@
 import java.util.ArrayList;
 import java.util.Collection;
 
+import org.apache.log4j.Logger;
+
+import gnu.trove.TIntHashSet;
 import gnu.trove.TIntObjectHashMap;
 import gnu.trove.TDoubleArrayList;
-import gnu.trove.TIntObjectIterator;
+import gnu.trove.TObjectProcedure;
 
 import de.intevation.gnv.raster.Vectorizer.RingsHandler;
 import de.intevation.gnv.raster.Vectorizer.Edge;
@@ -24,6 +27,11 @@
 public class IsoPolygonSeriesProducer
 implements   RingsHandler
 {
+	private static Logger log = Logger.getLogger(
+		IsoPolygonSeriesProducer.class);
+
+	public static final Float LINE_WIDTH = Float.valueOf(0.1f);
+
     public interface LabelGenerator {
 
         String generateLabel(int neighbor1, int neighbor2);
@@ -132,6 +140,7 @@
     public Collection<PolygonSeries> getSeries(LabelGenerator labelGenerator) {
 
         ArrayList<PolygonSeries> series = new ArrayList<PolygonSeries>();
+
         double b1 = minX;
         double m1 = width != 1
             ? (maxX - minX)/(width-1)
@@ -155,13 +164,6 @@
 
         for (IJKey key: pairs) {
             PolygonSeries ps = new PolygonSeries();
-            series.add(ps);
-
-            if (labelGenerator != null) {
-                ps.setAttribute(
-                    "label",
-                    labelGenerator.generateLabel(key.i, key.j));
-            }
 
             // process complete
             ArrayList<Edge> completeList = complete.get(key);
@@ -173,7 +175,9 @@
                         vertices.add(m2*(current.a / width) + b2);
                     }
                     while ((current = current.next) != head);
-                    // TODO: Do we need to copy b of the tail?
+                    // add head again to close shape
+                    vertices.add(m1*(head.a % width) + b1);
+                    vertices.add(m2*(head.a / width) + b2);
                     ps.addRing(new CompactXYItems(vertices.toNativeArray()));
                     vertices.clear();
                 }
@@ -181,16 +185,22 @@
 
             // process open
             TIntObjectHashMap map = commonOpen.get(key);
+
             if (map != null) {
-                for (TIntObjectIterator it = map.iterator(); it.hasNext();) {
-                    it.advance();
-                    int  k    = it.key();
-                    Edge head = (Edge)it.value();
-                    // ignore tails
-                    if ((head.next == null && head.prev != null)
-                    ||  (head.next == null && head.prev == null && head.b == k)) {
-                        continue;
-                    }
+				final ArrayList<Edge> headList = new ArrayList<Edge>();
+				map.forEachValue(new TObjectProcedure() {
+					TIntHashSet headSet = new TIntHashSet();
+					public boolean execute(Object value) {
+						Edge head = ((Edge)value).head();
+						if (headSet.add(head.a)) {
+							headList.add(head);
+						}
+						return true;
+					}
+				});
+
+                for (Edge head: headList) {
+
                     head = Vectorizer.simplify(head, width);
                     Edge current = head, last = head;
                     do {
@@ -206,6 +216,18 @@
                     vertices.clear();
                 } // for all in common open
             } // if map defined for key
+
+			int itemCount = ps.getItemCount();
+
+			if (itemCount > 0) {
+				series.add(ps);
+				if (labelGenerator != null) {
+					ps.setAttribute(
+						"label",
+						labelGenerator.generateLabel(key.i, key.j));
+				}
+				ps.setAttribute("line.width", LINE_WIDTH);
+			}
         } // for all pairs
 
         return series;
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java	Fri Jan 01 21:52:41 2010 +0000
@@ -8,6 +8,8 @@
 
 import java.awt.Color;
 
+import org.apache.log4j.Logger;
+
 import de.intevation.gnv.raster.Raster.ValueToIndex;
 
 /**
@@ -15,7 +17,9 @@
  */
 public class Palette
 implements   ValueToIndex
-{
+{   
+	private static Logger log = Logger.getLogger(Palette.class);
+
     public static final class Entry 
     implements                Comparable
     {
@@ -176,7 +180,7 @@
                 double from  = origEntry.from;
                 double to    = origEntry.to;
                 double delta = (to - from)/N;
-                while (from < to) {
+				for (int k = 0; k < N; ++k) {
                     Entry nEntry = new Entry(origEntry);
                     nEntry.from = from;
                     nEntry.to   = from + delta;
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java	Fri Jan 01 21:52:41 2010 +0000
@@ -84,6 +84,14 @@
             return length;
         }
 
+		public Edge head() {
+			Edge current = this;
+			while (current.prev != null) {
+				current = current.prev;
+			}
+			return current;
+		}
+
         public int hashCode() {
             return (a << 16) | b;
         }
@@ -96,9 +104,7 @@
 
     protected static Edge simplify(Edge edge, int width) {
 
-        Edge e1 = edge;
-        Edge e2 = edge.next;
-        Edge e3 = e2.next;
+        Edge e1 = edge, start = edge;
 
         int length = edge.length();
 
@@ -106,35 +112,42 @@
             return e1;
         }
 
+        Edge e2 = edge.next;
+
         int count = 0;
 
         do {
             int e1x = e1.a % width;
             int e1y = e1.a / width;
-            int e2x = e2.a % width;
-            int e2y = e2.a / width;
-            int e3x = e3.a % width;
-            int e3y = e3.a / width;
+            int e2x = e1.b % width;
+            int e2y = e1.b / width;
+            int e3x = e2.b % width;
+            int e3y = e2.b / width;
 
             if ((e1x == e2x && e2x == e3x && e1x == e3x)
-            ||  (e1y == e2y && e2y == e3y && e1y == e3y)) {
-                e1.next = e3;
-                e3.prev = e1;
-                e2 = e3;
-                e3 = e2.next;
+                    ||  (e1y == e2y && e2y == e3y && e1y == e3y)) {
+                e1.b = e2.b;
+                Edge removed = e1.next;
+                e1.next = e2.next;
+                if (e1.next != null) {
+                    e1.next.prev = e1;
+                }
+                e2 = e1.next;
                 count = 0;
                 --length;
+                if (removed == start) {
+                    start = e1;
+                }
             }
             else {
                 e1 = e1.next;
                 e2 = e2.next;
-                e3 = e3.next;
                 ++count;
             }
         }
-        while (length > 1 && count < length + 2);
+        while (length > 1 && e2 != null && count < length + 2);
 
-        return e1;
+        return start;
     }
 
     protected int []            raster;
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/verticalcrosssection/VerticalCrossSectionOutputState.java	Fri Jan 01 12:08:05 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/verticalcrosssection/VerticalCrossSectionOutputState.java	Fri Jan 01 21:52:41 2010 +0000
@@ -76,9 +76,11 @@
 import de.intevation.gnv.raster.Palette;
 import de.intevation.gnv.raster.Raster;
 import de.intevation.gnv.raster.PolygonDatasetProducer;
+import de.intevation.gnv.raster.IsoPolygonSeriesProducer;
 import de.intevation.gnv.raster.Vectorizer;
 
 import de.intevation.gnv.jfreechart.PolygonDataset;
+import de.intevation.gnv.jfreechart.PolygonSeries;
 
 /**
  * @author Tim Englich         (tim.englich@intevation.de)
@@ -292,24 +294,47 @@
             log.debug("vectorize indexed raster");
         }
 
+        double maxDepth = interpolation.getMaxDepth();
+
         PolygonDatasetProducer pdsp = new PolygonDatasetProducer(
             0, 0,
-            distance, interpolation.getMaxDepth());
+            distance, maxDepth);
 
-        Vectorizer vectorizer = new Vectorizer(
-            intRaster, rasterSize.width);
+        int numRegions = new Vectorizer(intRaster, rasterSize.width)
+            .process(pdsp);
 
-        int numRegions = vectorizer.process(pdsp);
-
-        vectorizer = null; intRaster = null; // help gc
+        intRaster = null; // help gc
 
         PolygonDataset pds = pdsp.getPolygonDataset();
 
         if (debug) {
             log.debug("number of regions: " + numRegions);
-            log.debug("number of series: " + pds.getSeriesCount());
+            log.debug("number of series:  " + pds.getSeriesCount());
         }
 
+        // generate iso lines
+
+        Palette isoPalette = paletteManager.getLevel(2);
+
+        intRaster = raster.toIndexed(isoPalette);
+
+        IsoPolygonSeriesProducer ipsp = new IsoPolygonSeriesProducer(
+            0, 0,
+            distance, maxDepth);
+
+        numRegions = new Vectorizer(false, intRaster, rasterSize.width)
+            .process(ipsp);
+
+        // TODO: Use label generator
+        Collection<PolygonSeries> ps = ipsp.getSeries(/* label generator */); 
+
+        if (debug) {
+            log.debug("num of iso regions: " + numRegions);
+            log.debug("num of iso series:  " + ps.size());
+        }
+
+        pds.addAllSeries(ps);
+
         columns.setInterpolation(interpolation);
         columns.setPolygonDataset(pds);
 

http://dive4elements.wald.intevation.org