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: Fix the 'gridToCRS' coefficients for non-linear transform and compute an envelope even if a grid range is [0 … 0].
Date Tue, 20 Nov 2018 15:53:26 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 d0a7fc7  Fix the 'gridToCRS' coefficients for non-linear transform and compute an
envelope even if a grid range is [0 … 0].
d0a7fc7 is described below

commit d0a7fc7c4c2ad94b9f19606142871cab04fe170e
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Nov 20 16:52:13 2018 +0100

    Fix the 'gridToCRS' coefficients for non-linear transform and compute an envelope even
if a grid range is [0 … 0].
---
 .../org/apache/sis/coverage/grid/GridExtent.java   | 74 ++++++++++++++++++++--
 .../org/apache/sis/coverage/grid/GridGeometry.java |  8 +--
 .../apache/sis/internal/referencing/Resources.java |  6 ++
 .../sis/internal/referencing/Resources.properties  |  1 +
 .../internal/referencing/Resources_fr.properties   |  1 +
 .../operation/transform/MathTransforms.java        |  2 +-
 .../operation/transform/TransformSeparator.java    | 39 +++++++-----
 .../transform/TransformSeparatorTest.java          | 36 +++++++++--
 .../java/org/apache/sis/internal/netcdf/Grid.java  | 16 ++++-
 .../org/apache/sis/internal/netcdf/Variable.java   | 21 +++---
 .../sis/internal/netcdf/impl/VariableInfo.java     | 38 ++++++++++-
 11 files changed, 199 insertions(+), 43 deletions(-)

diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
index 9c6a299..055b499 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridExtent.java
@@ -24,11 +24,13 @@ import java.util.Locale;
 import java.io.Serializable;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import org.opengis.util.FactoryException;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.util.ArgumentChecks;
@@ -42,6 +44,8 @@ import org.apache.sis.geometry.AbstractEnvelope;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralDirectPosition;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.util.iso.Types;
 
