sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/03: First part of building a "grid to CRS" transform for netCDF file, including only the linear parts for now. https://issues.apache.org/jira/browse/SIS-316
Date Sat, 17 Nov 2018 17:22:17 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 e303b6f75b7afee79e6522fee098147205a36e2b
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Sat Nov 17 17:23:04 2018 +0100

    First part of building a "grid to CRS" transform for netCDF file, including only the linear
parts for now.
    https://issues.apache.org/jira/browse/SIS-316
---
 .../sis/internal/metadata/AxisDirections.java      | 11 +++
 .../sis/internal/metadata/AxisDirectionsTest.java  | 10 +++
 .../apache/sis/internal/netcdf/GridGeometry.java   | 81 ++++++++++++++++++++++
 .../org/apache/sis/internal/netcdf/Variable.java   | 55 +++++++++++++++
 .../sis/internal/netcdf/impl/GridGeometryInfo.java | 17 +++++
 .../sis/internal/netcdf/impl/VariableInfo.java     |  2 +-
 .../internal/netcdf/ucar/GridGeometryWrapper.java  | 21 ++++++
 .../sis/internal/netcdf/ucar/VariableWrapper.java  | 23 +++++-
 8 files changed, 218 insertions(+), 2 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
index 9ec0a20..7549a7f 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/AxisDirections.java
@@ -272,6 +272,17 @@ public final class AxisDirections extends Static {
     }
 
     /**
+     * Returns {@code true} if the given direction is {@code FUTURE} or {@code PAST}.
+     *
+     * @param  dir  the direction to test, or {@code null}.
+     * @return {@code true} if the direction is temporal, or {@code false} otherwise.
+     */
+    public static boolean isTemporal(final AxisDirection dir) {
+        if (dir == null) return false;
+        return ((dir.ordinal() - FUTURE.ordinal()) & ~1) == 0;
+    }
+
+    /**
      * Returns {@code true} if the given direction is {@code GEOCENTRIC_X}, {@code GEOCENTRIC_Y}
      * or {@code GEOCENTRIC_Z}.
      *
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
index 74c44d8..911815b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/metadata/AxisDirectionsTest.java
@@ -182,6 +182,16 @@ public final strictfp class AxisDirectionsTest extends TestCase {
     }
 
     /**
+     * Tests {@link AxisDirections#isTemporal(AxisDirection)}.
+     */
+    @Test
+    public void testIsTemporal() {
+        for (final AxisDirection dir : AxisDirection.values()) {
+            assertEquals(dir.name(), dir == FUTURE || dir == PAST, AxisDirections.isTemporal(dir));
+        }
+    }
+
+    /**
      * Tests {@link AxisDirections#isGeocentric(AxisDirection)}.
      */
     @Test
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
index 8ced0ec..c137f27 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridGeometry.java
@@ -21,9 +21,14 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.io.IOException;
 import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.metadata.spatial.DimensionNameType;
+import org.apache.sis.internal.metadata.AxisDirections;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.NullArgumentException;
 
