# HG changeset patch # User Sascha L. Teichmann # Date 1262382761 0 # Node ID 92b7ccbf6163e16ed024426e6fd2f97ec5ae38d8 # Parent f5a0410003575635b47e440090b1dd49cd47676b Improved generation of iso lines in vertical cross section. gnv-artifacts/trunk@495 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/ChangeLog --- 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 + + * 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 * doc/conf/conf.xml: Deactivated gauss filter because diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonDataset.java --- 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 series) { + data.addAll(series); + } public Range getDomainBounds() { double lower = Double.POSITIVE_INFINITY; diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java --- 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 */ 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)); } diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonSeries.java --- 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; } diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java --- 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 getSeries(LabelGenerator labelGenerator) { ArrayList series = new ArrayList(); + 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 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 headList = new ArrayList(); + 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; diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java --- 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; diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java --- 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; diff -r f5a041000357 -r 92b7ccbf6163 gnv-artifacts/src/main/java/de/intevation/gnv/state/profile/verticalcrosssection/VerticalCrossSectionOutputState.java --- 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 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);