changeset 465:f7038820df2e

Added support to trace rasters to JTS multi polygons and multi line strings. Write them to shape files with GeoTools. gnv-artifacts/trunk@526 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 11 Jan 2010 00:29:45 +0000
parents 70df44021a9f
children 147d1e46b239
files gnv-artifacts/ChangeLog gnv-artifacts/pom.xml gnv-artifacts/src/main/java/de/intevation/gnv/math/QueriedXYDepth.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/AbstractProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/DemuxRingsHandler.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoAttributeGenerator.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/JTSMultiLineStringProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/JTSMultiPolygonProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java gnv-artifacts/src/main/java/de/intevation/gnv/statistics/AbstractStatistics.java gnv-artifacts/src/main/java/de/intevation/gnv/utils/Pair.java gnv-artifacts/src/main/java/de/intevation/gnv/utils/ShapeFileWriter.java
diffstat 14 files changed, 853 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/gnv-artifacts/ChangeLog	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/ChangeLog	Mon Jan 11 00:29:45 2010 +0000
@@ -1,3 +1,54 @@
+2010-01-11	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Introduced dependency to GeoTools 2.4.5 (maybe a bit too old)
+	  Needed to write traced polygons and line strings to shape files.
+
+	* src/main/java/de/intevation/gnv/utils/Pair.java: New
+	  simple pair tuple for type safety.
+
+	* src/main/java/de/intevation/gnv/utils/ShapeFileWriter.java:
+	  New. Writes multi polygons and multi line strings to
+	  shapefiles. Writing the multi polygons works. 
+
+	  !! Writing the multi line strings fails!
+	  !! Its not clear by now if its a geotool bug or a
+	  !! misunderstanding of the API. The polygon and
+	  !! the line string export are coded symmetrically
+	  !! but the line string export complains about invalid 
+	  !! FIDs states.
+
+	* src/main/java/de/intevation/gnv/raster/AbstractProducer.java:
+	  New. Abstract base class for multi line string/polygons producers
+	  as backends of the vectorizer.
+
+	* src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java:
+	  Inherits from AbstractProducer now.
+
+	* src/main/java/de/intevation/gnv/raster/IsoProducer.java:
+	  New. Common base class for the iso line producers, inherits
+	  from AbstractProducer.
+
+	* src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java:
+	  Moved some code to new base class IsoProducer.
+
+	* src/main/java/de/intevation/gnv/raster/DemuxRingsHandler.java:
+	  New. Little helper class to demultiplex many ring handlers on
+	  one single vectorizer.
+
+	* src/main/java/de/intevation/gnv/raster/IsoAttributeGenerator.java:
+	  The implemented AttributeGenerator interface is defined in IsoProducer
+	  now.
+
+	* src/main/java/de/intevation/gnv/raster/JTSMultiPolygonProducer.java:
+	  New. Traces regions directly to JTS multi polygons.
+
+	* src/main/java/de/intevation/gnv/raster/JTSMultiLineStringProducer.java:
+	  New. Traces iso lines directly to JTS multi line strings.
+
+	* src/main/java/de/intevation/gnv/math/QueriedXYDepth.java,
+	  src/main/java/de/intevation/gnv/statistics/AbstractStatistics.java: 
+	  Removed needless imports.
+
 2010-01-09  Ingo Weinzierl <ingo.weinzierl@intevation.de>
 
 	* src/main/java/de/intevation/gnv/artifacts/fis/FISSelectArtifact.java:
--- a/gnv-artifacts/pom.xml	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/pom.xml	Mon Jan 11 00:29:45 2010 +0000
@@ -77,12 +77,12 @@
       <groupId>org.apache.xmlgraphics</groupId>
       <artifactId>batik-dom</artifactId>
       <version>1.7</version>
-    </dependency> 
+    </dependency>
     <dependency>
       <groupId>org.apache.xmlgraphics</groupId>
       <artifactId>batik-svggen</artifactId>
       <version>1.7</version>
-    </dependency> 
+    </dependency>
     <dependency>
       <groupId>com.lowagie</groupId>
       <artifactId>itext</artifactId>
