From b31e88f14407d5321a7adb1dd62c9bbac24d0515 Mon Sep 17 00:00:00 2001
From: ?? ? <ulysseskao@ximple.com.tw>
Date: Fri, 13 Jun 2008 15:46:03 +0800
Subject: [PATCH] update for EOFM-117 createSchemaTexts and createNewSchemaTexts

---
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java                                |  187 ++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java                                 |  180 ++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java                            |  469 +++++++++++
 xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties                                                         |    2 
 .gitattributes                                                                                                         |    6 
 xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java                                        |   43 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java                                   |  339 ++++++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java                         |    2 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java   |   92 +
 xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml                                                          |    4 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java     |   16 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java  |   23 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java                             |   68 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java                              |  173 ++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java                            |  287 +++++++
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java |  109 +
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java                           |    2 
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java     |  352 ++++++++
 18 files changed, 2,197 insertions(+), 157 deletions(-)

diff --git a/.gitattributes b/.gitattributes
index 304111a..b80d943 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -122,6 +122,12 @@
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TPCLIDConverter.java svneol=native#text/plain
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWD97GeometryConverterDecorator.java svneol=native#text/plain
 xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/TWDDatumConverter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java svneol=native#text/plain
+xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java svneol=native#text/plain
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/ElementDispatcherRules.xml svneol=native#text/xml
 xdgnjobs/ximple-spatialjob/src/main/resources/com/ximple/eofms/filter/FeatureClassificationRules.xml svneol=native#text/xml
 xdgnjobs/ximple-spatialjob/src/main/resources/conf/ConvertShpFilterForLayer.xml svneol=native#text/xml
diff --git a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
index 001f83a..363de3c 100644
--- a/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
+++ b/xdgnjobs/ximple-dgnio/src/main/java/com/ximple/io/dgn7/ComplexChainElement.java
@@ -11,6 +11,8 @@
 import com.vividsolutions.jts.geom.CoordinateList;
 import com.vividsolutions.jts.geom.Geometry;
 import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.LineString;
 
 /**
  * ComplexChainElement
@@ -148,36 +150,8 @@
 
     public Geometry toGeometry(GeometryFactory factory)
     {
-        if (size() == 1)
-        {
-            Element element = (Element) get(0);
+        ArrayList<LineString> lineStrings = new ArrayList<LineString>();
 
-            if (element instanceof LineStringElement)
-            {
-                if (((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
-                {
-                    return ((LineStringElement) element).toGeometry(factory);
-                }
-
-            } else if (element instanceof LineElement)
-            {
-                if (((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1)
-                {
-                    return ((LineElement) element).toGeometry(factory);
-                }
-
-            } else
-            {
-                if (element instanceof GeometryConverter)
-                {
-                    return ((GeometryConverter) element).toGeometry(factory);
-                }
-
-                return null;
-            }
-        }
-
-        CoordinateList pts = new CoordinateList();
         for (ListIterator it = listIterator(); it.hasNext();)
         {
             Element element = (Element) it.next();
@@ -186,7 +160,7 @@
             {
                 if (((LineStringElement) element).getVerticeSize() == 0 || ((LineStringElement) element).getVerticeSize() > 1)
                 {
-                    pts.add(((LineStringElement) element).toGeometry(factory).getCoordinates(), true);
+                    lineStrings.add((LineString) ((LineStringElement) element).toGeometry(factory));
                 }
 
             } else if (element instanceof LineElement)
@@ -194,15 +168,18 @@
 
                 if (((LineElement) element).getVertices().length == 0 || ((LineElement) element).getVertices().length > 1)
                 {
-                    pts.add(((LineElement) element).toGeometry(factory).getCoordinates(), true);
+                    lineStrings.add((LineString) ((LineElement) element).toGeometry(factory));
                 }
             } else if (element instanceof ArcElement)
             {
-                pts.add(((ArcElement) element).toGeometry(factory).getCoordinates(), true);
+                lineStrings.add((LineString) ((ArcElement) element).toGeometry(factory));
             }
         }
 
-        return factory.createLineString(pts.toCoordinateArray());
+        LineString[] lines = lineStrings.toArray(new LineString[lineStrings.size()]);
+        if ((lines == null) || (lines.length == 0))
+            return null;
+        return factory.createMultiLineString(lines);
     }
 
     public double getElementSize()
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
index e7c6650..2691982 100644
--- a/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/log4j.properties
@@ -25,4 +25,4 @@
 
 # Print messages of level INFO or above for examples
 log4j.logger.org.cavaness.quartzbook=INFO
-log4j.logger.com.ximple.eofms=DEBUG
\ No newline at end of file
+log4j.logger.com.ximple.eofms=INFO
\ No newline at end of file
diff --git a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
index 1bd55fc..76ea5f6 100644
--- a/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
+++ b/xdgnjobs/ximple-jobcarrier/src/main/resources/quartz_jobs.xml
@@ -76,7 +76,7 @@
         </entry>
         <entry>
           <key>CONVERTDB</key>
-          <value>true</value>
+          <value>false</value>
         </entry>
         <entry>
           <key>CONVERTFILE</key>
@@ -108,7 +108,7 @@
         </entry>
         <entry>
           <key>COPYCONNECTIVITYMODE</key>
-          <value>true</value>
+          <value>false</value>
         </entry>
       </job-data-map>
     </job-detail>
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
index ae666a3..881cfc0 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineStringStrategy.java
@@ -60,7 +60,7 @@
     {
         if (!typeBuilders.containsKey(featureName))
         {
-            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createLineStringFeatureTypeBuilder(featureName);
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createLineFeatureTypeBuilder(featureName);
             typeBuilders.put(featureName, typeBuilder);
             fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
         }
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
index b8095bc..d4fae84 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/filter/CreateLineTextStrategy.java
@@ -63,7 +63,7 @@
     {
         if (!typeBuilders.containsKey(featureName))
         {
-            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createLineStringFeatureTypeBuilder(featureName);
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createLineFeatureTypeBuilder(featureName);
             typeBuilders.put(featureName, typeBuilder);
             fireFeatureTypeEvent(new FeatureTypeEvent(this, typeBuilder.getFeatureType()));
         }
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java
index 7e3e342..de90663 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/mysql/GeneralDgnConvertMySQLJobContext.java
@@ -417,7 +417,7 @@
         String typeName;
         if (element instanceof TextElement)
         {
-            typeName = getFeatureBaseName() + "P";
+            typeName = getFeatureBaseName() + "_P";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createPointFeatureElement(typeName));
@@ -425,7 +425,7 @@
             return featureTypes.get(typeName);
         } else if (element instanceof TextNodeElement)
         {
-            typeName = getFeatureBaseName() + "P";
+            typeName = getFeatureBaseName() + "_P";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createPointFeatureElement(typeName));
@@ -435,7 +435,7 @@
         {
             if (element instanceof ShapeElement)
             {
-                typeName = getFeatureBaseName() + "R";
+                typeName = getFeatureBaseName() + "_R";
                 if (!featureTypes.containsKey(typeName))
                 {
                     featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -443,7 +443,7 @@
                 return featureTypes.get(typeName);
             } else
             {
-                typeName = getFeatureBaseName() + "L";
+                typeName = getFeatureBaseName() + "_L";
                 if (!featureTypes.containsKey(typeName))
                 {
                     featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -452,7 +452,7 @@
             }
         } else if (element instanceof LineElement)
         {
-            typeName = getFeatureBaseName() + "L";
+            typeName = getFeatureBaseName() + "_L";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -460,7 +460,7 @@
             return featureTypes.get(typeName);
         } else if (element instanceof ComplexChainElement)
         {
-            typeName = getFeatureBaseName() + "L";
+            typeName = getFeatureBaseName() + "_ML";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -468,7 +468,7 @@
             return featureTypes.get(typeName);
         } else if (element instanceof ArcElement)
         {
-            typeName = getFeatureBaseName() + "A";
+            typeName = getFeatureBaseName() + "_A";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createArcFeatureElement(typeName));
@@ -476,7 +476,7 @@
             return featureTypes.get(typeName);
         } else if (element instanceof EllipseElement)
         {
-            typeName = getFeatureBaseName() + "R";
+            typeName = getFeatureBaseName() + "_R";
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createEllipseFeatureElement(typeName));
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java
index 983a897..93a0b8b 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractDgnToPostGISJobContext.java
@@ -4,6 +4,7 @@
 import java.math.BigDecimal;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -44,28 +45,31 @@
 import com.vividsolutions.jts.io.WKTWriter;
 
 import com.ximple.eofms.jobs.context.AbstractDgnFileJobContext;
+import com.ximple.eofms.util.postjts.JtsBinaryWriter;
 
 public abstract class AbstractDgnToPostGISJobContext extends AbstractDgnFileJobContext
 {
     private static Map<String, Class> GEOM_TYPE_MAP = new HashMap<String, Class>();
+    private static Map<String, Class> GEOM3D_TYPE_MAP = new HashMap<String, Class>();
 
     static
     {
         GEOM_TYPE_MAP.put("GEOMETRY", Geometry.class);
         GEOM_TYPE_MAP.put("POINT", Point.class);
-        GEOM_TYPE_MAP.put("POINTM", Point.class);
         GEOM_TYPE_MAP.put("LINESTRING", LineString.class);
-        GEOM_TYPE_MAP.put("LINESTRINGM", LineString.class);
         GEOM_TYPE_MAP.put("POLYGON", Polygon.class);
-        GEOM_TYPE_MAP.put("POLYGONM", Polygon.class);
         GEOM_TYPE_MAP.put("MULTIPOINT", MultiPoint.class);
-        GEOM_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
         GEOM_TYPE_MAP.put("MULTILINESTRING", MultiLineString.class);
-        GEOM_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
         GEOM_TYPE_MAP.put("MULTIPOLYGON", MultiPolygon.class);
-        GEOM_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
         GEOM_TYPE_MAP.put("GEOMETRYCOLLECTION", GeometryCollection.class);
-        GEOM_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
+
+        GEOM3D_TYPE_MAP.put("POINTM", Point.class);
+        GEOM3D_TYPE_MAP.put("LINESTRINGM", LineString.class);
+        GEOM3D_TYPE_MAP.put("POLYGONM", Polygon.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
+        GEOM3D_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
+        GEOM3D_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
     }
 
     private static Map<Class, String> CLASS_MAPPINGS = new HashMap<Class, String>();
@@ -112,12 +116,14 @@
      */
     private static final int MAX_ALLOWED_VALUE = 10485760;
 
-    protected static final int BATCHSIZE = 25;
+    // protected static final int BATCHSIZE = 2048;
+    protected static final int BATCHSIZE = 256;
 
     /**
      * Well Known Text writer (from JTS).
      */
     protected static WKTWriter geometryWriter = new WKTWriter();
+    protected static JtsBinaryWriter binaryWriter = new JtsBinaryWriter();
 
     protected PostgisDataStore targetDataStore;
     private Connection connection;
@@ -228,6 +234,46 @@
         conn.commit();
     }
 
