sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Transform coordinates of JTS geometries by batches instead than one-by-one. Take in account the region of interest of geometries to transform.
Date Wed, 05 Jun 2019 14:04:37 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 51fc7a3  Transform coordinates of JTS geometries by batches instead than one-by-one.
Take in account the region of interest of geometries to transform.
51fc7a3 is described below

commit 51fc7a32c06cf1126686663cda2807f566e6a2d3
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Jun 5 15:51:12 2019 +0200

    Transform coordinates of JTS geometries by batches instead than one-by-one.
    Take in account the region of interest of geometries to transform.
---
 .../feature/jts/GeometryCoordinateTransform.java   |  80 +++++-----
 .../internal/feature/jts/GeometryTransform.java    | 161 ++++++++++++++-------
 .../org/apache/sis/internal/feature/jts/JTS.java   |  30 +++-
 .../apache/sis/internal/feature/jts/JTSTest.java   |  11 +-
 4 files changed, 183 insertions(+), 99 deletions(-)

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 bc73bcb..d1f2f61 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
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.feature.jts;
 
 import org.locationtech.jts.geom.CoordinateSequence;
-import org.locationtech.jts.geom.CoordinateSequenceFactory;
 import org.locationtech.jts.geom.GeometryFactory;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
@@ -26,6 +25,7 @@ import org.opengis.referencing.operation.TransformException;
 /**
  * A geometry transformer which uses a {@link MathTransform} for changing coordinate values.
  * This class does not change the number of points.
+ * This class is not thread-safe.
  *
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
@@ -38,45 +38,55 @@ final class GeometryCoordinateTransform extends GeometryTransform {
      */
     private final MathTransform transform;
 
-    public GeometryCoordinateTransform(MathTransform transform) {
-        this.transform = transform;
-    }
-
-    public GeometryCoordinateTransform(MathTransform transform, final CoordinateSequenceFactory
csf) {
-        super(csf);
-        this.transform = transform;
-    }
+    /**
+     * A temporary buffer holding coordinates to transform.
+     * Created when first needed in order to have an estimation of size needed.
+     */
+    private double[] coordinates;
 