@@ -113,15 +113,20 @@
       <artifactId>oro</artifactId>
       <version>2.0.8</version>
     </dependency>
-  <dependency>
+    <dependency>
+      <groupId>org.geotools</groupId>
+      <artifactId>gt2-shapefile</artifactId>
+      <version>2.4.5</version>
+    </dependency>
+    <dependency>
       <groupId>com.vividsolutions</groupId>
       <artifactId>jts</artifactId>
       <version>1.9</version>
     </dependency>
     <dependency>
-        <groupId>trove</groupId>
-        <artifactId>trove</artifactId>
-        <version>2.1.1</version>
+      <groupId>trove</groupId>
+      <artifactId>trove</artifactId>
+      <version>2.1.1</version>
     </dependency>
   </dependencies>
   <repositories>
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/math/QueriedXYDepth.java	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/math/QueriedXYDepth.java	Mon Jan 11 00:29:45 2010 +0000
@@ -5,7 +5,6 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 
 import java.lang.ref.SoftReference;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/AbstractProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,26 @@
+package de.intevation.gnv.raster;
+
+import de.intevation.gnv.raster.Vectorizer.RingsHandler;
+
+public abstract class AbstractProducer
+implements            RingsHandler
+{
+    protected double minX;
+    protected double minY;
+    protected double maxX;
+    protected double maxY;
+
+    public AbstractProducer() {
+    }
+
+    public AbstractProducer(
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        this.minX = minX;
+        this.minY = minY;
+        this.maxX = maxX;
+        this.maxY = maxY;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/DemuxRingsHandler.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,40 @@
+package de.intevation.gnv.raster;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.intevation.gnv.raster.Vectorizer.RingsHandler;
+import de.intevation.gnv.raster.Vectorizer.Edge;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class DemuxRingsHandler
+implements   RingsHandler
+{
+    protected ArrayList<RingsHandler> handlers;
+
+    public DemuxRingsHandler() {
+        handlers = new ArrayList<RingsHandler>();
+    }
+
+    public void addHandler(RingsHandler handler) {
+        handlers.add(handler);
+    }
+
+    public void handleRings(
+        List<Edge> rings, 
+        int        value, 
+        int        width,
+        int        height
+    ) {
+        for (RingsHandler handler: handlers) {
+            handler.handleRings(rings, value, width, height);
+        }
+    }
+
+    public void clear() {
+        handlers.clear();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoAttributeGenerator.java	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoAttributeGenerator.java	Mon Jan 11 00:29:45 2010 +0000
@@ -1,7 +1,10 @@
 package de.intevation.gnv.raster;
 
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
 public class IsoAttributeGenerator
-implements   IsoPolygonSeriesProducer.AttributeGenerator
+implements   IsoProducer.AttributeGenerator
 {
     protected Palette palette;
 
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoPolygonSeriesProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -1,19 +1,13 @@
 package de.intevation.gnv.raster;
 
-import java.util.List;
-import java.util.HashSet;
-import java.util.HashMap;
 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.TObjectProcedure;
 
-import de.intevation.gnv.raster.Vectorizer.RingsHandler;
 import de.intevation.gnv.raster.Vectorizer.Edge;
 
 import de.intevation.gnv.math.IJKey;
@@ -25,114 +19,20 @@
  * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
  */
 public class IsoPolygonSeriesProducer
-implements   RingsHandler
+extends      IsoProducer
 {
     private static Logger log = Logger.getLogger(
         IsoPolygonSeriesProducer.class);
 
 	public static final Float LINE_WIDTH = Float.valueOf(0.1f);
 
-    public interface AttributeGenerator {
-
-        Object generateAttribute(int neighbor1, int neighbor2);
-
-    } // interface AttributeGenerator
-
-    protected HashMap<Edge, Integer>            open;
-    protected HashMap<IJKey, TIntObjectHashMap> commonOpen;
-    protected HashMap<IJKey, ArrayList<Edge>>   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<Edge, Integer>();
-        commonOpen = new HashMap<IJKey, TIntObjectHashMap>();
-        complete   = new HashMap<IJKey, ArrayList<Edge>>();
+        super(minX, minY, maxX, maxY);
     }
 
-    public void handleRings(
-        List<Edge> 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<PolygonSeries> getSeries() {
         return getSeries(null);
     }
@@ -152,18 +52,9 @@
             ? (maxY - minY)/(height-1)
             : 0d;
 
-        // join keys of complete and open polygons
-        HashSet<IJKey> pairs = new HashSet<IJKey>();
-        for (IJKey key: complete.keySet()) {
-            pairs.add(key);
-        }
-        for (IJKey key: commonOpen.keySet()) {
-            pairs.add(key);
-        }
-
         TDoubleArrayList vertices = new TDoubleArrayList();
 
-        for (IJKey key: pairs) {
+        for (IJKey key: joinPairs()) {
             PolygonSeries ps = new PolygonSeries();
 
             // process complete
@@ -188,19 +79,7 @@
             TIntObjectHashMap map = commonOpen.get(key);
 
             if (map != null) {
-                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) {
+                for (Edge head: headList(map)) {
 
                     head = Vectorizer.simplify(head, width);
                     Edge current = head, last = head;
@@ -234,11 +113,5 @@
 
         return series;
     }
-
-    public void clear() {
-        open.clear();
-        complete.clear();
-        commonOpen.clear();
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/IsoProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,143 @@
+package de.intevation.gnv.raster;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import gnu.trove.TIntObjectHashMap;
+import gnu.trove.TIntHashSet;
+import gnu.trove.TObjectProcedure;
+
+import de.intevation.gnv.math.IJKey;
+
+import de.intevation.gnv.raster.Vectorizer.Edge;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class IsoProducer
+extends      AbstractProducer
+{
+    public interface AttributeGenerator {
+
+        Object generateAttribute(int neighbor1, int neighbor2);
+
+    } // interface AttributeGenerator
+
+    protected HashMap<Edge, Integer>            open;
+    protected HashMap<IJKey, TIntObjectHashMap> commonOpen;
+    protected HashMap<IJKey, ArrayList<Edge>>   complete;
+
+    protected int    width;
+    protected int    height;
+
+    public IsoProducer(
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        super(minX, minY, maxX, maxY);
+
+        open       = new HashMap<Edge, Integer>();
+        commonOpen = new HashMap<IJKey, TIntObjectHashMap>();
+        complete   = new HashMap<IJKey, ArrayList<Edge>>();
+    }
+
+    public void handleRings(
+        List<Edge> 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
+
+    protected HashSet<IJKey> joinPairs() {
+        HashSet<IJKey> pairs = new HashSet<IJKey>();
+        pairs.addAll(complete  .keySet());
+        pairs.addAll(commonOpen.keySet());
+        return pairs;
+    }
+
+    protected static ArrayList<Edge> headList(TIntObjectHashMap map) {
+        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;
+            }
+        });
+        return headList;
+    }
+
+    public void clear() {
+        open.clear();
+        complete.clear();
+        commonOpen.clear();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/JTSMultiLineStringProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,140 @@
+package de.intevation.gnv.raster;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.Coordinate;
+
+import gnu.trove.TIntObjectHashMap;
+
+import de.intevation.gnv.raster.Vectorizer.Edge;
+
+import de.intevation.gnv.utils.Pair;
+
+import de.intevation.gnv.math.IJKey;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class JTSMultiLineStringProducer
+extends      IsoProducer
+{
+    private static Logger log = Logger.getLogger(
+        JTSMultiLineStringProducer.class);
+
+    protected GeometryFactory geometryFactory;
+
+    public JTSMultiLineStringProducer(
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        this(
+            JTSMultiPolygonProducer.createDefaultGeometryFactory(),
+            minX, minY,
+            maxX, maxY);
+    }
+
+    public JTSMultiLineStringProducer(
+        GeometryFactory geometryFactory,
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        super(minX, minY, maxX, maxY);
+        this.geometryFactory = geometryFactory;
+    }
+
+    public List<Pair<Object, MultiLineString>> getMultiLineStrings(
+        AttributeGenerator attributeGenerator
+    ) {
+        ArrayList<Pair<Object, MultiLineString>> multiLineStrings =
+            new ArrayList<Pair<Object, MultiLineString>>();
+
+        double b1 = minX;
+        double m1 = width != 1
+            ? (maxX - minX)/(width-1)
+            : 0d;
+
+         double b2 = minY;
+         double m2 = height != 1
+            ? (maxY - minY)/(height-1)
+            : 0d;
+
+        ArrayList<Coordinate> coordinates = new ArrayList<Coordinate>();
+
+        for (IJKey key: joinPairs()) {
+            ArrayList<LineString> lineStrings = new ArrayList<LineString>();
+
+            // process complete
+            ArrayList<Edge> completeList = complete.get(key);
+            if (completeList != null) {
+                for (Edge head: completeList) {
+                    Edge current = head;
+                    do {
+                        coordinates.add(new Coordinate(
+                            m1*(current.a % width) + b1,
+                            m2*(current.a / width) + b2));
+                    }
+                    while ((current = current.next) != head);
+                    // add head again to close shape
+                    coordinates.add(new Coordinate(
+                        m1*(head.a % width) + b1,
+                        m2*(head.a / width) + b2));
+                    lineStrings.add(
+                        geometryFactory.createLineString(
+                            coordinates.toArray(
+                                new Coordinate[coordinates.size()])));
+                    coordinates.clear();
+                }
+            }
+
+            // process open
+            TIntObjectHashMap map = commonOpen.get(key);
+
+            if (map != null) {
+                for (Edge head: headList(map)) {
+
+                    head = Vectorizer.simplify(head, width);
+                    Edge current = head, last = head;
+                    do {
+                        coordinates.add(new Coordinate(
+                            m1*(current.a % width) + b1,
+                            m2*(current.a / width) + b2));
+                        last = current;
+                    }
+                    while ((current = current.next) != null);
+                    // add b from tail
+                    coordinates.add(new Coordinate(
+                        m1*(last.b % width) + b1,
+                        m2*(last.b / width) + b2));
+                    lineStrings.add(
+                        geometryFactory.createLineString(
+                            coordinates.toArray(
+                                new Coordinate[coordinates.size()])));
+                    coordinates.clear();
+                } // for all in common open
+            } // if map defined for key
+
+			if (!lineStrings.isEmpty()) {
+                MultiLineString multiLineString =
+                    geometryFactory.createMultiLineString(
+                        lineStrings.toArray(
+                            new LineString[lineStrings.size()]));
+                lineStrings.clear();
+                Object attribute = attributeGenerator != null
+                    ? attributeGenerator.generateAttribute(key.i, key.j)
+                    : key;
+                multiLineStrings.add(new Pair<Object, MultiLineString>(
+                    attribute,
+                    multiLineString));
+			}
+        } // for all pairs
+
+        return multiLineStrings;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/JTSMultiPolygonProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,183 @@
+package de.intevation.gnv.raster;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.util.ArrayList;
+
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.PrecisionModel;
+
+import com.vividsolutions.jts.algorithm.CGAlgorithms;
+
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.gnv.raster.Vectorizer.Edge;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class JTSMultiPolygonProducer
+extends      AbstractProducer
+{
+    private static Logger log = Logger.getLogger(
+        JTSMultiPolygonProducer.class);
+
+    public static final int SRID_WGS84 = 4326;
+
+    public interface ValueConverter {
+
+        Integer convert(Integer value);
+
+    } // interface ValueConverter
+
+    protected GeometryFactory geometryFactory;
+
+    protected HashMap<Integer, ArrayList<Polygon>> valueToPolygons;
+
+    public JTSMultiPolygonProducer() {
+    }
+
+    public JTSMultiPolygonProducer(
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        this(
+            createDefaultGeometryFactory(),
+            minX, minY,
+            maxX, maxY);
+    }
+
+    public JTSMultiPolygonProducer(
+        GeometryFactory geometryFactory,
+        double minX, double minY,
+        double maxX, double maxY
+    ) {
+        super(minX, minY, maxX, maxY);
+        this.geometryFactory = geometryFactory;
+        valueToPolygons = new HashMap<Integer, ArrayList<Polygon>>();
+    }
+
+    public static GeometryFactory createDefaultGeometryFactory() {
+        return new GeometryFactory(
+            new PrecisionModel(
+                PrecisionModel.FLOATING),
+            SRID_WGS84,
+            new PackedCoordinateSequenceFactory(
+                PackedCoordinateSequenceFactory.DOUBLE,
+                2));
+    }
+
+    public void handleRings(
+        List<Edge> rings, 
+        int        value, 
+        int        width,
+        int        height
+    ) {
+        if (value == -1) {
+            return;
+        }
+
+        Integer v = Integer.valueOf(value);
+
+        ArrayList<Polygon> polygons = valueToPolygons.get(v);
+
+        if (polygons == null) {
+            valueToPolygons.put(v, polygons = new ArrayList<Polygon>());
+        }
+
+        double b1 = minX;
+        double m1 = width != 1
+           ? (maxX - minX)/(width-1)
+           : 0d;
+
+        double b2 = minY;
+        double m2 = height != 1
+           ? (maxY - minY)/(height-1)
+           : 0d;
+
+        ArrayList<Coordinate> vertices = new ArrayList<Coordinate>();
+
+        LinearRing shell = null;
+        ArrayList<LinearRing> holes = new ArrayList<LinearRing>();
+
+        for (Edge head: rings) {
+            Edge current = head;
+            do {
+                vertices.add(new Coordinate(
+                    m1*(current.a % width) + b1,
+                    m2*(current.a / width) + b2));
+            }
+            while ((current = current.next) != head);
+            vertices.add(new Coordinate(
+                m1*(head.a % width) + b1,
+                m2*(head.a / width) + b2));
+
+            Coordinate [] coordinates =
+                vertices.toArray(new Coordinate[vertices.size()]);
+
+            vertices.clear();
+
+            LinearRing ring = geometryFactory.createLinearRing(
+                coordinates);
+
+            if (CGAlgorithms.isCCW(coordinates)) {
+                shell = ring;
+            }
+            else {
+                holes.add(ring);
+            }
+        }
+
+        if (shell == null) {
+            log.error("polygon has no shell");
+            return;
+        }
+
+        Polygon polygon = geometryFactory.createPolygon(
+            shell,
+            holes.toArray(new LinearRing[holes.size()]));
+
+        polygons.add(polygon);
+    }
+
+    public Map<Integer, MultiPolygon> getMultiPolygons() {
+        return getMultiPolygons(null);
+    }
+
+    public Map<Integer, MultiPolygon> getMultiPolygons(
+        ValueConverter valueConverter
+    ) {
+        TreeMap<Integer, MultiPolygon> multiPolygons =
+            new TreeMap<Integer, MultiPolygon>();
+
+        for (Map.Entry<Integer, ArrayList<Polygon>> entry:
+            valueToPolygons.entrySet()
+        ) {
+            ArrayList<Polygon> polygons = entry.getValue();
+
+            Integer value = valueConverter != null
+                ? valueConverter.convert(entry.getKey())
+                : entry.getKey();
+
+            multiPolygons.put(
+                value,
+                geometryFactory.createMultiPolygon(
+                    polygons.toArray(new Polygon[polygons.size()])));
+        }
+
+        return multiPolygons;
+    }
+
+    public void clear() {
+        valueToPolygons.clear();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/PolygonDatasetProducer.java	Mon Jan 11 00:29:45 2010 +0000
@@ -5,7 +5,6 @@
 
 import gnu.trove.TDoubleArrayList;
 
-import de.intevation.gnv.raster.Vectorizer.RingsHandler;
 import de.intevation.gnv.raster.Vectorizer.Edge;
 
 import de.intevation.gnv.jfreechart.PolygonSeries;
@@ -16,13 +15,8 @@
  * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
  */
 public class PolygonDatasetProducer
-implements   RingsHandler
+extends      AbstractProducer
 {
-    protected double minX;
-    protected double minY;
-    protected double maxX;
-    protected double maxY;
-
     protected HashMap<Integer, PolygonSeries> polygonSeries;
 
     protected PolygonDatasetProducer() {
@@ -33,11 +27,7 @@
         double maxX, double maxY
 
     ) {
-        this.minX = minX;
-        this.minY = minY;
-        this.maxX = maxX;
-        this.maxY = maxY;
-
+        super(minX, minY, maxX, maxY);
         polygonSeries = new HashMap<Integer, PolygonSeries>();
     }
 
@@ -53,7 +43,7 @@
 
         Integer v = Integer.valueOf(value);
 
-        PolygonSeries ps = (PolygonSeries)polygonSeries.get(v);
+        PolygonSeries ps = polygonSeries.get(v);
 
         if (ps == null) {
             polygonSeries.put(v, ps = new PolygonSeries());
--- a/gnv-artifacts/src/main/java/de/intevation/gnv/statistics/AbstractStatistics.java	Sat Jan 09 17:55:45 2010 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/statistics/AbstractStatistics.java	Mon Jan 11 00:29:45 2010 +0000
@@ -1,8 +1,5 @@
 package de.intevation.gnv.statistics;
 
-import java.util.Collection;
-import java.util.ArrayList;
-
 import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;
 
 import org.apache.commons.math.stat.regression.SimpleRegression;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/Pair.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,30 @@
+package de.intevation.gnv.utils;
+
+import java.io.Serializable;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public final class Pair<A, B>
+implements         Serializable
+{
+    private A a;
+    private B b;
+
+    private Pair() {
+    }
+
+    public Pair(A a, B b) {
+        this.a = a;
+        this.b = b;
+    }
+
+    public A getA() {
+        return a;
+    }
+
+    public B getB() {
+        return b;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/ShapeFileWriter.java	Mon Jan 11 00:29:45 2010 +0000
@@ -0,0 +1,218 @@
+package de.intevation.gnv.utils;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.net.MalformedURLException;
+
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.MultiLineString;
+
+import org.geotools.data.DataUtilities;
+import org.geotools.data.Transaction;
+import org.geotools.data.DefaultTransaction;
+import org.geotools.data.FeatureWriter;
+
+import org.geotools.feature.Feature;
+import org.geotools.feature.FeatureType;
+import org.geotools.feature.SchemaException;
+import org.geotools.feature.IllegalAttributeException;
+
+import org.geotools.data.shapefile.ShapefileDataStore;
+import org.geotools.data.shapefile.ShapefileDataStoreFactory;
+
+import org.apache.log4j.Logger;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public final class ShapeFileWriter
+{
+    private static Logger log = Logger.getLogger(
+        ShapeFileWriter.class);
+
+    private ShapeFileWriter() {
+    }
+
+    public static boolean writeMultiLineStringsToFile(
+        File                                shapeFile,
+        List<Pair<Object, MultiLineString>> multiLineStrings
+    ) {
+        return writeMultiLineStringsToFile(shapeFile, multiLineStrings, null);
+    }
+
+    public static boolean writeMultiLineStringsToFile(
+        File                                shapeFile,
+        List<Pair<Object, MultiLineString>> multiLineStrings,
+        String                              typeName
+    ) {
+        HashMap params = new HashMap();
+
+        try {
+            params.put(
+                ShapefileDataStoreFactory.URLP.key,
+                shapeFile.toURI().toURL());
+        }
+        catch (MalformedURLException mue) {
+            log.error(mue.getLocalizedMessage(), mue);
+            return false;
+        }
+
+        boolean     success = false;
+        Transaction tx      = null;
+
+        try {
+            ShapefileDataStoreFactory sfdsf =
+                new ShapefileDataStoreFactory();
+
+            ShapefileDataStore sfds =
+                (ShapefileDataStore)sfdsf.createNewDataStore(params);
+
+            if (typeName == null) {
+                typeName = shapeFile.getName();
+            }
+
+             FeatureType featureType = DataUtilities.createType(
+                typeName,
+                "geom:MultiLineString,VALUE:Double");
+
+            sfds.createSchema(featureType);
+
+            tx = new DefaultTransaction();
+
+            FeatureWriter featureWriter = sfds.getFeatureWriter(
+                typeName, tx);
+
+            for (Pair<Object, MultiLineString> pair: multiLineStrings) {
+                log.debug("00000000000000000 -> " + pair.getA());
+                Feature feature = featureWriter.next();
+                feature.setAttribute("geom",  pair.getB());
+                feature.setAttribute("VALUE", asDouble(pair.getA()));
+                featureWriter.write();
+            }
+
+            tx.commit();
+            success = true;
+        }
+        catch (IllegalAttributeException iae) {
+            log.error(iae.getLocalizedMessage(), iae);
+        }
+        catch (SchemaException se) {
+            log.error(se.getLocalizedMessage(), se);
+        }
+        catch (IOException ioe) {
+            log.error(ioe.getLocalizedMessage(), ioe);
+        }
+        finally {
+            if (!success && tx != null) {
+                try { tx.rollback(); }
+                catch (IOException ioe) {}
+            }
+        }
+
+        if (tx != null) {
+            try { tx.close(); }
+            catch (IOException ioe) {}
+        }
+
+        return success;
+    }
+
+    public static boolean writeMultiPolygonsToFile(
+        File                       shapeFile,
+        Map<Integer, MultiPolygon> multiPolygons
+    ) {
+        return writeMultiPolygonsToFile(shapeFile, multiPolygons, null);
+    }
+
+    public static boolean writeMultiPolygonsToFile(
+        File                       shapeFile,
+        Map<Integer, MultiPolygon> multiPolygons,
+        String                     typeName
+    ) {
+        HashMap params = new HashMap();
+
+        try {
+            params.put(
+                ShapefileDataStoreFactory.URLP.key,
+                shapeFile.toURI().toURL());
+        }
+        catch (MalformedURLException mue) {
+            log.error(mue.getLocalizedMessage(), mue);
+            return false;
+        }
+
+        boolean     success = false;
+        Transaction tx      = null;
+
+        try {
+            ShapefileDataStoreFactory sfdsf =
+                new ShapefileDataStoreFactory();
+
+            ShapefileDataStore sfds =
+                (ShapefileDataStore)sfdsf.createNewDataStore(params);
+
+            if (typeName == null) {
+                typeName = shapeFile.getName();
+            }
+
+             FeatureType featureType = DataUtilities.createType(
+                typeName,
+                "geom:MultiPolygon,CLASS:Integer");
+
+            sfds.createSchema(featureType);
+
+            tx = new DefaultTransaction();
+
+            FeatureWriter featureWriter = sfds.getFeatureWriter(
+                typeName, tx);
+
+            for (Map.Entry<Integer, MultiPolygon> entry:
+                multiPolygons.entrySet()
+            ) {
+                Feature feature = featureWriter.next();
+                feature.setAttribute("geom",  entry.getValue());
+                feature.setAttribute("CLASS", entry.getKey());
+                featureWriter.write();
+            }
+
+            tx.commit();
+            success = true;
+        }
+        catch (IllegalAttributeException iae) {
+            log.error(iae.getLocalizedMessage(), iae);
+        }
+        catch (SchemaException se) {
+            log.error(se.getLocalizedMessage(), se);
+        }
+        catch (IOException ioe) {
+            log.error(ioe.getLocalizedMessage(), ioe);
+        }
+        finally {
+            if (!success && tx != null) {
+                try { tx.rollback(); }
+                catch (IOException ioe) {}
+            }
+        }
+
+        if (tx != null) {
+            try { tx.close(); }
+            catch (IOException ioe) {}
+        }
+
+        return success;
+    }
+
+    private static final Double asDouble(Object a) {
+        if (a instanceof Double)
+            return (Double)a;
+        if (a instanceof Number)
+            return Double.valueOf(((Number)a).doubleValue());
+        return 0d;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org