+    protected String dropGeometryColumn(String dbSchema, String tableName, String geomField)
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SELECT DropGeometryColumn('");
+        sb.append(dbSchema);
+        sb.append("','");
+        sb.append(tableName);
+        sb.append("','");
+        sb.append(geomField);
+        sb.append("')");
+        getLogger().info("Execute-" + sb.toString());
+        return sb.toString();
+    }
+
+    private String addGeometryColumn(String dbSchema, String tableName, GeometryAttributeType geometryAttribute, int srid)
+    {
+        StringBuilder sql;
+        String typeName = getGeometrySQLTypeName(geometryAttribute.getBinding());
+        if (typeName == null)
+        {
+            getLogger().warn("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+            throw new RuntimeException("Error: " + geometryAttribute.getLocalName() + " unknown type!!!");
+        }
+
+        sql = new StringBuilder("SELECT AddGeometryColumn('");
+        sql.append(dbSchema);
+        sql.append("','");
+        sql.append(tableName);
+        sql.append("','");
+        sql.append(geometryAttribute.getLocalName());
+        sql.append("','");
+        sql.append(srid);
+        sql.append("','");
+        sql.append(typeName);
+        sql.append("', 2);");
+
+        //prints statement for later reuse
+        return sql.toString();
+    }
+
     public ArrayList<String> createSchemaTexts(FeatureType featureType) throws IOException
     {
         String tableName = featureType.getTypeName();
@@ -246,7 +292,7 @@
         {
             StringBuffer sql = new StringBuffer("CREATE TABLE ");
             sql.append(encodeSchemaTableName(tableName));
-            sql.append(" (");
+            sql.append(" ( gid serial PRIMARY KEY, ");
             sql.append(makeSqlCreate(attributeType));
             sql.append(");");
 
@@ -406,6 +452,7 @@
                         result.add(sqlStr);
                     }
 
+                    /*
                     if (!typeName.equals("GEOMETRY"))
                     {
                         sql = new StringBuffer("ALTER TABLE ");
@@ -426,7 +473,7 @@
                             result.add(sqlStr);
                         }
                     }
-
+                    */
                 } else
                 {
                     getLogger().warn("Error: " + geomAttribute.getLocalName() + " unknown type!!!");
@@ -476,6 +523,103 @@
         if (!shouldExecute)
         {
             throw new IOException("The table " + tableName + " already exists.");
+        }
+
+        return result;
+    }
+
+    public ArrayList<String> createNewSchemaTexts(FeatureType featureType) throws IOException
+    {
+        String origintableName = featureType.getTypeName();
+        String tableName = origintableName.toLowerCase();
+
+        ArrayList<String> result = new ArrayList<String>();
+
+        AttributeType[] attributeType = featureType.getAttributeTypes();
+        // String dbSchema = targetDataStore.getDatabaseSchemaName();
+
+        Connection con = getConnection();
+
+        boolean shouldDrop = tablePresent(tableName, con);
+        if (shouldDrop)
+        {
+            String sqlStr = "DROP TABLE " + encodeSchemaTableName(tableName) + ";";
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+        }
+
+        StringBuffer sql = new StringBuffer("CREATE TABLE ");
+        sql.append(encodeSchemaTableName(tableName));
+        sql.append(" ( gid serial PRIMARY KEY, ");
+        sql.append(makeNonGeomSqlCreate(attributeType));
+        sql.append(");");
+
+        String sqlStr = sql.toString();
+        getLogger().info(sqlStr);
+        result.add(sqlStr);
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            if (!(anAttributeType instanceof GeometryAttributeType))
+            {
+                continue;
+            }
+            GeometryAttributeType geomAttribute = (GeometryAttributeType) anAttributeType;
+
+            if (shouldDrop)
+            {
+            sqlStr = dropGeometryColumn("", tableName, geomAttribute.getLocalName());
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+            }
+            
+            CoordinateReferenceSystem refSys = geomAttribute.getCoordinateSystem();
+            int SRID;
+
+            if (refSys != null)
+            {
+                try
+                {
+                    Set ident = refSys.getIdentifiers();
+                    if ((ident == null || ident.isEmpty()) && refSys == DefaultGeographicCRS.WGS84)
+                    {
+                        SRID = 4326;
+                    } else
+                    {
+                        String code = ((NamedIdentifier) ident.toArray()[0]).getCode();
+                        SRID = Integer.parseInt(code);
+                    }
+                } catch (Exception e)
+                {
+                    getLogger().warn("SRID could not be determined");
+                    SRID = -1;
+                }
+            } else
+            {
+                SRID = -1;
+            }
+
+            sqlStr = addGeometryColumn("", tableName, geomAttribute, SRID);
+            getLogger().info(sqlStr);
+            result.add(sqlStr);
+
+
+            String indexName = tableName.replace('-', '_');
+            //also build a spatial index on each geometry column.
+            sql = new StringBuffer("CREATE INDEX spatial_");
+            sql.append(indexName);
+            sql.append("_");
+            sql.append(anAttributeType.getLocalName().toLowerCase());
+            sql.append(" ON ");
+            sql.append(encodeSchemaTableName(tableName));
+            sql.append(" USING GIST (");
+            sql.append(encodeSchemaColumnName(anAttributeType.getLocalName()));
+            sql.append(" gist_geometry_ops);");
+
+            sqlStr = sql.toString();
+            getLogger().info(sqlStr);
+
+            result.add(sqlStr);
         }
 
         return result;
@@ -553,6 +697,96 @@
                 {
                     typeName = "GEOMETRY";
                 } else if (typeName.equals("VARCHAR"))
+                {
+                    int length = -1;
+                    Filter f = anAttributeType.getRestriction();
+                    if (f != null && f != Filter.EXCLUDE && f != Filter.INCLUDE &&
+                            (f instanceof PropertyIsLessThan || f instanceof PropertyIsLessThanOrEqualTo))
+                    {
+                        try
+                        {
+                            BinaryComparisonOperator cf = (BinaryComparisonOperator) f;
+                            if (cf.getExpression1() instanceof LengthFunction)
+                            {
+                                length = Integer.parseInt(((Literal) cf.getExpression2()).getValue().toString());
+                            } else
+                            {
+                                if (cf.getExpression2() instanceof LengthFunction)
+                                {
+                                    length = Integer.parseInt(((Literal) cf.getExpression1()).getValue().toString());
+                                }
+                            }
+                        } catch (NumberFormatException e)
+                        {
+                            length = 256;
+                        }
+                    } else
+                    {
+                        length = 256;
+                    }
+
+                    if (length < 1)
+                    {
+                        getLogger().warn("FeatureType did not specify string length; defaulted to 256");
+                        length = 256;
+                    } else if (length > MAX_ALLOWED_VALUE)
+                    {
+                        length = MAX_ALLOWED_VALUE;
+                    }
+                    typeName = typeName + "(" + length + ")";
+                }
+
+                if (!anAttributeType.isNillable())
+                {
+                    typeName = typeName + " NOT NULL";
+                }
+
+                //TODO review!!! Is toString() always OK???
+                Object defaultValue = anAttributeType.createDefaultValue();
+
+                if (defaultValue != null)
+                {
+                    typeName = typeName + " DEFAULT '"
+                            + defaultValue.toString() + "'";
+                }
+
+                buf.append(" \"").append(anAttributeType.getLocalName()).append("\" ").append(typeName).append(",");
+
+            } else
+            {
+                String msg;
+                if (anAttributeType == null)
+                {
+                    msg = "AttributeType was null!";
+                } else
+                {
+                    msg = "Type '" + anAttributeType.getBinding() + "' not supported!";
+                }
+                throw (new IOException(msg));
+            }
+        }
+
+        return buf.deleteCharAt(buf.length() - 1);
+    }
+
+    private StringBuffer makeNonGeomSqlCreate(AttributeType[] attributeType)
+            throws IOException
+    {
+        StringBuffer buf = new StringBuffer("");
+
+        for (AttributeType anAttributeType : attributeType)
+        {
+            String typeName;
+            typeName = CLASS_MAPPINGS.get(anAttributeType.getBinding());
+            if (typeName == null)
+            {
+                typeName = GEOM_CLASS_MAPPINGS.get(anAttributeType.getBinding());
+                if (typeName != null) continue;
+            }
+
+            if (typeName != null)
+            {
+                if (typeName.equals("VARCHAR"))
                 {
                     int length = -1;
                     Filter f = anAttributeType.getRestriction();
@@ -729,6 +963,38 @@
         return (statementSQL.toString());
     }
 
+    protected String makePrepareInsertSql(FeatureType featureType)
+    {
+        String tableName = encodeSchemaTableName(featureType.getTypeName());
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+
+        String attrValue;
+
+        StringBuffer statementSQL = new StringBuffer("INSERT INTO " + tableName + " (");
+
+        // encode insertion for attributes, but remember to avoid auto-increment ones,
+        // they may be included in the feature type as well
+        for (AttributeType attributeType : attributeTypes)
+        {
+            String attName = attributeType.getLocalName();
+
+            String colName = encodeSchemaColumnName(attName);
+            statementSQL.append(colName).append(",");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+        statementSQL.append(" VALUES (");
+
+        for (AttributeType attributeType : attributeTypes)
+        {
+            statementSQL.append(" ? ,");
+        }
+
+        statementSQL.setCharAt(statementSQL.length() - 1, ')');
+
+        return (statementSQL.toString());
+    }
+
     protected String addQuotes(Object value)
     {
         String retString;
@@ -765,12 +1031,72 @@
         return encodeName(colName);
     }
 
-    public String encodeSchemaTableName(String tableName) {
+    public String encodeSchemaTableName(String tableName)
+    {
         return schemaEnabled ? ("\"" + targetDataStore.getDatabaseSchemaName() + "\".\"" + tableName + "\"")
-                             : ("\"" + tableName + "\"");
+                : ("\"" + tableName + "\"");
     }
 
-    public String encodeSchemaColumnName(String columnName) {
+    public String encodeSchemaColumnName(String columnName)
+    {
         return "\"" + columnName + "\"";
     }
+
+    protected void bindFeatureParameters(PreparedStatement pstmt, Feature feature) throws SQLException
+    {
+        FeatureType featureType = feature.getFeatureType();
+
+        AttributeType[] attributeTypes = featureType.getAttributeTypes();
+        Object[] attributes = feature.getAttributes(null);
+
+        for (int i = 0; i < attributeTypes.length; i++)
+        {
+            if (attributeTypes[i] instanceof GeometryAttributeType)
+            {
+                pstmt.setBytes(i + 1, binaryWriter.writeBinary((Geometry) attributes[i]));
+            } else
+            {
+                if (attributeTypes[i].getBinding().equals(Short.class))
+                {
+                    pstmt.setShort(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Integer.class))
+                {
+                    pstmt.setInt(i + 1, (Short) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Long.class))
+                {
+                    pstmt.setLong(i + 1, (Long) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(String.class))
+                {
+                    pstmt.setString(i + 1, (String) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Float.class))
+                {
+                    pstmt.setFloat(i + 1, (Float) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Double.class))
+                {
+                    pstmt.setDouble(i + 1, (Double) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(Boolean.class))
+                {
+                    pstmt.setBoolean(i + 1, (Boolean) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(BigDecimal.class))
+                {
+                    pstmt.setBigDecimal(i + 1, (BigDecimal) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Date.class))
+                {
+                    pstmt.setDate(i + 1, (java.sql.Date) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Time.class))
+                {
+                    pstmt.setTime(i + 1, (java.sql.Time) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.sql.Timestamp.class))
+                {
+                    pstmt.setTimestamp(i + 1, (java.sql.Timestamp) attributes[i]);
+                } else if (attributeTypes[i].getBinding().equals(java.util.Date.class))
+                {
+                    java.sql.Date sDate = new java.sql.Date(((java.util.Date) attributes[i]).getTime());
+                    pstmt.setDate(i + 1, sDate);
+                }
+            }
+
+        }
+    }
+
 }
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java
index 774e7cd..9f8d017 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/AbstractOracleToPostGISJobContext.java
@@ -44,28 +44,31 @@
 import com.vividsolutions.jts.io.WKBWriter;
 
 import com.ximple.eofms.jobs.context.AbstractOracleJobContext;
+import com.ximple.eofms.util.postjts.JtsBinaryWriter;
 
 public abstract class AbstractOracleToPostGISJobContext extends AbstractOracleJobContext
 {
     private static Map<String, Class> GEOM_TYPE_MAP = new HashMap<String, Class>();
+    private static Map<String, Class> GEOM3D_TYPE_MAP = new HashMap<String, Class>();
 
     static
     {
         GEOM_TYPE_MAP.put("GEOMETRY", Geometry.class);
         GEOM_TYPE_MAP.put("POINT", Point.class);
-        GEOM_TYPE_MAP.put("POINTM", Point.class);
         GEOM_TYPE_MAP.put("LINESTRING", LineString.class);
-        GEOM_TYPE_MAP.put("LINESTRINGM", LineString.class);
         GEOM_TYPE_MAP.put("POLYGON", Polygon.class);
-        GEOM_TYPE_MAP.put("POLYGONM", Polygon.class);
         GEOM_TYPE_MAP.put("MULTIPOINT", MultiPoint.class);
-        GEOM_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
         GEOM_TYPE_MAP.put("MULTILINESTRING", MultiLineString.class);
-        GEOM_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
         GEOM_TYPE_MAP.put("MULTIPOLYGON", MultiPolygon.class);
-        GEOM_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
         GEOM_TYPE_MAP.put("GEOMETRYCOLLECTION", GeometryCollection.class);
-        GEOM_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
+
+        GEOM3D_TYPE_MAP.put("POINTM", Point.class);
+        GEOM3D_TYPE_MAP.put("LINESTRINGM", LineString.class);
+        GEOM3D_TYPE_MAP.put("POLYGONM", Polygon.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
+        GEOM3D_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
+        GEOM3D_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
+        GEOM3D_TYPE_MAP.put("GEOMETRYCOLLECTIONM", GeometryCollection.class);
     }
 
     private static Map<Class, String> CLASS_MAPPINGS = new HashMap<Class, String>();
@@ -112,15 +115,15 @@
      */
     private static final int MAX_ALLOWED_VALUE = 10485760;
 
-    protected static final int BATCHSIZE = 25;
+    protected static final int BATCHSIZE = 256;
 
     /**
      * Well Known Text writer (from JTS).
      */
     protected static WKTWriter geometryWriter = new WKTWriter();
+    protected static JtsBinaryWriter binaryWriter = new JtsBinaryWriter();
 
     protected PostgisDataStore targetDataStore;
-
     private Connection connection;
     protected boolean schemaEnabled = true;
 
@@ -252,7 +255,7 @@
         {
             StringBuffer sql = new StringBuffer("CREATE TABLE ");
             sql.append(encodeSchemaTableName(tableName));
-            sql.append(" (");
+            sql.append(" ( gid serial PRIMARY KEY, ");
             sql.append(makeSqlCreate(attributeType));
             sql.append(");");
 
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java
index 12fca64..da212f0 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/GeneralDgnConvertPostGISJobContext.java
@@ -3,7 +3,6 @@
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.net.MalformedURLException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -12,26 +11,19 @@
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.PreparedStatement;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.commons.transaction.memory.PessimisticMapWrapper;
 import org.apache.commons.transaction.util.CommonsLoggingLogger;
 import org.apache.commons.transaction.util.LoggerFacade;
 import org.geotools.data.DataStore;
-import org.geotools.data.FeatureWriter;
 import org.geotools.data.Transaction;
-import org.geotools.data.DefaultTransaction;
-import org.geotools.data.DataSourceException;
-import org.geotools.data.FeatureStore;
 import org.geotools.feature.Feature;
 import org.geotools.feature.FeatureType;
 import org.geotools.feature.FeatureTypeBuilder;
 import org.geotools.feature.IllegalAttributeException;
 import org.geotools.feature.SchemaException;
-import org.geotools.feature.SimpleFeature;
-import org.geotools.feature.FeatureCollection;
-import org.geotools.feature.FeatureCollections;
 import org.postgresql.util.PSQLException;
 
 import com.vividsolutions.jts.geom.Geometry;
@@ -58,7 +50,7 @@
     static final LoggerFacade sLogger = new CommonsLoggingLogger(logger);
     static final GeometryFactory geometryFactory = new GeometryFactory();
 
-    private HashMap<FeatureType, ArrayList<String>> txFeaturesContext = new HashMap<FeatureType, ArrayList<String>>();
+    private HashMap<FeatureType, ArrayList<Feature>> txFeaturesContext = new HashMap<FeatureType, ArrayList<Feature>>();
 
     private TreeMap<String, FeatureTypeBuilder> typeBuilders = new TreeMap<String, FeatureTypeBuilder>();
     private TreeMap<String, FeatureType> featureTypes = new TreeMap<String, FeatureType>();
@@ -66,6 +58,8 @@
     private TWD97GeometryConverterDecorator convertDecorator = null;
     private String featureBaseName = null;
     private boolean dropTableMode = true;
+
+    private int accumulate = 0;
 
     public GeneralDgnConvertPostGISJobContext(String dataPath, DataStore targetDataStore)
     {
@@ -102,18 +96,26 @@
 
             if (!txFeaturesContext.containsKey(feature.getFeatureType()))
             {
-                txFeaturesContext.put(feature.getFeatureType(), new ArrayList<String>());
+                txFeaturesContext.put(feature.getFeatureType(), new ArrayList<Feature>());
             }
-            ArrayList<String> arrayList = txFeaturesContext.get(feature.getFeatureType());
+            ArrayList<Feature> arrayList = txFeaturesContext.get(feature.getFeatureType());
             if (feature.getDefaultGeometry() != null && !feature.getDefaultGeometry().isEmpty())
-                arrayList.add(makeInsertSql(feature, -1));
+            {
+                arrayList.add(feature);
+                accumulate++;
+            }
         } else
         {
             logger.info("Unknown Element :" + element.getType() + ", lv=" + element.getLevelIndex());
         }
+
+        if (accumulate > BATCHSIZE)
+        {
+            commitTransaction();
+        }
     }
 
-    private Transaction transaction;
+    // private Transaction transaction;
 
     public void startTransaction()
     {
@@ -154,18 +156,22 @@
                 FeatureType featureType = it.next();
                 logger.debug("Begin Save into PostGIS:" + featureType.getTypeName());
 
-                ArrayList<String> stmtTexts = txFeaturesContext.get(featureType);
+                String bindingStmt = makePrepareInsertSql(featureType, -1);
+                ArrayList<Feature> features = txFeaturesContext.get(featureType);
                 Connection conn = getConnection();
                 boolean autoCommit = conn.getAutoCommit();
                 conn.setAutoCommit(true);
+                PreparedStatement pstmt = conn.prepareStatement(bindingStmt);
 
-                for (String stmtText : stmtTexts)
+                for (Feature feature : features)
                 {
-                    currentStmt = stmtText;
-                    Statement stmt = conn.createStatement();
+                    // currentStmt = feature;
+                    // Statement stmt = conn.createStatement();
                     try
                     {
-                        stmt.execute(stmtText);
+                        // stmt.execute(feature);
+                        bindFeatureParameters(pstmt, feature);
+                        pstmt.execute();
                     } catch (PSQLException e)
                     {
                         if (currentStmt != null)
@@ -174,24 +180,29 @@
                         }
                         logger.error(e.getServerErrorMessage());
                         logger.error(e.getMessage(), e);
+                        /*
                     } finally {
                         stmt.close();
+                        */
                     }
+
                     /*
                     if ((i % BATCHSIZE) != 0)
                     {
-                        stmt.addBatch(stmtText);
+                        stmt.addBatch(feature);
                     } else {
-                        stmt.addBatch(stmtText);
+                        stmt.addBatch(feature);
                         stmt.executeBatch();
                     }
                     i++;
                     */
                 }
-                stmtTexts.clear();
+                pstmt.close();
+                features.clear();
                 conn.setAutoCommit(autoCommit);
                 logger.debug("End Save into PostGIS:" + featureType.getTypeName());
             }
+            accumulate = 0;
         } catch (SQLException e)
         {
             logger.error(e.getMessage(), e);
@@ -221,11 +232,33 @@
         return typeBuilders.get(featureName).getFeatureType();
     }
 
+    public FeatureType createPolygonFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalPolygonFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
     public FeatureType createLineFeatureElement(String featureName) throws SchemaException
     {
         if (!typeBuilders.containsKey(featureName))
         {
             FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalLineFeatureTypeBuilder(featureName);
+            typeBuilders.put(featureName, typeBuilder);
+            clearFeatureData(typeBuilder);
+        }
+        return typeBuilders.get(featureName).getFeatureType();
+    }
+
+    public FeatureType createMultiLineFeatureElement(String featureName) throws SchemaException
+    {
+        if (!typeBuilders.containsKey(featureName))
+        {
+            FeatureTypeBuilder typeBuilder = FeatureTypeBuilderUtil.createNormalMultiLineFeatureTypeBuilder(featureName);
             typeBuilders.put(featureName, typeBuilder);
             clearFeatureData(typeBuilder);
         }
@@ -431,7 +464,8 @@
         String typeName;
         if (element instanceof TextElement)
         {
-            typeName = getFeatureBaseName() + "P";
+            typeName = getFeatureBaseName() + "_P";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createPointFeatureElement(typeName));
@@ -439,7 +473,8 @@
             return featureTypes.get(typeName);
         } else if (element instanceof TextNodeElement)
         {
-            typeName = getFeatureBaseName() + "P";
+            typeName = getFeatureBaseName() + "_P";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createPointFeatureElement(typeName));
@@ -449,15 +484,17 @@
         {
             if (element instanceof ShapeElement)
             {
-                typeName = getFeatureBaseName() + "R";
+                typeName = getFeatureBaseName() + "_R";
+                typeName = typeName.toLowerCase();
                 if (!featureTypes.containsKey(typeName))
                 {
-                    featureTypes.put(typeName, createLineFeatureElement(typeName));
+                    featureTypes.put(typeName, createPolygonFeatureElement(typeName));
                 }
                 return featureTypes.get(typeName);
             } else
             {
-                typeName = getFeatureBaseName() + "L";
+                typeName = getFeatureBaseName() + "_L";
+                typeName = typeName.toLowerCase();
                 if (!featureTypes.containsKey(typeName))
                 {
                     featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -466,7 +503,8 @@
             }
         } else if (element instanceof LineElement)
         {
-            typeName = getFeatureBaseName() + "L";
+            typeName = getFeatureBaseName() + "_L";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createLineFeatureElement(typeName));
@@ -474,15 +512,17 @@
             return featureTypes.get(typeName);
         } else if (element instanceof ComplexChainElement)
         {
-            typeName = getFeatureBaseName() + "L";
+            typeName = getFeatureBaseName() + "_ML";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
-                featureTypes.put(typeName, createLineFeatureElement(typeName));
+                featureTypes.put(typeName, createMultiLineFeatureElement(typeName));
             }
             return featureTypes.get(typeName);
         } else if (element instanceof ArcElement)
         {
-            typeName = getFeatureBaseName() + "A";
+            typeName = getFeatureBaseName() + "_A";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createArcFeatureElement(typeName));
@@ -490,7 +530,8 @@
             return featureTypes.get(typeName);
         } else if (element instanceof EllipseElement)
         {
-            typeName = getFeatureBaseName() + "R";
+            typeName = getFeatureBaseName() + "_R";
+            typeName = typeName.toLowerCase();
             if (!featureTypes.containsKey(typeName))
             {
                 featureTypes.put(typeName, createEllipseFeatureElement(typeName));
@@ -546,7 +587,7 @@
                             typeBuilder.getFeatureType().getDefaultGeometry().getLocalName());
                     dropTable(conn, featureName);
 
-                    ArrayList<String> schemaTexts = createSchemaTexts(typeBuilder.getFeatureType());
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilder.getFeatureType());
                     for (String stmtText : schemaTexts)
                     {
                         Statement stmt = conn.createStatement();
@@ -569,7 +610,7 @@
             try
             {
                 Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
-                ArrayList<String> schemaTexts = createSchemaTexts(typeBuilder.getFeatureType());
+                ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilder.getFeatureType());
                 for (String stmtText : schemaTexts)
                 {
                     Statement stmt = conn.createStatement();
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java
index 0280990..123221a 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/jobs/context/postgis/IndexDgnConvertPostGISJobContext.java
@@ -6,6 +6,7 @@
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.PreparedStatement;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -46,7 +47,7 @@
     static final GeometryFactory geometryFactory = new GeometryFactory();
     TWD97GeometryConverterDecorator convertDecorator = new TWD97GeometryConverterDecorator();
 
-    private HashMap<FeatureType, ArrayList<String>> txFeaturesContext = new HashMap<FeatureType, ArrayList<String>>();
+    private HashMap<FeatureType, ArrayList<Feature>> txFeaturesContext = new HashMap<FeatureType, ArrayList<Feature>>();
 
     private FeatureTypeBuilder typeBuilderPnt = null;
     private FeatureTypeBuilder typeBuilderRect = null;
@@ -54,6 +55,7 @@
     private FeatureType featureType2 = null;
 
     private boolean dropTableMode = true;
+    private int accumulate = 0;
 
     public IndexDgnConvertPostGISJobContext(String dataPath, DataStore targetDataStore)
     {
@@ -77,10 +79,10 @@
 
         if (!txFeaturesContext.containsKey(feature.getFeatureType()))
         {
-            txFeaturesContext.put(feature.getFeatureType(), new ArrayList<String>());
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList<Feature>());
         }
-        ArrayList<String> arrayList = txFeaturesContext.get(feature.getFeatureType());
-        arrayList.add(makeInsertSql(feature, -1));
+        ArrayList<Feature> arrayList = txFeaturesContext.get(feature.getFeatureType());
+        arrayList.add(feature);
 
         feature = createFeature2((TextElement) element);
         if (feature == null)
@@ -92,10 +94,16 @@
 
         if (!txFeaturesContext.containsKey(feature.getFeatureType()))
         {
-            txFeaturesContext.put(feature.getFeatureType(), new ArrayList<String>());
+            txFeaturesContext.put(feature.getFeatureType(), new ArrayList<Feature>());
         }
         arrayList = txFeaturesContext.get(feature.getFeatureType());
-        arrayList.add(makeInsertSql(feature, -1));
+        arrayList.add(feature);
+        accumulate++;
+
+        if (accumulate > BATCHSIZE)
+        {
+            commitTransaction();
+        }
     }
 
     public void startTransaction()
@@ -136,20 +144,22 @@
                 FeatureType featureType = it.next();
                 logger.debug("Begin Save PostGIS:" + featureType.getTypeName());
 
-                ArrayList<String> stmtTexts = txFeaturesContext.get(featureType);
+                String bindingStmt = makePrepareInsertSql(featureType, -1);
+                ArrayList<Feature> features = txFeaturesContext.get(featureType);
                 Connection conn = getConnection();
                 boolean autoCommit = conn.getAutoCommit();
                 conn.setAutoCommit(true);
-                // conn.setAutoCommit(false);
-                // Statement stmt = conn.createStatement();
-                // int i = 0;
-                for (String stmtText : stmtTexts)
+                PreparedStatement pstmt = conn.prepareStatement(bindingStmt);
+
+                for (Feature feature : features)
                 {
-                    currentStmt = stmtText;
-                    Statement stmt = conn.createStatement();
+                    // currentStmt = feature;
+                    // Statement stmt = conn.createStatement();
                     try
                     {
-                        stmt.execute(stmtText);
+                        // stmt.execute(feature);
+                        bindFeatureParameters(pstmt, feature);
+                        pstmt.execute();
                     } catch (PSQLException e)
                     {
                         if (currentStmt != null)
@@ -158,19 +168,11 @@
                         }
                         logger.error(e.getServerErrorMessage());
                         logger.error(e.getMessage(), e);
+                        /*
                     } finally {
                         stmt.close();
+                        */
                     }
-                    /*
-                    if ((i % BATCHSIZE) != 0)
-                    {
-                        stmt.addBatch(stmtText);
-                    } else {
-                        stmt.addBatch(stmtText);
-                        stmt.executeBatch();
-                    }
-                    i++;
-                    */
                 }
                 /*
                 if ((i % BATCHSIZE) != 0)
@@ -178,11 +180,15 @@
                     stmt.executeBatch();
                 }
                 stmt.close();
-                stmtTexts.clear();
                 */
+
+                pstmt.close();
+                features.clear();
+
                 conn.setAutoCommit(autoCommit);
                 logger.debug("End Save PostGIS:" + featureType.getTypeName());
             }
+            accumulate = 0;
         } catch (PSQLException e)
         {
             if (currentStmt != null)
@@ -221,10 +227,22 @@
                     Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
                     if (dropTableMode)
                     {
-                        dropGeometryColumn(conn, featureName,
-                                typeBuilderRect.getFeatureType().getDefaultGeometry().getLocalName());
-                        dropTable(conn, featureName);
-                        ArrayList<String> schemaTexts = createSchemaTexts(typeBuilderRect.getFeatureType());
+                        try
+                        {
+                            dropGeometryColumn(conn, featureName,
+                                    typeBuilderRect.getFeatureType().getDefaultGeometry().getLocalName());
+                        } catch (PSQLException e)
+                        {
+                            logger.debug(e.getMessage(), e);
+                        }
+                        try
+                        {
+                            dropTable(conn, featureName);
+                        } catch (PSQLException e)
+                        {
+                            logger.debug(e.getMessage(), e);
+                        }
+                        ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderRect.getFeatureType());
                         for (String stmtText : schemaTexts)
                         {
                             Statement stmt = conn.createStatement();
@@ -248,7 +266,7 @@
                 try
                 {
                     Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
-                    ArrayList<String> schemaTexts = createSchemaTexts(typeBuilderRect.getFeatureType());
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderRect.getFeatureType());
                     for (String stmtText : schemaTexts)
                     {
                         Statement stmt = conn.createStatement();
@@ -284,7 +302,7 @@
                                 typeBuilderPnt.getFeatureType().getDefaultGeometry().getLocalName());
                         dropTable(conn, featureName);
 
-                        ArrayList<String> schemaTexts = createSchemaTexts(typeBuilderPnt.getFeatureType());
+                        ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderPnt.getFeatureType());
                         for (String stmtText : schemaTexts)
                         {
                             Statement stmt = conn.createStatement();
@@ -308,7 +326,7 @@
                 try
                 {
                     Connection conn = targetDataStore.getConnection(Transaction.AUTO_COMMIT);
-                    ArrayList<String> schemaTexts = createSchemaTexts(typeBuilderPnt.getFeatureType());
+                    ArrayList<String> schemaTexts = createNewSchemaTexts(typeBuilderPnt.getFeatureType());
                     for (String stmtText : schemaTexts)
                     {
                         Statement stmt = conn.createStatement();
@@ -337,14 +355,14 @@
             String tpclid = textElement.getText();
 
             Envelope extent = TPCLIDConverter.convertTpclIdToEnvelope(tpclid);
-            Geometry geom = geometryFactory.createLinearRing(new Coordinate[]
+            Geometry geom = geometryFactory.createPolygon(geometryFactory.createLinearRing(new Coordinate[]
                     {
                             TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMinY())),
                             TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMaxX(), extent.getMinY())),
                             TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMaxX(), extent.getMaxY())),
                             TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMaxY())),
                             TWDDatumConverter.fromTM2ToTWD97(new Coordinate(extent.getMinX(), extent.getMinY())),
-                    });
+                    }), null);
 
             return featureType.create(new Object[]{
                     geom,
@@ -396,7 +414,7 @@
             {
                 dgnname = dgnname.substring(0, i);
             }
-            featureType = createFeatureElement(dgnname);
+            featureType = createFeatureElement(dgnname.toLowerCase());
         }
         return createFeature(featureType, element);
     }