@@ -85,6 +90,14 @@ public abstract class GridGeometry extends NamedElement {
     public abstract int getTargetDimensions();
 
     /**
+     * Returns the number of cells along each source dimension, in "natural" order.
+     * This method may return {@code null} if the grid shape can not be determined.
+     *
+     * @return number of cells along each source dimension, in "natural" (opposite of netCDF)
order, or {@code null}.
+     */
+    protected abstract long[] getShape();
+
+    /**
      * Returns the axes of the coordinate reference system. The size of this array is expected
equals to the
      * value returned by {@link #getTargetDimensions()}, but the caller should be robust
to inconsistencies.
      * The axis order is as declared in the netCDF file (reverse of "natural" order).
@@ -164,4 +177,72 @@ public abstract class GridGeometry extends NamedElement {
         }
         return crs;
     }
+
+    /**
+     * Returns an object containing the grid size, the CRS and the conversion from grid indices
to CRS coordinates.
+     *
+     * @param   decoder  the decoder for which grid geometries are constructed.
+     * @throws  IOException if an I/O operation was necessary but failed.
+     * @throws  DataStoreException if the CRS can not be constructed.
+     */
+    @SuppressWarnings("fallthrough")
+    public final void createGridGeometry(final Decoder decoder) throws IOException, DataStoreException
{
+        final Axis[] axes = getAxes();      // In netCDF order (reverse of "natural" order).
+        /*
+         * Build the grid extent if the shape is available. The shape may not be available
+         * if a dimension has unlimited length. The dimension names are informative only.
+         */
+        final GridExtent extent;
+        final long[] high = getShape();
+        if (high != null) {
+            final DimensionNameType[] names = new DimensionNameType[high.length];
+            switch (names.length) {
+                default: names[1] = DimensionNameType.ROW;      // Fall through
+                case 1:  names[0] = DimensionNameType.COLUMN;   // Fall through
+                case 0:  break;
+            }
+            for (final Axis axis : axes) {
+                if (axis.sourceDimensions.length == 1) {
+                    final DimensionNameType name;
+                    if (AxisDirections.isVertical(axis.direction)) {
+                        name = DimensionNameType.VERTICAL;
+                    } else if (AxisDirections.isTemporal(axis.direction)) {
+                        name = DimensionNameType.TIME;
+                    } else {
+                        continue;
+                    }
+                    final int dim = axis.sourceDimensions[0];
+                    if (dim >= 0 && dim < names.length) {
+                        names[names.length - 1 - dim] = name;
+                    }
+                }
+            }
+            extent = new GridExtent(names, new long[high.length], high, false);
+        } else {
+            extent = null;
+        }
+        /*
+         * Creates the "grid to CRS" transform. The number of columns is the number of dimensions
in the grid
+         * (the source) +1, and the number of rows is the number of dimensions in the CRS
(the target) +1.
+         * The order of dimensions in the transform is the reverse of the netCDF axis order.
+         */
+        int srcEnd = getSourceDimensions();
+        int tgtEnd = getTargetDimensions();
+        final Matrix gridToCRS = Matrices.createZero(tgtEnd-- + 1, srcEnd-- + 1);
+        for (int i=axes.length; --i >= 0;) {
+            final Axis axis = axes[i];
+            final int tgtDim = tgtEnd - i;
+            if (axis.sourceDimensions.length == 1) {
+                final int srcDim = srcEnd - axis.sourceDimensions[0];
+                if (axis.coordinates.trySetTransform(gridToCRS, srcDim, tgtDim)) {
+                    continue;
+                }
+            }
+            for (int srcDim : axis.sourceDimensions) {
+                gridToCRS.setElement(tgtDim, srcEnd - srcDim, 1);
+                // TODO: prepare non-linear transform here for later concatenation.
+            }
+        }
+        // TODO
+    }
 }
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 e3b4b59..0c334d5 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
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.awt.image.DataBuffer;
 import java.time.Instant;
 import javax.measure.Unit;
