You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2022/09/28 15:39:05 UTC
[sis] 04/04: Add a workaround for missing "no data" value in some GCOM files. Because the workaround may introduce two categories with same name, add a "#2", "#3", etc. suffix after duplicated names.
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 5c4559bba6a0ef592c25eaf7d60d6f70ed170a98
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Sep 28 17:26:11 2022 +0200
Add a workaround for missing "no data" value in some GCOM files.
Because the workaround may introduce two categories with same name,
add a "#2", "#3", etc. suffix after duplicated names.
---
.../apache/sis/internal/earth/netcdf/GCOM_C.java | 52 +++++++++++++++++++---
.../apache/sis/internal/netcdf/RasterResource.java | 23 +++++-----
2 files changed, 55 insertions(+), 20 deletions(-)
diff --git a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
index 0f47ed5f66..b8e13667e5 100644
--- a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
+++ b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
@@ -47,6 +47,7 @@ import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.coverage.Category;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
+import org.apache.sis.util.Workaround;
import ucar.nc2.constants.CF;
@@ -171,22 +172,39 @@ public final class GCOM_C extends Convention {
/**
* Names of attributes for sample values having "no-data" meaning.
- * All those names have {@value #SUFFIX} suffix.
+ * Pad values should be first, followed by missing values.
+ * All those names have the {@value #SUFFIX} suffix.
*
* @see #nodataValues(Variable)
*/
private static final String[] NO_DATA = {
"Error_DN", // Must be first: will be used as "no data" value.
- "Land_DN",
+ "Retrieval_error_DN", // First fallback if "Error_DN" is not found.
"Cloud_error_DN",
- "Retrieval_error_DN"
+ "Land_DN"
};
+ /**
+ * Names of attributes for minimum and maximum valid sample values.
+ */
+ private static final String MINIMUM = "Minimum_valid_DN",
+ MAXIMUM = "Maximum_valid_DN";
+
/**
* Suffix of all attribute names enumerated in {@link #NO_DATA}.
*/
private static final String SUFFIX = "_DN";
+ /**
+ * A "No data" value which should be present in all GCOM files but appear to be missing.
+ * All values in the range {@value} to {@code 0xFFFF} will be "no data", unless the range
+ * of valid values overlap.
+ *
+ * <p>This hack may be removed after VGI data files fixed their missing "no data" attribute.</p>
+ */
+ @Workaround(library = "Vegetation Index (VGI)", version = "3 (2021)")
+ private static final int MISSING_NODATA = 0xFFFE;
+
/**
* Creates a new instance of GCOM-C conventions.
*/
@@ -472,8 +490,8 @@ public final class GCOM_C extends Convention {
public NumberRange<?> validRange(final Variable data) {
NumberRange<?> range = super.validRange(data);
if (range == null) {
- final Number min = data.getAttributeAsNumber("Minimum_valid_DN");
- final Number max = data.getAttributeAsNumber("Maximum_valid_DN");
+ final Number min = data.getAttributeAsNumber(MINIMUM);
+ final Number max = data.getAttributeAsNumber(MAXIMUM);
if (min != null || max != null) {
range = NumberRange.createBestFit(min, true, max, true);
}
@@ -484,13 +502,15 @@ public final class GCOM_C extends Convention {
/**
* Returns all no-data values declared for the given variable, or an empty map if none.
* The map keys are the no-data values (pad sample values or missing sample values).
- * The map values are {@link String} instances containing the description of the no-data value.
+ * The map values are {@link String} instances containing the description of the no-data value,
+ * except for {@code "Error_DN"} which is used as a more generic pad value.
*
* @param data the variable for which to get no-data values.
* @return no-data values with textual descriptions.
*/
@Override
public Map<Number,Object> nodataValues(final Variable data) {
+ boolean addMissingNodata = true;
final Map<Number, Object> pads = super.nodataValues(data);
for (int i=0; i<NO_DATA.length; i++) {
String name = NO_DATA[i];
@@ -505,7 +525,25 @@ public final class GCOM_C extends Convention {
} else {
label = FILL_VALUE_MASK | MISSING_VALUE_MASK;
}
- pads.put(value, label);
+ if (pads.putIfAbsent(value, label) == null && addMissingNodata) {
+ addMissingNodata = Math.floor(value.doubleValue()) > MISSING_NODATA;
+ }
+ }
+ }
+ /*
+ * Workaround for missing "no data" attribute in VGI files. As of September 2022, GCOM files have a
+ * "Land_DN" attribute for missing data caused by land, but no "Sea_DN" attribute for the converse.
+ * As an heuristic rule, if valid values are short integers and "no data" values are either absent
+ * of greater than `MISSING_NODATA`, add "missing values" category for all values up to 0xFFFF.
+ */
+ if (addMissingNodata) {
+ final double valid = data.getAttributeAsDouble(MAXIMUM);
+ if (valid >= 0x100 && valid < MISSING_NODATA) {
+ int label = FILL_VALUE_MASK | MISSING_VALUE_MASK;
+ for (int value=0xFFFF; value >= MISSING_NODATA; value--) {
+ pads.putIfAbsent(value, label);
+ label = MISSING_VALUE_MASK;
+ }
}
}
return pads;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
index 7250fa9369..65aaee48a3 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
@@ -533,14 +533,13 @@ public final class RasterResource extends AbstractGridCoverageResource implement
listeners.warning(e);
}
/*
- * Adds the "missing value" or "fill value" as qualitative categories. If a value has both roles, use "missing value"
- * as category name. If the sample values are already real values, then the "no data" values have been replaced by NaN
- * values by Variable.replaceNaN(Object). The qualitative categories constructed below must be consistent with the NaN
- * values created by `replaceNaN`.
+ * Adds the "missing value" or "fill value" as qualitative categories. If the sample values are already
+ * real values, then the "no data" values have been replaced by NaN values by Variable.replaceNaN(Object).
+ * The qualitative categories constructed below must be consistent with NaN values created by `replaceNaN`.
*/
boolean setBackground = true;
+ int missingValueOrdinal = 0;
int ordinal = band.hasRealValues() ? 0 : -1;
- final CharSequence[] names = new CharSequence[2];
for (final Map.Entry<Number,Object> entry : band.getNodataValues().entrySet()) {
final Number n;
if (ordinal >= 0) {
@@ -551,18 +550,16 @@ public final class RasterResource extends AbstractGridCoverageResource implement
CharSequence name;
final Object label = entry.getValue();
if (label instanceof Integer) {
- final int role = (Integer) label; // Bit 0 set (value 1) = pad value, bit 1 set = missing value.
- final int i = (role == Convention.FILL_VALUE_MASK) ? 1 : 0; // i=1 if role is only pad value, i=0 otherwise.
- name = names[i];
- if (name == null) {
- name = Vocabulary.formatInternational(i == 0 ? Vocabulary.Keys.MissingValue : Vocabulary.Keys.FillValue);
- names[i] = name;
- }
- if (setBackground & (role & Convention.FILL_VALUE_MASK) != 0) {
+ final boolean isFill = setBackground && (((Integer) label) & Convention.FILL_VALUE_MASK) != 0;
+ name = Vocabulary.formatInternational(isFill ? Vocabulary.Keys.FillValue : Vocabulary.Keys.MissingValue);
+ if (isFill) {
setBackground = false; // Declare only one fill value.
builder.setBackground(name, n);
continue;
}
+ if (++missingValueOrdinal >= 2) {
+ name = name.toString() + " #" + missingValueOrdinal;
+ }
} else {
name = (CharSequence) label;
}