sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/04: The check for source dimension order needs to be robust to the case where the localization grid in a netCDF call contains longitude values crossing the +180/-180° meridian.
Date Sat, 26 Jan 2019 17:18:56 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

commit 0768f1fe683416a49a47881e6ba94af40d6e86b6
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Jan 26 15:53:28 2019 +0100

    The check for source dimension order needs to be robust to the case where the localization
grid in a netCDF call contains longitude values crossing the +180/-180° meridian.
---
 .../java/org/apache/sis/internal/netcdf/Axis.java  | 61 ++++++----------------
 .../java/org/apache/sis/internal/netcdf/Grid.java  | 60 +++++++++++++++++++--
 .../apache/sis/internal/netcdf/impl/GridInfo.java  |  8 ++-
 .../sis/internal/netcdf/ucar/GridWrapper.java      |  9 ++--
 4 files changed, 79 insertions(+), 59 deletions(-)

diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
index 79cce0c..5736831 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Axis.java
@@ -38,7 +38,6 @@ import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.iso.Types;
-import org.apache.sis.util.ArraysExt;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
@@ -125,25 +124,17 @@ public final class Axis extends NamedElement implements Comparable<Axis>
{
     final Variable coordinates;
 
     /**
-     * Constructs a new axis associated to an arbitrary number of grid dimension.
-     * In the particular case where the number of dimensions is equals to 2, this constructor
will detect
-     * by itself which grid dimension varies fastest and reorder in-place the elements in
the given arrays
-     * (those array are modified, not cloned).
+     * Constructs a new axis associated to an arbitrary number of grid dimension. The given
arrays are stored
+     * as-in (not cloned) and their content may be modified after construction by {@link
Grid#getAxes()}.
      *
-     * @param  owner             provides callback for the conversion from grid coordinates
to geodetic coordinates.
      * @param  axis              an implementation-dependent object representing the axis.
      * @param  abbreviation      axis abbreviation, also identifying its type. This is a
controlled vocabulary.
      * @param  direction         direction of positive values ("up" or "down"), or {@code
null} if unknown.
      * @param  sourceDimensions  the index of the grid dimension associated to this axis.
      * @param  sourceSizes       the number of cell elements along that axis.
-     * @param  previousAxes      other axes built before this axis. May contain null elements.
This array will not be modified.
-     * @throws IOException if an I/O operation was necessary but failed.
-     * @throws DataStoreException if a logical error occurred.
-     * @throws ArithmeticException if the size of an axis exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
-    public Axis(final Grid owner, final Variable axis, char abbreviation, final String direction,
-                final int[] sourceDimensions, final int[] sourceSizes, final Axis[] previousAxes)
-            throws IOException, DataStoreException
+    public Axis(final Variable axis, char abbreviation, final String direction,
+                final int[] sourceDimensions, final int[] sourceSizes)
     {
         /*
          * Try to get the axis direction from one of the following sources,
@@ -190,38 +181,6 @@ public final class Axis extends NamedElement implements Comparable<Axis>
{
         this.sourceDimensions = sourceDimensions;
         this.sourceSizes      = sourceSizes;
         this.coordinates      = axis;
-        /*
-         * The grid dimension which varies fastest should be first.  The code below will
swap axes if needed in order to
-         * achieve that goal, except if a previous axis was already using the same order.
We avoid collision only in the
-         * first dimension because it is the one used by metadata and by trySetTransform(…).
-         */
-        if (sourceDimensions.length >= 2) {
-            boolean s = false;
-            for (final Axis previous : previousAxes) {
-                if (previous != null && previous.sourceDimensions.length != 0) {
-                    final int first = previous.sourceDimensions[0];
-                    if  (first == sourceDimensions[1]) return;              // Swapping would
cause a collision. Avoid that.
-                    s = (first == sourceDimensions[0]);                     // Tell if need
swapping for avoiding collision.
-                    if (s) break;
-                }
-            }
-            final int up0 = sourceSizes[0];
-            final int up1 = sourceSizes[1];
-            if (!s) {
-                final int mid0 = up0 / 2;
-                final int mid1 = up1 / 2;
-                final double inc0 = (owner.coordinateForAxis(axis,     0, mid1) -
-                                     owner.coordinateForAxis(axis, up0-1, mid1)) / up0;
-                final double inc1 = (owner.coordinateForAxis(axis, mid0,     0) -
-                                     owner.coordinateForAxis(axis, mid0, up1-1)) / up1;
-                s = Math.abs(inc1) > Math.abs(inc0);
-            }
-            if (s) {
-                sourceSizes[0] = up1;
-                sourceSizes[1] = up0;
-                ArraysExt.swap(sourceDimensions, 0, 1);
-            }
-        }
     }
 
     /**
@@ -272,6 +231,18 @@ public final class Axis extends NamedElement implements Comparable<Axis>
{
     }
 
     /**
+     * Returns {@code true} if this axis is likely to have a "wraparound" range. The main
case is the longitude
+     * axis with a [-180 … +180]° range or a [0 … 360]° range, where the next value
after +180° may be -180°.
+     */
+    final boolean isWraparound() {
+        if (abbreviation == 0) {
+            return AxisDirection.EAST.equals(AxisDirections.absolute(direction)) &&
Units.isAngular(getUnit());
+        } else {
+            return abbreviation == 'λ';
+        }
+    }
+
+    /**
      * Returns {@code true} if coordinates in this axis seem to map cell corner instead than
cell center.
      * A {@code false} value does not necessarily means that the axis maps cell center; it
can be unknown.
      * This method assumes a geographic CRS.
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 ec4ff5b..1b79253 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
@@ -40,6 +40,7 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.IllegalGridGeometryException;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.NullArgumentException;
+import org.apache.sis.util.ArraysExt;
 import org.apache.sis.math.Vector;
 
 
@@ -149,6 +150,61 @@ public abstract class Grid extends NamedElement {
     public final Axis[] getAxes() throws IOException, DataStoreException {
         if (axes == null) {
             axes = createAxes();
+            /*
+             * The grid dimension which varies fastest should be first.  The code below will
swap axes if needed in order to
+             * achieve that goal, except if a previous axis was already using the same order.
We avoid collision only in the
+             * first dimension because it is the one used by metadata and by trySetTransform(…).
+             */
+            final Axis[] workspace = new Axis[axes.length];
+            int i = 0, deferred = workspace.length;
+            for (final Axis axis : axes) {
+                // Put one-dimensional axes first, all other axes last.
+                workspace[axis.sourceDimensions.length <= 1 ? i++ : --deferred] = axis;
+            }
+            deferred = workspace.length;        // Will become index of the first axis whose
examination has been deferred.
+            while (i < workspace.length) {      // Start the loop at the first n-dimensional
axis (n > 1).
+                final Axis axis = workspace[i];
+                final int[] sourceDimensions = axis.sourceDimensions;
+                /*
+                 * If an axis has a "wraparound" range (for example a longitude axis where
the next value after +180°
+                 * may be -180°), we will examine it last. The reason is that if a wraparound
occurs in the middle of
+                 * the localization grid, it will confuse the computation based on 'coordinateForAxis(…)'
calls below.
+                 * We are better to resolve the latitude axis first, and then resolve the
longitude axis with the code
+                 * path checking for dimension collisions, without using coordinateForAxis(…)
on longitude axis.
+                 */
+                if (i < deferred && axis.isWraparound()) {
+                    System.arraycopy(workspace, i+1, workspace, i, --deferred - i);
+                    workspace[deferred] = axis;
+                    continue;                                           // Continue the loop
without incrementing 'i'.
+                }
+                Boolean swap = null;
+                for (int p=0; p<i; p++) {
+                    final int[] other = workspace[p].sourceDimensions;
+                    if (other.length != 0) {
+                        final int first = other[0];
+                        if (first == sourceDimensions[1]) {swap=false; break;}    // Swapping
would cause a collision.
+                        if (first == sourceDimensions[0]) {swap=true;  break;}    // Need
swapping for avoiding collision.
+                    }
+                }
+                final int[] sourceSizes = axis.sourceSizes;
+                if (swap == null) {
+                    final int up0  = sourceSizes[0];
+                    final int up1  = sourceSizes[1];
+                    final int mid0 = up0 / 2;
+                    final int mid1 = up1 / 2;
+                    final Variable coordinates = axis.coordinates;
+                    final double inc0 = (coordinateForAxis(coordinates,     0, mid1) -
+                                         coordinateForAxis(coordinates, up0-1, mid1)) / up0;
+                    final double inc1 = (coordinateForAxis(coordinates, mid0,     0) -
+                                         coordinateForAxis(coordinates, mid0, up1-1)) / up1;
+                    swap = Math.abs(inc1) > Math.abs(inc0);
+                }
+                if (swap) {
+                    ArraysExt.swap(sourceSizes,      0, 1);
+                    ArraysExt.swap(sourceDimensions, 0, 1);
+                }
+                i++;
+            }
         }
         return axes;
     }
