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:01 UTC
[sis] branch geoapi-4.0 updated (e7f297691f -> 5c4559bba6)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
from e7f297691f Preserve the sign of scale factor in automatic "grid to CRS" transform.
new 68b8c2981f When unwrapping an exception, unwrap also the suppressed exceptions.
new 0c35e9fa7e Preserve the type of missing values in netCDF files. Before this commit, missing values in GCOM files were always represented as floating points. After this commit, missing values declared as integers stay integers. It makes a difference when computing which value is next.
new b6b4fde4a3 Add `ColorModelFactory.toString()` implementation for debugging purposes.
new 5c4559bba6 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.
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../internal/coverage/j2d/ColorModelFactory.java | 53 ++++++++++++++---
.../sis/internal/coverage/j2d/ColorsForRange.java | 31 +++++++++-
.../main/java/org/apache/sis/util/Exceptions.java | 24 +++++---
.../sis/util/collection/BackingStoreException.java | 3 +-
.../apache/sis/internal/earth/netcdf/GCOM_C.java | 68 +++++++++++++++++-----
.../apache/sis/internal/earth/netcdf/GCOM_W.java | 4 +-
.../org/apache/sis/internal/netcdf/Convention.java | 6 +-
.../java/org/apache/sis/internal/netcdf/Node.java | 29 ++++++---
.../apache/sis/internal/netcdf/RasterResource.java | 23 ++++----
.../org/apache/sis/internal/netcdf/Variable.java | 6 +-
.../apache/sis/storage/netcdf/MetadataReader.java | 4 +-
.../apache/sis/internal/netcdf/VariableTest.java | 4 +-
12 files changed, 188 insertions(+), 67 deletions(-)
[sis] 02/04: Preserve the type of missing values in netCDF files. Before this commit, missing values in GCOM files were always represented as floating points. After this commit, missing values declared as integers stay integers. It makes a difference when computing which value is next.
Posted by de...@apache.org.
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 0c35e9fa7e224bd5a272fa245a6f2200d474f77b
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Sep 28 11:42:45 2022 +0200
Preserve the type of missing values in netCDF files.
Before this commit, missing values in GCOM files were always represented as floating points.
After this commit, missing values declared as integers stay integers.
It makes a difference when computing which value is next.
---
.../apache/sis/internal/earth/netcdf/GCOM_C.java | 20 +++++++--------
.../apache/sis/internal/earth/netcdf/GCOM_W.java | 4 +--
.../org/apache/sis/internal/netcdf/Convention.java | 6 ++---
.../java/org/apache/sis/internal/netcdf/Node.java | 29 ++++++++++++++++------
.../org/apache/sis/internal/netcdf/Variable.java | 6 ++---
.../apache/sis/storage/netcdf/MetadataReader.java | 4 +--
.../apache/sis/internal/netcdf/VariableTest.java | 4 +--
7 files changed, 43 insertions(+), 30 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 8672c85d57..0f47ed5f66 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
@@ -418,7 +418,7 @@ public final class GCOM_C extends Convention {
public MathTransform gridToCRS(final Node node, final MathTransform baseToCRS) throws TransformException {
final double[] corners = new double[CORNERS.length];
for (int i=0; i<corners.length; i++) {
- corners[i] = node.getAttributeAsNumber(CORNERS[i]);
+ corners[i] = node.getAttributeAsDouble(CORNERS[i]);
}
baseToCRS.transform(corners, 0, corners, 0, corners.length / 2);
/*
@@ -431,8 +431,8 @@ public final class GCOM_C extends Convention {
/*
* Transform the spans into pixel sizes (resolution), then build the transform.
*/
- sx /= (node.getAttributeAsNumber("Number_of_pixels") - 1);
- sy /= (node.getAttributeAsNumber("Number_of_lines") - 1);
+ sx /= (node.getAttributeAsDouble("Number_of_pixels") - 1);
+ sy /= (node.getAttributeAsDouble("Number_of_lines") - 1);
if (Double.isFinite(sx) && Double.isFinite(sy)) {
final Matrix3 m = new Matrix3();
m.m00 = sx;
@@ -472,9 +472,9 @@ public final class GCOM_C extends Convention {
public NumberRange<?> validRange(final Variable data) {
NumberRange<?> range = super.validRange(data);
if (range == null) {
- final double min = data.getAttributeAsNumber("Minimum_valid_DN");
- final double max = data.getAttributeAsNumber("Maximum_valid_DN");
- if (Double.isFinite(min) && Double.isFinite(max)) {
+ final Number min = data.getAttributeAsNumber("Minimum_valid_DN");
+ final Number max = data.getAttributeAsNumber("Maximum_valid_DN");
+ if (min != null || max != null) {
range = NumberRange.createBestFit(min, true, max, true);
}
}
@@ -494,8 +494,8 @@ public final class GCOM_C extends Convention {
final Map<Number, Object> pads = super.nodataValues(data);
for (int i=0; i<NO_DATA.length; i++) {
String name = NO_DATA[i];
- final double value = data.getAttributeAsNumber(name);
- if (Double.isFinite(value)) {
+ final Number value = data.getAttributeAsNumber(name);
+ if (value != null) {
final Object label;
if (i != 0) {
if (name.endsWith(SUFFIX)) {
@@ -522,8 +522,8 @@ public final class GCOM_C extends Convention {
public TransferFunction transferFunction(final Variable data) {
final TransferFunction tr = super.transferFunction(data);
if (tr.isIdentity()) {
- final double slope = data.getAttributeAsNumber("Slope");
- final double offset = data.getAttributeAsNumber("Offset");
+ final double slope = data.getAttributeAsDouble("Slope");
+ final double offset = data.getAttributeAsDouble("Offset");
if (Double.isFinite(slope)) tr.setScale (slope);
if (Double.isFinite(offset)) tr.setOffset(offset);
}
diff --git a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
index 82b485e1a5..6d8f432adb 100644
--- a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
+++ b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
@@ -255,8 +255,8 @@ public final class GCOM_W extends Convention {
public TransferFunction transferFunction(final Variable data) {
final TransferFunction tr = super.transferFunction(data);
if (tr.isIdentity()) {
- final double slope = data.getAttributeAsNumber("SCALE FACTOR");
- final double offset = data.getAttributeAsNumber("OFFSET");
+ final double slope = data.getAttributeAsDouble("SCALE FACTOR");
+ final double offset = data.getAttributeAsDouble("OFFSET");
if (Double.isFinite(slope)) tr.setScale (slope);
if (Double.isFinite(offset)) tr.setOffset(offset);
}
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
index 8570cbd890..35e1f97fa1 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
@@ -351,7 +351,7 @@ public class Convention {
* @return the "grid indices to data indices" scale factor, or {@link Double#NaN} if none.
*/
public double gridToDataIndices(final Variable axis) {
- return axis.getAttributeAsNumber("resampling_interval");
+ return axis.getAttributeAsDouble("resampling_interval");
}
/**
@@ -755,8 +755,8 @@ public class Convention {
* a "packed" variable. Otherwise the transfer function is the identity transform.
*/
final TransferFunction tr = new TransferFunction();
- final double scale = data.getAttributeAsNumber(CDM.SCALE_FACTOR);
- final double offset = data.getAttributeAsNumber(CDM.ADD_OFFSET);
+ final double scale = data.getAttributeAsDouble(CDM.SCALE_FACTOR);
+ final double offset = data.getAttributeAsDouble(CDM.ADD_OFFSET);
if (!Double.isNaN(scale)) tr.setScale (scale);
if (!Double.isNaN(offset)) tr.setOffset(offset);
return tr;
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
index 561a8761c5..d96e7cf015 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
@@ -30,7 +30,7 @@ import org.apache.sis.internal.util.Strings;
* The common characteristic of those objects is to have attributes.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.3
* @since 1.0
* @module
*/
@@ -182,16 +182,16 @@ public abstract class Node extends NamedElement {
}
/**
- * Returns the value of the given attribute as a number, or {@link Double#NaN}.
- * If the number is stored with single-precision, it is assumed casted from a
- * representation in base 10.
+ * Returns the value of the given attribute as a number, or {@code null}.
+ * This method returns a number of the type that most closely matches the
+ * type in the netCDF file.
*
* @param attributeName the name of the attribute for which to get the value.
- * @return the singleton attribute value, or {@code NaN} if none or ambiguous.
+ * @return the singleton attribute value, or {@code null} if none or ambiguous.
*/
- public final double getAttributeAsNumber(final String attributeName) {
- final Object value = getAttributeValue(attributeName);
+ public final Number getAttributeAsNumber(final String attributeName) {
Number singleton = null;
+ final Object value = getAttributeValue(attributeName);
if (value instanceof Number) {
singleton = (Number) value;
} else if (value instanceof String) {
@@ -205,11 +205,24 @@ public abstract class Node extends NamedElement {
if (singleton == null) {
singleton = n;
} else if (!singleton.equals(n)) {
- return Double.NaN;
+ return null;
}
}
}
}
+ return singleton;
+ }
+
+ /**
+ * Returns the value of the given attribute as a number, or {@link Double#NaN}.
+ * If the number is stored with single-precision, it is assumed casted from a
+ * representation in base 10.
+ *
+ * @param attributeName the name of the attribute for which to get the value.
+ * @return the singleton attribute value, or {@code NaN} if none or ambiguous.
+ */
+ public final double getAttributeAsDouble(final String attributeName) {
+ Number singleton = getAttributeAsNumber(attributeName);
if (singleton == null) {
return Double.NaN;
}
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 4305b22bfa..cda5fa818e 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
@@ -434,10 +434,10 @@ public abstract class Variable extends Node {
return convention.transferFunction(this).isIdentity();
}
// Shortcut for common case.
- double c = getAttributeAsNumber(CDM.SCALE_FACTOR);
- if (Double.isNaN(c) || c == 1) {
+ Number c = getAttributeAsNumber(CDM.SCALE_FACTOR);
+ if (c == null || c.doubleValue() == 1) {
c = getAttributeAsNumber(CDM.ADD_OFFSET);
- return Double.isNaN(c) || c == 0;
+ return c == null || c.doubleValue() == 0;
}
}
return false;
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 d4996187bc..cc5409b623 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
@@ -964,8 +964,8 @@ split: while ((start = CharSequences.skipLeadingWhitespaces(value, start, lengt
addBandDescription(description);
}
setSampleUnits(variable.getUnit());
- setTransferFunction(variable.getAttributeAsNumber(CDM.SCALE_FACTOR),
- variable.getAttributeAsNumber(CDM.ADD_OFFSET));
+ setTransferFunction(variable.getAttributeAsDouble(CDM.SCALE_FACTOR),
+ variable.getAttributeAsDouble(CDM.ADD_OFFSET));
addContentType(forCodeName(CoverageContentType.class, stringValue(ACDD.coverage_content_type)));
}
diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
index d05cf6bcf3..1074a38cb3 100644
--- a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
+++ b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/VariableTest.java
@@ -266,7 +266,7 @@ public strictfp class VariableTest extends TestCase {
assertArrayEquals("getAttributeAsStrings", new String[] {t}, variable.getAttributeAsStrings(name, ' '));
if (expected instanceof Number) {
final double en = ((Number) expected).doubleValue();
- assertEquals("getAttributeAsNumber", en, variable.getAttributeAsNumber(name), STRICT);
+ assertEquals("getAttributeAsDouble", en, variable.getAttributeAsDouble(name), STRICT);
final Vector vector = variable.getAttributeAsVector(name);
assertNotNull("getAttributeAsVector", vector);
assertEquals(1, vector.size());
@@ -284,7 +284,7 @@ public strictfp class VariableTest extends TestCase {
final Vector values = variable.getAttributeAsVector(name);
assertNotNull(name, values);
assertEquals ("size", expected.length, values.size());
- assertTrue ("getAttributeAsNumber", Double.isNaN(variable.getAttributeAsNumber(name)));
+ assertTrue ("getAttributeAsDouble", Double.isNaN(variable.getAttributeAsDouble(name)));
assertEquals ("getAttributeValue", values, variable.getAttributeValue(name));
final Object[] texts = Arrays.stream(expected).map(Object::toString).toArray();
assertArrayEquals("getAttributeAsStrings", texts, variable.getAttributeAsStrings(name, ' '));
[sis] 01/04: When unwrapping an exception, unwrap also the suppressed exceptions.
Posted by de...@apache.org.
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 68b8c2981f634843ce854c1f6bacd1cbbbc6a494
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Sep 27 22:13:35 2022 +0200
When unwrapping an exception, unwrap also the suppressed exceptions.
---
.../main/java/org/apache/sis/util/Exceptions.java | 24 ++++++++++++++--------
.../sis/util/collection/BackingStoreException.java | 3 ++-
2 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java b/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
index e38fde2cef..e9a223dba6 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/Exceptions.java
@@ -202,8 +202,8 @@ public final class Exceptions extends Static {
* because it is used in very specific contexts. Furthermore classes related to security manager are deprecated
* since Java 17.</div>
*
- * This method uses only the exception class and the absence of {@linkplain Exception#getSuppressed() suppressed
- * exceptions} as criterion; it does not verify if the exception messages are the same.
+ * This method uses only the exception class as criterion;
+ * it does not verify if the exception messages are the same.
*
* @param exception the exception to unwrap (may be {@code null}.
* @return the unwrapped exception (may be the given argument itself).
@@ -212,24 +212,32 @@ public final class Exceptions extends Static {
*/
public static Exception unwrap(Exception exception) {
if (exception != null) {
- while (exception.getSuppressed().length == 0 &&
- (exception instanceof InvocationTargetException ||
+ while (exception instanceof InvocationTargetException ||
exception instanceof ExecutionException ||
exception instanceof BackingStoreException ||
exception instanceof UncheckedIOException ||
- exception instanceof DirectoryIteratorException))
+ exception instanceof DirectoryIteratorException)
{
final Throwable cause = exception.getCause();
if (!(cause instanceof Exception)) break;
+ copySuppressed(exception, cause);
exception = (Exception) cause;
}
Throwable cause;
- while (exception.getSuppressed().length == 0 &&
- exception.getClass().isInstance(cause = exception.getCause()))
- {
+ while (exception.getClass().isInstance(cause = exception.getCause())) {
+ copySuppressed(exception, cause);
exception = (Exception) cause; // Should never fail because of isInstance(…) check.
}
}
return exception;
}
+
+ /**
+ * Unwraps and copies suppressed exceptions from the given source to the given target.
+ */
+ private static void copySuppressed(final Exception source, final Throwable target) {
+ for (final Throwable suppressed : source.getSuppressed()) {
+ target.addSuppressed(suppressed instanceof Exception ? unwrap((Exception) suppressed) : suppressed);
+ }
+ }
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java b/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
index 7ecefbbb90..a57b3d3472 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/collection/BackingStoreException.java
@@ -18,6 +18,7 @@ package org.apache.sis.util.collection;
import java.io.IOException;
import java.sql.SQLException;
+import org.apache.sis.util.Exceptions;
/**
@@ -153,7 +154,7 @@ public class BackingStoreException extends RuntimeException {
*/
private void copySuppressed(final Throwable cause) {
for (final Throwable s : getSuppressed()) {
- cause.addSuppressed(s);
+ cause.addSuppressed(s instanceof Exception ? Exceptions.unwrap((Exception) s) : s);
}
}
}
[sis] 03/04: Add `ColorModelFactory.toString()` implementation for debugging purposes.
Posted by de...@apache.org.
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 b6b4fde4a325ec599ef7d2bc8de9567220423431
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Sep 28 12:41:36 2022 +0200
Add `ColorModelFactory.toString()` implementation for debugging purposes.
---
.../internal/coverage/j2d/ColorModelFactory.java | 53 ++++++++++++++++++----
.../sis/internal/coverage/j2d/ColorsForRange.java | 31 ++++++++++++-
2 files changed, 74 insertions(+), 10 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
index c5ff8d778a..a5d61124a8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorModelFactory.java
@@ -30,10 +30,13 @@ import java.awt.image.DirectColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.SampleModel;
import java.awt.image.DataBuffer;
+import org.apache.sis.image.DataType;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.util.Strings;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
import org.apache.sis.util.collection.WeakHashSet;
import org.apache.sis.util.collection.WeakValueHashMap;
import org.apache.sis.util.Debug;
@@ -45,7 +48,7 @@ import org.apache.sis.util.Debug;
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Johann Sorel (Geomatys)
* @author Alexis Manin (Geomatys)
- * @version 1.2
+ * @version 1.3
* @since 1.0
* @module
*/
@@ -130,8 +133,8 @@ public final class ColorModelFactory {
* end of the last piece. The indices are integers. Never {@code null} but may be empty.
*
* <div class="note"><b>Note:</b>
- * indices as unsigned short are not sufficient since in the worst case the last next index will
- * be 65536, which would be converted to 0 as a short, causing several exception afterward.</div>
+ * indices as unsigned short are not sufficient because in the worst case the last next index will be 65536,
+ * which would be converted to 0 as a short, causing several exceptions afterward.</div>
*/
private final int[] pieceStarts;
@@ -176,9 +179,17 @@ public final class ColorModelFactory {
final int before = starts[count];
if (before != lower) {
if (before > lower) {
- // TODO: remove the overlapped colors in previous range.
+ /*
+ * The range of sample values for current entry overlaps
+ * the range of sample values of previous entry.
+ *
+ * TODO: remove the overlapped colors in previous range.
+ */
} else {
- // TODO: we could reduce the amount of copies.
+ /*
+ * There is a gap between this range of sample values and the previous range.
+ * Instruct to fill this gap with ARGB code 0 (fully transparent pixels).
+ */
codes = Arrays.copyOf(codes, codes.length + 1);
starts = Arrays.copyOf(starts, starts.length + 1);
codes[count++] = ArraysExt.EMPTY_INT;
@@ -196,9 +207,9 @@ public final class ColorModelFactory {
maximum = 1;
}
/*
- * The length of 'pieceStarts' may differ from the expected length if there is holes between categories.
- * We need to adjust the array length since it will determine the number of categories. Note that there
- * is one more element than the number of categories.
+ * The length of `pieceStarts` may differ from the expected length if there is holes between categories.
+ * We need to adjust the array length because it will determine the number of categories.
+ * Note that there is one more element than the number of categories.
*/
if (starts.length != 0) {
starts = ArraysExt.resize(starts, count + 1);
@@ -678,6 +689,7 @@ public final class ColorModelFactory {
/**
* Copies {@code colors} into {@code ARGB} array from index {@code lower} inclusive to index {@code upper} exclusive.
* If {@code upper-lower} is not equal to the length of {@code colors} array, then colors will be interpolated.
+ * The given {@code colors} array must be initialized with zero values in the {@code lower} … {@code upper} range.
*
* @param colors colors to copy into the {@code ARGB} array.
* @param ARGB array of integer to write ARGB values into.
@@ -734,4 +746,29 @@ public final class ColorModelFactory {
private static int roundByte(final float value) {
return Math.min(Math.max(Math.round(value), 0), 255);
}
+
+ /**
+ * Returns a string representation for debugging purposes.
+ *
+ * @return a string representation of this color factory.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder buffer = new StringBuilder(Strings.toString(getClass(),
+ "dataType", DataType.forDataBufferType(dataType), "numBands", numBands, "visibleBand", visibleBand,
+ "range", NumberRange.create(minimum, true, maximum, false)));
+ final int n = (pieceStarts != null) ? pieceStarts.length : 0;
+ for (int i=0; i<n; i++) {
+ final String start = Integer.toString(pieceStarts[i]);
+ buffer.append(System.lineSeparator()).append(CharSequences.spaces(9 - start.length())).append(start);
+ if (i < ARGB.length) {
+ buffer.append('…');
+ final int[] colors = ARGB[i];
+ if (colors != null) {
+ ColorsForRange.appendColorRange(buffer, colors.length, (j) -> colors[j]);
+ }
+ }
+ }
+ return buffer.toString();
+ }
}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorsForRange.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorsForRange.java
index 23a6bbd66b..74f640389f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorsForRange.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ColorsForRange.java
@@ -19,6 +19,7 @@ package org.apache.sis.internal.coverage.j2d;
import java.util.Map;
import java.util.Collection;
import java.util.function.Function;
+import java.util.function.IntUnaryOperator;
import java.awt.Color;
import java.awt.image.IndexColorModel;
import org.apache.sis.coverage.Category;
@@ -32,7 +33,7 @@ import org.apache.sis.util.ArraysExt;
* the time needed for {@link ColorModelFactory#createColorModel(int, int, int, ColorsForRange[])}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.3
*
* @see ColorModelFactory#createColorModel(int, int, int, ColorsForRange[])
*
@@ -118,7 +119,33 @@ final class ColorsForRange implements Comparable<ColorsForRange> {
*/
@Override
public String toString() {
- return name.toString();
+ final StringBuilder buffer = new StringBuilder(name).append(": ").append(sampleRange);
+ if (colors != null) {
+ appendColorRange(buffer, colors.length, (i) -> colors[i].getRGB());
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Appends the range of ARGB codes as hexadecimal values.
+ * If the count of ARGB codes is 0, then this method does nothing.
+ * If the count is 1, then this method formats the single value.
+ * If the count is 2 or more, then this method formats the first and last values.
+ *
+ * @param buffer where to append the range of ARGB codes.
+ * @param count number of ARGB codes.
+ * @param colors providers of ARGB codes for given indices.
+ */
+ static void appendColorRange(final StringBuilder buffer, final int count, final IntUnaryOperator colors) {
+ if (count != 0) {
+ String s = " → ARGB[";
+ int i = 0;
+ do {
+ buffer.append(s).append(Integer.toHexString(colors.applyAsInt(i)).toUpperCase());
+ s = " … ";
+ } while (i < (i = count-1));
+ buffer.append(']');
+ }
}
/**
[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.
Posted by de...@apache.org.
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;
}