sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 08/33: Take axis direction in account when computing the geographic bounding box.
Date Mon, 18 Jun 2018 21:02:30 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 7f9281ca60cf77bf13f18d1f582af58587e016f7
Author: Martin Desruisseaux <desruisseaux@apache.org>
AuthorDate: Mon May 28 13:44:53 2018 +0000

    Take axis direction in account when computing the geographic bounding box.
    
    
    git-svn-id: https://svn.apache.org/repos/asf/sis/branches/JDK8@1832397 13f79535-47bb-0310-9956-ffa450edef68
---
 .../java/org/apache/sis/internal/netcdf/Axis.java  |  29 ++++-
 .../org/apache/sis/internal/netcdf/Decoder.java    |  25 +---
 .../sis/internal/netcdf/impl/GridGeometryInfo.java |   2 +-
 .../sis/internal/netcdf/impl/package-info.java     |   2 +-
 .../apache/sis/internal/netcdf/package-info.java   |   6 +-
 .../sis/internal/netcdf/ucar/package-info.java     |   7 +-
 .../apache/sis/storage/netcdf/MetadataReader.java  | 128 +++++++++++++--------
 .../apache/sis/storage/netcdf/package-info.java    |   2 +-
 .../internal/netcdf/impl/ChannelDecoderTest.java   |   3 +-
 9 files changed, 118 insertions(+), 86 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 7376e3e..5939443 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
@@ -17,9 +17,12 @@
 package org.apache.sis.internal.netcdf;
 
 import java.io.IOException;
-import org.apache.sis.util.ArraysExt;
+import org.opengis.referencing.cs.AxisDirection;
+import org.apache.sis.internal.metadata.AxisDirections;
 import org.apache.sis.storage.netcdf.AttributeNames;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.ArraysExt;
 
 
 /**
@@ -29,7 +32,7 @@ import org.apache.sis.storage.DataStoreException;
  * {@link ucar.nc2.dataset.CoordinateAxis1D} or {@link ucar.nc2.dataset.CoordinateAxis2D}
respectively.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see GridGeometry#getAxes()
  *
@@ -96,4 +99,26 @@ public final class Axis {
             }
         }
     }
+
+    /**
+     * Returns the axis direction for the given unit of measurement as a sign relative to
the given direction.
+     * This method performs the second half of the work of parsing "degrees_east" or "degrees_west"
units.
+     *
+     * @param  unit      the string representation of the netCDF unit, or {@code null}.
+     * @param  positive  the direction to take as positive value: {@link AxisDirection#EAST}
or {@link AxisDirection#NORTH}.
+     * @return the axis direction as a sign relative to the positive direction, or 0 if unrecognized.
+     */
+    public static int direction(final String unit, final AxisDirection positive) {
+        if (unit != null) {
+            final int s = unit.indexOf('_');
+            if (s > 0) {
+                final AxisDirection dir = Types.forCodeName(AxisDirection.class, unit.substring(s+1),
false);
+                if (dir != null) {
+                    if (dir.equals(positive)) return +1;
+                    if (dir.equals(AxisDirections.opposite(positive))) return -1;
+                }
+            }
+        }
+        return 0;
+    }
 }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
index e596c77..45dd2ae 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
@@ -21,9 +21,6 @@ import java.util.Objects;
 import java.util.Collection;
 import java.io.Closeable;
 import java.io.IOException;
-import javax.measure.Unit;
-import javax.measure.format.ParserException;
-import org.apache.sis.measure.Units;
 import org.apache.sis.setup.GeometryLibrary;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.DataStoreException;
@@ -37,7 +34,7 @@ import org.apache.sis.util.logging.WarningListeners;
  * Synchronizations are caller's responsibility.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -154,26 +151,6 @@ public abstract class Decoder implements Closeable {
     public abstract Date dateValue(final String name);
 
     /**
-     * Returns the value of the attribute of the given name as a unit of measurement, or
{@code null} if none.
-     *
-     * @param  name  the name of the attribute to search, or {@code null}.
-     * @return the attribute value, or {@code null} if none or unparsable or if the given
name was null.
-     *
-     * @todo Current Units.valueOf(String) implementation ignore direction in "degrees_east"
or "degrees_west".
-     *       We may need to take that in account (with "degrees_west" to "degrees_east" converter
that reverse
-     *       the sign).
-     */
-    public final Unit<?> unitValue(final String name) {
-        final String unit = stringValue(name);
-        if (unit != null) try {
-            return Units.valueOf(unit);
-        } catch (ParserException e) {
-            listeners.warning(null, e);
-        }
-        return null;
-    }
-
-    /**
      * Converts the given numerical values to date, using the information provided in the
given unit symbol.
      * The unit symbol is typically a string like <cite>"days since 1970-01-01T00:00:00Z"</cite>.
      *
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 23c683e..d221142 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
@@ -178,7 +178,7 @@ final class GridGeometryInfo extends GridGeometry {
      */
     @Override
     protected double coordinateForAxis(final Object axis, final int j, final int i) throws
