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: More robust determination of whether an axis should be handled as a wraparound axis.
Date Tue, 15 Sep 2020 19:06:08 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 472b2c0  More robust determination of whether an axis should be handled as a wraparound
axis.
472b2c0 is described below

commit 472b2c0f87d467b6e85c94fc12c86dc5ef2f23a2
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Sep 15 21:05:23 2020 +0200

    More robust determination of whether an axis should be handled as a wraparound axis.
---
 .../coverage/grid/CoordinateOperationFinder.java   | 83 ++++++++++---------
 .../sis/geometry/AbstractDirectPosition.java       | 34 +++++++-
 .../org/apache/sis/geometry/AbstractEnvelope.java  |  5 --
 .../internal/referencing/DirectPositionView.java   | 22 -----
 .../internal/referencing/WraparoundAdjustment.java |  6 +-
 .../internal/referencing/WraparoundTransform.java  | 93 ++++++++++++----------
 .../referencing/WraparoundTransformTest.java       | 10 +--
 7 files changed, 133 insertions(+), 120 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
index c92cc6e..eab1616 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/CoordinateOperationFinder.java
@@ -24,7 +24,6 @@ import org.opengis.geometry.Envelope;
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.datum.PixelInCell;
-import org.opengis.referencing.cs.RangeMeaning;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.CoordinateOperation;
@@ -35,8 +34,8 @@ import org.apache.sis.internal.referencing.CoordinateOperations;
 import org.apache.sis.internal.referencing.WraparoundTransform;
 import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.geometry.AbstractDirectPosition;
 import org.apache.sis.geometry.Envelopes;
-import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.image.ImageProcessor;
 import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