+import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.math.Vector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.util.logging.WarningListeners;
@@ -328,6 +329,60 @@ public abstract class Variable extends NamedElement {
     public abstract Vector read(int[] areaLower, int[] areaUpper, int[] subsampling) throws
IOException, DataStoreException;
 
     /**
+     * Wraps the given data in a {@link Vector} with the assumption that accuracy in base
10 matters.
+     * This method is suitable for coordinate axis variables, but should not be used for
the main data.
+     *
+     * @param  data        the data to wrap in a vector.
+     * @param  isUnsigned  whether the data type is an unsigned type.
+     * @return vector wrapping the given data.
+     */
+    protected static Vector createDecimalVector(final Object data, final boolean isUnsigned)
{
+        if (data instanceof float[]) {
+            return Vector.createForDecimal((float[]) data);
+        } else {
+            return Vector.create(data, isUnsigned);
+        }
+    }
+
+    /**
+     * Sets the scale and offset coefficients in the given "grid to CRS" transform if possible.
+     * This method is invoked only for variables that represent a coordinate system axis.
+     * Setting the coefficient is possible only if values in this variable are regular,
+     * i.e. the difference between two consecutive values is constant.
+     *
+     * @param  gridToCRS  the matrix in which to set scale and offset coefficient.
+     * @param  srcDim     the source dimension, which is a dimension of the grid. Identifies
the matrix column of scale factor.
+     * @param  tgtDim     the target dimension, which is a dimension of the CRS.  Identifies
the matrix row of scale factor.
+     * @return whether this method successfully set the scale and offset coefficients.
+     * @throws IOException if an error occurred while reading the data.
+     * @throws DataStoreException if a logical error occurred.
+     */
+    protected boolean trySetTransform(final Matrix gridToCRS, final int srcDim, final int
tgtDim)
+            throws IOException, DataStoreException
+    {
+        final Vector values = read();
+        final int n = values.size() - 1;
+        if (n >= 1) {
+            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));
+            } else {
+                error = Math.max(Math.ulp(first), Math.ulp(last));
+            }
+            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);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Returns the locale to use for warnings and error messages.
      */
     final Locale getLocale() {
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
index 116c43e..d8a5d9c 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/GridGeometryInfo.java
@@ -39,6 +39,7 @@ import ucar.nc2.constants.CF;
  * (domain) and output (range) of the function that convert grid indices to geodetic coordinates.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Johann Sorel (Geomatys)
  * @version 1.0
  * @since   0.3
  * @module
@@ -86,6 +87,7 @@ final class GridGeometryInfo extends GridGeometry {
 
     /**
      * Constructs a new grid geometry information.
+     * The {@code domain} and {@code range} arrays often have the same length, but not necessarily.
      *
      * @param  domain  describes the input values of the "grid to CRS" conversion.
      * @param  range   the output values of the "grid to CRS" conversion.
@@ -150,6 +152,21 @@ final class GridGeometryInfo extends GridGeometry {
     }
 
     /**
+     * Returns the number of cells along each source dimension, in "natural" order.
+     *
+     * @return number of cells along each source dimension, in "natural" (opposite of netCDF)
order.
+     */
+    @Override
+    protected long[] getShape() {
+        final int    dim  = domain.length;
+        final long[] size = new long[dim];
+        for (int i=0; i<dim; i++) {
+            size[(dim-1) - i] = Integer.toUnsignedLong(domain[i].length);
+        }
+        return size;
+    }
+
+    /**
      * Returns all axes of the netCDF coordinate system, together with the grid dimension
to which the axis
      * is associated. See {@link org.apache.sis.internal.netcdf.ucar.GridGeometryWrapper#getAxes()}
for a
      * closer look on the relationship between this algorithm and the UCAR library.
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 e321dde..6878d9d 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
@@ -576,7 +576,7 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo>
{
             }
             final Region region = new Region(upper, lower, upper, subsampling);
             applyUnlimitedDimensionStride(region);
-            values = Vector.create(reader.read(region), dataType.isUnsigned).compress(0);
+            values = createDecimalVector(reader.read(region), dataType.isUnsigned).compress(0);
         }
         return values;
     }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
index 9d80ce8..2ce80dd 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/GridGeometryWrapper.java
@@ -93,6 +93,27 @@ final class GridGeometryWrapper extends GridGeometry {
     }
 
     /**
+     * Returns the number of cells along each source dimension, in "natural" order.
+     * This method may return {@code null} if the grid shape can not be determined.
+     *
+     * @return number of cells along each source dimension, in "natural" (opposite of netCDF)
order, or {@code null}.
+     */
+    @Override
+    protected long[] getShape() {
+        final List<Dimension> domain = netcdfCS.getDomain();
+        final int    dim  = domain.size();
+        final long[] size = new long[dim];
+        for (int i=0; i<dim; i++) {
+            final int length = domain.get(i).getLength();
+            if (length <= 0) {
+                return null;
+            }
+            size[(dim-1) - i] = length;
+        }
+        return size;
+    }
+
+    /**
      * Returns all axes of the netCDF coordinate system, together with the grid dimension
to which the axis
      * is associated.
      *
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
index dac6ddb..560594e 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
@@ -28,8 +28,10 @@ import ucar.nc2.Attribute;
 import ucar.nc2.Dimension;
 import ucar.nc2.VariableIF;
 import ucar.nc2.dataset.VariableEnhanced;
+import ucar.nc2.dataset.CoordinateAxis1D;
 import ucar.nc2.units.SimpleUnit;
 import ucar.nc2.units.DateUnit;
+import org.opengis.referencing.operation.Matrix;
 import org.apache.sis.math.Vector;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Variable;
@@ -319,7 +321,26 @@ final class VariableWrapper extends Variable {
         } catch (InvalidRangeException e) {
             throw new DataStoreException(e);
         }
-        return Vector.create(array.get1DJavaArray(array.getElementType()), variable.isUnsigned());
+        return createDecimalVector(array.get1DJavaArray(array.getElementType()), variable.isUnsigned());
+    }
+
+    /**
+     * Sets the scale and offset coefficients in the given "grid to CRS" transform if possible.
+     * This method is invoked only for variables that represent a coordinate system axis.
+     */
+    @Override
+    protected boolean trySetTransform(final Matrix gridToCRS, final int srcDim, final int
tgtDim)
+            throws IOException, DataStoreException
+    {
+        if (variable instanceof CoordinateAxis1D) {
+            final CoordinateAxis1D axis = (CoordinateAxis1D) variable;
+            if (axis.isRegular()) {
+                gridToCRS.setElement(tgtDim, srcDim, axis.getIncrement());
+                gridToCRS.setElement(tgtDim, gridToCRS.getNumCol() - 1, axis.getStart());
+                return true;
+            }
+        }
+        return super.trySetTransform(gridToCRS, srcDim, tgtDim);
     }
 
     /**


Mime
View raw message