-    public GeometryCoordinateTransform(MathTransform transform, final GeometryFactory gf)
{
-        super(gf);
+    /**
+     * Creates a new geometry transformer using the given coordinate transform.
+     * It is caller's responsibility to ensure that the number of source and target dimensions
+     * of the given transform are equal to the number of dimensions of the geometries to
transform.
+     *
+     * @param  transform  the transform to apply on coordinate values.
+     * @param  factory    the factory to use for creating geometries. Shall not be null.
+     */
+    GeometryCoordinateTransform(final MathTransform transform, final GeometryFactory factory)
{
+        super(factory);
         this.transform = transform;
     }
 
+    /**
+     * Transforms the given sequence of coordinate tuples, producing a new sequence of tuples.
+     * This method tries to transform coordinates in batches, in order to reduce the amount
of
+     * calls to {@link MathTransform#transform(double[], int, double[], int, int)}.
+     *
+     * @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.
+     */
     @Override
-    protected CoordinateSequence transform(CoordinateSequence in, int minpoints) throws TransformException
{
-        final int dim = in.getDimension();
-        final int size = in.size();
-        final CoordinateSequence out = coordinateFactory.create(size, dim);
-
-        final double[] val = new double[dim];
-        for (int i = 0; i<size; i++) {
-            switch (dim) {
-                case 3 :
-                    val[0] = in.getOrdinate(i, 0);
-                    val[1] = in.getOrdinate(i, 1);
-                    val[2] = in.getOrdinate(i, 2);
-                    transform.transform(val, 0, val, 0, 1);
-                    out.setOrdinate(i, 0, val[0]);
-                    out.setOrdinate(i, 1, val[1]);
-                    out.setOrdinate(i, 2, val[2]);
-                    break;
-                default :
-                    val[0] = in.getOrdinate(i, 0);
-                    val[1] = in.getOrdinate(i, 1);
-                    transform.transform(val, 0, val, 0, 1);
-                    out.setOrdinate(i, 0, val[0]);
-                    out.setOrdinate(i, 1, val[1]);
-                    break;
+    protected CoordinateSequence transform(final CoordinateSequence sequence, final int minPoints)
throws TransformException {
+        final int srcDim   = transform.getSourceDimensions();
+        final int tgtDim   = transform.getTargetDimensions();
+        final int maxDim   = Math.max(srcDim, tgtDim);
+        final int count    = sequence.size();
+        final int capacity = Math.max(4, Math.min(100, count));
+        final CoordinateSequence out = coordinateFactory.create(count, sequence.getDimension());
+        if (coordinates == null || coordinates.length / maxDim < capacity) {
+            coordinates = new double[capacity * maxDim];
+        }
+        for (int base=0, n; (n = Math.min(count - base, capacity)) > 0; base += n) {
+            int batch = n * srcDim;
+            for (int i=0; i<batch; i++) {
+                coordinates[i] = sequence.getOrdinate(base + i/srcDim, i % srcDim);
+            }
+            transform.transform(coordinates, 0, coordinates, 0, n);
+            batch = n * tgtDim;
+            for (int i=0; i<batch; i++) {
+                out.setOrdinate(base + i/tgtDim, i % tgtDim, coordinates[i]);
             }
         }
         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 4fca59d..eafb9c2 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
@@ -29,13 +29,15 @@ import org.locationtech.jts.geom.MultiPolygon;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
 import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.Classes;
 
 
 /**
  * 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.
+ * 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)
  * @version 1.0
@@ -53,75 +55,87 @@ public abstract class GeometryTransform {
      */
     protected final CoordinateSequenceFactory coordinateFactory;
 
-    protected GeometryTransform() {
-        this((GeometryFactory) null);
-    }
-
-    protected GeometryTransform(final CoordinateSequenceFactory factory) {
-        if (factory == null) {
-            geometryFactory   = new GeometryFactory();
-            coordinateFactory = geometryFactory.getCoordinateSequenceFactory();
-        } else {
-            coordinateFactory = factory;
-            geometryFactory   = new GeometryFactory(factory);
-        }
-    }
-
-    protected GeometryTransform(GeometryFactory factory) {
-        if (factory == null) {
-            factory = new GeometryFactory();
-        }
+    /**
+     * Creates a new operation using the given factory.
+     *
+     * @param  factory  the factory to use for creating geometries. Shall not be null.
+     */
+    protected GeometryTransform(final GeometryFactory factory) {
         geometryFactory   = factory;
         coordinateFactory = factory.getCoordinateSequenceFactory();
     }
 
+    /**
+     * Transforms the given geometry. This method delegates to one of the {@code transform(…)}
methods
+     * based on the type of the given geometry.
+     *
+     * @param  geom  the geometry to transform.
+     * @return the transformed geometry.
+     * @throws TransformException if an error occurred while transforming the geometry.
+     */
     public Geometry transform(final Geometry geom) throws TransformException {
-        if (geom instanceof Point) {
-            return transform((Point) geom);
-        } else if (geom instanceof MultiPoint) {
-            return transform((MultiPoint) geom);
-        } else if (geom instanceof LineString) {
-            return transform((LineString) geom);
-        } else if (geom instanceof LinearRing) {
-            return transform((LinearRing) geom);
-        } else if (geom instanceof MultiLineString) {
-            return transform((MultiLineString) geom);
-        } else if (geom instanceof Polygon) {
-            return transform((Polygon) geom);
-        } else if (geom instanceof MultiPolygon) {
-            return transform((MultiPolygon) geom);
-        } else if (geom instanceof GeometryCollection) {
-            return transform((GeometryCollection) geom);
-        } else {
-            throw new IllegalArgumentException("Geometry type is unknown or null: " + geom);
-        }
+        if (geom instanceof Point)              return transform((Point)              geom);
+        if (geom instanceof MultiPoint)         return transform((MultiPoint)         geom);
+        if (geom instanceof LinearRing)         return transform((LinearRing)         geom);
   // Must be tested before LineString.
+        if (geom instanceof LineString)         return transform((LineString)         geom);
+        if (geom instanceof MultiLineString)    return transform((MultiLineString)    geom);
+        if (geom instanceof Polygon)            return transform((Polygon)            geom);
+        if (geom instanceof MultiPolygon)       return transform((MultiPolygon)       geom);
+        if (geom instanceof GeometryCollection) return transform((GeometryCollection) geom);
+        throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, Classes.getClass(geom)));
     }
 
-    protected Point transform(final Point geom) throws TransformException {
+    /**
+     * Transforms the given point. Can be invoked directly if the type is known at compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the point to transform.
+     * @return the transformed point.
+     * @throws TransformException if an error occurred while transforming the geometry.
+     */
+    public Point transform(final Point geom) throws TransformException {
         final CoordinateSequence coord = geom.getCoordinateSequence();
         return geometryFactory.createPoint(transform(coord, 1));
     }
 
-    protected MultiPoint transform(final MultiPoint geom) throws TransformException {
-        final int nbGeom = geom.getNumGeometries();
+    /**
+     * Transforms the given points. Can be invoked directly if the type is known at compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the points to transform.
+     * @return the transformed points.
+     * @throws TransformException if an error occurred while transforming a geometry.
+     */
+    public MultiPoint transform(final MultiPoint geom) throws TransformException {
         final Point[] subs = new Point[geom.getNumGeometries()];
-        for (int i = 0; i < nbGeom; i++) {
+        for (int i = 0; i < subs.length; i++) {
             subs[i] = transform((Point) geom.getGeometryN(i));
         }
         return geometryFactory.createMultiPoint(subs);
     }
 
-    protected LineString transform(final LineString geom) throws TransformException {
+    /**
+     * Transforms the given line string. Can be invoked directly if the type is known at
compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the line string to transform.
+     * @return the transformed line string.
+     * @throws TransformException if an error occurred while transforming the geometry.
+     */
+    public LineString transform(final LineString geom) throws TransformException {
         final CoordinateSequence seq = transform(geom.getCoordinateSequence(), 2);
         return geometryFactory.createLineString(seq);
     }
 
-    protected LinearRing transform(final LinearRing geom) throws TransformException {
-        final CoordinateSequence seq = transform(geom.getCoordinateSequence(), 4);
-        return geometryFactory.createLinearRing(seq);
-    }
-
-    protected MultiLineString transform(final MultiLineString geom) throws TransformException
{
+    /**
+     * Transforms the given line strings. Can be invoked directly if the type is known at
compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the line strings to transform.
+     * @return the transformed line strings.
+     * @throws TransformException if an error occurred while transforming a geometry.
+     */
+    public MultiLineString transform(final MultiLineString geom) throws TransformException
{
         final LineString[] subs = new LineString[geom.getNumGeometries()];
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform((LineString) geom.getGeometryN(i));
@@ -129,7 +143,28 @@ public abstract class GeometryTransform {
         return geometryFactory.createMultiLineString(subs);
     }
 
-    protected Polygon transform(final Polygon geom) throws TransformException {
+    /**
+     * Transforms the given linear ring. Can be invoked directly if the type is known at
compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the linear ring to transform.
+     * @return the transformed linear ring.
+     * @throws TransformException if an error occurred while transforming the geometry.
+     */
+    public LinearRing transform(final LinearRing geom) throws TransformException {
+        final CoordinateSequence seq = transform(geom.getCoordinateSequence(), 4);
+        return geometryFactory.createLinearRing(seq);
+    }
+
+    /**
+     * Transforms the given polygon. Can be invoked directly if the type is known at compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the polygon to transform.
+     * @return the transformed polygon.
+     * @throws TransformException if an error occurred while transforming the geometry.
+     */
+    public Polygon transform(final Polygon geom) throws TransformException {
         final LinearRing exterior = transform((LinearRing) geom.getExteriorRing());
         final LinearRing[] holes = new LinearRing[geom.getNumInteriorRing()];
         for (int i = 0; i < holes.length; i++) {
@@ -138,7 +173,15 @@ public abstract class GeometryTransform {
         return geometryFactory.createPolygon(exterior, holes);
     }
 
-    protected MultiPolygon transform(final MultiPolygon geom) throws TransformException {
+    /**
+     * Transforms the given polygons. Can be invoked directly if the type is known at compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the polygons to transform.
+     * @return the transformed polygons.
+     * @throws TransformException if an error occurred while transforming a geometry.
+     */
+    public MultiPolygon transform(final MultiPolygon geom) throws TransformException {
         final Polygon[] subs = new Polygon[geom.getNumGeometries()];
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform((Polygon) geom.getGeometryN(i));
@@ -146,7 +189,15 @@ public abstract class GeometryTransform {
         return geometryFactory.createMultiPolygon(subs);
     }
 
-    protected GeometryCollection transform(final GeometryCollection geom) throws TransformException
{
+    /**
+     * Transforms the given geometries. Can be invoked directly if the type is known at compile-time,
+     * or indirectly through a call to the more generic {@link #transform(Geometry)} method.
+     *
+     * @param  geom  the geometries to transform.
+     * @return the transformed geometries.
+     * @throws TransformException if an error occurred while transforming a geometry.
+     */
+    public GeometryCollection transform(final GeometryCollection geom) throws TransformException
{
         final Geometry[] subs = new Geometry[geom.getNumGeometries()];
         for (int i = 0; i < subs.length; i++) {
             subs[i] = transform(geom.getGeometryN(i));
@@ -158,9 +209,9 @@ public abstract class GeometryTransform {
      * Transforms the given sequence of coordinate tuples, producing a new sequence of tuples.
      *
      * @param  sequence   sequence of coordinate tuples to transform.
-     * @param  minpoints  minimum number of points to preserve.
+     * @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;
+    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
index 019c7d1..af9d29a 100644
--- 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
@@ -26,6 +26,12 @@ 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.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+import org.apache.sis.geometry.Envelope2D;
+import org.apache.sis.internal.system.Loggers;
+import org.locationtech.jts.geom.Envelope;
 import org.locationtech.jts.geom.Geometry;
 
 
@@ -34,6 +40,9 @@ import org.locationtech.jts.geom.Geometry;
  * 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.
  *
+ * <p>This method may be modified or removed in any future version.
+ * For example we may replace it by a more general mechanism working also on other geometry
libraries.</p>
+ *
  * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   1.0
@@ -45,7 +54,7 @@ public final class JTS extends Static {
      *
      * @see #getCoordinateReferenceSystem(Geometry)
      */
-    public static final String CRS_KEY = "CRS";
+    static final String CRS_KEY = "CRS";
 
     /**
      * Do not allow instantiation of this class.
@@ -93,10 +102,15 @@ public final class JTS extends Static {
     }
 
     /**
-     * Transform the given geometry to the specified Coordinate Reference System (CRS).
+     * Transforms 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.
      *
+     * <p><b>This operation may be slow!</b>
+     * If many geometries need to be transformed, it is better to fetch the {@link CoordinateOperation}
only once,
+     * then invoke {@link #transform(Geometry, CoordinateOperation)} for each geometry. Alternatively
the geometries
+     * can be stored in a single geometry collection in order to invoke this method only
once.</p>
+     *
      * @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.
@@ -109,10 +123,18 @@ public final class JTS extends Static {
         if (geometry != null && targetCRS != null) {
             final CoordinateReferenceSystem sourceCRS = getCoordinateReferenceSystem(geometry);
             if (sourceCRS == null) {
-                throw new TransformException("Geometry CRS is undefined.");
+                throw new TransformException(Errors.format(Errors.Keys.UnspecifiedCRS));
             }
             if (!Utilities.equalsIgnoreMetadata(sourceCRS, targetCRS)) {
-                geometry = transform(geometry, CRS.findOperation(sourceCRS, targetCRS, null));
+                DefaultGeographicBoundingBox areaOfInterest = new DefaultGeographicBoundingBox();
+                try {
+                    final Envelope e = geometry.getEnvelopeInternal();
+                    areaOfInterest.setBounds(new Envelope2D(sourceCRS, e.getMinX(), e.getMinY(),
e.getWidth(), e.getHeight()));
+                } catch (TransformException ex) {
+                    areaOfInterest = null;
+                    Logging.ignorableException(Logging.getLogger(Loggers.GEOMETRY), JTS.class,
"transform", ex);
+                }
+                geometry = transform(geometry, CRS.findOperation(sourceCRS, targetCRS, areaOfInterest));
             }
         }
         return geometry;
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
index 87d3d70..171340b 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/feature/jts/JTSTest.java
@@ -48,8 +48,8 @@ public final strictfp class JTSTest extends TestCase {
      */
     @Test
     public void testGetCoordinateReferenceSystem() throws FactoryException {
-        final GeometryFactory gf = new GeometryFactory();
-        final Geometry geometry = gf.createPoint(new Coordinate(5, 6));
+        final GeometryFactory factory = new GeometryFactory();
+        final Geometry geometry = factory.createPoint(new Coordinate(5, 6));
 
         CoordinateReferenceSystem crs = JTS.getCoordinateReferenceSystem(geometry);
         assertNull(crs);
@@ -72,7 +72,8 @@ public final strictfp class JTSTest extends TestCase {
     }
 
     /**
-     * Tests various {@code transform} methods. This include (sometime indirectly):
+     * Tests various {@code transform} methods. This includes (sometime indirectly):
+     *
      * <ul>
      *   <li>{@link JTS#transform(Geometry, CoordinateReferenceSystem)}</li>
      *   <li>{@link JTS#transform(Geometry, CoordinateOperation)}</li>
@@ -84,8 +85,8 @@ public final strictfp class JTSTest extends TestCase {
      */
     @Test
     public void testTransform() throws FactoryException, TransformException {
-        final GeometryFactory gf = new GeometryFactory();
-        final Geometry in = gf.createPoint(new Coordinate(5, 6));
+        final GeometryFactory factory = new GeometryFactory();
+        final Geometry in = factory.createPoint(new Coordinate(5, 6));
         /*
          * Test exception when transforming geometry without CRS.
          */


Mime
View raw message