IOException, DataStoreException {
-        final VariableInfo v = ((VariableInfo) axis);
+        final VariableInfo v = (VariableInfo) axis;
         final int n = v.dimensions[0].length;
         return v.read().doubleValue(j + n*i);
     }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/package-info.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/package-info.java
index a9e953b..3a34638 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/package-info.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/package-info.java
@@ -29,7 +29,7 @@
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/package-info.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/package-info.java
index d64d02f..3a164ec 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/package-info.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/package-info.java
@@ -34,11 +34,11 @@
  * ISO 19123 <cite>"domain"</cite>, the netCDF coordinate system <cite>"domain"</cite>
is closer to
  * ISO 19123 <cite>"grid envelope"</cite> and the ISO 19123 <cite>"range"</cite>
is rather related
  * to the netCDF variable minimum and maximum values. Trying to use OGC/ISO and netCDF objects
in
- * the same code appears to be <strong>very</strong> confusing. This isolation
layer allows our code
- * to use a more consistent vocabulary (compared to the rest of Apache SIS).</p>
+ * the same code appears to be very confusing. This isolation layer allows our code to use
a more
+ * consistent vocabulary (more similar to the rest of Apache SIS).</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/package-info.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/package-info.java
index e419e83..f6e3b2e 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/package-info.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/package-info.java
@@ -20,8 +20,11 @@
  * as wrappers around the UCAR netCDF library.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
- * @since   0.3
+ * @version 1.0
+ *
+ * @see <a href="https://www.unidata.ucar.edu/software/thredds/current/netcdf-java/javadoc/index.html">UCAR
javadoc</a>
+ *
+ * @since 0.3
  * @module
  */
 package org.apache.sis.internal.netcdf.ucar;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
index 48d59f5..0158a85 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
@@ -45,6 +45,7 @@ import org.opengis.metadata.citation.*;
 import org.opengis.metadata.identification.*;
 import org.opengis.metadata.maintenance.ScopeCode;
 import org.opengis.metadata.constraint.Restriction;
+import org.opengis.referencing.cs.AxisDirection;
 import org.opengis.referencing.crs.VerticalCRS;
 
 import org.apache.sis.util.iso.Types;
@@ -281,6 +282,14 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start,
lengt
     }
 
     /**
+     * Reads the numeric value for the given value, or returns {@code NaN} if none.
+     */
+    private double numericValue(final String name) {
+        final Number v = decoder.numericValue(name);
+        return (v != null) ? v.doubleValue() : Double.NaN;
+    }
+
+    /**
      * Returns the enumeration constant for the given name, or {@code null} if the given
name is not recognized.
      * In the later case, this method emits a warning.
      */
@@ -711,9 +720,23 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start,
lengt
             final AttributeNames.Dimension attributeNames = axis.attributeNames;
             if (attributeNames != null) {
                 setAxisName(dim, attributeNames.DEFAULT_NAME_TYPE);
-                final Number value = decoder.numericValue(attributeNames.RESOLUTION);
-                if (value != null) {
-                    setAxisResolution(dim, value.doubleValue());
+                final String res = stringValue(attributeNames.RESOLUTION);
+                if (res != null) try {
+                    /*
+                     * ACDD convention recommends to write units after the resolution.
+                     * Examples: "100 meters", "0.1 degree".
+                     */
+                    final int s = res.indexOf(' ');
+                    final double value;
+                    if (s < 0) {
+                        value = numericValue(attributeNames.RESOLUTION);
+                    } else {
+                        value = Double.parseDouble(res.substring(0, s));
+                        // TODO: parse units and build a Quantity object.
+                    }
+                    setAxisResolution(dim, value);
+                } catch (NumberFormatException e) {
+                    warning(e);
                 }
             }
         }