@@ -157,11 +213,9 @@ public abstract class Grid extends NamedElement {
      * Creates the axes to be returned by {@link #getAxes()}. This method is invoked only
once when first needed.
      *
      * @return the CRS axes, in netCDF order (reverse of "natural" order).
-     * @throws IOException if an I/O operation was necessary but failed.
      * @throws DataStoreException if a logical error occurred.
-     * @throws ArithmeticException if the size of an axis exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
-    protected abstract Axis[] createAxes() throws IOException, DataStoreException;
+    protected abstract Axis[] createAxes() throws DataStoreException;
 
     /**
      * Returns a coordinate for the given two-dimensional grid coordinate axis. This is (indirectly)
a callback
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
index da24245..8dd2da0 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridInfo.java
@@ -190,12 +190,10 @@ final class GridInfo extends Grid {
      * "two-dimensional axes" (in {@link ucar.nc2.dataset.CoordinateAxis2D} sense).</p>
      *
      * @return the CRS axes, in netCDF order (reverse of "natural" order).
-     * @throws IOException if an I/O operation was necessary but failed.
      * @throws DataStoreException if a logical error occurred.
-     * @throws ArithmeticException if the size of an axis exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
     @Override
-    protected Axis[] createAxes() throws IOException, DataStoreException {
+    protected Axis[] createAxes() throws DataStoreException {
         /*
          * Process the variables in the order the appear in the sequence of bytes that make
the netCDF files.
          * This is often the same order than the indices, but not necessarily. The intent
is to reduce the
@@ -243,8 +241,8 @@ final class GridInfo extends Grid {
                     }
                 }
             }
-            axes[targetDim] = new Axis(this, axis, abbreviation, axis.getAttributeAsString(CF.POSITIVE),
-                                       ArraysExt.resize(indices, i), ArraysExt.resize(sizes,
i), axes);
+            axes[targetDim] = new Axis(axis, abbreviation, axis.getAttributeAsString(CF.POSITIVE),
+                                       ArraysExt.resize(indices, i), ArraysExt.resize(sizes,
i));
         }
         if (sortAxes) {
             Arrays.sort(axes);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
index 1e40003..bbf7556 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridWrapper.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.internal.netcdf.ucar;
 
-import java.io.IOException;
 import java.util.List;
 import ucar.nc2.Dimension;
 import ucar.nc2.constants.AxisType;
@@ -126,12 +125,10 @@ final class GridWrapper extends Grid {
      * of {@link CoordinateAxis2D}.</p>
      *
      * @return the CRS axes, in netCDF order (reverse of "natural" order).
-     * @throws IOException if an I/O operation was necessary but failed.
      * @throws DataStoreException if a logical error occurred.
-     * @throws ArithmeticException if the size of an axis exceeds {@link Integer#MAX_VALUE},
or other overflow occurs.
      */
     @Override
-    protected Axis[] createAxes() throws IOException, DataStoreException {
+    protected Axis[] createAxes() throws DataStoreException {
         final List<Dimension> domain = netcdfCS.getDomain();
         final List<CoordinateAxis> range = netcdfCS.getCoordinateAxes();
         /*
@@ -183,8 +180,8 @@ final class GridWrapper extends Grid {
                  * package, we can proceed as if the dimension does not exist ('i' not incremented).
                  */
             }
-            axes[targetDim] = new Axis(this, decoder.getWrapperFor(axis), abbreviation, axis.getPositive(),
-                    ArraysExt.resize(indices, i), ArraysExt.resize(sizes, i), axes);
+            axes[targetDim] = new Axis(decoder.getWrapperFor(axis), abbreviation, axis.getPositive(),
+                                       ArraysExt.resize(indices, i), ArraysExt.resize(sizes,
i));
         }
         return axes;
     }


Mime
View raw message