From: <svn...@os...> - 2012-05-15 21:21:46
|
Author: mdavis Date: 2012-05-15 14:21:38 -0700 (Tue, 15 May 2012) New Revision: 38732 Added: trunk/modules/library/main/src/test/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformerTest.java Modified: trunk/modules/library/api/src/main/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformer.java Log: GEOT-4130, GeometryCoordinateSequenceTransformer now uses input CoordinateSequenceFactory by default Modified: trunk/modules/library/api/src/main/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformer.java =================================================================== --- trunk/modules/library/api/src/main/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformer.java 2012-05-15 21:21:30 UTC (rev 38731) +++ trunk/modules/library/api/src/main/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformer.java 2012-05-15 21:21:38 UTC (rev 38732) @@ -17,6 +17,7 @@ package org.geotools.geometry.jts; import com.vividsolutions.jts.geom.CoordinateSequence; +import com.vividsolutions.jts.geom.CoordinateSequenceFactory; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; @@ -34,24 +35,51 @@ /** - * Service object that takes a geometry an applies a MathTransform on top - * of it. + * Service object that takes a geometry and applies a {@link MathTransform} + * to the coordinates it contains, creating a new + * geometry as the transformed output. + * <p> + * The standard usage pattern is + * to supply a {@link MathTransform} and @link CoordinateReferenceSystem} explicitly. + * The {@link #transform(Geometry)} method can then be + * used to construct transformed geometries using the {@link GeometryFactory} + * and {@link CoordinateSequenceFactory} of the input geometry. + * * @author Andrea Aime + * @author Martin Davis * * * @source $URL$ */ public class GeometryCoordinateSequenceTransformer { - private MathTransform transform; - private CoordinateSequenceTransformer csTransformer; + private MathTransform transform = null; private CoordinateReferenceSystem crs; + private CoordinateSequenceTransformer inputCSTransformer = null; + private CoordinateSequenceTransformer csTransformer = null; + private GeometryFactory currGeometryFactory = null; + /** + * Creates a transformer which uses the {@link CoordinateSequenceFactory} + * of the source geometries. + */ public GeometryCoordinateSequenceTransformer() { - csTransformer = new DefaultCoordinateSequenceTransformer(); + // the csTransformer is initialized from the first geometry + // and the supplied transform } + /** + * Creates a transformer which uses a client-specified + * {@link CoordinateSequenceTransformer}. + * <p> + * <b>WARNING:</b> The CoordinateSequenceTransformer must use + * the same {@link CoordinateSequenceFactory} as the output + * GeometryFactory, so that geometries are constructed consistently. + * + * @param transformer + */ public GeometryCoordinateSequenceTransformer(CoordinateSequenceTransformer transformer) { - csTransformer = transformer; + inputCSTransformer = transformer; + csTransformer = transformer; } /** @@ -62,7 +90,6 @@ this.transform = transform; } - /** * Sets the target coordinate reference system. * <p> @@ -76,14 +103,40 @@ } /** - * Applies the transform to the provided geometry, given - * @param g + * Initializes the internal CoordinateSequenceTransformer + * if not specified explicitly. + * + * @param gf the factory to use + */ + private void init(GeometryFactory gf) + { + // don't init if csTransformer already exists + if (inputCSTransformer != null) + return; + // don't reinit if gf is the same (the usual case) + if (currGeometryFactory == gf) + return; + + currGeometryFactory = gf; + CoordinateSequenceFactory csf = gf.getCoordinateSequenceFactory(); + csTransformer = new DefaultCoordinateSequenceTransformer(csf); + } + + /** + * Applies the transform to the provided geometry, + * creating a new transformed geometry. + * + * @param g the geometry to transform + * @return a new transformed geometry * @throws TransformException */ public Geometry transform(Geometry g) throws TransformException { GeometryFactory factory = g.getFactory(); Geometry transformed = null; + // lazily init csTransformer using geometry's CSFactory + init(factory); + if (g instanceof Point) { transformed = transformPoint((Point) g, factory); } else if (g instanceof MultiPoint) { @@ -130,8 +183,8 @@ throw new IllegalArgumentException("Unsupported geometry type " + g.getClass()); } - //copy over user data, do a special check for coordinate reference - // systme + //copy over user data + // do a special check for coordinate reference system transformed.setUserData(g.getUserData()); if ((g.getUserData() == null) || g.getUserData() instanceof CoordinateReferenceSystem) { @@ -150,6 +203,10 @@ */ public LineString transformLineString(LineString ls, GeometryFactory gf) throws TransformException { + + // if required, init csTransformer using geometry's CSFactory + init(gf); + CoordinateSequence cs = projectCoordinateSequence(ls.getCoordinateSequence()); LineString transformed = null; @@ -170,8 +227,12 @@ */ public Point transformPoint(Point point, GeometryFactory gf) throws TransformException { + + // if required, init csTransformer using geometry's CSFactory + init(gf); + CoordinateSequence cs = projectCoordinateSequence(point.getCoordinateSequence()); - Point transformed = gf.createPoint(cs);; + Point transformed = gf.createPoint(cs); transformed.setUserData( point.getUserData() ); return transformed; } @@ -181,7 +242,7 @@ * * @throws TransformException */ - public CoordinateSequence projectCoordinateSequence(CoordinateSequence cs) + private CoordinateSequence projectCoordinateSequence(CoordinateSequence cs) throws TransformException { return csTransformer.transform(cs, transform); } Added: trunk/modules/library/main/src/test/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformerTest.java =================================================================== --- trunk/modules/library/main/src/test/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformerTest.java (rev 0) +++ trunk/modules/library/main/src/test/java/org/geotools/geometry/jts/GeometryCoordinateSequenceTransformerTest.java 2012-05-15 21:21:38 UTC (rev 38732) @@ -0,0 +1,211 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) + * + * 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; + * 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. + */ +package org.geotools.geometry.jts; + +// J2SE dependencies +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +import org.geotools.referencing.crs.DefaultGeographicCRS; +import org.geotools.referencing.operation.transform.ProjectiveTransform; +import org.junit.Test; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.TransformException; + +import com.vividsolutions.jts.geom.CoordinateSequence; +import com.vividsolutions.jts.geom.CoordinateSequenceFilter; +import com.vividsolutions.jts.geom.Geometry; +import com.vividsolutions.jts.geom.GeometryFactory; + +/** + * Tests the {@link GeometryCoordinateSequenceTransformer} implementation. + * + * @since 2.2 + * + * + * @source $URL$ + * @version $Id$ + * @author Martin Davis + */ +public class GeometryCoordinateSequenceTransformerTest { + + private GeometryFactory geomFact = new GeometryFactory( + new LiteCoordinateSequenceFactory()); + + private GeometryBuilder gb = new GeometryBuilder(geomFact); + + @Test + public void testLineString() throws Exception { + checkTransform(gb.lineStringZ(10, 11, 1, 20, 21, 2)); + checkTransform(gb.lineString(10, 11, 20, 21)); + } + + @Test + public void testPoint() throws Exception { + checkTransform(gb.point(10, 11)); + checkTransform(gb.pointZ(10, 11, 1)); + } + + @Test + public void testPolygon() throws Exception { + checkTransform(gb.circle(10, 10, 5, 20)); + checkTransform(gb.boxZ(10, 10, 20, 20, 99)); + checkTransform(gb.polygon(gb.boxZ(10, 10, 20, 20, 99), + gb.boxZ(11, 11, 19, 19, 99))); + } + + @Test + public void testMulti() throws Exception { + checkTransform(gb.multiPoint(10, 10, 5, 20)); + checkTransform(gb.multiLineString(gb.lineString(10, 10, 20, 20), + gb.lineString(10, 10, 20, 20))); + checkTransform(gb.multiPolygon(gb.boxZ(10, 10, 20, 20, 99), + gb.boxZ(11, 11, 19, 19, 99))); + } + + @Test + public void testGeometryCollection() throws Exception { + checkTransform(gb.geometryCollection(gb.point(10, 11), + gb.lineString(10, 10, 20, 20), gb.box(10, 10, 20, 20))); + } + + /** + * Confirm that testing method is accurate! + * + * @throws Exception + */ + @Test + public void testDifferentDimensionsFailure() throws Exception { + Geometry g1 = gb.box(10, 10, 20, 20); + Geometry g2 = gb.boxZ(10, 10, 20, 20, 99); + assertFalse(hasSameValuesAndStructure(g1, g2)); + } + + private static final double ORD_TOLERANCE = 1.0e-6; + + /** + * Check transformation correctness by transforming forwards and backwards using + * inverse MathTransforms. + * + * @param g + * @throws TransformException + */ + private void checkTransform(Geometry g) throws TransformException { + GeometryCoordinateSequenceTransformer gcsTrans = new GeometryCoordinateSequenceTransformer(); + gcsTrans.setCoordinateReferenceSystem(DefaultGeographicCRS.WGS84); + MathTransform trans = ProjectiveTransform.createTranslation(2, 100); + gcsTrans.setMathTransform(trans); + + GeometryCoordinateSequenceTransformer gcsTransInv = new GeometryCoordinateSequenceTransformer(); + gcsTransInv.setCoordinateReferenceSystem(DefaultGeographicCRS.WGS84); + MathTransform transInv = ProjectiveTransform.createTranslation(2, -100); + gcsTransInv.setMathTransform(transInv); + + Geometry gTrans = gcsTrans.transform(g); + Geometry g2 = gcsTransInv.transform(gTrans); + + // result better be a different geometry + assertTrue(g != g2); + assertTrue(hasSameValuesAndStructure(g, g2)); + } + + boolean hasSameValuesAndStructure(Geometry g1, Geometry g2) { + if (!g1.equalsExact(g2, ORD_TOLERANCE)) + return false; + if (g1.getFactory() != g2.getFactory()) + return false; + + CoordinateSequence seq = CoordinateSequenceFinder.find(g1); + if (!CoordinateSequenceSchemaChecker.check(g2, seq.getClass(), + seq.getDimension())) + return false; + return true; + } + + static class CoordinateSequenceFinder implements CoordinateSequenceFilter { + + public static CoordinateSequence find(Geometry g) { + CoordinateSequenceFinder finder = new CoordinateSequenceFinder(); + g.apply(finder); + return finder.getSeq(); + } + + private CoordinateSequence firstSeqFound = null; + + public CoordinateSequence getSeq() { + return firstSeqFound; + } + + public void filter(CoordinateSequence seq, int i) { + if (firstSeqFound == null) + firstSeqFound = seq; + + } + + public boolean isDone() { + return firstSeqFound != null; + } + + public boolean isGeometryChanged() { + return false; + } + } + + static class CoordinateSequenceSchemaChecker implements + CoordinateSequenceFilter { + + public static boolean check(Geometry g, Class coordSeqClass, int dimension) { + CoordinateSequenceSchemaChecker checkCS = new CoordinateSequenceSchemaChecker( + coordSeqClass, dimension); + g.apply(checkCS); + return checkCS.isSame(); + } + + private Class coordSeqClass; + + private int dimension; + + private boolean isSame = true; + + public CoordinateSequenceSchemaChecker(Class coordSeqClass, int dimension) { + this.coordSeqClass = coordSeqClass; + this.dimension = dimension; + } + + public boolean isSame() { + return isSame; + } + + public void filter(CoordinateSequence seq, int i) { + if (seq.getClass() != coordSeqClass) + isSame = false; + if (seq.getDimension() != dimension) + isSame = false; + } + + public boolean isDone() { + return !isSame; + } + + + public boolean isGeometryChanged() { + return false; + } + + } + +} |