@@ -411,8 +429,8 @@
             {
                 dgnname = dgnname.substring(0, i);
             }
-            dgnname = dgnname + "P";
-            featureType2 = createFeatureElement2(dgnname);
+            dgnname = dgnname + "_p";
+            featureType2 = createFeatureElement2(dgnname.toLowerCase());
         }
         return createFeature2(featureType2, element);
     }
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java
index 0abd936..4fb6712 100644
--- a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/FeatureTypeBuilderUtil.java
@@ -3,9 +3,11 @@
 import org.geotools.feature.AttributeTypeFactory;
 import org.geotools.feature.FeatureTypeBuilder;
 
-import com.vividsolutions.jts.geom.Geometry;
 import com.vividsolutions.jts.geom.GeometryFactory;
-import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiLineString;
 
 public final class FeatureTypeBuilderUtil
 {
@@ -14,7 +16,7 @@
     public static FeatureTypeBuilder createNormalPointFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, true));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("font", Short.class, false, 1, (short) 0));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("just", Short.class, false, 1, (short) 0));
@@ -28,7 +30,27 @@
     public static FeatureTypeBuilder createNormalLineFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", LineString.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalMultiLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", MultiLineString.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createNormalPolygonFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, true));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
@@ -50,7 +72,7 @@
 
     public static FeatureTypeBuilder createNormalEllipseFeatureTypeBuilder(String featureName)
     {
-        return createNormalLineFeatureTypeBuilder(featureName);
+        return createNormalPolygonFeatureTypeBuilder(featureName);
         /*
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
@@ -64,7 +86,7 @@
     public static FeatureTypeBuilder createNormalIndexFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, true));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("x1", Double.class, false, 1, 0.0));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("y1", Double.class, false, 1, 0.0));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("x2", Double.class, false, 1, 0.0));
@@ -80,7 +102,7 @@
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
         typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, true));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, true));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12, ""));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false, 1, (short) 0));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false, 1, (short) 0));
@@ -95,7 +117,7 @@
     public static FeatureTypeBuilder createPointFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
@@ -115,7 +137,7 @@
     public static FeatureTypeBuilder createSymbolFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Point.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
@@ -134,10 +156,8 @@
 
     public static FeatureTypeBuilder createPolygonFeatureTypeBuilder(String featureName)
     {
-        return createLineStringFeatureTypeBuilder(featureName);
-        /*
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Polygon.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
@@ -147,13 +167,27 @@
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
         return typeBuilder;
-        */
     }
 
