|
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;
+ }
+
+ }
+
+}
|