@@ -289,7 +293,7 @@ public class GridExtent implements Serializable {
      *
      * @param  envelope  the envelope containing cell indices to store in a {@code GridExtent}.
      *
-     * @see #toCRS(MathTransform)
+     * @see #toCRS(MathTransform, MathTransform)
      */
     GridExtent(final AbstractEnvelope envelope) {
         final int dimension = envelope.getDimension();
@@ -525,18 +529,78 @@ public class GridExtent implements Serializable {
      * Transforms this grid extent to a "real world" envelope using the given transform.
      * The transform shall map <em>pixel corner</em> to real world coordinates.
      *
-     * @param  gridToCRS  a transform from <em>pixel corner</em> to real world
coordinates
+     * @param  cornerToCRS  a transform from <em>pixel corners</em> to real world
coordinates.
+     * @param  gridToCRS    the transform specified by the user. May be the same as {@code
cornerToCRS}.
+     *                      If different, then this is assumed to map pixel centers instead
than pixel corners.
      * @return this grid extent in real world coordinates.
      *
      * @see #GridExtent(AbstractEnvelope)
      */
-    final GeneralEnvelope toCRS(final MathTransform gridToCRS) throws TransformException
{
+    final GeneralEnvelope toCRS(final MathTransform cornerToCRS, final MathTransform gridToCRS)
throws TransformException {
         final int dimension = getDimension();
-        final GeneralEnvelope envelope = new GeneralEnvelope(dimension);
+        GeneralEnvelope envelope = new GeneralEnvelope(dimension);
         for (int i=0; i<dimension; i++) {
             envelope.setRange(i, ordinates[i], ordinates[i + dimension] + 1.0);
         }
-        return Envelopes.transform(gridToCRS, envelope);
+        envelope = Envelopes.transform(cornerToCRS, envelope);
+        if (envelope.isEmpty()) try {
+            /*
+             * If the envelope contains some NaN values, try to replace them by constant
values inferred from the math transform.
+             * We must use the MathTransform specified by the user (gridToCRS), not necessarily
the cornerToCRS transform, because
+             * because inferring a 'cornerToCRS' by translation a 'centertoCRS' by 0.5 pixels
increase the amount of NaN values in
+             * the matrix. For giving a chance to TransformSeparator to perform its work,
we need the minimal amount of NaN values.
+             */
+            final boolean isCenter = (gridToCRS != cornerToCRS);
+            TransformSeparator separator = null;
+            for (int srcDim=0; srcDim < dimension; srcDim++) {
+                if (ordinates[srcDim + dimension] == 0 && ordinates[srcDim] == 0)
{
+                    /*
+                     * At this point we found a grid dimension with [0 … 0] range. Only
this specific range is processed because
+                     * it is assumed associated to NaN scale factors in the 'gridToCRS' matrix,
since the resolution is computed
+                     * by 0/0.  We require the range to be [0 … 0] instead of [n … n]
because if grid indices are not zero, then
+                     * we would need to know the scale factors for computing the offset.
+                     */
+                    if (separator == null) {
+                        separator = new TransformSeparator(gridToCRS);
+                    }
+                    separator.addSourceDimensionRange(srcDim, srcDim + 1);
+                    final Matrix component = MathTransforms.getMatrix(separator.separate());
+                    if (component != null) {
+                        final int[] targets = separator.getTargetDimensions();
+                        for (int j=0; j<targets.length; j++) {
+                            final int tgtDim = targets[j];
+                            double lower = envelope.getLower(tgtDim);
+                            double upper = envelope.getUpper(tgtDim);
+                            final double value = component.getElement(j, component.getNumCol()
- 1);
+                            /*
+                             * Replace only the envelope NaN values by the translation term
(non-NaN values are left unchanged).
+                             * If the gridToCRS map pixel corners, then we update only the
lower bound since the transform maps
+                             * lower-left corner; the upper bound is unknown. If the gridToCRS
maps pixel center, then we update
+                             * both lower and upper bounds to a value assumed to be in the
center; the span is set to zero.
+                             */
+                            if (isCenter) {
+                                double span = upper - value;
+                                if (Double.isNaN(span)) {
+                                    span = value - lower;
+                                    if (Double.isNaN(span)) {
+                                        span = 0;
+                                    }
+                                }
+                                if (Double.isNaN(lower)) lower = value - span;
+                                if (Double.isNaN(upper)) upper = value + span;
+                            } else if (Double.isNaN(lower)) {
+                                lower = value;
+                            }
+                            envelope.setRange(tgtDim, lower, upper);
+                        }
+                    }
+                    separator.clear();
+                }
+            }
+        } catch (FactoryException e) {
+            GridGeometry.recoverableException(e);
+        }
+        return envelope;
     }
 
     /**
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index 6fb4840..04b34cc 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -264,7 +264,7 @@ public class GridGeometry implements Serializable {
         this.cornerToCRS = PixelTranslation.translate(gridToCRS, anchor, PixelInCell.CELL_CORNER);
         GeneralEnvelope env = null;
         if (extent != null && cornerToCRS != null) {
-            env = extent.toCRS(cornerToCRS);
+            env = extent.toCRS(cornerToCRS, gridToCRS);         // 'gridToCRS' specified
by the user, not 'this.gridToCRS'.
             env.setCoordinateReferenceSystem(crs);
         } else if (crs != null) {
             env = new GeneralEnvelope(crs);
@@ -340,7 +340,7 @@ public class GridGeometry implements Serializable {
         if (envelope != null && cornerToCRS != null) {
             GeneralEnvelope env = Envelopes.transform(cornerToCRS.inverse(), envelope);
             extent = new GridExtent(env);
-            env = extent.toCRS(cornerToCRS);
+            env = extent.toCRS(cornerToCRS, gridToCRS);         // 'gridToCRS' specified
by the user, not 'this.gridToCRS'.
             env.setCoordinateReferenceSystem(envelope.getCoordinateReferenceSystem());
             this.envelope = new ImmutableEnvelope(env);
             if (scales == null) try {
@@ -377,7 +377,7 @@ public class GridGeometry implements Serializable {
      * Invoked when a recoverable exception occurred. Those exceptions must be minor enough
      * that they can be silently ignored in most cases.
      */
-    private static void recoverableException(final TransformException exception) {
+    static void recoverableException(final Exception exception) {
         Logging.recoverableException(Logging.getLogger(Modules.RASTER), GridGeometry.class,
"transform", exception);
     }
 
@@ -922,7 +922,7 @@ public class GridGeometry implements Serializable {
                     table.append(Double.toString(envelope.getLower(i))).nextColumn();
                     table.setCellAlignment(TableAppender.ALIGN_LEFT);
                     table.append(" … ").append(Double.toString(envelope.getUpper(i)));
-                    if (appendResolution) {
+                    if (appendResolution && !Double.isNaN(resolution[i])) {
                         final boolean isLinear = (i < Long.SIZE) && (nonLinears
& (1L << i)) == 0;
                         table.nextColumn();
                         table.append("  Δ");
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
index 4688fda..02c9f59 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
@@ -118,6 +118,12 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CanNotSeparateTargetDimension_1 = 7;
 
         /**
+         * Can not separate the transform because result would have {2} {0,choice,0#source|1#target}
+         * dimension{2,choice,1#|2#s} instead of {1}.
+         */
+        public static final short CanNotSeparateTransform_3 = 83;
+
+        /**
          * Can not transform envelope to a geodetic reference system.
          */
         public static final short CanNotTransformEnvelopeToGeodetic = 8;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
index c11c140..35b4be2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
@@ -51,6 +51,7 @@ CanNotInferGridSizeFromValues_1   = Can not infer a grid size from the given
val
 CanNotInstantiateGeodeticObject_1 = Can not instantiate geodetic object for \u201c{0}\u201d.
 CanNotMapAxisToDirection_1        = Can not map an axis from the specified coordinate system
to the \u201c{0}\u201d direction.
 CanNotParseCombinedReference_2    = Can not parse component {1} in the combined {0,choice,0#URN|1#URL}.
+CanNotSeparateTransform_3         = Can not separate the transform because result would have
{2} {0,choice,0#source|1#target} dimension{2,choice,1#|2#s} instead of {1}.
 CanNotSeparateTargetDimension_1   = Target dimension {0} depends on excluded source dimensions.
 CanNotTransformEnvelopeToGeodetic = Can not transform envelope to a geodetic reference system.
 CanNotUseGeodeticParameters_2     = Can not use the {0} geodetic parameters: {1}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
index 6f829a7..1181bcc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
@@ -55,6 +55,7 @@ CanNotFindCommonCRS               = Ne peut pas trouver un syst\u00e8me
de r\u00
 CanNotInferGridSizeFromValues_1   = Ne peut pas inf\u00e9rer une taille de grille \u00e0
partir des valeurs donn\u00e9es dans la plage {0}.
 CanNotInstantiateGeodeticObject_1 = Ne peut pas cr\u00e9er l\u2019objet g\u00e9od\u00e9tique
pour \u00ab\u202f{0}\u202f\u00bb.
 CanNotMapAxisToDirection_1        = Aucun axe du syst\u00e8me de coordonn\u00e9es sp\u00e9cifi\u00e9
n\u2019a pu \u00eatre associ\u00e9 \u00e0 la direction \u00ab\u202f{0}\u202f\u00bb.
+CanNotSeparateTransform_3         = Ne peut pas s\u00e9parer la transformation parce-que
le r\u00e9sultat aurait {2} dimension{2,choice,1#|2#s} en {0,choice,0#entr\u00e9|1#sortie}
au lieu de {1}.
 CanNotSeparateTargetDimension_1   = La dimension de destination {0} d\u00e9pend de dimensions
sources qui ont \u00e9t\u00e9 exclues.
 CanNotParseCombinedReference_2    = Ne peut pas d\u00e9coder la composante {1} dans l\u2019{0,choice,0#URN|1#URL}
combin\u00e9.
 CanNotTransformEnvelopeToGeodetic = Ne peut pas transformer l\u2019enveloppe vers un r\u00e9f\u00e9rentiel
g\u00e9od\u00e9sique.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
index 3845904..5fb3123 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java
@@ -80,7 +80,7 @@ public final class MathTransforms extends Static {
      * @return an identity transform of the specified dimension.
      */
     public static LinearTransform identity(final int dimension) {
-        ArgumentChecks.ensureStrictlyPositive("dimension", dimension);
+        ArgumentChecks.ensurePositive("dimension", dimension);
         return IdentityTransform.create(dimension);
     }
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
index 8a7d051..64823c4 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/TransformSeparator.java
@@ -48,7 +48,7 @@ import org.apache.sis.util.ArraysExt;
  * The output dimensions can be verified with a call to {@link #getTargetDimensions()}.</div>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.0
  * @since   0.7
  * @module
  */
@@ -393,7 +393,7 @@ public class TransformSeparator {
             }
         } else {
             /*
-             * At this point there is at least one source dimensions to take in account.
+             * At this point there is at least one source dimension to take in account.
              * Source dimensions are more difficult to process than target dimensions.
              */
             final int[] requested = targetDimensions;
@@ -422,18 +422,18 @@ public class TransformSeparator {
         /*
          * We are done. But do a final verification on the number of dimensions.
          */
-        int type     = 0;
+        int side     = 0;
         int expected = sourceDimensions.length;
         int actual   = tr.getSourceDimensions();
         if (actual == expected) {
-            type     = 1;
+            side     = 1;
             expected = targetDimensions.length;
             actual   = tr.getTargetDimensions();
             if (actual == expected) {
                 return tr;
             }
         }
-        throw new FactoryException(Resources.format(Resources.Keys.MismatchedTransformDimension_3,
type, expected, actual));
+        throw new FactoryException(Resources.format(Resources.Keys.CanNotSeparateTransform_3,
side, expected, actual));
     }
 
     /**
@@ -552,14 +552,14 @@ public class TransformSeparator {
         final Matrix matrix = MathTransforms.getMatrix(step);
         if (matrix != null) {
             targetDimensions = null;
-            int startOfRow = 0;
-            boolean isLastRowAccepted = false;
+            int startOfRow = 0;                         // Index of next row to be stored
in the 'elements' array.
+            boolean isLastRowAccepted = false;          // To be set to 'true' if we complete
successfully up to last row.
             final int numFilteredColumns = (dimensions.length + 1);
             double[] elements = new double[(numTgt + 1) * numFilteredColumns];
 reduce:     for (int j=0; j <= numTgt; j++) {
                 /*
                  * For each target dimension (i.e. a matrix row), find the matrix elements
(excluding translation
-                 * terms in the last column) for each source dimension to be kept. If a dependancy
to at least one
+                 * terms in the last column) for each source dimension to be kept. If a dependency
to at least one
                  * discarded input dimension is found, then the whole output dimension is
discarded.
                  */
                 int filteredColumn = 0;
@@ -575,20 +575,29 @@ reduce:     for (int j=0; j <= numTgt; j++) {
                         continue reduce;
                     }
                 }
+                /*
+                 * We reach this point only if we determined that for current matrix row,
all dependencies are listed
+                 * in the array of source dimensions to keep. The matrix coefficients for
that row are copied in the
+                 * 'elements' array.
+                 */
                 elements[startOfRow + filteredColumn++] = matrix.getElement(j, numSrc); 
// Copy the translation term.
                 assert filteredColumn == numFilteredColumns : filteredColumn;           
// We should have used all values in the 'dimensions' array.
                 startOfRow += numFilteredColumns;
-                if (j == numTgt) {
-                    /*
-                     * In an affine transform, the last row is usually [0 0 0 … 1].
-                     * This is not a real dimension, but nevertheless mandatory.
-                     */
-                    isLastRowAccepted = true;
-                } else {
+                /*
+                 * In an affine transform, the last row is usually [0 0 0 … 1].
+                 * This is not a real dimension, but nevertheless mandatory and
+                 * needs to be identified as valid by above code.
+                 */
+                isLastRowAccepted = (j == numTgt);
+                if (!isLastRowAccepted) {                           // Update target dimensions
for every rows except the last one.
                     targetDimensions = insert(targetDimensions, j);
                 }
             }
             if (isLastRowAccepted) {
+                if (targetDimensions == null) {
+                    targetDimensions = ArraysExt.EMPTY_INT;
+                    return MathTransforms.identity(0);
+                }
                 elements = ArraysExt.resize(elements, startOfRow);
                 return factory.createAffineTransform(Matrices.create(startOfRow / numFilteredColumns,
numFilteredColumns, elements));
             }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java
index 64eb9fa..1ca57a5 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/TransformSeparatorTest.java
@@ -23,6 +23,9 @@ import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.referencing.datum.HardCodedDatum;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.referencing.operation.matrix.Matrix3;
+import org.apache.sis.referencing.operation.matrix.Matrix4;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.measure.Units;
 import org.apache.sis.test.DependsOnMethod;
@@ -30,6 +33,7 @@ import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
+import static java.lang.Double.NaN;
 import static org.opengis.test.Assert.*;
 
 
@@ -37,7 +41,7 @@ import static org.opengis.test.Assert.*;
  * Tests {@link TransformSeparator}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.7
  * @module
  */
@@ -109,12 +113,12 @@ public final strictfp class TransformSeparatorTest extends TestCase
{
      */
     @Test
     public void testLinearTransform() throws FactoryException {
-        Matrix matrix = Matrices.create(4, 4, new double[] {
+        Matrix matrix = new Matrix4(
             2, 0, 0, 7,                                         // Some random values.
             0, 5, 0, 6,
             1, 0, 3, 8,
             0, 0, 0, 1
-        });
+        );
         final TransformSeparator s = new TransformSeparator(MathTransforms.linear(matrix));
         /*
          * Trivial case: no dimension specified, we should get the transform unchanged.
@@ -180,6 +184,28 @@ public final strictfp class TransformSeparatorTest extends TestCase {
     }
 
     /**
+     * Tests separation of a linear transform containing {@link Double#NaN} values.
+     *
+     * @throws FactoryException if an error occurred while creating a new transform.
+     */
+    @Test
+    public void testIncompleteTransform() throws FactoryException {
+        Matrix matrix = new Matrix4(
+            1,   0,   0,   7,
+            0,   0,   1,   8,
+            0, NaN,   0,   6,
+            0,   0,   0,   1
+        );
+        TransformSeparator s = new TransformSeparator(MathTransforms.linear(matrix));
+        s.addSourceDimensions(1);
+        assertMatrixEquals("transform", new Matrix2(
+               NaN, 6,
+                 0, 1
+        ), ((LinearTransform) s.separate()).getMatrix(), STRICT);
+        assertArrayEquals(new int[] {2}, s.getTargetDimensions());
+    }
+
+    /**
      * Tests separation of a concatenated transform.
      *
      * @throws FactoryException if an error occurred while creating a new transform.
@@ -257,11 +283,11 @@ public final strictfp class TransformSeparatorTest extends TestCase
{
          * Filter source dimensions. If we ask only for dimensions not in the pass-through
transform,
          * then TransformSeparator should return an affine transform.
          */
-        matrix = Matrices.create(3, 3, new double[] {
+        matrix = new Matrix3(
             1, 0, 0,
             0, 1, 0,
             0, 0, 1
-        });
+        );
         s.clear();
         s.addSourceDimensions(0, 6);
         r = s.separate();
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index 073dd59..af17ba7 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -260,9 +260,21 @@ public abstract class Grid extends NamedElement {
                         continue;
                     }
                 }
-                for (int srcDim : axis.sourceDimensions) {
-                    affine.setElement(tgtDim, srcEnd - srcDim, 1);
+                /*
+                 * If we have not been able to set coefficients in the matrix (because the
transform is non-linear),
+                 * set a single scale factor to 1 in the matrix row; we will concatenate
later with a non-linear transform.
+                 * The coefficient that we set to 1 is the one for the source dimension which
is not already taken by another row.
+                 */
+findFree:       for (int srcDim : axis.sourceDimensions) {
+                    srcDim = srcEnd - srcDim;
+                    for (int j=affine.getNumRow(); --j>=0;) {
+                        if (affine.getElement(j, srcDim) != 0) {
+                            continue findFree;
+                        }
+                    }
+                    affine.setElement(tgtDim, srcDim, 1);
                     // TODO: prepare non-linear transform here for later concatenation.
+                    break;
                 }
             }
             MathTransform gridToCRS = MathTransforms.linear(affine);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
index 24c82c1..e7cc8dc 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
@@ -379,17 +379,22 @@ public abstract class Variable extends NamedElement {
     {
         final Vector values = read();
         final int n = values.size() - 1;
-        if (n >= 1) {
+        if (n >= 0) {
             final double first = values.doubleValue(0);
-            final double last  = values.doubleValue(n);
-            double error;
-            if (getDataType() == DataType.FLOAT) {
-                error = Math.max(Math.ulp((float) first), Math.ulp((float) last));
+            Number increment;
+            if (n >= 1) {
+                final double last = values.doubleValue(n);
+                double error;
+                if (getDataType() == DataType.FLOAT) {
+                    error = Math.max(Math.ulp((float) first), Math.ulp((float) last));
+                } else {
+                    error = Math.max(Math.ulp(first), Math.ulp(last));
+                }
+                error = Math.max(Math.ulp(last - first), error) / n;
+                increment = values.increment(error);
             } else {
-                error = Math.max(Math.ulp(first), Math.ulp(last));
+                increment = Double.NaN;
             }
-            error = Math.max(Math.ulp(last - first), error) / n;
-            final Number increment = values.increment(error);
             if (increment != null) {
                 gridToCRS.setElement(tgtDim, srcDim, increment.doubleValue());
                 gridToCRS.setElement(tgtDim, gridToCRS.getNumCol() - 1, first);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
index 524716c..b2d6561 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
@@ -16,7 +16,10 @@
  */
 package org.apache.sis.internal.netcdf.impl;
 
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.regex.Matcher;
@@ -151,7 +154,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
      *
      * @see #isCoordinateSystemAxis()
      */
-    private final boolean isCoordinateSystemAxis;
+    private boolean isCoordinateSystemAxis;
 
     /**
      * The values of the whole variable, or {@code null} if not yet read. This vector should
be assigned only
@@ -252,8 +255,6 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
                 }
             }
             isCoordinateSystemAxis = dimensions[0].name.equals(value);
-        } else {
-            isCoordinateSystemAxis = false;
         }
         /*
          * Verify if this variable is an enumeration. If yes, we remove the attributes that
define the
@@ -292,11 +293,14 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
      * @throws ArithmeticException if the stride between two records exceeds {@link Long#MAX_VALUE}.
      */
     static void complete(final VariableInfo[] variables) {
+        final Set<CharSequence> referencedAsAxis = new HashSet<>();
         final VariableInfo[] unlimited = new VariableInfo[variables.length];
         int     count        = 0;               // Number of valid elements in the 'unlimited'
array.
         long    recordStride = 0;               // Sum of the size of all variables having
a unlimited dimension.
         boolean isUnknown    = false;           // True if 'total' is actually unknown.
         for (final VariableInfo variable : variables) {
+            // Opportunistically store names of all axes listed in "coordinates" attributes
of all variables.
+            referencedAsAxis.addAll(Arrays.asList(CharSequences.split(variable.getAttributeString(CF.COORDINATES),
' ')));
             if (variable.isUnlimited()) {
                 final long paddedSize = variable.paddedSize();
                 unlimited[count++] = variable;
@@ -314,6 +318,33 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
         } else for (int i=0; i<count; i++) {
             unlimited[i].offsetToNextRecord = recordStride - unlimited[i].offsetToNextRecord;
         }
+        /*
+         * If some variables have a "coordinates" attribute listing names of variables used
as axes,
+         * mark those variables as axes. We perform this check as a complement for the check
done in
+         * the constructor in order to detect two-dimensional axes. Example:
+         *
+         *    dimensions:
+         *        Y = 471 ;
+         *        X = 720 ;
+         *    variables:
+         *        float lon(Y, X) ;                       // Not detected as coordinate axis
by the constructor.
+         *            lon:long_name = "longitude" ;
+         *            lon:units = "degree_east" ;
+         *        float lat(Y, X) ;                       // Not detected as coordinate axis
by the constructor.
+         *            lat:long_name = "latitude" ;
+         *            lat:units = "degree_north" ;
+         *        short temperature(Y, X) ;
+         *            temperature:long_name = "Potential Temperature" ;
+         *            temperature:coordinates = "lon lat" ;
+         */
+        if (!referencedAsAxis.isEmpty()) {
+            for (final VariableInfo variable : variables) {
+                if (referencedAsAxis.remove(variable.name)) {
+                    variable.isCoordinateSystemAxis = true;
+                    if (referencedAsAxis.isEmpty()) break;
+                }
+            }
+        }
     }
 
     /**
@@ -405,6 +436,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
     /**
      * Returns {@code true} if this variable seems to be a coordinate system axis,
      * determined by comparing its name with the name of all dimensions in the netCDF file.
+     * Also determined by inspection of {@code "coordinates"} attribute on other variables.
      */
     @Override
     public boolean isCoordinateSystemAxis() {


Mime
View raw message