@@ -308,18 +307,15 @@ final class CoordinateOperationFinder implements Supplier<double[]>
{
          * which may be identity. A wraparound may be applied for keeping target coordinates
inside the expected
          * target domain.
          */
-        if (forwardOp == null) {
+apply:  if (forwardOp == null) {
             forwardOp = operation.getMathTransform();
             final CoordinateSystem cs = operation.getTargetCRS().getCoordinateSystem();
-wraparound: if (mayRequireWraparound(cs)) {
-                DirectPosition median = median(target);
-                if (median == null) {
-                    median = median(source);
-                    if (median == null) break wraparound;
-                    median = forwardOp.transform(median, null);
-                }
-                forwardOp = WraparoundTransform.forDomainOfUse(forwardOp, cs, median);
+            DirectPosition median = median(target, null);
+            if (median == null) {
+                median = median(source, forwardOp);
+                if (median == null) break apply;
             }
+            forwardOp = WraparoundTransform.forDomainOfUse(forwardOp, cs, median);
         }
         return MathTransforms.concatenate(tr, forwardOp);
     }
@@ -415,48 +411,51 @@ check:  if (gridToCRS != null) {
         if (!isWraparoundApplied) {
             isWraparoundApplied = true;
             final CoordinateSystem cs = operation.getSourceCRS().getCoordinateSystem();
-            if (mayRequireWraparound(cs)) {
-                final DirectPosition median = median(source);
-                if (median != null) {
-                    inverseOp = WraparoundTransform.forDomainOfUse(inverseOp, cs, median);
-                    crsToGrid = MathTransforms.concatenate(inverseOp, tr);
-                }
+            final DirectPosition median = median(source, null);
+            if (median != null) {
+                inverseOp = WraparoundTransform.forDomainOfUse(inverseOp, cs, median);
+                crsToGrid = MathTransforms.concatenate(inverseOp, tr);
             }
         }
         return crsToGrid;
     }
 
     /**
-     * Returns {@code true} if a transform to the specified target coordinate system may
require handling
-     * of wraparound axes. A {@code true} return value does not mean that wraparounds actually
happen;
-     * it only means that more expensive checks will be required.
-     */
-    private static boolean mayRequireWraparound(final CoordinateSystem cs) {
-        final int dimension = cs.getDimension();
-        for (int i=0; i<dimension; i++) {
-            if (RangeMeaning.WRAPAROUND.equals(cs.getAxis(i).getRangeMeaning())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Returns the point of interest converted to the Coordinate Reference System.
      * If the grid does not define a point of interest or does not define a CRS,
      * then this method returns {@code null}.
+     *
+     * @param  grid       the source or target grid providing the point of interest.
+     * @param  forwardOp  transform from source CRS to target CRS, or {@code null} if none.
      */
-    private static DirectPosition median(final GridGeometry grid) throws TransformException
{
-        if (grid.isDefined(GridGeometry.EXTENT | GridGeometry.GRID_TO_CRS)) {
-            final double[] poi = grid.getExtent().getPointOfInterest();
-            if (poi != null) {
-                final MathTransform tr = grid.getGridToCRS(PixelInCell.CELL_CENTER);
-                final GeneralDirectPosition median = new GeneralDirectPosition(tr.getTargetDimensions());
-                tr.transform(poi, 0, median.coordinates, 0, 1);
-                return median;
-            }
+    private static DirectPosition median(final GridGeometry grid, final MathTransform forwardOp)
throws TransformException {
+        if (!grid.isDefined(GridGeometry.EXTENT | GridGeometry.GRID_TO_CRS)) {
+            return null;
         }
-        return null;
+        return new AbstractDirectPosition() {
+            /** The coordinates, computed when first needed. */
+            private double[] coordinates;
+
+            @Override public int    getDimension()     {return coordinates().length;}
+            @Override public double getOrdinate(int i) {return coordinates()[i];}
+
+            /** Returns the coordinate tuple. */
+            @SuppressWarnings("ReturnOfCollectionOrArrayField")
+            private double[] coordinates() {
+                if (coordinates == null) try {
+                    final double[] poi = grid.getExtent().getPointOfInterest();
+                    MathTransform tr = grid.getGridToCRS(PixelInCell.CELL_CENTER);
+                    if (forwardOp != null) {
+                        tr = MathTransforms.concatenate(tr, forwardOp);
+                    }
+                    coordinates = new double[tr.getTargetDimensions()];
+                    tr.transform(poi, 0, coordinates, 0, 1);
+                } catch (TransformException e) {
+                    throw new BackingStoreException(e);
+                }
+                return coordinates;
+            }
+        };
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
index da831a5..2271ac2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
@@ -57,7 +57,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
  * serializable, is left to subclasses.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */
@@ -98,6 +98,21 @@ public abstract class AbstractDirectPosition extends FormattableObject
implement
     }
 
     /**
+     * Returns the coordinate reference system in which the coordinate tuple is given.
+     * May be {@code null} if this particular {@code DirectPosition} is included in a larger
object
+     * with such a reference to a {@linkplain CoordinateReferenceSystem coordinate reference
system}.
+     *
+     * <p>The default implementation returns {@code null}.
+     * Subclasses should override this method if the CRS can be provided.</p>
+     *
+     * @return the coordinate reference system, or {@code null}.
+     */
+    @Override
+    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+        return null;
+    }
+
+    /**
      * Returns a sequence of numbers that hold the coordinate of this position in its reference
system.
      *
      * @return the coordinates.
@@ -112,6 +127,23 @@ public abstract class AbstractDirectPosition extends FormattableObject
implement
     }
 
     /**
+     * Sets the coordinate value along the specified dimension.
+     *
+     * <p>The default implementation throws {@link UnsupportedOperationException}.
+     * Subclasses need to override this method if this direct position is mutable.</p>
+     *
+     * @param  dimension  the dimension for the coordinate of interest.
+     * @param  value      the coordinate value of interest.
+     * @throws IndexOutOfBoundsException if the given index is negative or is equals or greater
+     *         than the {@linkplain #getDimension() position dimension}.
+     * @throws UnsupportedOperationException if this direct position is immutable.
+     */
+    @Override
+    public void setOrdinate(int dimension, double value) {
+        throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1,
getClass()));
+    }
+
+    /**
      * Sets this direct position to the given position. If the given position is
      * {@code null}, then all coordinate values are set to {@link Double#NaN NaN}.
      *
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
index 2a40240..077acaa 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
@@ -1306,11 +1306,6 @@ public abstract class AbstractEnvelope extends FormattableObject implements
Enve
         @Override public double getOrdinate(final int dimension) throws IndexOutOfBoundsException
{
             return getMedian(dimension);
         }
-
-        /** Unsupported operation. */
-        @Override public void setOrdinate(int dimension, double value) {
-            throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1,
getClass()));
-        }
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DirectPositionView.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DirectPositionView.java
index 1562c5d..1c115bd 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DirectPositionView.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DirectPositionView.java
@@ -17,7 +17,6 @@
 package org.apache.sis.internal.referencing;
 
 import java.util.Arrays;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.geometry.AbstractDirectPosition;
 
 
@@ -54,16 +53,6 @@ public abstract class DirectPositionView extends AbstractDirectPosition
{
     }
 
     /**
-     * Returns {@code null} since there is no CRS associated with this position.
-     *
-     * @return {@code null}.
-     */
-    @Override
-    public final CoordinateReferenceSystem getCoordinateReferenceSystem() {
-        return null;
-    }
-
-    /**
      * Returns the dimension given at construction time.
      *
      * @return number of dimensions.
@@ -74,17 +63,6 @@ public abstract class DirectPositionView extends AbstractDirectPosition
{
     }
 
     /**
-     * Do not allow any change.
-     *
-     * @param  dimension  ignored.
-     * @param  value      ignored.
-     */
-    @Override
-    public final void setOrdinate(final int dimension, final double value) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
      * The double-precision version of {@link DirectPositionView}.
      */
     public static final class Double extends DirectPositionView {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundAdjustment.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundAdjustment.java
index a84d79e..3f94331 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundAdjustment.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundAdjustment.java
@@ -116,9 +116,9 @@ public final class WraparoundAdjustment {
     }
 
     /**
-     * Returns the range (maximum - minimum) of the given axis if it has wraparound meaning,
-     * or {@link Double#NaN} otherwise. This method implements a fallback for the longitude
-     * axis if it does not declare the minimum and maximum values as expected.
+     * Returns the range (maximum - minimum) of axis in specified dimension if it has wraparound
meaning,
+     * or {@link Double#NaN} otherwise. This method implements a fallback for longitude axis
if it does
+     * not declare the minimum and maximum values as expected.
      *
      * @param  cs         the coordinate system for which to get wraparound range.
      * @param  dimension  dimension of the axis to test.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundTransform.java
index 19411d6..84439db 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WraparoundTransform.java
@@ -21,9 +21,7 @@ import java.util.function.Supplier;
 import java.util.function.IntToDoubleFunction;
 import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
-import org.opengis.referencing.cs.RangeMeaning;
 import org.opengis.referencing.cs.CoordinateSystem;
-import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
@@ -37,9 +35,7 @@ import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.io.wkt.Formatter;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
-import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.collection.BackingStoreException;
 
@@ -144,13 +140,13 @@ public final class WraparoundTransform extends AbstractMathTransform
{
      *
      * @param  op       the coordinate operation for which to get the math transform.
      * @return the math transform for the given coordinate operation.
+     * @throws TransformException if a coordinate can not be computed.
      */
-    public static MathTransform forTargetCRS(final CoordinateOperation op) {
+    public static MathTransform forTargetCRS(final CoordinateOperation op) throws TransformException
{
         MathTransform tr = op.getMathTransform();
         final CoordinateSystem targetCS = op.getTargetCRS().getCoordinateSystem();
         for (final int wraparoundDimension : CoordinateOperations.wrapAroundChanges(op))
{
-            tr = MathTransforms.concatenate(tr, create(targetCS.getDimension(),
-                    wraparoundDimension, targetCS.getAxis(wraparoundDimension), Double.NaN));
+            tr = concatenate(tr, wraparoundDimension, targetCS, null);
         }
         return tr;
     }
@@ -170,59 +166,72 @@ public final class WraparoundTransform extends AbstractMathTransform
{
      * @param  targetCS  the target coordinate system.
      * @param  median    the coordinates to put at the center of new ranges.
      * @return the math transform with wraparound if needed.
+     * @throws TransformException if a coordinate can not be computed.
      */
-    public static MathTransform forDomainOfUse(MathTransform tr, final CoordinateSystem targetCS,
final DirectPosition median) {
+    public static MathTransform forDomainOfUse(MathTransform tr, final CoordinateSystem targetCS,
+            final DirectPosition median) throws TransformException
+    {
         final int dimension = targetCS.getDimension();
         for (int i=0; i<dimension; i++) {
-            final CoordinateSystemAxis axis = targetCS.getAxis(i);
-            if (RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning())) {
-                tr = MathTransforms.concatenate(tr,
-                        create(dimension, i, targetCS.getAxis(i), median.getOrdinate(i)));
-            }
+            tr = concatenate(tr, i, targetCS, median);
         }
         return tr;
     }
 
     /**
-     * Creates a transform with a "wrap around" behavior in the given dimension.
-     * The wraparound is implemented by a concatenation of affine transform before
-     * and after the {@link WraparoundTransform} instance.
+     * Concatenates the given transform with a "wrap around" transform if applicable.
+     * The wraparound is implemented by concatenations of affine transforms before and
+     * after the {@link WraparoundTransform} instance.
+     * If there is no wraparound to apply, then this method returns {@code tr} unchanged.
      *
-     * @param  dimension            the number of source and target dimensions.
-     * @param  wraparoundDimension  the dimension where "wrap around" behavior apply.
-     * @param  axis                 the coordinate system axis in the "wrap around" dimension.
+     * @param  tr                   the transform to concatenate with a wraparound transform.
+     * @param  wraparoundDimension  the dimension where "wrap around" behavior may apply.
+     * @param  targetCS             the target coordinate system.
      * @param  median               the coordinate to put at the center of new range,
-     *                              or {@link Double#NaN} for standard center of given axis.
+     *                              or {@code null} for standard axis center.
      * @return the math transform with "wrap around" behavior in the specified dimension.
+     * @throws TransformException if a coordinate can not be computed.
      */
-    private static MathTransform create(final int dimension, final int wraparoundDimension,
-            final CoordinateSystemAxis axis, final double median)
+    private static MathTransform concatenate(final MathTransform tr, final int wraparoundDimension,
+            final CoordinateSystem targetCS, final DirectPosition median) throws TransformException
     {
-        ArgumentChecks.ensureStrictlyPositive("dimension", dimension);
-        ArgumentChecks.ensureBetween("wraparoundDimension", 0, dimension - 1, wraparoundDimension);
-        NoninvertibleTransformException cause = null;
-        final double minimum = axis.getMinimumValue();
-        final double maximum = axis.getMaximumValue();
-        final double span = maximum - minimum;
-        if (span > 0 && span != Double.POSITIVE_INFINITY) {
-            final MatrixSIS m = Matrices.createIdentity(dimension + 1);
-            m.setElement(wraparoundDimension, wraparoundDimension, span);
-            m.setElement(wraparoundDimension, dimension, Double.isNaN(median) ? minimum :
median - span/2);
-            final MathTransform denormalize = MathTransforms.linear(m);
-            final WraparoundTransform wraparound = create(dimension, wraparoundDimension);
+        final double span = WraparoundAdjustment.range(targetCS, wraparoundDimension);
+        if (!(span > 0 && span != Double.POSITIVE_INFINITY)) {
+            return tr;
+        }
+        double start;
+        if (median != null) {
             try {
+                start = median.getOrdinate(wraparoundDimension) + -0.5 * span;
+            } catch (BackingStoreException e) {
+                // Some implementations compute coordinates only when first needed.
+                throw e.unwrapOrRethrow(TransformException.class);
+            }
+            if (!Double.isFinite(start)) return tr;
+        } else {
+            start = targetCS.getAxis(wraparoundDimension).getMinimumValue();
+            if (!Double.isFinite(start)) {
                 /*
-                 * Do not use the 3-arguments method because we need to
-                 * control the order in which concatenation is applied.
+                 * May happen if `WraparoundAdjustment.range(…)` recognized a longitude
axis
+                 * despite the `CoordinateSystemAxis` not declarining minimum/maximum values.
+                 * Use 0 as the range center (e.g. center of [-180 … 180]° longitude range).
                  */
-                return MathTransforms.concatenate(denormalize.inverse(),
-                       MathTransforms.concatenate(wraparound, denormalize));
-            } catch (NoninvertibleTransformException e) {
-                // Matrix is non-invertible only if the range given in argument is illegal.
-                cause = e;
+                start = -0.5 * span;
             }
         }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, minimum,
maximum), cause);
+        final int dimension = tr.getTargetDimensions();
+        final MatrixSIS m = Matrices.createIdentity(dimension + 1);
+        m.setElement(wraparoundDimension, wraparoundDimension, span);
+        m.setElement(wraparoundDimension, dimension, start);
+        final MathTransform denormalize = MathTransforms.linear(m);
+        MathTransform wraparound = create(dimension, wraparoundDimension);
+        /*
+         * Do not use the 3-arguments method because we need to
+         * control the order in which concatenation is applied.
+         */
+        wraparound = MathTransforms.concatenate(denormalize.inverse(),
+                     MathTransforms.concatenate(wraparound, denormalize));
+        return MathTransforms.concatenate(tr, wraparound);
     }
 
     /**
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WraparoundTransformTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WraparoundTransformTest.java
index f860c10..0aad7e3 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WraparoundTransformTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WraparoundTransformTest.java
@@ -20,13 +20,13 @@ import java.util.List;
 import java.util.Collections;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.referencing.cs.AxesConvention;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.matrix.Matrix3;
 import org.apache.sis.referencing.operation.matrix.Matrix4;
-import org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -65,10 +65,10 @@ public final strictfp class WraparoundTransformTest extends TestCase {
     /**
      * Tests wraparound on one axis.
      *
-     * @throws NoninvertibleMatrixException if the expected matrix can not be inverted.
+     * @throws TransformException if a coordinate can not be computed.
      */
     @Test
-    public void testOneAxis() throws NoninvertibleMatrixException {
+    public void testOneAxis() throws TransformException {
         final AbstractCoordinateOperation op = new AbstractCoordinateOperation(
                 Collections.singletonMap(AbstractCoordinateOperation.NAME_KEY, "Wrapper"),
                 HardCodedCRS.WGS84_φλ.forConvention(AxesConvention.POSITIVE_RANGE),
@@ -108,10 +108,10 @@ public final strictfp class WraparoundTransformTest extends TestCase
{
      * transform between them. The absence of separation between the two {@link WraparoundTransform}s
is an
      * indirect test of {@link WraparoundTransform#tryConcatenate(boolean, MathTransform,
MathTransformFactory)}.
      *
-     * @throws NoninvertibleMatrixException if the expected matrix can not be inverted.
+     * @throws TransformException if a coordinate can not be computed.
      */
     @Test
-    public void testTwoAxes() throws NoninvertibleMatrixException {
+    public void testTwoAxes() throws TransformException {
         final AbstractCoordinateOperation op = new AbstractCoordinateOperation(
                 Collections.singletonMap(AbstractCoordinateOperation.NAME_KEY, "Wrapper"),
                 HardCodedCRS.WGS84_3D_TIME.forConvention(AxesConvention.POSITIVE_RANGE),


Mime
View raw message