sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1535824 - in /sis/branches/JDK7/core/sis-metadata/src: main/java/org/apache/sis/metadata/iso/extent/ test/java/org/apache/sis/metadata/iso/extent/
Date Fri, 25 Oct 2013 19:10:15 GMT
Author: desruisseaux
Date: Fri Oct 25 19:10:15 2013
New Revision: 1535824

URL: http://svn.apache.org/r1535824
Log:
Union or intersection of NaN values shall produce NaN.
Extends.area(GeographicBoundingBox) supports anti-meridian spanning (SIS-143).

Modified:
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBoxTest.java
    sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/ExtentsTest.java

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java?rev=1535824&r1=1535823&r2=1535824&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
[UTF-8] Fri Oct 25 19:10:15 2013
@@ -563,11 +563,20 @@ public class DefaultGeographicBoundingBo
     }
 
     /**
-     * Adds a geographic bounding box to this box. If the {@linkplain #getInclusion() inclusion}
-     * status is the same for this box and the box to be added, then the resulting bounding
box
-     * is the union of the two boxes. If the inclusion status are opposite (<cite>exclusion</cite>),
-     * then this method attempt to exclude some area of specified box from this box.
-     * The resulting bounding box is smaller if the exclusion can be performed without ambiguity.
+     * Adds a geographic bounding box to this box.
+     * This method behavior depends on whether the bounding boxes encompass an area covered
by the data
+     * (<cite>inclusion</cite>) or an area where data is not present (<cite>exclusion</cite>):
+     *
+     * <ul>
+     *   <li>If the {@linkplain #getInclusion() inclusion} status is the same for this
box and the box to be added,
+     *       then the resulting bounding box is the union of the two boxes.</li>
+     *   <li>If the inclusion/exclusion status are opposite, then this method attempts
to exclude some area
+     *       of the specified box from this box. The resulting bounding box is smaller if
the exclusion can
+     *       be performed without ambiguity.</li>
+     * </ul>
+     *
+     * In both cases, if either this box or the specified box has {@linkplain Double#NaN
NaN} bounds,
+     * then the corresponding bounds of the result will bet set to NaN.
      *
      * @param box The geographic bounding box to add to this box.
      */
@@ -584,24 +593,24 @@ public class DefaultGeographicBoundingBo
          */
         final boolean i1 = MetadataUtilities.getInclusion(this.getInclusion());
         final boolean i2 = MetadataUtilities.getInclusion(box. getInclusion());
