This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit e891f327a4dcef7f5c9ce97a9a17627085230fa2
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Jan 8 20:48:43 2019 +0100

    Move JTS specific methods to "jts" subpackage.
---
 .../apache/sis/internal/feature/Geometries.java    |  52 ++-----
 .../java/org/apache/sis/internal/feature/JTS.java  | 158 ++-------------------
 .../feature/jts/GeometryCoordinateTransform.java   |  14 +-
 .../internal/feature/jts/GeometryTransform.java    |  77 +++++-----
 .../org/apache/sis/internal/feature/jts/JTS.java   | 156 ++++++++++++++++++++
 .../sis/internal/feature/jts/package-info.java     |  31 ++++
 .../sis/internal/feature/GeometriesTestCase.java   |   2 +-
 .../org/apache/sis/internal/feature/JTSTest.java   |  71 ---------
 .../sis/internal/feature/{ => jts}/JTSTest.java    |  71 ++-------
 .../apache/sis/test/suite/FeatureTestSuite.java    |   1 +
 10 files changed, 268 insertions(+), 365 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
index 0d6beae..0c83667 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Geometries.java
@@ -19,8 +19,6 @@ package org.apache.sis.internal.feature;
 import java.util.Iterator;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
-import org.opengis.util.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.geometry.GeneralEnvelope;
@@ -107,7 +105,7 @@ public abstract class Geometries<G> {
         String classname = Geometries.class.getName();
         classname = classname.substring(0, 
classname.lastIndexOf('.')+1).concat(name);
         try {
-            implementation = (Geometries) 
Class.forName(classname).newInstance();
+            implementation = (Geometries) 
Class.forName(classname).getDeclaredConstructor().newInstance();
         } catch (ReflectiveOperationException | LinkageError e) {
             LogRecord record = 
Resources.forLocale(null).getLogRecord(Level.CONFIG,
                     Resources.Keys.OptionalLibraryNotFound_2, name, 
e.toString());
@@ -163,8 +161,6 @@ public abstract class Geometries<G> {
      *         or {@code null} if the given object is not a recognized 
implementation.
      *
      * @see #createPoint(double, double)
-     *
-     * @see #getCoordinateReferenceSystem(Object)
      */
     public static double[] getCoordinate(final Object point) {
         for (Geometries<?> g = implementation; g != null; g = g.fallback) {
@@ -227,6 +223,15 @@ public abstract class Geometries<G> {
     }
 
     /**
+     * Parses the given WKT.
+     *
+     * @param  wkt  the WKT to parse.
+     * @return the geometry object for the given WKT.
+     * @throws Exception if the WKT can not be parsed. The exception sub-class 
depends on the implementation.
+     */
+    public abstract Object parseWKT(String wkt) throws Exception;
+
+    /**
      * Creates a two-dimensional point from the given coordinate. If the CRS 
is geographic, then the
      * (x,y) values should be (longitude, latitude) for compliance with usage 
in ESRI and JTS libraries.
      *
@@ -288,43 +293,6 @@ public abstract class Geometries<G> {
     }
 
     /**
-     * Parses the given WKT.
-     *
-     * @param  wkt  the WKT to parse.
-     * @return the geometry object for the given WKT.
-     * @throws Exception if the WKT can not be parsed. The exception sub-class 
depends on the implementation.
-     */
-    public abstract Object parseWKT(String wkt) throws Exception;
-
-    /**
-     * If the given geometry is an implementation of this library, returns its 
coordinate reference system.
-     * Otherwise returns {@code null}.
-     *
-     * @param  geometry  the geometry from which to get the CRS.
-     * @return the coordinate reference system, or {@code null} if none.
-     * @throws FactoryException if the CRS can not be created from the SRID 
code.
-     */
-    CoordinateReferenceSystem tryGetCoordinateReferenceSystem(Object geometry) 
throws FactoryException {
-        return null;
-    }
-
-    /**
-     * If the given object is one of the recognized geometry implementations, 
gets its Coordinate Reference System (CRS).
-     * Otherwise returns {@code null}.
-     *
-     * @param  geometry  the geometry from which to get the CRS.
-     * @return the coordinate reference system, or {@code null} if none.
-     * @throws FactoryException if the CRS can not be created from the SRID 
code.
-     */
-    public static CoordinateReferenceSystem getCoordinateReferenceSystem(final 
Object geometry) throws FactoryException {
-        for (Geometries<?> g = implementation; g != null; g = g.fallback) {
-            CoordinateReferenceSystem crs = 
g.tryGetCoordinateReferenceSystem(geometry);
-            if (crs != null) return crs;
-        }
-        return null;
-    }
-
-    /**
      * Returns an error message for an unsupported geometry object.
      *
      * @param  dimension  number of dimensions (2 or 3) requested for the 
geometry object.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
index 27879af..9234435 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/JTS.java
@@ -20,12 +20,6 @@ import java.util.List;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.Map;
-import org.opengis.util.FactoryException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.CoordinateOperation;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.TransformException;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
@@ -37,12 +31,9 @@ import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.io.WKTReader;
 import org.locationtech.jts.io.ParseException;
 import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.internal.feature.jts.GeometryCoordinateTransform;
 import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.math.Vector;
-import org.apache.sis.referencing.CRS;
 import org.apache.sis.util.Classes;
-import org.apache.sis.util.Utilities;
 
 
 /**
@@ -56,13 +47,7 @@ import org.apache.sis.util.Utilities;
  * @since   0.7
  * @module
  */
-public final class JTS extends Geometries<Geometry> {
-    /**
-     * Key used in {@linkplain Geometry#getUserData() user data} map for 
storing
-     * an instance of {@link CoordinateReferenceSystem}.
-     */
-    public static final String CRS_KEY = "CRS";
-
+final class JTS extends Geometries<Geometry> {
     /**
      * The factory to use for creating JTS geometries. Currently set to a 
factory using
      * double-precision floating point numbers and a spatial-reference ID of 0.
@@ -78,6 +63,17 @@ public final class JTS extends Geometries<Geometry> {
     }
 
     /**
+     * Parses the given WKT.
+     *
+     * @return the geometry object for the given WKT.
+     * @throws ParseException if the WKT can not be parsed.
+     */
+    @Override
+    public Object parseWKT(final String wkt) throws ParseException {
+        return new WKTReader(factory).read(wkt);
+    }
+
+    /**
      * If the given object is a JTS geometry, returns a short string 
representation of the class name.
      */
     @Override
@@ -260,134 +256,4 @@ public final class JTS extends Geometries<Geometry> {
         toLineString(coordinates, lines);
         return toGeometry(lines);
     }
-
-    /**
-     * Parses the given WKT.
-     *
-     * @return the geometry object for the given WKT.
-     * @throws ParseException if the WKT can not be parsed.
-     */
-    @Override
-    public Object parseWKT(final String wkt) throws ParseException {
-        return new WKTReader(factory).read(wkt);
-    }
-
-    /**
-     * Gets the Coordinate Reference System (CRS) from the given geometry.
-     * This method expects the CRS to be stored in one the following ways:
-     *
-     * <ul>
-     *   <li>Geometry {@linkplain Geometry#getUserData() user data} is an 
instance of {@code CoordinateReferenceSystem}.</li>
-     *   <li>{@linkplain Geometry#getUserData() user data} is a (@link Map} 
with a value for the {@value #CRS_KEY} key.</li>
-     *   <li>Geometry SRID is strictly positive, in which case it is 
interpreted as an EPSG code.</li>
-     * </ul>
-     *
-     * If none of the above is valid, {@code null} is returned.
-     *
-     * @param  geometry the geometry from which to get the CRS.
-     * @return the coordinate reference system, or {@code null} if none.
-     * @throws FactoryException if the CRS can not be created from the SRID 
code.
-     */
-    @Override
-    CoordinateReferenceSystem tryGetCoordinateReferenceSystem(final Object 
geometry) throws FactoryException {
-        if (geometry instanceof Geometry) {
-            final Geometry jts = (Geometry) geometry;
-            final Object userData = jts.getUserData();
-            if (userData instanceof CoordinateReferenceSystem) {
-                return (CoordinateReferenceSystem) userData;
-            } else if (userData instanceof Map<?,?>) {
-                final Map<?,?> map = (Map<?,?>) userData;
-                final Object value = map.get(CRS_KEY);
-                if (value instanceof CoordinateReferenceSystem) {
-                    return (CoordinateReferenceSystem) value;
-                }
-            }
-            /*
-             * Fallback on SRID.
-             */
-            final int srid = jts.getSRID();
-            if (srid > 0) {
-                return CRS.forCode("EPSG:" + srid);
-            }
-        }
-        return super.tryGetCoordinateReferenceSystem(geometry);
-    }
-
-    /**
-     * Transform given geometry to a new CoordinateReferenceSystem.
-     *
-     * <p>
-     * If CoordinateReferenceSystem of geometry is null, geometry is returned 
unchanged.
-     * </p>
-     * <p>
-     * If geometry has no CoordinateReferenceSystem a TransformException is 
throwed.
-     * </p>
-     *
-     * @param geometry source geometry
-     * @param targetCrs target CoordinateReferenceSystem
-     * @return transformed geometry, or same geometry if it is already in 
target crs
-     * @throws org.opengis.referencing.operation.TransformException if geometry
-     *         has no CRS or failed to apply transform.
-     * @throws org.opengis.util.FactoryException
-     */
-    public static Geometry transform(Geometry geometry, 
CoordinateReferenceSystem targetCrs)
-            throws TransformException, FactoryException
-    {
-        if (geometry == null || targetCrs == null) {
-            return geometry;
-        }
-        final CoordinateReferenceSystem sourceCrs = 
getCoordinateReferenceSystem(geometry);
-        if (sourceCrs == null) {
-            throw new TransformException("Geometry CRS is undefined");
-        } else if (Utilities.equalsIgnoreMetadata(sourceCrs, targetCrs)) {
-            return geometry;
-        }
-        return transform(geometry, CRS.findOperation(sourceCrs, targetCrs, 
null));
-    }
-
-    /**
-     * Transform geometry, result is a new geometry.
-     *
-     * <p>
-     * If geometry or operation is null, geometry is returned unchanged.
-     * </p>
-     *
-     * TODO : handle antemeridian cases.
-     *
-     * @param geometry
-     * @param operation
-     * @return
-     * @throws TransformException if transformation failed
-     */
-    public static Geometry transform(Geometry geometry, CoordinateOperation 
operation) throws TransformException {
-        if (geometry == null || operation == null) {
-            return geometry;
-        }
-        geometry = transform(geometry, operation.getMathTransform());
-        geometry.setUserData(operation.getTargetCRS());
-        return geometry;
-    }
-
-    /**
-     * Transform geometry, result is a new geometry.
-     *
-     * <p>
-     * If geometry or transform is null, geometry is returned unchanged.
-     * </p>
-     *
-     * @param geometry
-     * @param transform
-     * @return transformed geometry or null.
-     * @throws TransformException if transformation failed
-     */
-    public static Geometry transform(Geometry geometry, MathTransform 
transform) throws TransformException {
-        if (geometry == null || transform == null) {
-            return geometry;
-        }
-        final GeometryCoordinateTransform gct = new 
GeometryCoordinateTransform(transform, geometry.getFactory());
-        final Geometry result = gct.transform(geometry);
-        result.setSRID(geometry.getSRID());
-        result.setUserData(geometry.getUserData());
-        return result;
-    }
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryCoordinateTransform.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryCoordinateTransform.java
index fa9307d..43269e0 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryCoordinateTransform.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryCoordinateTransform.java
@@ -22,17 +22,20 @@ import org.locationtech.jts.geom.GeometryFactory;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 
+
 /**
- * This implementation use a MathTransform to transform each coordinate of
- * the geometry.
+ * A geometry transformer which uses a {@link MathTransform} for changing 
coordinate values.
+ * This class does not change the number of points.
  *
- * @author Johann Sorel (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
 public class GeometryCoordinateTransform extends GeometryTransform {
-
+    /**
+     * The transform to apply on coordinate values.
+     */
     private final MathTransform transform;
 
     public GeometryCoordinateTransform(MathTransform transform) {
@@ -53,7 +56,7 @@ public class GeometryCoordinateTransform extends 
GeometryTransform {
     protected CoordinateSequence transform(CoordinateSequence in, int 
minpoints) throws TransformException {
         final int dim = in.getDimension();
         final int size = in.size();
-        final CoordinateSequence out = csf.create(size, dim);
+        final CoordinateSequence out = coordinateFactory.create(size, dim);
 
         final double[] val = new double[dim];
         for (int i = 0; i<size; i++) {
@@ -78,5 +81,4 @@ public class GeometryCoordinateTransform extends 
GeometryTransform {
         }
         return out;
     }
-
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryTransform.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryTransform.java
index 9c0be5c..4fca59d 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryTransform.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/GeometryTransform.java
@@ -30,43 +30,49 @@ import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
 import org.opengis.referencing.operation.TransformException;
 
+
 /**
- * Abstract class parent to all JTS geometry transformations.
- * This class decompose the geometry to it's most primitive element, the
- * CoordinateSequence, then rebuild the geometry.
+ * An operation transforming a geometry into another geometry. This class 
decomposes the geometry into it's
+ * most primitive elements, the {@link CoordinateSequence}, applies an 
operation, then rebuilds the geometry.
+ * The operation may change coordinate values (for example a map projection), 
but not necessarily. An operation
+ * could also be a clipping for example.
  *
- * @author Johann Sorel (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
 public abstract class GeometryTransform {
+    /**
+     * The factory to use for creating geometries.
+     */
+    private final GeometryFactory geometryFactory;
 
-    protected final GeometryFactory gf;
-    protected final CoordinateSequenceFactory csf;
+    /**
+     * The factory to use for creating sequences of coordinate tuples.
+     */
+    protected final CoordinateSequenceFactory coordinateFactory;
 
-    public GeometryTransform() {
-        this((CoordinateSequenceFactory) null);
+    protected GeometryTransform() {
+        this((GeometryFactory) null);
     }
 
-    public GeometryTransform(final CoordinateSequenceFactory csf) {
-        if (csf == null) {
-            this.gf = new GeometryFactory();
-            this.csf = gf.getCoordinateSequenceFactory();
+    protected GeometryTransform(final CoordinateSequenceFactory factory) {
+        if (factory == null) {
+            geometryFactory   = new GeometryFactory();
+            coordinateFactory = geometryFactory.getCoordinateSequenceFactory();
         } else {
-            this.csf = csf;
-            this.gf = new GeometryFactory(csf);
+            coordinateFactory = factory;
+            geometryFactory   = new GeometryFactory(factory);
         }
     }
 
-    public GeometryTransform(final GeometryFactory gf) {
-        if (gf == null) {
-            this.gf = new GeometryFactory();
-            this.csf = gf.getCoordinateSequenceFactory();
-        } else {
-            this.csf = gf.getCoordinateSequenceFactory();
-            this.gf = gf;
+    protected GeometryTransform(GeometryFactory factory) {
+        if (factory == null) {
+            factory = new GeometryFactory();
         }
+        geometryFactory   = factory;
+        coordinateFactory = factory.getCoordinateSequenceFactory();
     }
 
     public Geometry transform(final Geometry geom) throws TransformException {
@@ -87,33 +93,32 @@ public abstract class GeometryTransform {
         } else if (geom instanceof GeometryCollection) {
             return transform((GeometryCollection) geom);
         } else {
-            throw new IllegalArgumentException("Geometry type is unknowed or 
null : " + geom);
+            throw new IllegalArgumentException("Geometry type is unknown or 
null: " + geom);
         }
     }
 
     protected Point transform(final Point geom) throws TransformException {
         final CoordinateSequence coord = geom.getCoordinateSequence();
-        return gf.createPoint(transform(coord, 1));
+        return geometryFactory.createPoint(transform(coord, 1));
     }
 
     protected MultiPoint transform(final MultiPoint geom) throws 
TransformException {
         final int nbGeom = geom.getNumGeometries();
-
         final Point[] subs = new Point[geom.getNumGeometries()];
         for (int i = 0; i < nbGeom; i++) {
             subs[i] = transform((Point) geom.getGeometryN(i));
         }
-        return gf.createMultiPoint(subs);
+        return geometryFactory.createMultiPoint(subs);
     }
 
     protected LineString transform(final LineString geom) throws 
TransformException {
         final CoordinateSequence seq = transform(geom.getCoordinateSequence(), 
2);
-        return gf.createLineString(seq);
+        return geometryFactory.createLineString(seq);
     }
 
     protected LinearRing transform(final LinearRing geom) throws 
TransformException {
         final CoordinateSequence seq = transform(geom.getCoordinateSequence(), 
4);
-        return gf.createLinearRing(seq);
+        return geometryFactory.createLinearRing(seq);
     }
 
     protected MultiLineString transform(final MultiLineString geom) throws 
TransformException {
@@ -121,7 +126,7 @@ public abstract class GeometryTransform {
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform((LineString) geom.getGeometryN(i));
         }
-        return gf.createMultiLineString(subs);
+        return geometryFactory.createMultiLineString(subs);
     }
 
     protected Polygon transform(final Polygon geom) throws TransformException {
@@ -130,7 +135,7 @@ public abstract class GeometryTransform {
         for (int i = 0; i < holes.length; i++) {
             holes[i] = transform((LinearRing) geom.getInteriorRingN(i));
         }
-        return gf.createPolygon(exterior, holes);
+        return geometryFactory.createPolygon(exterior, holes);
     }
 
     protected MultiPolygon transform(final MultiPolygon geom) throws 
TransformException {
@@ -138,7 +143,7 @@ public abstract class GeometryTransform {
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform((Polygon) geom.getGeometryN(i));
         }
-        return gf.createMultiPolygon(subs);
+        return geometryFactory.createMultiPolygon(subs);
     }
 
     protected GeometryCollection transform(final GeometryCollection geom) 
throws TransformException {
@@ -146,16 +151,16 @@ public abstract class GeometryTransform {
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform(geom.getGeometryN(i));
         }
-        return gf.createGeometryCollection(subs);
+        return geometryFactory.createGeometryCollection(subs);
     }
 
     /**
+     * Transforms the given sequence of coordinate tuples, producing a new 
sequence of tuples.
      *
-     * @param sequence Sequence to transform
-     * @param minpoints Minimum number of point to preserve
-     * @return transformed sequence
-     * @throws TransformException
+     * @param  sequence   sequence of coordinate tuples to transform.
+     * @param  minpoints  minimum number of points to preserve.
+     * @return the transformed sequence of coordinate tuples.
+     * @throws TransformException if an error occurred while transforming a 
tuple.
      */
     protected abstract CoordinateSequence transform(CoordinateSequence 
sequence, int minpoints) throws TransformException;
-
 }
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTS.java 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTS.java
new file mode 100644
index 0000000..019c7d1
--- /dev/null
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/JTS.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.feature.jts;
+
+import java.util.Map;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.util.Static;
+import org.apache.sis.util.Utilities;
+import org.locationtech.jts.geom.Geometry;
+
+
+/**
+ * Utilities for Java Topology Suite (JTS) objects.
+ * We use this class for functionalities not supported by Apache SIS with 
other libraries.
+ * For library-agnostic functionalities, see {@link 
org.apache.sis.internal.feature.Geometries} instead.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+public final class JTS extends Static {
+    /**
+     * Key used in {@linkplain Geometry#getUserData() user data} map for 
storing an instance of {@link CoordinateReferenceSystem}.
+     *
+     * @see #getCoordinateReferenceSystem(Geometry)
+     */
+    public static final String CRS_KEY = "CRS";
+
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private JTS() {
+    }
+
+    /**
+     * Gets the Coordinate Reference System (CRS) from the given geometry.
+     * This method expects the CRS to be stored in one the following ways:
+     *
+     * <ul>
+     *   <li>Geometry {@linkplain Geometry#getUserData() user data} is an 
instance of {@code CoordinateReferenceSystem}.</li>
+     *   <li>{@linkplain Geometry#getUserData() user data} is a (@link Map} 
with a value for the {@value #CRS_KEY} key.</li>
+     *   <li>Geometry SRID is strictly positive, in which case it is 
interpreted as an EPSG code.</li>
+     * </ul>
+     *
+     * If none of the above is valid, {@code null} is returned.
+     *
+     * @param  geometry the geometry from which to get the CRS, or {@code 
null}.
+     * @return the coordinate reference system, or {@code null} if none.
+     * @throws FactoryException if the CRS can not be created from the SRID 
code.
+     */
+    public static CoordinateReferenceSystem getCoordinateReferenceSystem(final 
Geometry geometry) throws FactoryException {
+        if (geometry != null) {
+            final Object userData = geometry.getUserData();
+            if (userData instanceof CoordinateReferenceSystem) {
+                return (CoordinateReferenceSystem) userData;
+            } else if (userData instanceof Map<?,?>) {
+                final Map<?,?> map = (Map<?,?>) userData;
+                final Object value = map.get(CRS_KEY);
+                if (value instanceof CoordinateReferenceSystem) {
+                    return (CoordinateReferenceSystem) value;
+                }
+            }
+            /*
+             * Fallback on SRID with the assumption that they are EPSG codes.
+             */
+            final int srid = geometry.getSRID();
+            if (srid > 0) {
+                return CRS.forCode(Constants.EPSG + ':' + srid);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Transform the given geometry to the specified Coordinate Reference 
System (CRS).
+     * If the given CRS or the given geometry is null, the geometry is 
returned unchanged.
+     * If the geometry has no Coordinate Reference System, a {@link 
TransformException} is thrown.
+     *
+     * @param  geometry   the geometry to transform, or {@code null}.
+     * @param  targetCRS  the target coordinate reference system, or {@code 
null}.
+     * @return the transformed geometry, or the same geometry if it is already 
in target CRS.
+     * @throws TransformException if the given geometry has no CRS or can not 
be transformed.
+     * @throws FactoryException if transformation to the target CRS can not be 
constructed.
+     */
+    public static Geometry transform(Geometry geometry, final 
CoordinateReferenceSystem targetCRS)
+            throws TransformException, FactoryException
+    {
+        if (geometry != null && targetCRS != null) {
+            final CoordinateReferenceSystem sourceCRS = 
getCoordinateReferenceSystem(geometry);
+            if (sourceCRS == null) {
+                throw new TransformException("Geometry CRS is undefined.");
+            }
+            if (!Utilities.equalsIgnoreMetadata(sourceCRS, targetCRS)) {
+                geometry = transform(geometry, CRS.findOperation(sourceCRS, 
targetCRS, null));
+            }
+        }
+        return geometry;
+    }
+
+    /**
+     * Transform the given geometry using the given coordinate operation.
+     * If the geometry or the operation is null, then the geometry is returned 
unchanged.
+     *
+     * @todo Handle antimeridian case.
+     *
+     * @param  geometry   the geometry to transform, or {@code null}.
+     * @param  operation  the coordinate operation to apply, or {@code null}.
+     * @return the transformed geometry, or the same geometry if it is already 
in target CRS.
+     * @throws TransformException if the given geometry can not be transformed.
+     */
+    public static Geometry transform(Geometry geometry, final 
CoordinateOperation operation) throws TransformException {
+        if (geometry != null && operation != null) {
+            geometry = transform(geometry, operation.getMathTransform());
+            geometry.setUserData(operation.getTargetCRS());
+        }
+        return geometry;
+    }
+
+    /**
+     * Transform the given geometry using the given math transform.
+     * If the geometry or the transform is null or identity, then the geometry 
is returned unchanged.
+     *
+     * @param  geometry   the geometry to transform, or {@code null}.
+     * @param  transform  the transform to apply, or {@code null}.
+     * @return the transformed geometry, or the same geometry if it is already 
in target CRS.
+     * @throws TransformException if the given geometry can not be transformed.
+     */
+    public static Geometry transform(Geometry geometry, MathTransform 
transform) throws TransformException {
+        if (geometry != null && transform != null) {
+            final GeometryCoordinateTransform gct = new 
GeometryCoordinateTransform(transform, geometry.getFactory());
+            geometry = gct.transform(geometry);
+        }
+        return geometry;
+    }
+}
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/package-info.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/package-info.java
new file mode 100644
index 0000000..190a783
--- /dev/null
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/jts/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Methods specific to the Java Topology Suite (JTS).
+ *
+ * <STRONG>Do not use!</STRONG>
+ *
+ * This package is for internal use by SIS only. Classes in this package
+ * may change in incompatible ways in any future version without notice.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @version 1.0
+ * @since   1.0
+ * @module
+ */
+package org.apache.sis.internal.feature.jts;
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java
 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java
index a6bb921..4105b5a 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/GeometriesTestCase.java
@@ -39,7 +39,7 @@ public abstract strictfp class GeometriesTestCase extends 
TestCase {
     /**
      * The factory to test.
      */
-    final Geometries<?> factory;
+    private final Geometries<?> factory;
 
     /**
      * The geometry created by the test. Provided for allowing sub-classes to 
perform additional verifications.
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java
index 4fedb0f..8347c6b 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java
@@ -16,17 +16,8 @@
  */
 package org.apache.sis.internal.feature;
 
-import java.util.Collections;
-import org.opengis.util.FactoryException;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.Geometry;
-import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.MultiLineString;
-import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -94,66 +85,4 @@ public final strictfp class JTSTest extends 
GeometriesTestCase {
                 new Coordinate(15, 11),
                 new Coordinate(13, 10)}, mp.getGeometryN(2).getCoordinates());
     }
-
-    /**
-     * Tests {@link JTS#tryGetCoordinateReferenceSystem(Object)}.
-     *
-     * @throws FactoryException if an EPSG code can not be resolved.
-     */
-    @Test
-    public void testGetCoordinateReferenceSystem() throws FactoryException {
-        final GeometryFactory gf = new GeometryFactory();
-        final Geometry geometry = gf.createPoint(new Coordinate(5, 6));
-
-        CoordinateReferenceSystem crs = 
factory.tryGetCoordinateReferenceSystem(geometry);
-        assertNull(crs);
-
-        // Test CRS as user data.
-        geometry.setUserData(CommonCRS.ED50.geographic());
-        assertEquals(CommonCRS.ED50.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
-
-        // Test CRS as map value.
-        geometry.setUserData(Collections.singletonMap(JTS.CRS_KEY, 
CommonCRS.NAD83.geographic()));
-        assertEquals(CommonCRS.NAD83.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
-
-        // Test CRS as srid.
-        geometry.setUserData(null);
-        geometry.setSRID(4326);
-        assertEquals(CommonCRS.WGS84.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
-    }
-
-    /**
-     * Tests {@link JTS#transform(org.locationtech.jts.geom.Geometry, 
org.opengis.referencing.crs.CoordinateReferenceSystem) }.
-     * Tests {@link JTS#transform(org.locationtech.jts.geom.Geometry, 
org.opengis.referencing.operation.CoordinateOperation) }.
-     * Tests {@link JTS#transform(org.locationtech.jts.geom.Geometry, 
org.opengis.referencing.operation.MathTransform) }.
-     */
-    @Test
-    public void testTransform() throws FactoryException, TransformException {
-        final GeometryFactory gf = new GeometryFactory();
-        final Geometry in = gf.createPoint(new Coordinate(5, 6));
-
-        // test exception when transforming geometry without CRS.
-        try {
-            JTS.transform(in, CommonCRS.WGS84.geographic());
-            fail("Geometry has no CRS, transform should have failed");
-        } catch (TransformException ex) {
-            //ok
-        }
-
-        // test axes inversion transform
-        in.setUserData(CommonCRS.WGS84.normalizedGeographic());
-        Geometry out = JTS.transform(in, CommonCRS.WGS84.geographic());
-        assertTrue(out instanceof Point);
-        assertEquals(6.0, ((Point) out).getX(), 0.0);
-        assertEquals(5.0, ((Point) out).getY(), 0.0);
-        assertEquals(CommonCRS.WGS84.geographic(), out.getUserData());
-
-        // test affine transform, user data must be preserved
-        final AffineTransform2D trs = new AffineTransform2D(1,0,0,1,10,20);
-        out = JTS.transform(in, trs);
-        assertTrue(out instanceof Point);
-        assertEquals(15.0, ((Point) out).getX(), 0.0);
-        assertEquals(26.0, ((Point) out).getY(), 0.0);
-        assertEquals(CommonCRS.WGS84.normalizedGeographic(), 
out.getUserData());
-    }
 }
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
similarity index 59%
copy from 
core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java
copy to 
core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
index 4fedb0f..4959846 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/JTSTest.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.feature;
+package org.apache.sis.internal.feature.jts;
 
 import java.util.Collections;
 import org.opengis.util.FactoryException;
@@ -24,9 +24,9 @@ import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Coordinate;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.MultiLineString;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -35,68 +35,14 @@ import static org.junit.Assert.*;
 /**
  * Tests {@link JTS} implementation.
  *
- * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
  * @module
  */
-public final strictfp class JTSTest extends GeometriesTestCase {
+public final strictfp class JTSTest extends TestCase {
     /**
-     * Creates a new test case.
-     */
-    public JTSTest() {
-        super(new JTS());
-    }
-
-    /**
-     * Tests {@link JTS#createPolyline(int, Vector...)}.
-     */
-    @Test
-    @Override
-    public void testCreatePolyline() {
-        super.testCreatePolyline();
-        final MultiLineString mp = (MultiLineString) geometry;
-        assertEquals("numGeometries", 2, mp.getNumGeometries());
-        verifyTwoFirstGeometries(mp);
-    }
-
-    /**
-     * Verifies the coordinates of the two first geometries of the given multi 
line string.
-     * If there is more than 2 geometries, it is caller responsibility to 
verify the other ones.
-     */
-    private static void verifyTwoFirstGeometries(final MultiLineString mp) {
-        assertArrayEquals(new Coordinate[] {
-                new Coordinate(4, 5),
-                new Coordinate(7, 9),
-                new Coordinate(9, 3),
-                new Coordinate(4, 5)}, mp.getGeometryN(0).getCoordinates());
-
-        assertArrayEquals(new Coordinate[] {
-                new Coordinate(-3, -2),
-                new Coordinate(-2, -5),
-                new Coordinate(-1, -6)}, mp.getGeometryN(1).getCoordinates());
-    }
-
-    /**
-     * Tests {@link Geometries#tryMergePolylines(Object, Iterator)}.
-     */
-    @Test
-    @Override
-    public void testTryMergePolylines() {
-        super.testTryMergePolylines();
-        final MultiLineString mp = (MultiLineString) geometry;
-        assertEquals("numGeometries", 3, mp.getNumGeometries());
-        verifyTwoFirstGeometries(mp);
-        assertArrayEquals(new Coordinate[] {
-                new Coordinate(13, 11),
-                new Coordinate(14, 12),
-                new Coordinate(15, 11),
-                new Coordinate(13, 10)}, mp.getGeometryN(2).getCoordinates());
-    }
-
-    /**
-     * Tests {@link JTS#tryGetCoordinateReferenceSystem(Object)}.
+     * Tests {@link JTS#getCoordinateReferenceSystem(Geometry)}.
      *
      * @throws FactoryException if an EPSG code can not be resolved.
      */
@@ -105,21 +51,21 @@ public final strictfp class JTSTest extends 
GeometriesTestCase {
         final GeometryFactory gf = new GeometryFactory();
         final Geometry geometry = gf.createPoint(new Coordinate(5, 6));
 
-        CoordinateReferenceSystem crs = 
factory.tryGetCoordinateReferenceSystem(geometry);
+        CoordinateReferenceSystem crs = 
JTS.getCoordinateReferenceSystem(geometry);
         assertNull(crs);
 
         // Test CRS as user data.
         geometry.setUserData(CommonCRS.ED50.geographic());
-        assertEquals(CommonCRS.ED50.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
+        assertEquals(CommonCRS.ED50.geographic(), 
JTS.getCoordinateReferenceSystem(geometry));
 
         // Test CRS as map value.
         geometry.setUserData(Collections.singletonMap(JTS.CRS_KEY, 
CommonCRS.NAD83.geographic()));
-        assertEquals(CommonCRS.NAD83.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
+        assertEquals(CommonCRS.NAD83.geographic(), 
JTS.getCoordinateReferenceSystem(geometry));
 
         // Test CRS as srid.
         geometry.setUserData(null);
         geometry.setSRID(4326);
-        assertEquals(CommonCRS.WGS84.geographic(), 
factory.tryGetCoordinateReferenceSystem(geometry));
+        assertEquals(CommonCRS.WGS84.geographic(), 
JTS.getCoordinateReferenceSystem(geometry));
     }
 
     /**
@@ -154,6 +100,5 @@ public final strictfp class JTSTest extends 
GeometriesTestCase {
         assertTrue(out instanceof Point);
         assertEquals(15.0, ((Point) out).getX(), 0.0);
         assertEquals(26.0, ((Point) out).getY(), 0.0);
-        assertEquals(CommonCRS.WGS84.normalizedGeographic(), 
out.getUserData());
     }
 }
diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
 
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 3c18e56..f2aa5c8 100644
--- 
a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ 
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -55,6 +55,7 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.feature.Java2DTest.class,
     org.apache.sis.internal.feature.ESRITest.class,
     org.apache.sis.internal.feature.JTSTest.class,
+    org.apache.sis.internal.feature.jts.JTSTest.class,
     org.apache.sis.feature.builder.CharacteristicTypeBuilderTest.class,
     org.apache.sis.feature.builder.AttributeTypeBuilderTest.class,
     org.apache.sis.feature.builder.AssociationRoleBuilderTest.class,

Reply via email to