@@ -728,35 +751,22 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value,
start, lengt
      */
     private boolean addExtent() {
         addExtent(stringValue(GEOGRAPHIC_IDENTIFIER));
+        final double[] extent = new double[4];
         /*
-         * If at least one geographic ordinates is available, add a GeographicBoundingBox.
+         * If at least one geographic coordinate is available, add a GeographicBoundingBox.
          */
-        final Number xmin = decoder.numericValue(LONGITUDE.MINIMUM);
-        final Number xmax = decoder.numericValue(LONGITUDE.MAXIMUM);
-        final Number ymin = decoder.numericValue(LATITUDE .MINIMUM);
-        final Number ymax = decoder.numericValue(LATITUDE .MAXIMUM);
-        final Number zmin = decoder.numericValue(VERTICAL .MINIMUM);
-        final Number zmax = decoder.numericValue(VERTICAL .MAXIMUM);
-        boolean hasExtent = (xmin != null || xmax != null || ymin != null || ymax != null);
+        boolean hasExtent;
+        hasExtent  = fillExtent(LONGITUDE, Units.DEGREE, AxisDirection.EAST,  extent, 0);
+        hasExtent |= fillExtent(LATITUDE,  Units.DEGREE, AxisDirection.NORTH, extent, 2);
         if (hasExtent) {
-            final UnitConverter xConv = getConverterTo(decoder.unitValue(LONGITUDE.UNITS),
Units.DEGREE);
-            final UnitConverter yConv = getConverterTo(decoder.unitValue(LATITUDE .UNITS),
Units.DEGREE);
-            addExtent(new double[] {valueOf(xmin, xConv), valueOf(xmax, xConv),
-                                    valueOf(ymin, yConv), valueOf(ymax, yConv)}, 0);
+            addExtent(extent, 0);
+            hasExtent = true;
         }
         /*
-         * If at least one vertical ordinates above is available, add a VerticalExtent.
+         * If at least one vertical coordinate is available, add a VerticalExtent.
          */
-        if (zmin != null || zmax != null) {
-            final UnitConverter c = getConverterTo(decoder.unitValue(VERTICAL.UNITS), Units.METRE);
-            double min = valueOf(zmin, c);
-            double max = valueOf(zmax, c);
-            if (CF.POSITIVE_DOWN.equals(stringValue(VERTICAL.POSITIVE))) {
-                final double tmp = min;
-                min = -max;
-                max = -tmp;
-            }
-            addVerticalExtent(min, max, VERTICAL_CRS);
+        if (fillExtent(VERTICAL, Units.METRE, null, extent, 0)) {
+            addVerticalExtent(extent[0], extent[1], VERTICAL_CRS);
             hasExtent = true;
         }
         /*
@@ -778,8 +788,8 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value, start,
lengt
             }
         }
         /*
-         * If at least one time values above is available, add a temporal extent.
-         * This operation requires the the sis-temporal module. If not available,
+         * If at least one time value above is available, add a temporal extent.
+         * This operation requires the sis-temporal module. If not available,
          * we will report a warning and leave the temporal extent missing.
          */
         if (startTime != null || endTime != null) try {
@@ -792,31 +802,49 @@ split:  while ((start = CharSequences.skipLeadingWhitespaces(value,
start, lengt
     }
 
     /**
-     * Returns the converter from the given source unit (which may be {@code null}) to the
-     * given target unit, or {@code null} if none or incompatible.
-     */
-    private UnitConverter getConverterTo(final Unit<?> source, final Unit<?>
target) {
-        if (source != null) try {
-            return source.getConverterToAny(target);
-        } catch (IncommensurableException e) {
-            warning(e);
-        }
-        return null;
-    }
-
-    /**
-     * Returns the values of the given number if non-null, or NaN if null. If the given
-     * converter is non-null, it is applied.
+     * Fills one dimension of the geographic bounding box or vertical extent.
+     * The extent values are written in the given {@code extent} array.
+     *
+     * @param  dim         the dimension for which to get the extent.
+     * @param  targetUnit  the destination unit of the extent.
+     * @param  positive    the direction considered positive, or {@code null} if the unit
symbol is not expected to contain a direction.
+     * @param  extent      where to store the minimum and maximum values.
+     * @param  index       index where to store the minimum value in {@code extent}. The
maximum value is stored at {@code index+1}.
+     * @return {@code true} if a minimum or a maximum value has been found.
      */
-    private static double valueOf(final Number value, final UnitConverter converter) {
-        double n = Double.NaN;
-        if (value != null) {
-            n = value.doubleValue();
-            if (converter != null) {
-                n = converter.convert(n);
+    private boolean fillExtent(final AttributeNames.Dimension dim, final Unit<?> targetUnit,
final AxisDirection positive,
+                               final double[] extent, final int index)
+    {
+        double min = numericValue(dim.MINIMUM);
+        double max = numericValue(dim.MAXIMUM);
+        boolean hasExtent = !Double.isNaN(min) || !Double.isNaN(max);
+        if (hasExtent) {
+            final String symbol = stringValue(dim.UNITS);
+            if (symbol != null) {
+                try {
+                    final UnitConverter c = Units.valueOf(symbol).getConverterToAny(targetUnit);
+                    min = c.convert(min);
+                    max = c.convert(max);
+                } catch (ParserException | IncommensurableException e) {
+                    warning(e);
+                }
+                boolean reverse = false;
+                if (positive != null) {
+                    reverse = Axis.direction(symbol, positive) < 0;
+                } else if (dim.POSITIVE != null) {
+                    // For now, only the vertical axis have a "positive" attribute.
+                    reverse = CF.POSITIVE_DOWN.equals(stringValue(dim.POSITIVE));
+                }
+                if (reverse) {
+                    final double tmp = min;
+                    min = -max;
+                    max = -tmp;
+                }
             }
         }
-        return n;
+        extent[index  ] = min;
+        extent[index+1] = max;
+        return hasExtent;
     }
 
     /**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/package-info.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/package-info.java
index 1e164c0..fd04b6c 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/package-info.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/package-info.java
@@ -48,7 +48,7 @@
  * Care must be taken for avoiding confusion when using SIS and UCAR libraries together.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
index 8588a92..4e3a61c 100644
--- a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
+++ b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java
@@ -31,8 +31,7 @@ import org.opengis.test.dataset.TestData;
 
 /**
  * Tests the {@link ChannelDecoder} implementation. This test shall be executed only if the
- * {@link ChannelDecoder} tests, which use the UCAR library has a reference implementation,
- * passed.
+ * {@link DecoderTest}, which use the UCAR library has a reference implementation, passed.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0


Mime
View raw message