-        final int status = denormalize(λmin, λmax);
+        final int status = denormalize(λmin, λmax); // Must be after call to getInclusion().
         switch (status) {
             case -1: λmin -= Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
             case +1: λmax += Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
         }
         if (i1 == i2) {
-            if (λmin < westBoundLongitude) westBoundLongitude = λmin;
-            if (λmax > eastBoundLongitude) eastBoundLongitude = λmax;
-            if (φmin < southBoundLatitude) southBoundLatitude = φmin;
-            if (φmax > northBoundLatitude) northBoundLatitude = φmax;
+            westBoundLongitude = Math.min(westBoundLongitude, λmin);
+            eastBoundLongitude = Math.max(eastBoundLongitude, λmax);
+            southBoundLatitude = Math.min(southBoundLatitude, φmin);
+            northBoundLatitude = Math.max(northBoundLatitude, φmax);
         } else {
             if (φmin <= southBoundLatitude && φmax >= northBoundLatitude)
{
-                if (λmin > westBoundLongitude) westBoundLongitude = λmin;
-                if (λmax < eastBoundLongitude) eastBoundLongitude = λmax;
+                westBoundLongitude = Math.max(westBoundLongitude, λmin);
+                eastBoundLongitude = Math.min(eastBoundLongitude, λmax);
             }
             if (λmin <= westBoundLongitude && λmax >= eastBoundLongitude)
{
-                if (φmin > southBoundLatitude) southBoundLatitude = φmin;
-                if (φmax < northBoundLatitude) northBoundLatitude = φmax;
+                southBoundLatitude = Math.max(southBoundLatitude, φmin);
+                northBoundLatitude = Math.min(northBoundLatitude, φmax);
             }
         }
         if (status == 3) {
@@ -617,6 +626,11 @@ public class DefaultGeographicBoundingBo
      * Sets this bounding box to the intersection of this box with the specified one.
      * The {@linkplain #getInclusion() inclusion} status must be the same for both boxes.
      *
+     * <p>If there is no intersection between the two bounding boxes, then this method
sets
+     * both longitudes and/or both latitudes to {@linkplain Double#NaN NaN}. If either this
+     * box or the specified box has NaN bounds, then the corresponding bounds of the
+     * intersection result will bet set to NaN.</p>
+     *
      * @param box The geographic bounding box to intersect with this box.
      * @throws IllegalArgumentException If the inclusion status is not the same for both
boxes.
      *
@@ -638,17 +652,17 @@ public class DefaultGeographicBoundingBo
             case -1: λmin -= Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
             case +1: λmax += Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
         }
-        if (λmin > westBoundLongitude) westBoundLongitude = λmin;
-        if (λmax < eastBoundLongitude) eastBoundLongitude = λmax;
-        if (φmin > southBoundLatitude) southBoundLatitude = φmin;
-        if (φmax < northBoundLatitude) northBoundLatitude = φmax;
+        westBoundLongitude = Math.max(westBoundLongitude, λmin);
+        eastBoundLongitude = Math.min(eastBoundLongitude, λmax);
+        southBoundLatitude = Math.max(southBoundLatitude, φmin);
+        northBoundLatitude = Math.min(northBoundLatitude, φmax);
         if (status != 3) {
             if (westBoundLongitude > eastBoundLongitude) {
-                westBoundLongitude = eastBoundLongitude = 0.5 * (westBoundLongitude + eastBoundLongitude);
+                westBoundLongitude = eastBoundLongitude = Double.NaN;
             }
         }
         if (southBoundLatitude > northBoundLatitude) {
-            southBoundLatitude = northBoundLatitude = 0.5 * (southBoundLatitude + northBoundLatitude);
+            southBoundLatitude = northBoundLatitude = Double.NaN;
         }
         normalize();
     }

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java?rev=1535824&r1=1535823&r2=1535824&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
[UTF-8] Fri Oct 25 19:10:15 2013
@@ -20,6 +20,7 @@ import org.opengis.metadata.extent.Exten
 import org.opengis.metadata.extent.BoundingPolygon;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.metadata.extent.GeographicBoundingBox;
+import org.apache.sis.measure.Longitude;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.Static;
 
@@ -149,7 +150,7 @@ public final class Extents extends Stati
      *
      * @param  box The geographic bounding box for which to compute the area, or {@code null}.
      * @return An estimation of the geographic area in the given bounding box,
-     *         or {@link Double#NaN} if the given box was null.
+     *         or {@linkplain Double#NaN NaN} if the given box was null.
      *
      * @since 0.4
      */
@@ -157,9 +158,10 @@ public final class Extents extends Stati
         if (box == null) {
             return Double.NaN;
         }
-        return max(0, sin(toRadians(box.getNorthBoundLatitude())) -
-                      sin(toRadians(box.getSouthBoundLatitude()))) *
-               max(0, toRadians(box.getEastBoundLongitude() - box.getWestBoundLongitude()))
*
-                (AUTHALIC_RADIUS * AUTHALIC_RADIUS);
+        double Δλ = box.getEastBoundLongitude() - box.getWestBoundLongitude(); // Negative
if spanning the anti-meridian
+        Δλ -= floor(Δλ / (Longitude.MAX_VALUE - Longitude.MIN_VALUE)) * (Longitude.MAX_VALUE
- Longitude.MIN_VALUE);
+        return (AUTHALIC_RADIUS * AUTHALIC_RADIUS) * toRadians(Δλ) *
+               max(0, sin(toRadians(box.getNorthBoundLatitude())) -
+                      sin(toRadians(box.getSouthBoundLatitude())));
     }
 }

Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBoxTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBoxTest.java?rev=1535824&r1=1535823&r2=1535824&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBoxTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBoxTest.java
[UTF-8] Fri Oct 25 19:10:15 2013
@@ -21,6 +21,7 @@ import org.apache.sis.test.DependsOnMeth
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
+import static java.lang.Double.NaN;
 import static org.junit.Assert.*;
 
 
@@ -54,6 +55,13 @@ public final strictfp class DefaultGeogr
     }
 
     /**
+     * Returns {@code true} if the given box is spanning over the anti-meridian.
+     */
+    static boolean isSpanningAntiMeridian(final GeographicBoundingBox box) {
+        return box.getWestBoundLongitude() > box.getEastBoundLongitude();
+    }
+
+    /**
      * Tests construction with an invalid range of latitudes.
      */
     @Test(expected = IllegalArgumentException.class)
