# HG changeset patch # User Sascha L. Teichmann # Date 1261891540 0 # Node ID b624879d29023f74fd62723071f2ad7bc68c1168 # Parent 6642ab6c583c289ab8389b3db32d5c51724317d8 Added vectorizer rings callback to generate iso lines. gnv-artifacts/trunk@485 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/ChangeLog --- a/gnv-artifacts/ChangeLog Sat Dec 26 15:32:08 2009 +0000 +++ b/gnv-artifacts/ChangeLog Sun Dec 27 05:25:40 2009 +0000 @@ -1,3 +1,33 @@ +2009-12-27 Sascha L. Teichmann + + * src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java: + New. Vectorizer rings callback which produces iso lines in + form of PolygonSeries. These series can be added to PolygonDatasets. + If a IsoPolygonSeriesProducer.LabelGenerator is given + each of the series has an attribute "label" which could be used + to label text on the plot. TODO: Add label rendering to plot. + + Iso line generation is a bit more sophisticated than pure + region tracing. Along a border of a region there could + be more than one type neighborhood. This is due to quantification + errors introduced by the fact that steep value gradients + are sampled to less points. The only ways out would be an + increase of the sample resolution or an other algorithm + working directly on the interpolated floating point samples. + + * src/main/java/de/intevation/gnv/raster/Vectorizer.java: Made + line simplification work with open polygons, too. + + * src/main/java/de/intevation/gnv/math/IJKey.java: Added method + to sort (i, j) in place. + + * src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java: + Do not close line shapes because iso lines are not closed shapes + in general. + + * src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java: + Added author. Some reformatting. + 2009-12-26 Sascha L. Teichmann * src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java: diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java Sat Dec 26 15:32:08 2009 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/jfreechart/PolygonRenderer.java Sun Dec 27 05:25:40 2009 +0000 @@ -64,16 +64,16 @@ if (colorIdx != null) { Paint paint = lookup.getPaint(colorIdx.intValue()); graphics.setPaint(paint != null ? paint : Color.black); - graphics.fill(constructShape(series)); + graphics.fill(constructShape(series, true)); } else { graphics.setPaint(Color.black); - graphics.draw(constructShape(series)); + graphics.draw(constructShape(series, false)); } } } - protected Shape constructShape(PolygonSeries series) { + protected Shape constructShape(PolygonSeries series, boolean close) { CompactXYItems [] rings = series.getRings(); GeneralPath path = new GeneralPath(); for (int i = 0; i < rings.length; ++i) { @@ -87,7 +87,9 @@ float y = (float)data[j++]; path.lineTo(x, y); } - path.closePath(); + if (close) { + path.closePath(); + } } return path; } diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/src/main/java/de/intevation/gnv/math/IJKey.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/math/IJKey.java Sat Dec 26 15:32:08 2009 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/math/IJKey.java Sun Dec 27 05:25:40 2009 +0000 @@ -19,6 +19,14 @@ this.j = j; } + public void sort() { + if (i > j) { + int t = i; + i = j; + j = t; + } + } + public int hashCode() { return (i << 16) | j; } diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java Sun Dec 27 05:25:40 2009 +0000 @@ -0,0 +1,215 @@ +package de.intevation.gnv.raster; + +import java.util.List; +import java.util.Map; +import java.util.HashSet; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Collection; + +import gnu.trove.TIntObjectHashMap; +import gnu.trove.TDoubleArrayList; +import gnu.trove.TIntObjectIterator; + +import de.intevation.gnv.raster.Vectorizer.RingsHandler; +import de.intevation.gnv.raster.Vectorizer.Edge; + +import de.intevation.gnv.math.IJKey; + +import de.intevation.gnv.jfreechart.PolygonSeries; +import de.intevation.gnv.jfreechart.CompactXYItems; + +/** + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + */ +public class IsoPolygonSeriesProducer +implements RingsHandler +{ + public interface LabelGenerator { + + String generateLabel(int neighbor1, int neighbor2); + + } // interface LabelGenerator + + protected HashMap open; + protected HashMap commonOpen; + protected HashMap> complete; + + protected int width; + protected int height; + + protected double minX; + protected double minY; + protected double maxX; + protected double maxY; + + public IsoPolygonSeriesProducer( + double minX, double minY, + double maxX, double maxY + ) { + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + + open = new HashMap(); + commonOpen = new HashMap(); + complete = new HashMap>(); + } + + public void handleRings( + List rings, + int value, + int width, + int height + ) { + if (value == -1) { + return; + } + this.width = width; + this.height = height; + + Integer v = Integer.valueOf(value); + + for (Edge head: rings) { + Edge current = head; + do { + Integer neighbor = open.remove(current); + + if (neighbor != null) { + IJKey pair = new IJKey(value, neighbor.intValue()); + pair.sort(); + + TIntObjectHashMap co = commonOpen.get(pair); + + if (co == null) { + commonOpen.put(pair, co = new TIntObjectHashMap()); + } + + Edge edge = new Edge(current); + + Edge otherA = (Edge)co.remove(edge.a); + if (otherA != null) { + otherA.chain(edge, edge.a); + } + + Edge otherB = (Edge)co.remove(edge.b); + if (otherB != null) { + otherB.chain(edge, edge.b); + } + + if (edge.isComplete()) { + ArrayList list = complete.get(pair); + if (list == null) { + complete.put(pair, list = new ArrayList()); + } + list.add(Vectorizer.simplify(edge, width)); + } + else { + if (otherA == null) { + co.put(edge.a, edge); + } + if (otherB == null) { + co.put(edge.b, edge); + } + } + } + else { + Edge edge = new Edge(current.b, current.a); + open.put(edge, v); + } + + current = current.next; + } + while (current != head); + } // for all rings + + } // handleRings + + public Collection getSeries() { + return getSeries(null); + } + + public Collection getSeries(LabelGenerator labelGenerator) { + + ArrayList series = new ArrayList(); + double b1 = minX; + double m1 = width != 1 + ? (maxX - minX)/(width-1) + : 0d; + + double b2 = minY; + double m2 = height != 1 + ? (maxY - minY)/(height-1) + : 0d; + + // join keys of complete and open polygons + HashSet pairs = new HashSet(); + for (IJKey key: complete.keySet()) { + pairs.add(key); + } + for (IJKey key: commonOpen.keySet()) { + pairs.add(key); + } + + TDoubleArrayList vertices = new TDoubleArrayList(); + + 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); + if (completeList != null) { + for (Edge head: completeList) { + Edge current = head; + do { + vertices.add(m1*(current.a % width) + b1); + vertices.add(m2*(current.a / width) + b2); + } + while ((current = current.next) != head); + // TODO: Do we need to copy b of the tail? + ps.addRing(new CompactXYItems(vertices.toNativeArray())); + vertices.clear(); + } + } + + // 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; + } + head = Vectorizer.simplify(head, width); + Edge current = head, last = head; + do { + vertices.add(m1*(current.a % width) + b1); + vertices.add(m2*(current.a / width) + b2); + last = current; + } + while ((current = current.next) != null); + // add b from tail + vertices.add(m1*(last.b % width) + b1); + vertices.add(m2*(last.b / width) + b2); + ps.addRing(new CompactXYItems(vertices.toNativeArray())); + vertices.clear(); + } // for all in common open + } // if map defined for key + } // for all pairs + + return series; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java Sat Dec 26 15:32:08 2009 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java Sun Dec 27 05:25:40 2009 +0000 @@ -12,12 +12,14 @@ import de.intevation.gnv.jfreechart.PolygonDataset; import de.intevation.gnv.jfreechart.CompactXYItems; +/** + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + */ public class PolygonDatasetProducer implements RingsHandler { protected double minX; protected double minY; - protected double maxX; protected double maxY; @@ -65,26 +67,23 @@ * m1 = (maxX - minX)/(width-1) */ - double b1 = minX; - double m1 = width != 1 - ? (maxX - minX)/(width-1) - : 0d; + double b1 = minX; + double m1 = width != 1 + ? (maxX - minX)/(width-1) + : 0d; - double b2 = minY; - double m2 = height != 1 - ? (maxY - minY)/(height-1) - : 0d; + double b2 = minY; + double m2 = height != 1 + ? (maxY - minY)/(height-1) + : 0d; for (Edge head: rings) { Edge current = head; do { - double x = m1*(current.a % width) + b1; - double y = m2*(current.a / width) + b2; - vertices.add(x); - vertices.add(y); - current = current.next; + vertices.add(m1*(current.a % width) + b1); + vertices.add(m2*(current.a / width) + b2); } - while (current != head); + while ((current = current.next) != head); ps.addRing(new CompactXYItems(vertices.toNativeArray())); vertices.clear(); } diff -r 6642ab6c583c -r b624879d2902 gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java Sat Dec 26 15:32:08 2009 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java Sun Dec 27 05:25:40 2009 +0000 @@ -38,6 +38,11 @@ this.b = b; } + public Edge(Edge other) { + a = other.a; + b = other.b; + } + public void chain(Edge other, int found) { if (found == a) { @@ -70,7 +75,8 @@ public int length() { int length = 0; Edge current = this; - do { ++length; } while ((current = current.next) != this); + do { ++length; } + while ((current = current.next) != null && current != this); return length; } @@ -92,6 +98,10 @@ int length = edge.length(); + if (length < 2) { + return e1; + } + int count = 0; do { @@ -118,7 +128,7 @@ ++count; } } - while (count < length + 2); + while (length > 1 && count < length + 2); return e1; } @@ -171,7 +181,6 @@ protected void emit(Edge edge) { Edge otherA = (Edge)openEdges.remove(edge.a); - if (otherA != null) { otherA.chain(edge, edge.a); }