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: When the netCDF variable does not specify explicitly the valid value range, infer a default range based on the data type.
Date Wed, 12 Dec 2018 12:54:52 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 a72bc6b  When the netCDF variable does not specify explicitly the valid value range,
infer a default range based on the data type.
a72bc6b is described below

commit a72bc6b21159f3a3175e9d5ef654c2dc3fbf7f71
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Dec 12 12:31:17 2018 +0100

    When the netCDF variable does not specify explicitly the valid value range, infer a default
range based on the data type.
---
 .../org/apache/sis/internal/netcdf/Variable.java   | 71 +++++++++++++++++++++-
 .../apache/sis/storage/netcdf/GridResource.java    | 50 ++++++---------
 2 files changed, 90 insertions(+), 31 deletions(-)

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 9a2db4b..55fb0be 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
@@ -16,8 +16,10 @@
  */
 package org.apache.sis.internal.netcdf;
 
-import java.util.Locale;
+import java.util.Map;
+import java.util.LinkedHashMap;
 import java.util.Collection;
+import java.util.Locale;
 import java.util.regex.Pattern;
 import java.io.IOException;
 import java.awt.image.DataBuffer;
@@ -31,6 +33,7 @@ import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.logging.WarningListeners;
 import org.apache.sis.util.resources.Errors;
+import ucar.nc2.constants.CDM;                      // We use only String constants.
 
 
 /**
@@ -56,6 +59,15 @@ public abstract class Variable extends NamedElement {
     };
 
     /**
+     * Names of attributes where to fetch missing or pad values. Order matter since it determines
the bits to be set
+     * in the map returned by {@link #getNodataValues()}. The main bit is bit 0, which identify
the background value.
+     */
+    private static final String[] NODATA_ATTRIBUTES = {
+        CDM.FILL_VALUE,
+        CDM.MISSING_VALUE
+    };
+
+    /**
      * The pattern to use for parsing temporal units of the form "days since 1970-01-01 00:00:00".
      *
      * @see #parseUnit(String)
@@ -425,10 +437,67 @@ public abstract class Variable extends NamedElement {
                 return range;
             }
         }
+        /*
+         * If we found no explicit range attribute and if the variable type is an integer
type,
+         * then infer the range from the variable type and exclude the ranges of nodata values.
+         */
+        final DataType dataType = getDataType();
+        if (dataType.isInteger) {
+            final int size = dataType.size() * Byte.SIZE;
+            if (size > 0 && size <= Long.SIZE) {
+                long min = 0;
+                long max = (1L << size) - 1;
+                if (!dataType.isUnsigned) {
+                    max >>>= 1;
+                    min = ~max;
+                }
+                for (final Number value : getNodataValues().keySet()) {
+                    final long n = value.longValue();
+                    final long Δmin = (n - min);            // Should be okay even with
long unsigned values.
+                    final long Δmax = (max - n);
+                    if (Δmin >= 0 && Δmax >= 0) {           // Test if the
pad/missing value is inside range.
+                        if (Δmin < Δmax) min = n + 1;       // Reduce the extremum closest
to the pad value.
+                        else             max = n - 1;
+                    }
+                }
+                if (max > min) {        // Note: this will also exclude unsigned long
if max > Long.MAX_VALUE.
+                    if (min >= Integer.MIN_VALUE && max <= Integer.MAX_VALUE)
{
+                        return NumberRange.create((int) min, true, (int) max, true);
+                    }
+                    return NumberRange.create(min, true, max, true);
+                }
+            }
+        }
         return null;
     }
 
     /**
+     * Returns all no-data values declared for this variable, or an empty map if none.
+     * The map keys are pad sample values or missing sample values. The map values are
+     * bitmask identifying the role of the pad/missing sample value:
+     *
+     * <ul>
+     *   <li>If bit 0 is set, then the value is a pad value. Those values can be used
for background.</li>
+     *   <li>If bit 1 is set, then the value is a missing value.</li>
+     * </ul>
+     *
+     * The same value may have more than one role.
+     *
+     * @return pad/missing values with bitmask of their role.
+     */
+    public final Map<Number,Integer> getNodataValues() {
+        final Map<Number,Integer> pads = new LinkedHashMap<>();
+        for (int i=0; i < NODATA_ATTRIBUTES.length; i++) {
+            for (final Object value : getAttributeValues(NODATA_ATTRIBUTES[i], true)) {
+                if (value instanceof Number) {
+                    pads.merge((Number) value, 1 << i, (v1, v2) -> v1 | v2);
+                }
+            }
+        }
+        return pads;
+    }
+
+    /**
      * Compares two numbers which shall be of the same class.
      */
     @SuppressWarnings("unchecked")
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
index b59a7a8..925a1f9 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.storage.netcdf;
 
+import java.util.Map;
 import java.util.List;
 import java.util.Collections;
 import java.io.IOException;
@@ -54,16 +55,6 @@ import ucar.nc2.constants.CDM;                      // We use only String
consta
  */
 final class GridResource extends AbstractGridResource implements ResourceOnFileSystem {
     /**
-     * Names of attributes where to fetch missing values, in preference order.
-     * The union of all "no data" values will be stored, but the category name
-     * will be inferred from the first attribute declaring the "no data" value.
-     */
-    private static final String[] NODATA_ATTRIBUTES = {
-        CDM.MISSING_VALUE,
-        CDM.FILL_VALUE
-    };
-
-    /**
      * The identifier of this grid resource. This is the variable name.
      *
      * @see #getIdentifier()
@@ -178,27 +169,26 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
             }
             /*
              * Adds the "missing value" or "fill value" as qualitative categories.
+             * If a value has both roles, use "missing value" for category name.
              */
-            for (final String attribute : NODATA_ATTRIBUTES) {
-                InternationalString name = null;
-                boolean isFillValue = false;
-                for (final Object value : data.getAttributeValues(attribute, true)) {
-                    if (value instanceof Number) {
-                        final Number n = (Number) value;
-                        final double fp = n.doubleValue();
-                        if (!builder.rangeCollides(fp, fp)) {
-                            if (name == null) {
-                                isFillValue = CDM.FILL_VALUE.equalsIgnoreCase(attribute);
-                                name = Vocabulary.formatInternational(isFillValue ? Vocabulary.Keys.FillValue
-                                                                                  : Vocabulary.Keys.MissingValue);
-                            }
-                            if (isFillValue) {
-                                isFillValue = false;                              // Declare
only one fill value.
-                                builder.setBackground(name, n);
-                            } else {
-                                builder.addQualitative(name, n, n);
-                            }
-                        }
+            boolean setBackground = true;
+            final InternationalString[] names = new InternationalString[2];
+            for (final Map.Entry<Number,Integer> entry : data.getNodataValues().entrySet())
{
+                final Number n = entry.getKey();
+                final double fp = n.doubleValue();
+                if (!builder.rangeCollides(fp, fp)) {
+                    final int role = entry.getValue();          // Bit 0 set (value 1) =
pad value, bit 1 set = missing value.
+                    final int i = (role == 1) ? 1 : 0;          // i=1 if role is only pad
value, i=0 otherwise.
+                    InternationalString name = names[i];
+                    if (name == null) {
+                        name = Vocabulary.formatInternational(i == 0 ? Vocabulary.Keys.MissingValue
: Vocabulary.Keys.FillValue);
+                        names[i] = name;
+                    }
+                    if (setBackground & (role & 1) != 0) {
+                        setBackground = false;                  // Declare only one fill
value.
+                        builder.setBackground(name, n);
+                    } else {
+                        builder.addQualitative(name, n, n);
                     }
                 }
             }


Mime
View raw message