@@ -71,31 +79,37 @@ public final strictfp class DefaultGeogr
     public void testNormalize() {
         final DefaultGeographicBoundingBox box = new DefaultGeographicBoundingBox(-180, +180,
-90, +90);
         assertBoxEquals(-180, +180, -90, +90, box);
+        assertFalse(isSpanningAntiMeridian(box));
         /*
          * Span more than the whole Earth.
          */
         box.setBounds  (-200, +200, -100, +100);
         assertBoxEquals(-180, +180,  -90,  +90, box);
+        assertFalse(isSpanningAntiMeridian(box));
         /*
          * Values in a shifted range, but without anti-meridian spanning.
          */
         box.setBounds  (380, 420, -8, 2);
         assertBoxEquals( 20,  60, -8, 2, box);
+        assertFalse(isSpanningAntiMeridian(box));
         /*
          * Anti-meridian spanning, without change needed.
          */
         box.setBounds  ( 160, -170, -8, 2);
         assertBoxEquals( 160, -170, -8, 2, box);
+        assertTrue(isSpanningAntiMeridian(box));
         /*
          * Anti-meridian spanning in the [0 … 360]° range.
          */
         box.setBounds  ( 160,  190, -8, 2);
         assertBoxEquals( 160, -170, -8, 2, box);
+        assertTrue(isSpanningAntiMeridian(box));
         /*
          * Random anti-meridian spanning outside of range.
          */
         box.setBounds  (-200, +20, -8, 2);
         assertBoxEquals( 160, +20, -8, 2, box);
+        assertTrue(isSpanningAntiMeridian(box));
         /*
          * Special care for the ±180° longitude bounds.
          */
@@ -115,35 +129,6 @@ public final strictfp class DefaultGeogr
     }
 
     /**
-     * Sets the given box to the given values, and verifies if the box spans or not the anti-meridian
as expected.
-     * This is a convenience method for the {@link #testAdd()} and {@link #testIntersect()}
methods for checking
-     * that we are really testing the case that we intended to test.
-     *
-     * @param isSpanningAntiMeridian {@code true} if the box shall spans the anti-meridian.
-     * @param box The box to set. Previous values will be overwritten.
-     */
-    private static void setBounds(final boolean isSpanningAntiMeridian,
-                                  final double λbgn, final double λend,
-                                  final double φmin, final double φmax,
-                                  final DefaultGeographicBoundingBox box)
-    {
-        box.setBounds(λbgn, λend, φmin, φmax);
-        assertEquals("isSpanningAntiMeridian", isSpanningAntiMeridian,
-                box.getWestBoundLongitude() > box.getEastBoundLongitude());
-    }
-
-    /**
-     * Flips the given box horizontally. Longitudes are interchanged and their sign reversed.
-     * Union and intersection tests what worked with the given boxes shall work as well with
flipped boxes.
-     */
-    private static void flipHorizontally(final DefaultGeographicBoundingBox box) {
-        box.setBounds(-box.getEastBoundLongitude(),
-                      -box.getWestBoundLongitude(),
-                       box.getSouthBoundLatitude(),
-                       box.getNorthBoundLatitude());
-    }
-
-    /**
      * Tests {@link DefaultGeographicBoundingBox#add(GeographicBoundingBox)}.
      */
     @Test