-    public static FeatureTypeBuilder createLineStringFeatureTypeBuilder(String featureName)
+    public static FeatureTypeBuilder createLineFeatureTypeBuilder(String featureName)
     {
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
-        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", LineString.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("lid", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("level", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symcolor", String.class, false, 12));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symweight", Short.class, false));
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("symstyle", Short.class, false));
+        return typeBuilder;
+    }
+
+    public static FeatureTypeBuilder createMultiLineFeatureTypeBuilder(String featureName)
+    {
+        FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
+        typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", MultiLineString.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("tid", Short.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("oid", Long.class, false));
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("cid", Short.class, false));
@@ -167,7 +201,7 @@
 
     public static FeatureTypeBuilder createEllipseFeatureTypeBuilder(String featureName)
     {
-        return createLineStringFeatureTypeBuilder(featureName);
+        return createPolygonFeatureTypeBuilder(featureName);
         /*
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
@@ -185,7 +219,7 @@
 
     public static FeatureTypeBuilder createArcFeatureTypeBuilder(String featureName)
     {
-        return createLineStringFeatureTypeBuilder(featureName);
+        return createLineFeatureTypeBuilder(featureName);
         /*
         FeatureTypeBuilder typeBuilder = FeatureTypeBuilder.newInstance(featureName);
         typeBuilder.addType(AttributeTypeFactory.newAttributeType("geom", Geometry.class, false));
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java
new file mode 100644
index 0000000..ebbcd74
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JTSShape.java
@@ -0,0 +1,339 @@
+package com.ximple.eofms.util.postjts;
+
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Envelope;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence;
+
+public class JTSShape implements Shape
+{
+    static GeometryFactory fac = new GeometryFactory();
+
+    Geometry geom;
+
+    final static LinearRing[] NOSHELLS = {};
+
+    public JTSShape(Geometry _geom)
+    {
+        this.geom = _geom;
+    }
+
+    public JTSShape(JtsGeometry _geom)
+    {
+        this(_geom.getGeometry());
+    }
+
+    public boolean contains(Point2D p)
+    {
+        return contains(p.getX(), p.getY());
+    }
+
+    public boolean contains(double x, double y)
+    {
+        Coordinate c = new Coordinate(x, y);
+        Point p = fac.createPoint(c);
+        return geom.contains(p);
+    }
+
+    public boolean contains(Rectangle2D r)
+    {
+        return contains(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+    }
+
+    public boolean contains(double x, double y, double w, double h)
+    {
+        Polygon p = createRect(x, y, w, h);
+        return geom.contains(p);
+    }
+
+    protected Polygon createRect(double x, double y, double w, double h)
+    {
+        double[] arr = {x, y, x + w, y, x + w, y + h, x, y + h, x, y};
+        PackedCoordinateSequence shell = new PackedCoordinateSequence.Double(arr, 2);
+        Polygon p = fac.createPolygon(fac.createLinearRing(shell), NOSHELLS);
+        return p;
+    }
+
+    public Rectangle2D getBounds2D()
+    {
+        Envelope env = geom.getEnvelopeInternal();
+        return new Rectangle2D.Double(env.getMinX(), env.getMaxX(), env.getWidth(), env.getHeight());
+    }
+
+    public Rectangle getBounds()
+    {
+        // We deal simple code for efficiency here, the getBounds() rounding
+        // rules are ugly...
+        return getBounds2D().getBounds();
+    }
+
+    public PathIterator getPathIterator(AffineTransform at)
+    {
+        return getPathIterator(geom, at);
+    }
+
+    public PathIterator getPathIterator(AffineTransform at, double flatness)
+    {
+        // we don't have much work here, as we only have linear segments, no
+        // "flattening" necessary.
+        return getPathIterator(at);
+    }
+
+    public boolean intersects(Rectangle2D r)
+    {
+        return intersects(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight());
+    }
+
+    public boolean intersects(double x, double y, double w, double h)
+    {
+        Polygon p = createRect(x, y, w, h);
+        return geom.intersects(p);
+    }
+
+    public static GeometryPathIterator getPathIterator(Geometry geometry, AffineTransform _at)
+    {
+        if (geometry instanceof Point)
+        {
+            return new PointPathIterator((Point) geometry, _at);
+        } else if (geometry instanceof LineString)
+        {
+            return new LineStringPathIterator((LineString) geometry, _at);
+        } else if (geometry instanceof Polygon)
+        {
+            return new PolygonPathIterator((Polygon) geometry, _at);
+        } else
+        {
+            return new GeometryCollectionPathIterator((GeometryCollection) geometry, _at);
+        }
+    }
+
+    public static abstract class GeometryPathIterator implements PathIterator
+    {
+
+        protected final AffineTransform at;
+        protected int index = 0;
+
+        GeometryPathIterator(AffineTransform _at)
+        {
+            this.at = _at;
+        }
+
+        public final int getWindingRule()
+        {
+            return PathIterator.WIND_EVEN_ODD;
+        }
+
+        public void next()
+        {
+            index++;
+        }
+    }
+
+    public static class PointPathIterator extends GeometryPathIterator
+    {
+        final Point p;
+
+        public PointPathIterator(Point _p, AffineTransform _at)
+        {
+            super(_at);
+            p = _p;
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            switch (index)
+            {
+            case 0:
+                coords[0] = (float) p.getX();
+                coords[1] = (float) p.getY();
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            case 1:
+                return PathIterator.SEG_CLOSE;
+            default:
+                throw new IllegalStateException();
+            }
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            switch (index)
+            {
+            case 0:
+                coords[0] = p.getX();
+                coords[1] = p.getY();
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            case 1:
+                return PathIterator.SEG_CLOSE;
+            default:
+                throw new IllegalStateException();
+            }
+        }
+
+        public boolean isDone()
+        {
+            return index > 1;
+        }
+    }
+
+    public static class LineStringPathIterator extends GeometryPathIterator
+    {
+        CoordinateSequence cs;
+
+        final boolean isRing;
+
+        public LineStringPathIterator(LineString ls, AffineTransform _at)
+        {
+            super(_at);
+            cs = ls.getCoordinateSequence();
+            isRing = ls instanceof LinearRing;
+        }
+
+        /**
+         * only to be called from PolygonPathIterator subclass
+         */
+        protected void reInit(CoordinateSequence _cs)
+        {
+            cs = _cs;
+            index = 0;
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            if (index == 0)
+            {
+                coords[0] = (float) cs.getOrdinate(index, 0);
+                coords[1] = (float) cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            } else if (index < cs.size())
+            {
+                coords[0] = (float) cs.getOrdinate(index, 0);
+                coords[1] = (float) cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_LINETO;
+            } else if (isRing && index == cs.size())
+            {
+                return PathIterator.SEG_CLOSE;
+            } else
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            if (index == 0)
+            {
+                coords[0] = cs.getOrdinate(index, 0);
+                coords[1] = cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_MOVETO;
+            } else if (index < cs.size())
+            {
+                coords[0] = cs.getOrdinate(index, 0);
+                coords[1] = cs.getOrdinate(index, 1);
+                at.transform(coords, 0, coords, 0, 1);
+                return PathIterator.SEG_LINETO;
+            } else if (isRing && index == cs.size())
+            {
+                return PathIterator.SEG_CLOSE;
+            } else
+            {
+                throw new IllegalStateException();
+            }
+        }
+
+        public boolean isDone()
+        {
+            return isRing ? index > cs.size() : index >= cs.size();
+        }
+    }
+
+    public static class PolygonPathIterator extends LineStringPathIterator
+    {
+        final Polygon pg;
+        int outerindex = -1;
+
+        public PolygonPathIterator(Polygon _pg, AffineTransform _at)
+        {
+            super(_pg.getExteriorRing(), _at);
+            pg = _pg;
+            index = -1;
+        }
+
+        public boolean isDone()
+        {
+            return outerindex >= pg.getNumInteriorRing();
+        }
+
+        public void next()
+        {
+            super.next();
+            if (super.isDone())
+            {
+                outerindex++;
+                if (outerindex < pg.getNumInteriorRing())
+                {
+                    super.reInit(pg.getInteriorRingN(outerindex).getCoordinateSequence());
+                }
+            }
+        }
+    }
+
+    public static class GeometryCollectionPathIterator extends GeometryPathIterator
+    {
+        final GeometryCollection coll;
+        GeometryPathIterator current;
+
+        public GeometryCollectionPathIterator(GeometryCollection _coll, AffineTransform _at)
+        {
+            super(_at);
+            coll = _coll;
+            current = getPathIterator(coll.getGeometryN(index), _at);
+        }
+
+        public boolean isDone()
+        {
+            return index > coll.getNumGeometries();
+        }
+
+        public void next()
+        {
+            current.next();
+            if (current.isDone())
+            {
+                index++;
+                if (index < coll.getNumGeometries())
+                {
+                    current = getPathIterator(coll.getGeometryN(index), at);
+                }
+            }
+        }
+
+        public int currentSegment(float[] coords)
+        {
+            return current.currentSegment(coords);
+        }
+
+        public int currentSegment(double[] coords)
+        {
+            return current.currentSegment(coords);
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java
new file mode 100644
index 0000000..1ee359a
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryParser.java
@@ -0,0 +1,287 @@
+/*
+ * JtsBinaryParser.java
+ *
+ * Binary Parser for JTS - relies on org.postgis V1.0.0+ package.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+package com.ximple.eofms.util.postjts;
+
+import org.postgis.binary.ByteGetter;
+import org.postgis.binary.ByteGetter.BinaryByteGetter;
+import org.postgis.binary.ByteGetter.StringByteGetter;
+import org.postgis.binary.ValueGetter;
+
+import com.vividsolutions.jts.geom.Coordinate;
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.LinearRing;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequence;
+
+/**
+ * Parse binary representation of geometries. Currently, only text rep (hexed)
+ * implementation is tested.
+ * <p/>
+ * It should be easy to add char[] and CharSequence ByteGetter instances,
+ * although the latter one is not compatible with older jdks.
+ * <p/>
+ * I did not implement real unsigned 32-bit integers or emulate them with long,
+ * as both java Arrays and Strings currently can have only 2^31-1 elements
+ * (bytes), so we cannot even get or build Geometries with more than approx.
+ * 2^28 coordinates (8 bytes each).
+ *
+ * @author Markus Schaber, markus.schaber@logix-tt.com
+ */
+public class JtsBinaryParser
+{
+
+    /**
+     * Get the appropriate ValueGetter for my endianness
+     *
+     * @param bytes The appropriate Byte Getter
+     * @return the ValueGetter
+     */
+    public static ValueGetter valueGetterForEndian(ByteGetter bytes)
+    {
+        if (bytes.get(0) == ValueGetter.XDR.NUMBER)
+        { // XDR
+            return new ValueGetter.XDR(bytes);
+        } else if (bytes.get(0) == ValueGetter.NDR.NUMBER)
+        {
+            return new ValueGetter.NDR(bytes);
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Endian type:" + bytes.get(0));
+        }
+    }
+
+    /**
+     * Parse a hex encoded geometry
+     */
+    public Geometry parse(String value)
+    {
+        StringByteGetter bytes = new ByteGetter.StringByteGetter(value);
+        return parseGeometry(valueGetterForEndian(bytes));
+    }
+
+    /**
+     * Parse a binary encoded geometry.
+     */
+    public Geometry parse(byte[] value)
+    {
+        BinaryByteGetter bytes = new ByteGetter.BinaryByteGetter(value);
+        return parseGeometry(valueGetterForEndian(bytes));
+    }
+
+    /**
+     * Parse a geometry starting at offset.
+     */
+    protected Geometry parseGeometry(ValueGetter data)
+    {
+        return parseGeometry(data, 0, false);
+    }
+
+    /**
+     * Parse with a known geometry factory
+     */
+    protected Geometry parseGeometry(ValueGetter data, int srid, boolean inheritSrid)
+    {
+        byte endian = data.getByte(); // skip and test endian flag
+        if (endian != data.endian)
+        {
+            throw new IllegalArgumentException("Endian inconsistency!");
+        }
+        int typeword = data.getInt();
+
+        int realtype = typeword & 0x1FFFFFFF; // cut off high flag bits
+
+        boolean haveZ = (typeword & 0x80000000) != 0;
+        boolean haveM = (typeword & 0x40000000) != 0;
+        boolean haveS = (typeword & 0x20000000) != 0;
+
+        if (haveS)
+        {
+            int newsrid = data.getInt();
+            if (inheritSrid && newsrid != srid)
+            {
+                throw new IllegalArgumentException("Inconsistent srids in complex geometry: " + srid + ", " + newsrid);
+            } else
+            {
+                srid = newsrid;
+            }
+        } else if (!inheritSrid)
+        {
+            srid = -1;
+        }
+
+        Geometry result;
+        switch (realtype)
+        {
+        case org.postgis.Geometry.POINT:
+            result = parsePoint(data, haveZ, haveM);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            result = parseLineString(data, haveZ, haveM);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            result = parsePolygon(data, haveZ, haveM, srid);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            result = parseMultiPoint(data, srid);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            result = parseMultiLineString(data, srid);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            result = parseMultiPolygon(data, srid);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            result = parseCollection(data, srid);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type!");
+        }
+
+        result.setSRID(srid);
+
+        return result;
+    }
+
+    private Point parsePoint(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        double X = data.getDouble();
+        double Y = data.getDouble();
+        Point result;
+        if (haveZ)
+        {
+            double Z = data.getDouble();
+            result = JtsGeometry.geofac.createPoint(new Coordinate(X, Y, Z));
+        } else
+        {
+            result = JtsGeometry.geofac.createPoint(new Coordinate(X, Y));
+        }
+
+        if (haveM)
+        { // skip M value
+            data.getDouble();
+        }
+
+        return result;
+    }
+
+    /**
+     * Parse an Array of "full" Geometries
+     */
+    private void parseGeometryArray(ValueGetter data, Geometry[] container, int srid)
+    {
+        for (int i = 0; i < container.length; i++)
+        {
+            container[i] = parseGeometry(data, srid, true);
+        }
+    }
+
+    /**
+     * Parse an Array of "slim" Points (without endianness and type, part of
+     * LinearRing and Linestring, but not MultiPoint!
+     *
+     * @param haveZ
+     * @param haveM
+     */
+    private CoordinateSequence parseCS(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        int count = data.getInt();
+        int dims = haveZ ? 3 : 2;
+        CoordinateSequence cs = new PackedCoordinateSequence.Double(count, dims);
+
+        for (int i = 0; i < count; i++)
+        {
+            for (int d = 0; d < dims; d++)
+            {
+                cs.setOrdinate(i, d, data.getDouble());
+            }
+            if (haveM)
+            { // skip M value
+                data.getDouble();
+            }
+        }
+        return cs;
+    }
+
+    private MultiPoint parseMultiPoint(ValueGetter data, int srid)
+    {
+        Point[] points = new Point[data.getInt()];
+        parseGeometryArray(data, points, srid);
+        return JtsGeometry.geofac.createMultiPoint(points);
+    }
+
+    private LineString parseLineString(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        return JtsGeometry.geofac.createLineString(parseCS(data, haveZ, haveM));
+    }
+
+    private LinearRing parseLinearRing(ValueGetter data, boolean haveZ, boolean haveM)
+    {
+        return JtsGeometry.geofac.createLinearRing(parseCS(data, haveZ, haveM));
+    }
+
+    private Polygon parsePolygon(ValueGetter data, boolean haveZ, boolean haveM, int srid)
+    {
+        int holecount = data.getInt() - 1;
+        LinearRing[] rings = new LinearRing[holecount];
+        LinearRing shell = parseLinearRing(data, haveZ, haveM);
+        shell.setSRID(srid);
+        for (int i = 0; i < holecount; i++)
+        {
+            rings[i] = parseLinearRing(data, haveZ, haveM);
+            rings[i].setSRID(srid);
+        }
+        return JtsGeometry.geofac.createPolygon(shell, rings);
+    }
+
+    private MultiLineString parseMultiLineString(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        LineString[] strings = new LineString[count];
+        parseGeometryArray(data, strings, srid);
+        return JtsGeometry.geofac.createMultiLineString(strings);
+    }
+
+    private MultiPolygon parseMultiPolygon(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        Polygon[] polys = new Polygon[count];
+        parseGeometryArray(data, polys, srid);
+        return JtsGeometry.geofac.createMultiPolygon(polys);
+    }
+
+    private GeometryCollection parseCollection(ValueGetter data, int srid)
+    {
+        int count = data.getInt();
+        Geometry[] geoms = new Geometry[count];
+        parseGeometryArray(data, geoms, srid);
+        return JtsGeometry.geofac.createGeometryCollection(geoms);
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java
new file mode 100644
index 0000000..36a9d7b
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsBinaryWriter.java
@@ -0,0 +1,469 @@
+/*
+ * JtsBinaryWriter.java
+ *
+ * PostGIS extension for PostgreSQL JDBC driver - Binary Writer
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+package com.ximple.eofms.util.postjts;
+
+import org.postgis.binary.ByteSetter;
+import org.postgis.binary.ValueSetter;
+
+import com.vividsolutions.jts.geom.CoordinateSequence;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.LineString;
+import com.vividsolutions.jts.geom.MultiLineString;
+import com.vividsolutions.jts.geom.MultiPoint;
+import com.vividsolutions.jts.geom.MultiPolygon;
+import com.vividsolutions.jts.geom.Point;
+import com.vividsolutions.jts.geom.Polygon;
+
+/**
+ * Create binary representation of geometries. Currently, only text rep (hexed)
+ * implementation is tested. Supports only 2 dimensional geometries.
+ * <p/>
+ * It should be easy to add char[] and CharSequence ByteGetter instances,
+ * although the latter one is not compatible with older jdks.
+ * <p/>
+ * I did not implement real unsigned 32-bit integers or emulate them with long,
+ * as both java Arrays and Strings currently can have only 2^31-1 elements
+ * (bytes), so we cannot even get or build Geometries with more than approx.
+ * 2^28 coordinates (8 bytes each).
+ *
+ * @author markus.schaber@logi-track.com
+ */
+public class JtsBinaryWriter
+{
+
+    /**
+     * Get the appropriate ValueGetter for my endianness
+     *
+     * @param bytes The appropriate Byte Getter
+     * @return the ValueGetter
+     */
+    public static ValueSetter valueSetterForEndian(ByteSetter bytes, byte endian)
+    {
+        if (endian == ValueSetter.XDR.NUMBER)
+        { // XDR
+            return new ValueSetter.XDR(bytes);
+        } else if (endian == ValueSetter.NDR.NUMBER)
+        {
+            return new ValueSetter.NDR(bytes);
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Endian type:" + endian);
+        }
+    }
+
+    /**
+     * Write a hex encoded geometry
+     * <p/>
+     * Currently, geometries with more than 2 dimensions and measures are not
+     * cleanly supported, but SRID is honored.
+     */
+    public String writeHexed(Geometry geom, byte REP)
+    {
+        int length = estimateBytes(geom);
+        ByteSetter.StringByteSetter bytes = new ByteSetter.StringByteSetter(length);
+        writeGeometry(geom, valueSetterForEndian(bytes, REP));
+        return bytes.result();
+    }
+
+    public String writeHexed(Geometry geom)
+    {
+        return writeHexed(geom, ValueSetter.NDR.NUMBER);
+    }
+
+    /**
+     * Write a binary encoded geometry.
+     * <p/>
+     * Currently, geometries with more than 2 dimensions and measures are not
+     * cleanly supported, but SRID is honored.
+     */
+    public byte[] writeBinary(Geometry geom, byte REP)
+    {
+        int length = estimateBytes(geom);
+        ByteSetter.BinaryByteSetter bytes = new ByteSetter.BinaryByteSetter(length);
+        writeGeometry(geom, valueSetterForEndian(bytes, REP));
+        return bytes.result();
+    }
+
+    public byte[] writeBinary(Geometry geom)
+    {
+        return writeBinary(geom, ValueSetter.NDR.NUMBER);
+    }
+
+    /**
+     * Parse a geometry starting at offset.
+     */
+    protected void writeGeometry(Geometry geom, ValueSetter dest)
+    {
+        final int dimension;
+        if (geom == null)
+        {
+            throw new NullPointerException();
+        } else if (geom.isEmpty())
+        {
+            // don't set any flag bits
+            dimension = 0;
+        } else
+        {
+            dimension = getCoordDim(geom);
+            if (dimension < 2 || dimension > 4)
+            {
+                throw new IllegalArgumentException("Unsupported geometry dimensionality: " + dimension);
+            }
+        }
+        // write endian flag
+        dest.setByte(dest.endian);
+
+        // write typeword
+        final int plaintype = getWKBType(geom);
+        int typeword = plaintype;
+        if (dimension == 3 || dimension == 4)
+        {
+            typeword |= 0x80000000;
+        }
+        if (dimension == 4)
+        {
+            typeword |= 0x40000000;
+        }
+
+        final boolean haveSrid = checkSrid(geom);
+        if (haveSrid)
+        {
+            typeword |= 0x20000000;
+        }
+
+        dest.setInt(typeword);
+
+        if (haveSrid)
+        {
+            dest.setInt(geom.getSRID());
+        }
+
+        switch (plaintype)
+        {
+        case org.postgis.Geometry.POINT:
+            writePoint((Point) geom, dest);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            writeLineString((LineString) geom, dest);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            writePolygon((Polygon) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            writeMultiPoint((MultiPoint) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            writeMultiLineString((MultiLineString) geom, dest);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            writeMultiPolygon((MultiPolygon) geom, dest);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            writeCollection((GeometryCollection) geom, dest);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type: " + plaintype);
+        }
+    }
+
+    public static int getWKBType(Geometry geom)
+    {
+        // We always write emtpy geometries as emtpy collections - for OpenGIS
+        // conformance
+        if (geom.isEmpty())
+        {
+            return org.postgis.Geometry.GEOMETRYCOLLECTION;
+        } else if (geom instanceof Point)
+        {
+            return org.postgis.Geometry.POINT;
+        } else if (geom instanceof com.vividsolutions.jts.geom.LineString)
+        {
+            return org.postgis.Geometry.LINESTRING;
+        } else if (geom instanceof com.vividsolutions.jts.geom.Polygon)
+        {
+            return org.postgis.Geometry.POLYGON;
+        } else if (geom instanceof MultiPoint)
+        {
+            return org.postgis.Geometry.MULTIPOINT;
+        } else if (geom instanceof MultiLineString)
+        {
+            return org.postgis.Geometry.MULTILINESTRING;
+        } else if (geom instanceof com.vividsolutions.jts.geom.MultiPolygon)
+        {
+            return org.postgis.Geometry.MULTIPOLYGON;
+        }
+        if (geom instanceof com.vividsolutions.jts.geom.GeometryCollection)
+        {
+            return org.postgis.Geometry.GEOMETRYCOLLECTION;
+        } else
+        {
+            throw new IllegalArgumentException("Unknown Geometry Type: " + geom.getClass().getName());
+        }
+    }
+
+    /**
+     * Writes a "slim" Point (without endiannes, srid ant type, only the
+     * ordinates and measure. Used by writeGeometry.
+     */
+    private void writePoint(Point geom, ValueSetter dest)
+    {
+        writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest);
+    }
+
+    /**
+     * Write a Coordinatesequence, part of LinearRing and Linestring, but not
+     * MultiPoint!
+     */
+    private void writeCoordinates(CoordinateSequence seq, int dims, ValueSetter dest)
+    {
+        for (int i = 0; i < seq.size(); i++)
+        {
+            for (int d = 0; d < dims; d++)
+            {
+                dest.setDouble(seq.getOrdinate(i, d));
+            }
+        }
+    }
+
+    private void writeMultiPoint(MultiPoint geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumPoints());
+        for (int i = 0; i < geom.getNumPoints(); i++)
+        {
+            writeGeometry(geom.getGeometryN(i), dest);
+        }
+    }
+
+    private void writeLineString(LineString geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumPoints());
+        writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest);
+    }
+
+    private void writePolygon(Polygon geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumInteriorRing() + 1);
+        writeLineString(geom.getExteriorRing(), dest);
+        for (int i = 0; i < geom.getNumInteriorRing(); i++)
+        {
+            writeLineString(geom.getInteriorRingN(i), dest);
+        }
+    }
+
+    private void writeMultiLineString(MultiLineString geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeMultiPolygon(MultiPolygon geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeCollection(GeometryCollection geom, ValueSetter dest)
+    {
+        writeGeometryArray(geom, dest);
+    }
+
+    private void writeGeometryArray(Geometry geom, ValueSetter dest)
+    {
+        dest.setInt(geom.getNumGeometries());
+        for (int i = 0; i < geom.getNumGeometries(); i++)
+        {
+            writeGeometry(geom.getGeometryN(i), dest);
+        }
+    }
+
+    /**
+     * Estimate how much bytes a geometry will need in WKB.
+     */
+    protected int estimateBytes(Geometry geom)
+    {
+        int result = 0;
+
+        // write endian flag
+        result += 1;
+
+        // write typeword
+        result += 4;
+
+        if (checkSrid(geom))
+        {
+            result += 4;
+        }
+
+        switch (getWKBType(geom))
+        {
+        case org.postgis.Geometry.POINT:
+            result += estimatePoint((Point) geom);
+            break;
+        case org.postgis.Geometry.LINESTRING:
+            result += estimateLineString((LineString) geom);
+            break;
+        case org.postgis.Geometry.POLYGON:
+            result += estimatePolygon((Polygon) geom);
+            break;
+        case org.postgis.Geometry.MULTIPOINT:
+            result += estimateMultiPoint((MultiPoint) geom);
+            break;
+        case org.postgis.Geometry.MULTILINESTRING:
+            result += estimateMultiLineString((MultiLineString) geom);
+            break;
+        case org.postgis.Geometry.MULTIPOLYGON:
+            result += estimateMultiPolygon((MultiPolygon) geom);
+            break;
+        case org.postgis.Geometry.GEOMETRYCOLLECTION:
+            result += estimateCollection((GeometryCollection) geom);
+            break;
+        default:
+            throw new IllegalArgumentException("Unknown Geometry Type: " + getWKBType(geom));
+        }
+        return result;
+    }
+
+    private boolean checkSrid(Geometry geom)
+    {
+        final int srid = geom.getSRID();
+        // SRID is default 0 with jts geometries
+        return (srid != -1) && (srid != 0);
+    }
+
+    private int estimatePoint(Point geom)
+    {
+        return 8 * getCoordDim(geom);
+    }
+
+    /**
+     * Write an Array of "full" Geometries
+     */
+    private int estimateGeometryArray(Geometry container)
+    {
+        int result = 0;
+        for (int i = 0; i < container.getNumGeometries(); i++)
+        {
+            result += estimateBytes(container.getGeometryN(i));
+        }
+        return result;
+    }
+
+    /**
+     * Estimate an array of "fat" Points
+     */
+    private int estimateMultiPoint(MultiPoint geom)
+    {
+        // int size
+        int result = 4;
+        if (geom.getNumGeometries() > 0)
+        {
+            // We can shortcut here, compared to estimateGeometryArray, as all
+            // subgeoms have the same fixed size
+            result += geom.getNumGeometries() * estimateBytes(geom.getGeometryN(0));
+        }
+        return result;
+    }
+
+    private int estimateLineString(LineString geom)
+    {
+        if (geom == null || geom.getNumGeometries() == 0)
+        {
+            return 0;
+        } else
+        {
+            return 4 + 8 * getCoordSequenceDim(geom.getCoordinateSequence()) * geom.getCoordinateSequence().size();
+        }
+    }
+
+    private int estimatePolygon(Polygon geom)
+    {
+        // int length
+        int result = 4;
+        result += estimateLineString(geom.getExteriorRing());
+        for (int i = 0; i < geom.getNumInteriorRing(); i++)
+        {
+            result += estimateLineString(geom.getInteriorRingN(i));
+        }
+        return result;
+    }
+
+    private int estimateMultiLineString(MultiLineString geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    private int estimateMultiPolygon(MultiPolygon geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    private int estimateCollection(GeometryCollection geom)
+    {
+        // 4-byte count + subgeometries
+        return 4 + estimateGeometryArray(geom);
+    }
+
+    public static final int getCoordDim(Geometry geom)
+    {
+        if (geom.isEmpty())
+        {
+            return 0;
+        }
+        if (geom instanceof Point)
+        {
+            return getCoordSequenceDim(((Point) geom).getCoordinateSequence());
+        } else if (geom instanceof LineString)
+        {
+            return getCoordSequenceDim(((LineString) geom).getCoordinateSequence());
+        } else if (geom instanceof Polygon)
+        {
+            return getCoordSequenceDim(((Polygon) geom).getExteriorRing().getCoordinateSequence());
+        } else
+        {
+            return getCoordDim(geom.getGeometryN(0));
+        }
+    }
+
+    public static final int getCoordSequenceDim(CoordinateSequence coords)
+    {
+        if (coords == null || coords.size() == 0)
+            return 0;
+        // JTS has a really strange way to handle dimensions!
+        // Just have a look at PackedCoordinateSequence and
+        // CoordinateArraySequence
+        int dimensions = coords.getDimension();
+        if (dimensions == 3)
+        {
+            // CoordinateArraySequence will always return 3, so we have to
+            // check, if
+            // the third ordinate contains NaN, then the geom is actually
+            // 2-dimensional
+            return Double.isNaN(coords.getOrdinate(0, CoordinateSequence.Z)) ? 2 : 3;
+        } else
+        {
+            return dimensions;
+        }
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java
new file mode 100644
index 0000000..18a6295
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGeometry.java
@@ -0,0 +1,187 @@
+/*
+ * JtsGeometry.java
+ *
+ * Wrapper for PostgreSQL JDBC driver to allow transparent reading and writing
+ * of JTS geometries
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.SQLException;
+
+import org.postgresql.util.PGobject;
+
+import com.vividsolutions.jts.geom.CoordinateSequenceFactory;
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jts.geom.GeometryCollection;
+import com.vividsolutions.jts.geom.GeometryFactory;
+import com.vividsolutions.jts.geom.Polygon;
+import com.vividsolutions.jts.geom.PrecisionModel;
+import com.vividsolutions.jts.geom.impl.PackedCoordinateSequenceFactory;
+import com.vividsolutions.jts.io.WKTReader;
+
+/**
+ * JTS Geometry SQL wrapper. Supports PostGIS 1.x (lwgeom hexwkb) for writing
+ * and both PostGIS 0.x (EWKT) and 1.x (lwgeom hexwkb) for reading.
+ *
+ * @author Markus Schaber
+ */
+
+public class JtsGeometry extends PGobject
+{
+    /* JDK 1.5 Serialization */
+    private static final long serialVersionUID = 0x100;
+
+    Geometry geom;
+
+    final static JtsBinaryParser bp = new JtsBinaryParser();
+
+    final static JtsBinaryWriter bw = new JtsBinaryWriter();
+
+    final static PrecisionModel prec = new PrecisionModel();
+
+    final static CoordinateSequenceFactory csfac = PackedCoordinateSequenceFactory.DOUBLE_FACTORY;
+
+    final static GeometryFactory geofac = new GeometryFactory(prec, 0, csfac);
+
+    static final WKTReader reader = new WKTReader(geofac);
+
+    /**
+     * Constructor called by JDBC drivers
+     */
+    public JtsGeometry()
+    {
+        setType("geometry");
+    }
+
+    public JtsGeometry(Geometry geom)
+    {
+        this();
+        this.geom = geom;
+    }
+
+    public JtsGeometry(String value) throws SQLException
+    {
+        this();
+        setValue(value);
+    }
+
+    public void setValue(String value) throws SQLException
+    {
+        geom = geomFromString(value);
+    }
+
+    public static Geometry geomFromString(String value) throws SQLException
+    {
+        try
+        {
+            value = value.trim();
+            if (value.startsWith("00") || value.startsWith("01"))
+            {
+                return bp.parse(value);
+            } else
+            {
+                Geometry result;
+                // no srid := 0 in JTS world
+                int srid = 0;
+                // break up geometry into srid and wkt
+                if (value.startsWith("SRID="))
+                {
+                    String[] temp = value.split(";");
+                    value = temp[1].trim();
+                    srid = Integer.parseInt(temp[0].substring(5));
+                }
+
+                result = reader.read(value);
+                setSridRecurse(result, srid);
+                return result;
+            }
+        } catch (Exception E)
+        {
+            E.printStackTrace();
+            throw new SQLException("Error parsing SQL data:" + E);
+        }
+    }
+
+    /**
+     * Recursively set a srid for the geometry and all subgeometries
+     */
+    public static void setSridRecurse(final Geometry geom, final int srid)
+    {
+        geom.setSRID(srid);
+        if (geom instanceof GeometryCollection)
+        {
+            final int subcnt = geom.getNumGeometries();
+            for (int i = 0; i < subcnt; i++)
+            {
+                setSridRecurse(geom.getGeometryN(i), srid);
+            }
+        } else if (geom instanceof Polygon)
+        {
+            Polygon poly = (Polygon) geom;
+            poly.getExteriorRing().setSRID(srid);
+            final int subcnt = poly.getNumInteriorRing();
+            for (int i = 0; i < subcnt; i++)
+            {
+                poly.getInteriorRingN(i).setSRID(srid);
+            }
+        }
+    }
+
+    public Geometry getGeometry()
+    {
+        return geom;
+    }
+
+    public String toString()
+    {
+        return geom.toString();
+    }
+
+    public String getValue()
+    {
+        return bw.writeHexed(getGeometry());
+    }
+
+    public Object clone()
+    {
+        JtsGeometry obj = new JtsGeometry(geom);
+        obj.setType(type);
+        return obj;
+    }
+
+    public boolean equals(Object obj)
+    {
+        if ((obj != null) && (obj instanceof JtsGeometry))
+        {
+            Geometry other = ((JtsGeometry) obj).geom;
+            if (this.geom == other)
+            { // handles identity as well as both
+                // ==null
+                return true;
+            } else if (this.geom != null && other != null)
+            {
+                return other.equals(this.geom);
+            }
+        }
+        return false;
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java
new file mode 100644
index 0000000..1af43b4
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsGisWrapper.java
@@ -0,0 +1,173 @@
+/*
+ * JtsWrapper.java
+ *
+ * Allows transparent usage of JTS Geometry classes via PostgreSQL JDBC driver
+ * connected to a PostGIS enabled PostgreSQL server.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.postgresql.Driver;
+import org.postgresql.PGConnection;
+
+/**
+ * JtsGisWrapper
+ * <p/>
+ * Wraps the PostGreSQL Driver to add the JTS/PostGIS Object Classes.
+ * <p/>
+ * This method currently works with J2EE DataSource implementations, and with
+ * DriverManager framework.
+ * <p/>
+ * Simply replace the "jdbc:postgresql:" with a "jdbc:postgresql_JTS" in the
+ * jdbc URL.
+ *
+ * @author markus.schaber@logix-tt.com
+ */
+public class JtsGisWrapper extends Driver
+{
+
+    private static final String POSTGRES_PROTOCOL = "jdbc:postgresql:";
+    private static final String POSTGIS_PROTOCOL = "jdbc:postgresql_JTS:";
+    public static final String REVISION = "$Revision: 1977 $";
+
+    public JtsGisWrapper()
+    {
+        super();
+    }
+
+    static
+    {
+        try
+        {
+            // Analogy to org.postgresql.Driver
+            java.sql.DriverManager.registerDriver(new JtsGisWrapper());
+        } catch (SQLException e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Creates a postgresql connection, and then adds the PostGIS data types to
+     * it calling addpgtypes()
+     *
+     * @param url  the URL of the database to connect to
+     * @param info a list of arbitrary tag/value pairs as connection arguments
+     * @return a connection to the URL or null if it isnt us
+     * @throws SQLException if a database access error occurs
+     * @see java.sql.Driver#connect
+     * @see org.postgresql.Driver
+     */
+    public java.sql.Connection connect(String url, Properties info) throws SQLException
+    {
+        url = mangleURL(url);
+        Connection result = super.connect(url, info);
+        addGISTypes((PGConnection) result);
+        return result;
+    }
+
+    /**
+     * adds the JTS/PostGIS Data types to a PG Connection.
+     *
+     * @param pgconn
+     * @throws SQLException
+     */
+    public static void addGISTypes(PGConnection pgconn) throws SQLException
+    {
+        pgconn.addDataType("geometry", JtsGeometry.class);
+        pgconn.addDataType("box3d", org.postgis.PGbox3d.class);
+        pgconn.addDataType("box2d", org.postgis.PGbox2d.class);
+    }
+
+    /**
+     * Mangles the PostGIS URL to return the original PostGreSQL URL
+     * @param url url
+     * @return string
+     * @throws java.sql.SQLException error
+     */
+    public static String mangleURL(String url) throws SQLException
+    {
+        if (url.startsWith(POSTGIS_PROTOCOL))
+        {
+            return POSTGRES_PROTOCOL + url.substring(POSTGIS_PROTOCOL.length());
+        } else
+        {
+            throw new SQLException("Unknown protocol or subprotocol in url " + url);
+        }
+    }
+
+    /**
+     * Returns true if the driver thinks it can open a connection to the given
+     * URL. Typically, drivers will return true if they understand the
+     * subprotocol specified in the URL and false if they don't. Our protocols
+     * start with jdbc:postgresql_postGIS:
+     *
+     * @param url the URL of the driver
+     * @return true if this driver accepts the given URL
+     * @throws SQLException if a database-access error occurs (Dont know why
+     *                      it would *shrug*)
+     * @see java.sql.Driver#acceptsURL
+     */
+    public boolean acceptsURL(String url) throws SQLException
+    {
+        try
+        {
+            url = mangleURL(url);
+        } catch (SQLException e)
+        {
+            return false;
+        }
+        return super.acceptsURL(url);
+    }
+
+    /**
+     * Gets the underlying drivers major version number
+     *
+     * @return the drivers major version number
+     */
+
+    public int getMajorVersion()
+    {
+        return super.getMajorVersion();
+    }
+
+    /**
+     * Get the underlying drivers minor version number
+     *
+     * @return the drivers minor version number
+     */
+    public int getMinorVersion()
+    {
+        return super.getMinorVersion();
+    }
+
+    /**
+     * Returns our own CVS version plus postgres Version
+     */
+    public static String getVersion()
+    {
+        return "JtsGisWrapper " + REVISION + ", wrapping " + Driver.getVersion();
+    }
+}
diff --git a/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java
new file mode 100644
index 0000000..b87f551
--- /dev/null
+++ b/xdgnjobs/ximple-spatialjob/src/main/java/com/ximple/eofms/util/postjts/JtsWrapper.java
@@ -0,0 +1,180 @@
+/*
+ * JtsWrapper.java
+ *
+ * Allows transparent usage of JTS Geometry classes via PostgreSQL JDBC driver
+ * connected to a PostGIS enabled PostgreSQL server.
+ *
+ * (C) 2005 Markus Schaber, markus.schaber@logix-tt.com
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA or visit the web at
+ * http://www.gnu.org.
+ *
+ * $Id$
+ */
+
+package com.ximple.eofms.util.postjts;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.postgresql.Driver;
+import org.postgresql.PGConnection;
+
+/**
+ * DriverWrapper
+ * <p/>
+ * Wraps the PostGreSQL Driver to add the JTS/PostGIS Object Classes.
+ * <p/>
+ * This method currently works with J2EE DataSource implementations, and with
+ * DriverManager framework.
+ * <p/>
+ * Simply replace the "jdbc:postgresql:" with a "jdbc:postgres_jts:" in the jdbc
+ * URL.
+ * <p/>
+ * When using the drivermanager, you need to initialize JtsWrapper instead of
+ * (or in addition to) org.postgresql.Driver. When using a J2EE DataSource
+ * implementation, set the driver class property in the datasource config, the
+ * following works for jboss:
+ * <p/>
+ * &lt;driver-class&gt;org.postgis.jts.PostGisWrapper&lt;/driver-class&gt;
+ *
+ * @author markus.schaber@logix-tt.com
+ */
+public class JtsWrapper extends Driver
+{
+
+    protected static final Logger logger = Logger.getLogger("org.postgis.DriverWrapper");
+
+    private static final String POSTGRES_PROTOCOL = "jdbc:postgresql:";
+    private static final String POSTGIS_PROTOCOL = "jdbc:postgres_jts:";
+    public static final String REVISION = "$Revision: 2570 $";
+
+    public JtsWrapper()
+    {
+        super();
+    }
+
+    static
+    {
+        try
+        {
+            // Try to register ourself to the DriverManager
+            java.sql.DriverManager.registerDriver(new JtsWrapper());
+        } catch (SQLException e)
+        {
+            logger.log(Level.WARNING, "Error registering PostgreSQL Jts Wrapper Driver", e);
+        }
+    }
+
+    /**
+     * Creates a postgresql connection, and then adds the JTS GIS data types to
+     * it calling addpgtypes()
+     *
+     * @param url  the URL of the database to connect to
+     * @param info a list of arbitrary tag/value pairs as connection arguments
+     * @return a connection to the URL or null if it isnt us
+     * @throws SQLException if a database access error occurs
+     * @see java.sql.Driver#connect
+     * @see org.postgresql.Driver
+     */
+    public java.sql.Connection connect(String url, Properties info) throws SQLException
+    {
+        url = mangleURL(url);
+        Connection result = super.connect(url, info);
+        addGISTypes((PGConnection) result);
+        return result;
+    }
+
+    /**
+     * adds the JTS/PostGIS Data types to a PG Connection.
+     *
+     * @param pgconn  postgres connection
+     * @throws SQLException error
+     */
+    public static void addGISTypes(PGConnection pgconn) throws SQLException
+    {
+        pgconn.addDataType("geometry", JtsGeometry.class);
+    }
+
+    /**
+     * Mangles the PostGIS URL to return the original PostGreSQL URL
+     * @param url url
+     * @return string
+     * @throws java.sql.SQLException erroe
+     */
+    public static String mangleURL(String url) throws SQLException
+    {
+        if (url.startsWith(POSTGIS_PROTOCOL))
+        {
+            return POSTGRES_PROTOCOL + url.substring(POSTGIS_PROTOCOL.length());
+        } else
+        {
+            throw new SQLException("Unknown protocol or subprotocol in url " + url);
+        }
+    }
+
+    /**
+     * Check whether the driver thinks he can handle the given URL.
+     *
+     * @param url the URL of the driver
+     * @return true if this driver accepts the given URL
+     * @throws SQLException Passed through from the underlying PostgreSQL
+     *                      driver, should not happen.
+     * @see java.sql.Driver#acceptsURL
+     */
+    public boolean acceptsURL(String url) throws SQLException
+    {
+        try
+        {
+            url = mangleURL(url);
+        } catch (SQLException e)
+        {
+            return false;
+        }
+        return super.acceptsURL(url);
+    }
+
+    /**
+     * Gets the underlying drivers major version number
+     *
+     * @return the drivers major version number
+     */
+
+    public int getMajorVersion()
+    {
+        return super.getMajorVersion();
+    }
+
+    /**
+     * Get the underlying drivers minor version number
+     *
+     * @return the drivers minor version number
+     */
+    public int getMinorVersion()
+    {
+        return super.getMinorVersion();
+    }
+
+    /**
+     * Returns our own CVS version plus postgres Version
+     * @return version
+     */
+    public static String getVersion()
+    {
+        return "JtsGisWrapper " + REVISION + ", wrapping " + Driver.getVersion();
+    }
+}

--
Gitblit v0.0.0-SNAPSHOT