sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Add NumberRange.getMedian() and getSpan() methods.
Date Fri, 05 Jun 2020 17:00:54 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 6ec7408b1494159404c44ca2a2762098d4202d12
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Jun 5 13:28:19 2020 +0200

    Add NumberRange.getMedian() and getSpan() methods.
---
 .../coverage/j2d/BandedSampleConverter.java        |  2 +-
 .../operation/builder/LocalizationGridBuilder.java |  2 +-
 .../java/org/apache/sis/measure/NumberRange.java   | 87 +++++++++++++++++++++-
 .../org/apache/sis/measure/NumberRangeTest.java    | 19 +++--
 4 files changed, 97 insertions(+), 13 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
index 6975c84..7592aca 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
@@ -113,7 +113,7 @@ public class BandedSampleConverter extends ComputedImage {
             if (ranges != null) {
                 final NumberRange<?> range = ranges[i];
                 if (range != null) {
-                    middle = (range.getMinDouble() + range.getMaxDouble()) / 2;
+                    middle = range.getMedian();
                 }
             }
             if (!Double.isFinite(middle)) {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
index ceb82e3..189d9d7 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LocalizationGridBuilder.java
@@ -255,7 +255,7 @@ public class LocalizationGridBuilder extends TransformBuilder {
     private static int infer(final Vector source, final Matrix fromGrid, final int dim) {
         final NumberRange<?> range = source.range();
         final double min  = range.getMinDouble(true);
-        final double span = range.getMaxDouble(true) - min;
+        final double span = range.getSpan();
         final Number increment = source.increment(EPS * span);
         double inc;
         if (increment != null) {
diff --git a/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java b/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
index 08a8e4e..67797cc 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/measure/NumberRange.java
@@ -20,6 +20,7 @@ import java.util.Objects;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.math.MathFunctions;
 import org.apache.sis.util.collection.WeakHashSet;
 
 
@@ -77,9 +78,9 @@ import org.apache.sis.util.collection.WeakHashSet;
  * given to {@linkplain org.apache.sis.parameter.DefaultParameterDescriptor parameter descriptor}.
  * Other methods do not check for shared instances, since the created object is often temporary.</p>
  *
- * @author  Martin Desruisseaux (IRD)
+ * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Jody Garnett (for parameterized type inspiration)
- * @version 1.0
+ * @version 1.1
  *
  * @param <E>  the type of range elements as a subclass of {@link Number}.
  *
@@ -571,6 +572,86 @@ public class NumberRange<E extends Number & Comparable<? super
E>> extends Range
     }
 
     /**
+     * Computes the average of minimum and maximum values. If numbers are integers, the average
is computed using
+     * inclusive values (e.g. equivalent to <code>{@linkplain #getMinDouble(boolean)
getMinDouble}(true)</code>).
+     * Otherwise the minimum and maximum values are used as-is
+     * (because making them inclusive is considered an infinitely small change).
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>If one bound is infinite, the return value is the same infinity.</li>
+     *   <li>If the two bounds are infinite, the return value is {@link Double#NaN}.</li>
+     *   <li>If the range {@linkplain #isEmpty() is empty}, the return value is {@link
Double#NaN}.</li>
+     *   <li>If a bound {@linkplain Double#isNaN(double) is NaN}, the return value
is the same NaN
+     *       (reminder: {@linkplain MathFunctions#toNanFloat(int) multiple NaN values} are
possible).</li>
+     * </ul>
+     *
+     * @return (<var>minimum</var> + <var>maximum</var>) / 2 computed
using inclusive values.
+     *
+     * @since 1.1
+     */
+    public double getMedian() {
+        return medianOrSpan(true);
+    }
+
+    /**
+     * Computes the difference between minimum and maximum values. If numbers are integers,
the difference is computed
+     * using inclusive values (e.g. equivalent to <code>{@linkplain #getMinDouble(boolean)
getMinDouble}(true)</code>).
+     * Otherwise the minimum and maximum values are used as-is
+     * (because making them inclusive is considered an infinitely small change).
+     *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>If the range {@linkplain #isEmpty() is empty}, the return value is 0.</li>
+     *   <li>If at least one bound is infinite, the return value is {@link Double#POSITIVE_INFINITY}.</li>
+     *   <li>If a bound {@linkplain Double#isNaN(double) is NaN}, the return value
is the same NaN
+     *       (reminder: {@linkplain MathFunctions#toNanFloat(int) multiple NaN values} are
possible).</li>
+     * </ul>
+     *
+     * @return (<var>maximum</var> − <var>minimum</var>) computed
using inclusive values.
+     *
+     * @since 1.1
+     */
+    public double getSpan() {
+        return medianOrSpan(false);
+    }
+
+    /**
+     * Implementation of {@link #getMedian()} and {@link #getSpan()}.
+     */
+    private double medianOrSpan(final boolean median) {
+        if (minValue == null) {
+            if (median) {
+                return (maxValue == null) ? Double.NaN : Double.NEGATIVE_INFINITY;
+            }
+            return Double.POSITIVE_INFINITY;
+        } else if (maxValue == null) {
+            return Double.POSITIVE_INFINITY;
+        }
+        if (Numbers.isInteger(getElementType())) {
+            long min = minValue.longValue();
+            long max = maxValue.longValue();
+            if (!isMinIncluded) min++;
+            if (!isMaxIncluded) max--;
+            if (min <= max) {
+                return median ? MathFunctions.average(min, max)
+                              : Numerics.toUnsignedDouble(max - min);
+            }
+        } else {
+            final double min = minValue.doubleValue();
+            final double max = maxValue.doubleValue();
+            if (min <= max) {
+                return median ? (min + max) * 0.5 : (max - min);
+            } else if (Double.isNaN(min) && (isMinIncluded || !Double.isNaN(max)))
{
+                return min;     // Same NaN with precedence to inclusive bound if the 2 bounds
are NaN.
+            } else if (Double.isNaN(max)) {
+                return max;
+            }
+        }
+        return median ? Double.NaN : 0;
+    }
+
+    /**
      * Returns the next value for the given type.
      *
      * @param  type   the element type.
@@ -588,7 +669,7 @@ public class NumberRange<E extends Number & Comparable<? super
E>> extends Range
             value = up ? Math.nextUp(value) : Math.nextDown(value);
         } else {
             // Thrown IllegalStateException instead than IllegalArgumentException because
-            // the 'type' argument given to this method come from a NumberRange field.
+            // the `type` argument given to this method come from a NumberRange field.
             throw new IllegalStateException(Errors.format(Errors.Keys.NotAPrimitiveWrapper_1,
type));
         }
         return value;
diff --git a/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java b/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
index ce5b83b..a85dca2 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/measure/NumberRangeTest.java
@@ -27,8 +27,8 @@ import static org.junit.Assert.*;
 /**
  * Tests the {@link NumberRange} class.
  *
- * @author  Martin Desruisseaux (IRD)
- * @version 1.0
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.1
  * @since   0.3
  * @module
  */
@@ -53,16 +53,19 @@ public final strictfp class NumberRangeTest extends TestCase {
 
     /**
      * Tests the endpoint values of a range of integers.
+     * Also tests median and span.
      */
     @Test
     public void testIntegerEndpoints() {
         final NumberRange<Integer> range = NumberRange.create(10, true, 20, true);
-        assertEquals(10, range.getMinDouble(     ), 0);
-        assertEquals(10, range.getMinDouble(true ), 0);
-        assertEquals( 9, range.getMinDouble(false), 0);
-        assertEquals(20, range.getMaxDouble(     ), 0);
-        assertEquals(20, range.getMaxDouble(true ), 0);
-        assertEquals(21, range.getMaxDouble(false), 0);
+        assertEquals(10, range.getMinDouble(     ), STRICT);
+        assertEquals(10, range.getMinDouble(true ), STRICT);
+        assertEquals( 9, range.getMinDouble(false), STRICT);
+        assertEquals(20, range.getMaxDouble(     ), STRICT);
+        assertEquals(20, range.getMaxDouble(true ), STRICT);
+        assertEquals(21, range.getMaxDouble(false), STRICT);
+        assertEquals(15, range.getMedian(),         STRICT);
+        assertEquals(10, range.getSpan(),           STRICT);
     }
 
     /**


Mime
View raw message