@@ -167,15 +152,22 @@ public final strictfp class DefaultGeogr
      * @param union {@code true} for {@code b1.add(b2)}, or {@code false} for {@code b1.intersect(b2)}.
      */
     private void testOperation(final boolean union) {
-        double λbgn, λend, φmin, φmax;
+        final DefaultGeographicBoundingBox b1 = new DefaultGeographicBoundingBox(NaN, 20,
-20, NaN);
+        final DefaultGeographicBoundingBox b2 = new DefaultGeographicBoundingBox(-20, 20,
NaN,  20);
+        assertFalse(isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
+        assertOperationEquals(union, NaN, 20, NaN, NaN, b1, b2);
         /*
          *    ┌─────────────┐
          *    │  ┌───────┐  │
          *    │  └───────┘  │
          *    └─────────────┘
          */
-        final DefaultGeographicBoundingBox b1 = new DefaultGeographicBoundingBox(-40, 30,
-38,  20);
-        final DefaultGeographicBoundingBox b2 = new DefaultGeographicBoundingBox(-20, 10,
-30, -25);
+        double λbgn, λend, φmin, φmax;
+        b1.setBounds(-40, 30, -38,  20);
+        b2.setBounds(-20, 10, -30, -25);
+        assertFalse(isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn = -40; φmin = -38;
             λend =  30; φmax =  20;
@@ -190,8 +182,10 @@ public final strictfp class DefaultGeogr
          *    └──┼───────┘  │
          *       └──────────┘
          */
-        setBounds(false, -40, 30, -38,  20, b1);
-        setBounds(false, -30, 50, -42, -20, b2);
+        b1.setBounds(-40, 30, -38,  20);
+        b2.setBounds(-30, 50, -42, -20);
+        assertFalse(isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn = -40; φmin = -42;
             λend =  50; φmax =  20;
@@ -201,13 +195,33 @@ public final strictfp class DefaultGeogr
         }
         assertOperationEquals(union, λbgn, λend, φmin,  φmax, b1, b2);
         /*
+         *   ┌─────────┐
+         *   │         │  ┌─────┐
+         *   │         │  └─────┘
+         *   └─────────┘
+         */
+        b1.setBounds(-40, 30, -80, 40);
+        b2.setBounds( 50, 80, -30, 20);
+        assertFalse(isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
+        if (union) {
+            λbgn = -40; φmin = -80;
+            λend =  80; φmax =  40;
+        } else {
+            λbgn = NaN; φmin = -30;
+            λend = NaN; φmax =  20;
+        }
+        assertOperationEquals(union, λbgn, λend, φmin,  φmax, b1, b2);
+        /*
          *   ──────────┐  ┌─────
          *     ┌────┐  │  │
          *     └────┘  │  │
          *   ──────────┘  └─────
          */
-        setBounds(true,    80, -100, -2, 2, b1);
-        setBounds(false, -140, -120, -1, 1, b2);
+        b1.setBounds(  80, -100, -2, 2);
+        b2.setBounds(-140, -120, -1, 1);
+        assertTrue (isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn =   80; φmin = -2;
             λend = -100; φmax =  2;
@@ -222,8 +236,10 @@ public final strictfp class DefaultGeogr
          *       └─┼────┘ │
          *    ─────┘      └─────
          */
-        setBounds(true,    80, -100, -2, 2, b1);
-        setBounds(false, -120,   50, -1, 1, b2);
+        b1.setBounds(  80, -100, -2, 2);
+        b2.setBounds(-120,   50, -1, 1);
+        assertTrue (isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn =   80;
             λend =   50;
@@ -238,8 +254,10 @@ public final strictfp class DefaultGeogr
          *     └──┼──┼─┘
          *    ────┘  └────
          */
-        setBounds(true,    80, -100, -2, 2, b1);
-        setBounds(false, -120,   90, -1, 1, b2);
+        b1.setBounds(  80, -100, -2, 2);
+        b2.setBounds(-120,   90, -1, 1);
+        assertTrue (isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn = -180;
             λend =  180;
@@ -253,8 +271,10 @@ public final strictfp class DefaultGeogr
          *    ──┘ │  │ └──
          *    ────┘  └────
          */
-        setBounds(true, 80, -100, -1, 1, b1);
-        setBounds(true, 90, -120, -2, 2, b2);
+        b1.setBounds(80, -100, -1, 1);
+        b2.setBounds(90, -120, -2, 2);
+        assertTrue(isSpanningAntiMeridian(b1));
+        assertTrue(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn =   80;
             λend = -100;
@@ -269,8 +289,10 @@ public final strictfp class DefaultGeogr
          *    ────┼──┼─┘└─
          *    ────┘  └────
          */
-        setBounds(true,  80, -100, -2, 2, b1);
-        setBounds(true, 100,   90, -1, 1, b2);
+        b1.setBounds( 80, -100, -2, 2);
+        b2.setBounds(100,   90, -1, 1);
+        assertTrue(isSpanningAntiMeridian(b1));
+        assertTrue(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn =  -180;
             λend =   180;
@@ -285,16 +307,18 @@ public final strictfp class DefaultGeogr
          *        │  └────┘  │
          *    ────┘          └────
          */
-        setBounds(true,  120, -110, -1, 1, b1);
-        setBounds(false, 100,  112, -2, 2, b2);
+        b1.setBounds(120, -110, -1, 1);
+        b2.setBounds(100,  112, -2, 2);
+        assertTrue (isSpanningAntiMeridian(b1));
+        assertFalse(isSpanningAntiMeridian(b2));
         if (union) {
             λbgn =  100;
             λend = -110;
-            assertOperationEquals(union, λbgn, λend, φmin,  φmax, b1, b2);
         } else {
-            applyOperation(union, b1, b2);
-            assertEquals("Expected empty box", b1.getEastBoundLongitude(), b1.getWestBoundLongitude(),
STRICT);
+            λbgn =  NaN;
+            λend =  NaN;
         }
+        assertOperationEquals(union, λbgn, λend, φmin,  φmax, b1, b2);
     }
 
     /**
@@ -343,6 +367,17 @@ public final strictfp class DefaultGeogr
     }
 
     /**
+     * Flips the given box horizontally. Longitudes are interchanged and their sign reversed.
+     * Union and intersection tests what worked with the given boxes shall work as well with
flipped boxes.
+     */
+    private static void flipHorizontally(final DefaultGeographicBoundingBox box) {
+        box.setBounds(-box.getEastBoundLongitude(),
+                      -box.getWestBoundLongitude(),
+                       box.getSouthBoundLatitude(),
+                       box.getNorthBoundLatitude());
+    }
+
+    /**
      * Applies the given operation on the given bounding boxes.
      * The operation is invoked on {@code b1}.
      *

Modified: sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/ExtentsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/ExtentsTest.java?rev=1535824&r1=1535823&r2=1535824&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/ExtentsTest.java
[UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/ExtentsTest.java
[UTF-8] Fri Oct 25 19:10:15 2013
@@ -74,5 +74,22 @@ public final strictfp class ExtentsTest 
         box.setSouthBoundLatitude(-90);
         box.setNorthBoundLatitude(-90+MINUTE);
         assertEquals(499.5, Extents.area(box), 0.1);
+        /*
+         * EPSG:1241    USA - CONUS including EEZ
+         * This is only an anti-regression test - the value has not been validated.
+         * However the expected area MUST be greater than the Alaska's one below,
+         * otherwise SIS will select the wrong datum shift operation over USA!!
+         */
+        box.setBounds(-129.16, -65.70, 23.82, 49.38);
+        assertFalse(DefaultGeographicBoundingBoxTest.isSpanningAntiMeridian(box));
+        assertEquals(15967665, Extents.area(box) / 1E6, 1); // Compare in km²
+        /*
+         * EPSG:2373    USA - Alaska including EEZ    (spanning the anti-meridian).
+         * This is only an anti-regression test - the value has not been validated.
+         * However the expected area MUST be smaller than the CONUS's one above.
+         */
+        box.setBounds(167.65, -129.99, 47.88, 74.71);
+        assertTrue(DefaultGeographicBoundingBoxTest.isSpanningAntiMeridian(box));
+        assertEquals(9845438, Extents.area(box) / 1E6, 1); // Compare in km²
     }
 }



Mime
View raw message