From: <svn...@os...> - 2012-05-17 17:02:07
|
Author: mdavis Date: 2012-05-17 10:01:52 -0700 (Thu, 17 May 2012) New Revision: 38742 Modified: trunk/modules/library/jdbc/src/test/java/org/geotools/jdbc/JDBC3DTest.java trunk/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteCoordinateSequenceFactory.java trunk/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/sdo/Coordinates.java trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/Oracle3DTest.java trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleTestSetup.java Log: GEOT-4140, Fix for Oracle not handling read/write of 3D Polygons correctly Modified: trunk/modules/library/jdbc/src/test/java/org/geotools/jdbc/JDBC3DTest.java =================================================================== --- trunk/modules/library/jdbc/src/test/java/org/geotools/jdbc/JDBC3DTest.java 2012-05-16 14:05:34 UTC (rev 38741) +++ trunk/modules/library/jdbc/src/test/java/org/geotools/jdbc/JDBC3DTest.java 2012-05-17 17:01:52 UTC (rev 38742) @@ -16,13 +16,17 @@ */ package org.geotools.jdbc; +import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultQuery; import org.geotools.data.FeatureReader; import org.geotools.data.FeatureWriter; +import org.geotools.data.Query; import org.geotools.data.Transaction; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; @@ -32,6 +36,7 @@ import org.geotools.factory.Hints; import org.geotools.feature.FeatureCollection; import org.geotools.feature.simple.SimpleFeatureBuilder; +import org.geotools.geometry.jts.LiteCoordinateSequence; import org.geotools.geometry.jts.LiteCoordinateSequenceFactory; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; @@ -43,6 +48,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.CoordinateSequenceFactory; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; @@ -55,13 +61,14 @@ * Tests the ability of the datastore to cope with 3D data * * @author Andrea Aime - OpenGeo - * - * - * + * @author Martin Davis - OpenGeo + * + * + * * @source $URL$ */ public abstract class JDBC3DTest extends JDBCTestSupport { - + protected static final String LINE3D = "line3d"; protected static final String POLY3D = "poly3d"; @@ -79,12 +86,10 @@ protected SimpleFeatureType poly3DType; protected SimpleFeatureType line3DType; - + protected CoordinateReferenceSystem epsg4326; protected abstract JDBC3DTestSetup createTestSetup(); - - @Override protected void connect() throws Exception { @@ -97,22 +102,21 @@ poly3DType = DataUtilities.createType(dataStore.getNamespaceURI() + "." + tname(POLY3D), aname(ID) + ":0," + aname(GEOM) + ":Polygon:srid=4326," + aname(NAME) + ":String"); poly3DType.getGeometryDescriptor().getUserData().put(Hints.COORDINATE_DIMENSION, 3); - + epsg4326 = CRS.decode("EPSG:4326"); } - protected Integer getNativeSRID() { return new Integer(4326); } - + public void testSchema() throws Exception { SimpleFeatureType schema = dataStore.getSchema(tname(LINE3D)); CoordinateReferenceSystem crs = schema.getGeometryDescriptor() .getCoordinateReferenceSystem(); assertEquals(new Integer(4326), CRS.lookupEpsgCode(crs, false)); - assertEquals(getNativeSRID(), schema.getGeometryDescriptor().getUserData().get( - JDBCDataStore.JDBC_NATIVE_SRID)); + assertEquals(getNativeSRID(), + schema.getGeometryDescriptor().getUserData().get(JDBCDataStore.JDBC_NATIVE_SRID)); } public void testReadPoint() throws Exception { @@ -125,7 +129,7 @@ } public void testReadLine() throws Exception { - SimpleFeatureCollection fc = dataStore.getFeatureSource(tname(LINE3D)).getFeatures(); + SimpleFeatureCollection fc = dataStore.getFeatureSource(tname(LINE3D)).getFeatures(); SimpleFeatureIterator fr = fc.features(); assertTrue(fr.hasNext()); LineString ls = (LineString) fr.next().getDefaultGeometry(); @@ -149,40 +153,82 @@ "l3" }, null); // insert it - SimpleFeatureStore fs = (SimpleFeatureStore) dataStore - .getFeatureSource(tname(LINE3D), Transaction.AUTO_COMMIT); + SimpleFeatureStore fs = (SimpleFeatureStore) dataStore.getFeatureSource(tname(LINE3D), + Transaction.AUTO_COMMIT); List<FeatureId> fids = fs.addFeatures(DataUtilities.collection(newFeature)); // retrieve it back - SimpleFeatureIterator fi = fs.getFeatures(FF.id(new HashSet<FeatureId>(fids))) - .features(); + SimpleFeatureIterator fi = fs.getFeatures(FF.id(new HashSet<FeatureId>(fids))).features(); assertTrue(fi.hasNext()); SimpleFeature f = fi.next(); assertTrue(ls.equalsExact((Geometry) f.getDefaultGeometry())); fi.close(); } + public void testCreateSchemaAndInsertPolyTriangle() throws Exception { + LiteCoordinateSequenceFactory csf = new LiteCoordinateSequenceFactory(); + GeometryFactory gf = new GeometryFactory(csf); + + LinearRing shell = gf.createLinearRing(csf.create(new double[] { 0, 0, 99, 1, 0, 33, 1, 1, + 66, 0, 0, 99 }, 3)); + Polygon poly = gf.createPolygon(shell, null); + + checkCreateSchemaAndInsert(poly); + } + + public void testCreateSchemaAndInsertPolyRectangle() throws Exception { + LiteCoordinateSequenceFactory csf = new LiteCoordinateSequenceFactory(); + GeometryFactory gf = new GeometryFactory(csf); + + LinearRing shell = gf.createLinearRing(csf.create(new double[] { 0, 0, 99, 1, 0, 33, 1, 1, + 66, 0, 1, 33, 0, 0, 99 }, 3)); + Polygon poly = gf.createPolygon(shell, null); + + checkCreateSchemaAndInsert(poly); + } + + public void testCreateSchemaAndInsertPolyRectangleWithHole() throws Exception { + LiteCoordinateSequenceFactory csf = new LiteCoordinateSequenceFactory(); + GeometryFactory gf = new GeometryFactory(csf); + + LinearRing shell = gf.createLinearRing(csf.create(new double[] { 0, 0, 99, 10, 0, 33, 10, + 10, 66, 0, 10, 66, 0, 0, 99 }, 3)); + LinearRing hole = gf.createLinearRing(csf.create(new double[] { 2, 2, 99, 3, 2, 44, 3, 3, + 99, 2, 3, 99, 2, 2, 99 }, 3)); + Polygon poly = gf.createPolygon(shell, new LinearRing[] { hole }); + + checkCreateSchemaAndInsert(poly); + } + + public void testCreateSchemaAndInsertPolyWithHoleCW() throws Exception { + LiteCoordinateSequenceFactory csf = new LiteCoordinateSequenceFactory(); + GeometryFactory gf = new GeometryFactory(csf); + + LinearRing shell = gf.createLinearRing(csf.create(new double[] { 1, 1, 99, 10, 1, 33, + 10, 10, 66, 1, 10, 66, 1, 1, 99 }, 3)); + LinearRing hole = gf.createLinearRing(csf.create(new double[] { 2, 2, 99, 8, 2, 44, 8, 8, + 99, 2, 8, 99, 2, 2, 99 }, 3)); + Polygon poly = gf.createPolygon(shell, new LinearRing[] { hole }); + + checkCreateSchemaAndInsert(poly); + } + /** - * Creates the polygon schema and then inserts a 3D geometry into the - * datastore and retrieves it back to make sure 3d data is really handled as - * such + * Creates the polygon schema, inserts a 3D geometry into the datastore, + * and retrieves it back to make sure 3d data is preserved. * * @throws Exception */ - public void testCreateSchemaAndInsert() throws Exception { + private void checkCreateSchemaAndInsert(Geometry poly) throws Exception { dataStore.createSchema(poly3DType); SimpleFeatureType actualSchema = dataStore.getSchema(tname(POLY3D)); assertFeatureTypesEqual(poly3DType, actualSchema); - assertEquals(getNativeSRID(), actualSchema.getGeometryDescriptor().getUserData().get( - JDBCDataStore.JDBC_NATIVE_SRID)); + assertEquals( + getNativeSRID(), + actualSchema.getGeometryDescriptor().getUserData() + .get(JDBCDataStore.JDBC_NATIVE_SRID)); - // build a 3d polygon (ordinates in ccw order) - GeometryFactory gf = new GeometryFactory(); - LinearRing shell = gf.createLinearRing(new Coordinate[] { new Coordinate(0, 0, 0), - new Coordinate(1, 1, 1), new Coordinate(1, 0, 1), new Coordinate(0, 0, 0) }); - Polygon poly = gf.createPolygon(shell, null); - - // insert it + // insert the feature FeatureWriter<SimpleFeatureType, SimpleFeature> fw = dataStore.getFeatureWriterAppend( tname(POLY3D), Transaction.AUTO_COMMIT); SimpleFeature f = fw.next(); @@ -192,20 +238,62 @@ fw.write(); fw.close(); - // read id back and compare + // read feature back + + /** + * Use a LiteCoordinateSequence, since this mimics GeoServer behaviour better, + * and it exposes bugs in CoordinateSequence handling. + */ + final Hints hints = new Hints(); + hints.put(Hints.JTS_COORDINATE_SEQUENCE_FACTORY, new LiteCoordinateSequenceFactory()); + Query query = new DefaultQuery(tname(POLY3D)); + query.setHints(hints); + FeatureReader<SimpleFeatureType, SimpleFeature> fr = dataStore.getFeatureReader( - new DefaultQuery(tname(POLY3D)), Transaction.AUTO_COMMIT); + query, Transaction.AUTO_COMMIT); assertTrue(fr.hasNext()); f = fr.next(); - // this unfortunately checks only the first 2d, but at the same time - // a coordinate by coordinate check is not possible since the ring orientation - // can be modified by the store - assertTrue(poly.equalsTopo((Geometry) f.getDefaultGeometry())); - + + /** + * Check the geometries are topologically equal. + * Check that the Z values are preserved + */ + Geometry fgeom = (Geometry) f.getDefaultGeometry(); + assertTrue("2D topology does not match", poly.equalsTopo(fgeom)); + assertTrue("Z values do not match", hasMatchingZValues(poly, fgeom)); fr.close(); } /** + * Tests whether two geometries have the same Z values for coordinates with identical 2D locations. Requires that each geometry is internally + * location-consistent in Z; that is, if two coordinates are identical in location, then the Z values are equal. This should always be the case + * for valid data. + * + * @param g1 + * @param g2 + * @return true if the geometries are location-equal in Z + */ + private static boolean hasMatchingZValues(Geometry g1, Geometry g2) { + Coordinate[] pt1 = g1.getCoordinates(); + Map<Coordinate, Double> coordZMap = new HashMap<Coordinate, Double>(); + for (int i = 0; i < pt1.length; i++) { + coordZMap.put(pt1[i], pt1[i].z); + } + + Coordinate[] pt2 = g2.getCoordinates(); + + for (int i2 = 0; i2 < pt2.length; i2++) { + Coordinate p2 = pt2[i2]; + double z = coordZMap.get(p2); + boolean isEqualZ = p2.z == z || (Double.isNaN(p2.z) && Double.isNaN(z)); + if (!isEqualZ) + return false; + } + + return true; + } + + /** * Make sure we can properly retrieve the bounds of 3d layers * * @throws Exception @@ -233,11 +321,11 @@ new LiteCoordinateSequenceFactory()); q.setHints(hints); - // check the srs you get is the expected one - FeatureCollection fc = fs.getFeatures(q); - FeatureType fcSchema = fc.getSchema(); + // check the srs you get is the expected one + FeatureCollection fc = fs.getFeatures(q); + FeatureType fcSchema = fc.getSchema(); assertEquals(epsg4326, fcSchema.getCoordinateReferenceSystem()); - assertEquals(epsg4326, fcSchema.getGeometryDescriptor().getCoordinateReferenceSystem()); + assertEquals(epsg4326, fcSchema.getGeometryDescriptor().getCoordinateReferenceSystem()); // build up the reference 2d line, the 3d one is (1 1 0, 2 2 0, 4 2 1, 5 // 1 1) @@ -246,7 +334,8 @@ new Coordinate(5, 1) }); // check feature reader and the schema - FeatureReader<SimpleFeatureType, SimpleFeature> fr = dataStore.getFeatureReader(q, Transaction.AUTO_COMMIT); + FeatureReader<SimpleFeatureType, SimpleFeature> fr = dataStore.getFeatureReader(q, + Transaction.AUTO_COMMIT); assertEquals(epsg4326, fr.getFeatureType().getCoordinateReferenceSystem()); assertEquals(epsg4326, fr.getFeatureType().getGeometryDescriptor() .getCoordinateReferenceSystem()); Modified: trunk/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteCoordinateSequenceFactory.java =================================================================== --- trunk/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteCoordinateSequenceFactory.java 2012-05-16 14:05:34 UTC (rev 38741) +++ trunk/modules/library/main/src/main/java/org/geotools/geometry/jts/LiteCoordinateSequenceFactory.java 2012-05-17 17:01:52 UTC (rev 38742) @@ -42,6 +42,12 @@ * @see com.vividsolutions.jts.geom.CoordinateSequenceFactory#create(com.vividsolutions.jts.geom.CoordinateSequence) */ public CoordinateSequence create(CoordinateSequence coordSeq) { + /** + * If copying a LiteCoordinateSequence, use the copy constructor + * to preserve dimensionality information. + */ + if (coordSeq instanceof LiteCoordinateSequence) + return new LiteCoordinateSequence((LiteCoordinateSequence) coordSeq); return new LiteCoordinateSequence(coordSeq.toCoordinateArray()); } Modified: trunk/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/sdo/Coordinates.java =================================================================== --- trunk/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/sdo/Coordinates.java 2012-05-16 14:05:34 UTC (rev 38741) +++ trunk/modules/plugin/jdbc/jdbc-oracle/src/main/java/org/geotools/data/oracle/sdo/Coordinates.java 2012-05-17 17:01:52 UTC (rev 38742) @@ -26,6 +26,7 @@ import com.vividsolutions.jts.geom.CoordinateList; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.CoordinateSequenceFactory; +import com.vividsolutions.jts.geom.CoordinateSequences; import com.vividsolutions.jts.geom.PrecisionModel; @@ -173,15 +174,13 @@ return c; } - Coordinate[] array = new Coordinate[toIndex - fromIndex]; - int index = 0; - for(int i = fromIndex; i < toIndex; i++, index++) { - array[index] = sequence.getCoordinate(i); - } + // handle coordinate sequence dimension correctly + int size = toIndex - fromIndex; + CoordinateSequence newSeq = factory.create(size, sequence.getDimension()); + CoordinateSequences.copy(sequence, fromIndex, newSeq, 0, size); + return newSeq; + } - return factory.create(array); - } - /** * DOCUMENT ME! * @@ -214,11 +213,11 @@ return c; } else // else CoordinateSequence - { - CoordinateList list = new CoordinateList(sequence.toCoordinateArray()); - Collections.reverse(list); - - return factory.create(list.toCoordinateArray()); + { + // handle coordinate sequence dimension correctly + CoordinateSequence revSeq = factory.create(sequence); + CoordinateSequences.reverse(revSeq); + return revSeq; } } Modified: trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/Oracle3DTest.java =================================================================== --- trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/Oracle3DTest.java 2012-05-16 14:05:34 UTC (rev 38741) +++ trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/Oracle3DTest.java 2012-05-17 17:01:52 UTC (rev 38742) @@ -31,5 +31,13 @@ return new Oracle3DTestSetup(); } + /** + * This test is overriden to disable it, since it is a known issue. + * The issue is that the Oracle driver writes Rectangles as Oracle SDO Rectangle structures, which don't preserve 3D + * See GEOT-4133 + */ + @Override + public void testCreateSchemaAndInsertPolyRectangle() throws Exception { + } } Modified: trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleTestSetup.java =================================================================== --- trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleTestSetup.java 2012-05-16 14:05:34 UTC (rev 38741) +++ trunk/modules/plugin/jdbc/jdbc-oracle/src/test/java/org/geotools/data/oracle/OracleTestSetup.java 2012-05-17 17:01:52 UTC (rev 38742) @@ -59,7 +59,8 @@ fixture.put("host", "192.168.1.200"); fixture.put("port", "1521"); fixture.put("database", "xe"); - fixture.put("username", "geoserver"); + fixture.put("usernamegit ", "geoserver"); + fixture.put("user", "geoserver"); fixture.put("password", "postgis"); fixture.put("dbtype", "Oracle" ); return fixture; |