sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1535870 [1/2] - in /sis/branches/JDK6: ./ core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/ core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/e...
Date Fri, 25 Oct 2013 21:07:48 GMT
Author: desruisseaux
Date: Fri Oct 25 21:07:47 2013
New Revision: 1535870

URL: http://svn.apache.org/r1535870
Log:
Merge from the JDK7 branch.

Added:
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/
      - copied from r1535869, sis/branches/JDK7/core/sis-metadata/src/test/java/org/apache/sis/metadata/iso/extent/
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
      - copied unchanged from r1535869, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/
      - copied from r1535869, sis/branches/JDK7/core/sis-referencing/src/test/java/org/apache/sis/referencing/datum/
Modified:
    sis/branches/JDK6/   (props changed)
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
    sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
    sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticObjects.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultTemporalDatum.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
    sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatricesTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/Matrix3Test.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/MatrixTestCase.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrixTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/matrix/SolverTest.java
    sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/jdk8/JDK8.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/CollectionsExt.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/DoubleDouble.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/measure/Latitude.java
    sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/measure/Longitude.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/CollectionsExtTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/DoubleDoubleTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/math/MathFunctionsTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/measure/AngleFormatTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/measure/AngleTest.java
    sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java

Propchange: sis/branches/JDK6/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK7:r1533382-1535869

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -40,6 +40,16 @@ import org.apache.sis.util.resources.Err
  */
 public abstract class ReferencingServices extends SystemListener {
     /**
+     * The length of one nautical mile, in metres.
+     */
+    public static final double NAUTICAL_MILE = 1852;
+
+    /**
+     * The GRS80 {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius() authalic radius}.
+     */
+    public static final double AUTHALIC_RADIUS = 6371007;
+
+    /**
      * The services, fetched when first needed.
      */
     private static ReferencingServices instance;

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/citation/Citations.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -119,11 +119,7 @@ public final class Citations extends Sta
      * The <a href="http://www.epsg.org">European Petroleum Survey Group</a> authority.
      * This citation is used as an authority for
      * {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system}
-     * identifiers. When searching an {@linkplain org.opengis.referencing.crs.CRSAuthorityFactory CRS
-     * authority factory} on EPSG data, SIS compares the {@code "EPSG"} string against the
-     * {@linkplain Citation#getIdentifiers identifiers} (or against the {@linkplain Citation#getTitle
-     * title} and {@linkplain Citation#getAlternateTitles alternate titles} if there is no identifier)
-     * using the {@link #identifierMatches(Citation,String) identifierMatches} method.
+     * identifiers.
      *
      * @see #AUTO
      * @see #AUTO2

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/DefaultGeographicBoundingBox.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -22,10 +22,10 @@ import javax.xml.bind.annotation.XmlType
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 import org.opengis.referencing.operation.TransformException;
-import org.apache.sis.measure.Angle;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.ValueRange;
+import org.apache.sis.math.MathFunctions;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
@@ -52,6 +52,25 @@ import org.apache.sis.internal.jdk7.Obje
  *   <li>{@link #intersect(GeographicBoundingBox)} for the intersection between the two bounding boxes.</li>
  * </ul>
  *
+ * {@section Validation and normalization}
+ * All constructors and setter methods in this class perform the following argument validation or normalization:
+ *
+ * <ul>
+ *   <li>If the {@linkplain #getSouthBoundLatitude() south bound latitude} is greater than the
+ *       {@linkplain #getNorthBoundLatitude() north bound latitude}, then an exception is thrown.</li>
+ *   <li>If any latitude is set to a value outside the
+ *       [{@linkplain Latitude#MIN_VALUE -90} … {@linkplain Latitude#MAX_VALUE 90}]° range,
+ *       then that latitude will be clamped.</li>
+ *   <li>If any longitude is set to a value outside the
+ *       [{@linkplain Longitude#MIN_VALUE -180} … {@linkplain Longitude#MAX_VALUE 180}]° range,
+ *       then a multiple of 360° will be added or subtracted to that longitude in order to bring
+ *       it back inside the range.</li>
+ * </ul>
+ *
+ * If the {@linkplain #getWestBoundLongitude() west bound longitude} is greater than the
+ * {@linkplain #getEastBoundLongitude() east bound longitude}, then the box spans the anti-meridian.
+ * See {@linkplain org.apache.sis.geometry.GeneralEnvelope} for more information on anti-meridian spanning.
+ *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Touraïvane (IRD)
  * @author  Cédric Briançon (Geomatys)
@@ -125,8 +144,7 @@ public class DefaultGeographicBoundingBo
      * @param southBoundLatitude The minimal φ value.
      * @param northBoundLatitude The maximal φ value.
      *
-     * @throws IllegalArgumentException If (<var>west bound</var> &gt; <var>east bound</var>)
-     *         or (<var>south bound</var> &gt; <var>north bound</var>).
+     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
      *         Note that {@linkplain Double#NaN NaN} values are allowed.
      *
      * @see #setBounds(double, double, double, double)
@@ -138,21 +156,12 @@ public class DefaultGeographicBoundingBo
             throws IllegalArgumentException
     {
         super(true);
+        verifyBounds(southBoundLatitude, northBoundLatitude);
         this.westBoundLongitude = westBoundLongitude;
         this.eastBoundLongitude = eastBoundLongitude;
         this.southBoundLatitude = southBoundLatitude;
         this.northBoundLatitude = northBoundLatitude;
-        final Angle min, max;
-        if (westBoundLongitude > eastBoundLongitude) {
-            min = new Longitude(westBoundLongitude);
-            max = new Longitude(eastBoundLongitude);
-        } else if (southBoundLatitude > northBoundLatitude) {
-            min = new Latitude(southBoundLatitude);
-            max = new Latitude(northBoundLatitude);
-        } else {
-            return;
-        }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, min, max));
+        normalize();
     }
 
     /**
@@ -171,6 +180,13 @@ public class DefaultGeographicBoundingBo
             eastBoundLongitude = object.getEastBoundLongitude();
             southBoundLatitude = object.getSouthBoundLatitude();
             northBoundLatitude = object.getNorthBoundLatitude();
+            verifyBounds(southBoundLatitude, northBoundLatitude);
+            normalize();
+        } else {
+            westBoundLongitude = Double.NaN;
+            eastBoundLongitude = Double.NaN;
+            southBoundLatitude = Double.NaN;
+            northBoundLatitude = Double.NaN;
         }
     }
 
@@ -203,7 +219,10 @@ public class DefaultGeographicBoundingBo
      * Returns the western-most coordinate of the limit of the dataset extent.
      * The value is expressed in longitude in decimal degrees (positive east).
      *
-     * @return The western-most longitude between -180 and +180°,
+     * <p>Note that the returned value is greater than the {@linkplain #getEastBoundLongitude()
+     * east bound longitude} if this box is spanning over the anti-meridian.</p>
+     *
+     * @return The western-most longitude between -180° and +180° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -216,12 +235,16 @@ public class DefaultGeographicBoundingBo
     /**
      * Sets the western-most coordinate of the limit of the dataset extent.
      * The value is expressed in longitude in decimal degrees (positive east).
+     * Values outside the [-180 … 180]° range are {@linkplain Longitude#normalize(double) normalized}.
      *
-     * @param newValue The western-most longitude between -180 and +180°,
+     * @param newValue The western-most longitude between -180° and +180° inclusive,
      *        or {@linkplain Double#NaN NaN} to undefine.
      */
-    public void setWestBoundLongitude(final double newValue) {
+    public void setWestBoundLongitude(double newValue) {
         checkWritePermission();
+        if (newValue != Longitude.MAX_VALUE) { // Do not normalize +180° to -180°.
+            newValue = Longitude.normalize(newValue);
+        }
         westBoundLongitude = newValue;
     }
 
@@ -229,7 +252,10 @@ public class DefaultGeographicBoundingBo
      * Returns the eastern-most coordinate of the limit of the dataset extent.
      * The value is expressed in longitude in decimal degrees (positive east).
      *
-     * @return The eastern-most longitude between -180 and +180°,
+     * <p>Note that the returned value is smaller than the {@linkplain #getWestBoundLongitude()
+     * west bound longitude} if this box is spanning over the anti-meridian.</p>
+     *
+     * @return The eastern-most longitude between -180° and +180° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -242,12 +268,16 @@ public class DefaultGeographicBoundingBo
     /**
      * Sets the eastern-most coordinate of the limit of the dataset extent.
      * The value is expressed in longitude in decimal degrees (positive east).
+     * Values outside the [-180 … 180]° range are {@linkplain Longitude#normalize(double) normalized}.
      *
-     * @param newValue The eastern-most longitude between -180 and +180°,
+     * @param newValue The eastern-most longitude between -180° and +180° inclusive,
      *        or {@linkplain Double#NaN NaN} to undefine.
      */
-    public void setEastBoundLongitude(final double newValue) {
+    public void setEastBoundLongitude(double newValue) {
         checkWritePermission();
+        if (newValue != Longitude.MAX_VALUE) { // Do not normalize +180° to -180°.
+            newValue = Longitude.normalize(newValue);
+        }
         eastBoundLongitude = newValue;
     }
 
@@ -255,7 +285,7 @@ public class DefaultGeographicBoundingBo
      * Returns the southern-most coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
      *
-     * @return The southern-most latitude between -90 and +90°,
+     * @return The southern-most latitude between -90° and +90° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -268,20 +298,26 @@ public class DefaultGeographicBoundingBo
     /**
      * Sets the southern-most coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
+     * Values outside the [-90 … 90]° range are {@linkplain Latitude#clamp(double) clamped}.
+     * If the result is greater than the {@linkplain #getNorthBoundLatitude() north bound latitude},
+     * then the north bound is set to {@link Double#NaN}.
      *
-     * @param newValue The southern-most latitude between -90 and +90°,
+     * @param newValue The southern-most latitude between -90° and +90° inclusive,
      *        or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setSouthBoundLatitude(final double newValue) {
         checkWritePermission();
-        southBoundLatitude = newValue;
+        southBoundLatitude = Latitude.clamp(newValue);
+        if (southBoundLatitude > northBoundLatitude) {
+            northBoundLatitude = Double.NaN;
+        }
     }
 
     /**
      * Returns the northern-most, coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
      *
-     * @return The northern-most latitude between -90 and +90°,
+     * @return The northern-most latitude between -90° and +90° inclusive,
      *         or {@linkplain Double#NaN NaN} if undefined.
      */
     @Override
@@ -294,13 +330,81 @@ public class DefaultGeographicBoundingBo
     /**
      * Sets the northern-most, coordinate of the limit of the dataset extent.
      * The value is expressed in latitude in decimal degrees (positive north).
+     * Values outside the [-90 … 90]° range are {@linkplain Latitude#clamp(double) clamped}.
+     * If the result is smaller than the {@linkplain #getSouthBoundLatitude() south bound latitude},
+     * then the south bound is set to {@link Double#NaN}.
      *
-     * @param newValue The northern-most latitude between -90 and +90°,
+     * @param newValue The northern-most latitude between -90° and +90° inclusive,
      *        or {@linkplain Double#NaN NaN} to undefine.
      */
     public void setNorthBoundLatitude(final double newValue) {
         checkWritePermission();
-        northBoundLatitude = newValue;
+        northBoundLatitude = Latitude.clamp(newValue);
+        if (northBoundLatitude < southBoundLatitude) {
+            southBoundLatitude = Double.NaN;
+        }
+    }
+
+    /**
+     * Verifies that the given bounding box is valid. This method verifies only the latitude values,
+     * because we allow the west bound longitude to be greater then east bound longitude (they are
+     * boxes spanning the anti-meridian).
+     *
+     * <p>This method should be invoked <strong>before</strong> {@link #normalize()}.</p>
+     *
+     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
+     *         Note that {@linkplain Double#NaN NaN} values are allowed.
+     */
+    private static void verifyBounds(final double southBoundLatitude, final double northBoundLatitude)
+            throws IllegalArgumentException
+    {
+        if (southBoundLatitude > northBoundLatitude) { // Accept NaN.
+            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2,
+                    new Latitude(southBoundLatitude), new Latitude(northBoundLatitude)));
+        }
+    }
+
+    /**
+     * Clamps the latitudes and normalizes the longitudes.
+     *
+     * @see #denormalize(double, double)
+     */
+    private void normalize() {
+        southBoundLatitude = Latitude.clamp(southBoundLatitude);
+        northBoundLatitude = Latitude.clamp(northBoundLatitude);
+        final double span = eastBoundLongitude - westBoundLongitude;
+        if (!(span >= (Longitude.MAX_VALUE - Longitude.MIN_VALUE))) { // 'span' may be NaN.
+            westBoundLongitude = Longitude.normalize(westBoundLongitude);
+            eastBoundLongitude = Longitude.normalize(eastBoundLongitude);
+            if (span != 0) {
+                /*
+                 * This is the usual case where east and west longitudes are different.
+                 * Since -180° and +180° are equivalent, always use -180° for the west
+                 * bound (this was ensured by the call to Longitude.normalize(…)), and
+                 * always use +180° for the east bound. So we get for example [5 … 180]
+                 * instead of [5 … -180] even if both are the same box.
+                 */
+                if (eastBoundLongitude == Longitude.MIN_VALUE) {
+                    eastBoundLongitude = Longitude.MAX_VALUE;
+                }
+                return;
+            }
+            /*
+             * If the longitude range is anything except [+0 … -0], we are done. Only in the
+             * particular case of [+0 … -0] we will replace the range by [-180 … +180].
+             */
+            if (!MathFunctions.isPositiveZero(westBoundLongitude) ||
+                !MathFunctions.isNegativeZero(eastBoundLongitude))
+            {
+                return;
+            }
+        }
+        /*
+         * If we reach this point, the longitude range is either [+0 … -0] or anything
+         * spanning 360° or more. Normalize the range to [-180 … +180].
+         */
+        westBoundLongitude = Longitude.MIN_VALUE;
+        eastBoundLongitude = Longitude.MAX_VALUE;
     }
 
     /**
@@ -317,8 +421,7 @@ public class DefaultGeographicBoundingBo
      * @param southBoundLatitude The minimal φ value.
      * @param northBoundLatitude The maximal φ value.
      *
-     * @throws IllegalArgumentException If (<var>west bound</var> &gt; <var>east bound</var>)
-     *         or (<var>south bound</var> &gt; <var>north bound</var>).
+     * @throws IllegalArgumentException If (<var>south bound</var> &gt; <var>north bound</var>).
      *         Note that {@linkplain Double#NaN NaN} values are allowed.
      */
     public void setBounds(final double westBoundLongitude,
@@ -328,23 +431,12 @@ public class DefaultGeographicBoundingBo
             throws IllegalArgumentException
     {
         checkWritePermission();
-        final Angle min, max;
-        if (westBoundLongitude > eastBoundLongitude) {
-            min = new Longitude(westBoundLongitude);
-            max = new Longitude(eastBoundLongitude);
-            // Exception will be thrown below.
-        } else if (southBoundLatitude > northBoundLatitude) {
-            min = new Latitude(southBoundLatitude);
-            max = new Latitude(northBoundLatitude);
-            // Exception will be thrown below.
-        } else {
-            this.westBoundLongitude = westBoundLongitude;
-            this.eastBoundLongitude = eastBoundLongitude;
-            this.southBoundLatitude = southBoundLatitude;
-            this.northBoundLatitude = northBoundLatitude;
-            return;
-        }
-        throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, min, max));
+        verifyBounds(southBoundLatitude, northBoundLatitude);
+        this.westBoundLongitude = westBoundLongitude;
+        this.eastBoundLongitude = eastBoundLongitude;
+        this.southBoundLatitude = southBoundLatitude;
+        this.northBoundLatitude = northBoundLatitude;
+        normalize();
     }
 
     /**
@@ -353,11 +445,11 @@ public class DefaultGeographicBoundingBo
      * is assumed already in appropriate CRS.
      *
      * <p>When coordinate transformation is required, the target geographic CRS is not necessarily
-     * {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS#WGS84 WGS84}. This method
-     * preserves the same {@linkplain org.opengis.referencing.datum.Ellipsoid ellipsoid} than
-     * in the envelope CRS when possible. This is because geographic bounding box are only
-     * approximative and the ISO specification do not mandates a particular CRS, so we avoid
-     * transformations that are not strictly necessary.</p>
+     * {@linkplain org.apache.sis.referencing.GeodeticObjects#WGS84 WGS84}. This method preserves
+     * the same {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid ellipsoid} than in
+     * the envelope CRS when possible. This is because geographic bounding box are only approximative
+     * and the ISO specification do not mandates a particular CRS,
+     * so we avoid transformations that are not strictly necessary.</p>
      *
      * <p><b>Note:</b> This method is available only if the referencing module is on the classpath.</p>
      *
@@ -388,72 +480,191 @@ public class DefaultGeographicBoundingBo
         setInclusion(box.getInclusion()); // Set only on success.
     }
 
+    /*
+     * IMPLEMENTATION NOTE: For the handling of anti-meridian spanning in union and intersection operations,
+     * this class applies a different strategy than GeneralEnvelope. Instead than trying to work directly with
+     * the ordinate values without adding or removing offset (which may cause rounding errors), we apply a ±360°
+     * shift on longitude values. This simpler strategy is okay here because the range is fixed in the code (not
+     * an arbitrarily high range), and GeographicBoundingBox are approximative by definition anyway.
+     */
+
     /**
-     * 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.
+     * Returns a code telling how to denormalize this box and/or the other box before to compute union or intersection.
+     * This method may also modify the {@link #westBoundLongitude} and {@link #eastBoundLongitude}. The codes are:
+     *
+     * <ul>
+     *   <li> 0 : Do nothing - both boxes are normal.</li>
+     *   <li> 3 : Do nothing - both boxes are spanning the anti-meridian.</li>
+     *   <li>-1 : Caller will need to subtract 360° from {@code λmin}.</li>
+     *   <li>+1 : Caller will need to add      360° to   {@code λmax}.</li>
+     *   <li>-2 : This method has subtracted   360° from {@link #westBoundLongitude}.</li>
+     *   <li>+2 : This method has added        360° to   {@link #eastBoundLongitude}.</li>
+     * </ul>
+     *
+     * @see #normalize()
+     */
+    private int denormalize(final double λmin, final double λmax) {
+        final boolean isSpanningAntiMeridian = westBoundLongitude > eastBoundLongitude;
+        if ((λmin > λmax) == isSpanningAntiMeridian) {
+            return isSpanningAntiMeridian ? 3 : 0;
+        }
+        final double left  = westBoundLongitude - λmin;
+        final double right = λmax - eastBoundLongitude;
+        if (!isSpanningAntiMeridian) {
+            /*
+             * If we were computing the union between this bounding box and the other box,
+             * by how much the width would be increased on the left side and on the right
+             * side? (ignore negative values for this first part). What we may get:
+             *
+             *   (+1) Left side is positive            (-1) Right side is positive
+             *
+             *          W┌──────────┐                     ┌──────────┐E
+             *   ──┐  ┌──┼──────────┼──                 ──┼──────────┼──┐  ┌──
+             *     │  │  └──────────┘                     └──────────┘  │  │
+             *   ──┘  └────────────────                 ────────────────┘  └──
+             *       λmin                                              λmax
+             *
+             * For each of the above case, if we apply the translation in the opposite way,
+             * the result would be much wort (for each lower rectangle, imagine translating
+             * the longuest part in the opposite direction instead than the shortest one).
+             *
+             * Note that only one of 'left' and 'right' can be positive, otherwise we would
+             * not be in the case where one box is spanning the anti-meridian while the other
+             * box does not.
+             */
+            if (left  >= 0) return +1;
+            if (right >= 0) return -1;
+            /*
+             * Both 'left' and 'right' are negative. For each alternatives (translating λmin
+             * or translating λmax), we will choose the one which give the closest result to
+             * a bound of this box:
+             *
+             *        W┌──────────┐E         Changes in width of the union compared to this box:
+             *   ───┐  │        ┌─┼──          ∙ if we move λmax to the right:  Δ = (λmax + 360) - E
+             *      │  └────────┼─┘            ∙ if we move λmin to the left:   Δ = W - (λmin + 360)
+             *   ───┘           └────
+             *    λmax         λmin
+             *
+             * We want the smallest option. We get the condition below after cancelation of both "+ 360" terms.
+             */
+            return (left < right) ? -1 : +1;
+        }
+        /*
+         * Same algorithm than above, but with the sign of 'left' an 'right' inversed.
+         * The "if" statements have been combined for avoiding to repeat the +/- operations.
+         */
+        if (!(left <= 0) && right <= 0 || left > right) {
+            westBoundLongitude -= Longitude.MAX_VALUE - Longitude.MIN_VALUE;
+            return -2;
+        } else {
+            eastBoundLongitude += Longitude.MAX_VALUE - Longitude.MIN_VALUE;
+            return +2;
+        }
+    }
+
+    /**
+     * 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.
      */
     public void add(final GeographicBoundingBox box) {
         checkWritePermission();
-        final double λmin = box.getWestBoundLongitude();
-        final double λmax = box.getEastBoundLongitude();
-        final double φmin = box.getSouthBoundLatitude();
-        final double φmax = box.getNorthBoundLatitude();
+        double λmin = box.getWestBoundLongitude();
+        double λmax = box.getEastBoundLongitude();
+        double φmin = box.getSouthBoundLatitude();
+        double φmax = box.getNorthBoundLatitude();
         /*
          * Reminder: 'inclusion' is a mandatory attribute, so it should never be null for a
          * valid metadata object.  If the metadata object is invalid, it is better to get a
          * an exception than having a code doing silently some inappropriate work.
          */
-        if (MetadataUtilities.getInclusion(    getInclusion()) ==
-            MetadataUtilities.getInclusion(box.getInclusion()))
-        {
-            if (λmin < westBoundLongitude) westBoundLongitude = λmin;
-            if (λmax > eastBoundLongitude) eastBoundLongitude = λmax;
-            if (φmin < southBoundLatitude) southBoundLatitude = φmin;
-            if (φmax > northBoundLatitude) northBoundLatitude = φmax;
+        final boolean i1 = MetadataUtilities.getInclusion(this.getInclusion());
+        final boolean i2 = MetadataUtilities.getInclusion(box. getInclusion());
+        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) {
+            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) {
+            if (eastBoundLongitude > westBoundLongitude) {
+                westBoundLongitude = Longitude.MIN_VALUE;
+                eastBoundLongitude = Longitude.MAX_VALUE;
             }
         }
+        normalize();
     }
 
     /**
      * 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.
+     *
+     * @see Extents#intersection(GeographicBoundingBox, GeographicBoundingBox)
      */
-    public void intersect(final GeographicBoundingBox box) {
+    public void intersect(final GeographicBoundingBox box) throws IllegalArgumentException {
         checkWritePermission();
         if (MetadataUtilities.getInclusion(    getInclusion()) !=
             MetadataUtilities.getInclusion(box.getInclusion()))
         {
             throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatiblePropertyValue_1, "inclusion"));
         }
-        final double λmin = box.getWestBoundLongitude();
-        final double λmax = box.getEastBoundLongitude();
-        final double φmin = box.getSouthBoundLatitude();
-        final double φmax = box.getNorthBoundLatitude();
-        if (λmin > westBoundLongitude) westBoundLongitude = λmin;
-        if (λmax < eastBoundLongitude) eastBoundLongitude = λmax;
-        if (φmin > southBoundLatitude) southBoundLatitude = φmin;
-        if (φmax < northBoundLatitude) northBoundLatitude = φmax;
-        if (westBoundLongitude > eastBoundLongitude) {
-            westBoundLongitude = eastBoundLongitude = 0.5 * (westBoundLongitude + eastBoundLongitude);
+        double λmin = box.getWestBoundLongitude();
+        double λmax = box.getEastBoundLongitude();
+        double φmin = box.getSouthBoundLatitude();
+        double φmax = box.getNorthBoundLatitude();
+        final int status = denormalize(λmin, λmax);
+        switch (status) {
+            case -1: λmin -= Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
+            case +1: λmax += Longitude.MAX_VALUE - Longitude.MIN_VALUE; break;
+        }
+        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 = Double.NaN;
+            }
         }
         if (southBoundLatitude > northBoundLatitude) {
-            southBoundLatitude = northBoundLatitude = 0.5 * (southBoundLatitude + northBoundLatitude);
+            southBoundLatitude = northBoundLatitude = Double.NaN;
         }
+        normalize();
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/Extents.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -20,10 +20,13 @@ 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;
 
+import static java.lang.Math.*;
 import static org.apache.sis.internal.metadata.MetadataUtilities.getInclusion;
+import static org.apache.sis.internal.metadata.ReferencingServices.AUTHALIC_RADIUS;
 
 
 /**
@@ -31,7 +34,7 @@ import static org.apache.sis.internal.me
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-2.2)
- * @version 0.3
+ * @version 0.4
  * @module
  *
  * @see org.apache.sis.geometry.Envelopes
@@ -108,4 +111,57 @@ public final class Extents extends Stati
         }
         return candidate;
     }
+
+    /**
+     * Returns the intersection of the given geographic bounding boxes. If any of the arguments is {@code null},
+     * then this method returns the other argument (which may be null). Otherwise this method returns a box which
+     * is the intersection of the two given boxes.
+     *
+     * <p>This method never modify the given boxes, but may return directly one of the given arguments if it
+     * already represents the intersection result.</p>
+     *
+     * @param  b1 The first bounding box, or {@code null}.
+     * @param  b2 The second bounding box, or {@code null}.
+     * @return The intersection (may be any of the {@code b1} or {@code b2} argument if unchanged),
+     *         or {@code null} if the two given boxes are null.
+     * @throws IllegalArgumentException If the {@linkplain DefaultGeographicBoundingBox#getInclusion() inclusion status}
+     *         is not the same for both boxes.
+     *
+     * @see DefaultGeographicBoundingBox#intersect(GeographicBoundingBox)
+     *
+     * @since 0.4
+     */
+    public static GeographicBoundingBox intersection(final GeographicBoundingBox b1, final GeographicBoundingBox b2) {
+        if (b1 == null) return b2;
+        if (b2 == null || b2 == b1) return b1;
+        final DefaultGeographicBoundingBox box = new DefaultGeographicBoundingBox(b1);
+        box.intersect(b2);
+        return box;
+    }
+
+    /**
+     * Returns an <em>estimation</em> of the area (in square metres) of the given bounding box.
+     * Since {@code GeographicBoundingBox} provides only approximative information (for example
+     * it does not specify the datum), the value returned by this method is also approximative.
+     *
+     * <p>The current implementation performs its computation on the
+     * {@linkplain org.apache.sis.referencing.GeodeticObjects#SPHERE GRS 1980 Authalic Sphere}.
+     * However this may change in any future SIS version.</p>
+     *
+     * @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 {@linkplain Double#NaN NaN} if the given box was null.
+     *
+     * @since 0.4
+     */
+    public static double area(final GeographicBoundingBox box) {
+        if (box == null) {
+            return Double.NaN;
+        }
+        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/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/extent/package-info.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -56,6 +56,14 @@
  *       <li>{@link org.apache.sis.metadata.iso.extent.Extents#getGeographicBoundingBox
  *       getGeographicBoundingBox(Extent)}
  *       for extracting a global geographic bounding box.</li>
+ *
+ *       <li>{@link org.apache.sis.metadata.iso.extent.Extents#intersection
+ *       intersection(GeographicBoundingBox, GeographicBoundingBox)}
+ *       for computing the intersection of two geographic bounding boxes, which may be null.</li>
+ *
+ *       <li>{@link org.apache.sis.metadata.iso.extent.Extents#area
+ *       area(GeographicBoundingBox)}
+ *       for estimating the area of a geographic bounding box.</li>
  *     </ul>
  *   </li>
  *   <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox}
@@ -64,19 +72,19 @@
  *       setBounds(double, double, double, double)}
  *       for setting the extent from (λ,φ) values.</li>
  *
- *   <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#setBounds(org.opengis.geometry.Envelope)
+ *       <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#setBounds(org.opengis.geometry.Envelope)
  *       setBounds(Envelope)}
  *       for setting the extent from the given envelope.</li>
  *
- *   <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#setBounds(org.opengis.metadata.extent.GeographicBoundingBox)
+ *       <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#setBounds(org.opengis.metadata.extent.GeographicBoundingBox)
  *       setBounds(GeographicBoundingBox)}
  *       for setting the extent from an other bounding box.</li>
  *
- *   <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#add
+ *       <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#add
  *       add(GeographicBoundingBox)}
  *       for expanding this extent to include an other bounding box.</li>
  *
- *   <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#intersect
+ *       <li>{@link org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox#intersect
  *       intersect(GeographicBoundingBox)}
  *       for the intersection between the two bounding boxes.</li>
  *     </ul>

Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/test/suite/MetadataTestSuite.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -40,6 +40,8 @@ import org.junit.BeforeClass;
     org.apache.sis.metadata.iso.spatial.DefaultGeorectifiedTest.class,
     org.apache.sis.metadata.iso.maintenance.DefaultScopeDescriptionTest.class,
     org.apache.sis.metadata.iso.quality.AbstractElementTest.class,
+    org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBoxTest.class,
+    org.apache.sis.metadata.iso.extent.ExtentsTest.class,
 
     // Classes using Java reflection.
     org.apache.sis.metadata.PropertyInformationTest.class,

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -448,6 +448,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMinimum(int) minimal} ordinate value for dimension 0.
+     *
+     * @return The minimal ordinate value for dimension 0.
      */
     @Override
     public double getMinX() {
@@ -456,6 +458,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMinimum(int) minimal} ordinate value for dimension 1.
+     *
+     * @return The minimal ordinate value for dimension 1.
      */
     @Override
     public double getMinY() {
@@ -464,6 +468,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMaximum(int) maximal} ordinate value for dimension 0.
+     *
+     * @return The maximal ordinate value for dimension 0.
      */
     @Override
     public double getMaxX() {
@@ -472,6 +478,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMaximum(int) maximal} ordinate value for dimension 1.
+     *
+     * @return The maximal ordinate value for dimension 1.
      */
     @Override
     public double getMaxY() {
@@ -480,6 +488,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMedian(int) median} ordinate value for dimension 0.
+     *
+     * @return The median ordinate value for dimension 0.
      */
     @Override
     public double getCenterX() {
@@ -488,6 +498,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getMedian(int) median} ordinate value for dimension 1.
+     *
+     * @return The median ordinate value for dimension 1.
      */
     @Override
     public double getCenterY() {
@@ -496,6 +508,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getSpan(int) span} for dimension 0.
+     *
+     * @return The span for dimension 0.
      */
     @Override
     public double getWidth() {
@@ -504,6 +518,8 @@ public class Envelope2D extends Rectangl
 
     /**
      * Returns the {@linkplain #getSpan(int) span} for dimension 1.
+     *
+     * @return The span for dimension 1.
      */
     @Override
     public double getHeight() {
@@ -520,6 +536,8 @@ public class Envelope2D extends Rectangl
      * {@link java.lang.Double#NaN NaN}, then the envelope is considered empty.
      * This is different than the default {@link java.awt.geom.Rectangle2D.Double#isEmpty()}
      * implementation, which doesn't check for {@code NaN} values.</p>
+     *
+     * @return {@code true} if this envelope is empty.
      */
     @Override
     public boolean isEmpty() {
@@ -822,8 +840,7 @@ public class Envelope2D extends Rectangl
             double min = Math.max(min0, min1);
             double max = Math.min(max0, max1);
             /*
-             * See GeneralEnvelope.intersect(Envelope) for an explanation of the algorithm applied
-             * below.
+             * See GeneralEnvelope.intersect(Envelope) for an explanation of the algorithm applied below.
              */
             if (isSameSign(span0, span1)) { // Always 'false' if any value is NaN.
                 if ((min1 > max0 || max1 < min0) && !isNegativeUnsafe(span0)) {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -585,7 +585,7 @@ public class GeneralEnvelope extends Arr
                  * given envelope spans to infinities.
                  */
                 if (max0 <= max1 || min0 >= min1) {
-                    ordinates[iLower]   = min1;
+                    ordinates[iLower] = min1;
                     ordinates[iUpper] = max1;
                     continue;
                 }

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -21,6 +21,7 @@ import org.apache.sis.measure.Latitude;
 
 import static java.lang.Math.*;
 import static org.apache.sis.math.MathFunctions.atanh;
+import static org.apache.sis.internal.metadata.ReferencingServices.NAUTICAL_MILE;
 
 
 /**
@@ -52,7 +53,7 @@ public final class Formulas extends Stat
      * @see #LINEAR_TOLERANCE
      * @see org.apache.sis.internal.util.Numerics#COMPARISON_THRESHOLD
      */
-    public static final double ANGULAR_TOLERANCE = LINEAR_TOLERANCE / (1852 * 60);
+    public static final double ANGULAR_TOLERANCE = LINEAR_TOLERANCE / (NAUTICAL_MILE * 60);
 
     /**
      * Do not allow instantiation of this class.

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -705,7 +705,7 @@ public class Formatter {
          * a unit conversion). In the case of WGS84 semi-major axis in metres, we still have a
          * maximum of 8 fraction digits, which is more than enough.
          */
-        numberFormat.setMaximumFractionDigits(MathFunctions.fractionDigitsForDelta(Math.ulp(number), false) - 2);
+        numberFormat.setMaximumFractionDigits(MathFunctions.fractionDigitsForValue(number) - 2);
         numberFormat.setMinimumFractionDigits(1); // Must be after setMaximumFractionDigits(…).
         numberFormat.format(number, buffer, dummy);
         resetColor();
@@ -822,6 +822,8 @@ public class Formatter {
 
     /**
      * Returns the WKT formatted by this object.
+     *
+     * @return The WKT formatted by this instance.
      */
     @Override
     public String toString() {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractReferenceSystem.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -189,6 +189,8 @@ public class AbstractReferenceSystem ext
      * {@linkplain #getDomainOfValidity() domain of validity} and the {@linkplain #getScope() scope}
      * properties are ignored, in addition to other ignored properties documented in the
      * {@linkplain AbstractIdentifiedObject#hashCode(ComparisonMode) super-class}.
+     *
+     * @return The hash code value for the given comparison mode.
      */
     @Override
     public int hashCode(final ComparisonMode mode) throws IllegalArgumentException {

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticObjects.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticObjects.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticObjects.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticObjects.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -27,6 +27,7 @@ import org.opengis.referencing.Identifie
 import org.opengis.referencing.crs.GeodeticCRS;
 import org.opengis.referencing.crs.VerticalCRS;
 import org.opengis.referencing.crs.TemporalCRS;
+import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.datum.PrimeMeridian;
@@ -211,6 +212,8 @@ public enum GeodeticObjects {
      *   <tr><th>Semi-minor axis length:</th>  <td>6371007 <i>(definitive)</i></td></tr>
      *   <tr><th>Ellipsoid axes unit:</th>     <td>{@link SI#METRE}</td></tr>
      * </table></blockquote>
+     *
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius()
      */
     SPHERE((short) 7048);
 

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/StandardDefinitions.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -32,6 +32,7 @@ import org.apache.sis.referencing.datum.
 import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
 import static org.opengis.referencing.IdentifiedObject.ALIAS_KEY;
 import static org.opengis.referencing.IdentifiedObject.IDENTIFIERS_KEY;
+import static org.apache.sis.internal.metadata.ReferencingServices.AUTHALIC_RADIUS;
 
 
 /**
@@ -85,7 +86,7 @@ final class StandardDefinitions {
             case 7019: alias = "International 1979";       name  = "GRS 1980";     semiMajorAxis = 6378137.0; other = 298.257222101; break;
             case 7022: name  = "International 1924";       alias = "Hayford 1909"; semiMajorAxis = 6378388.0; other = 297.0;         break;
             case 7008: name  = "Clarke 1866";              ivfDefinitive = false;  semiMajorAxis = 6378206.4; other = 6356583.8;     break;
-            case 7048: name  = "GRS 1980 Authalic Sphere"; ivfDefinitive = false;  semiMajorAxis =            other = 6371007;       break;
+            case 7048: name  = "GRS 1980 Authalic Sphere"; ivfDefinitive = false;  semiMajorAxis = other = AUTHALIC_RADIUS;          break;
             default:   throw new AssertionError(code);
         }
         final Map<String,Object> map = new HashMap<String,Object>(8);

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/BursaWolfParameters.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -39,9 +39,12 @@ import org.apache.sis.internal.jdk7.Obje
 
 /**
  * Parameters for a geographic transformation between two datum.
- * The Bursa Wolf parameters shall be applied to geocentric coordinates,
+ * For an explanation of Bursa-Wolf parameters purpose, see the <cite>Bursa-Wolf parameters</cite>
+ * section of {@link DefaultGeodeticDatum} class javadoc.
+ *
+ * <p>The Bursa-Wolf parameters shall be applied to geocentric coordinates,
  * where the <var>X</var> axis points towards the Greenwich Prime Meridian,
- * the <var>Y</var> axis points East, and the <var>Z</var> axis points North.
+ * the <var>Y</var> axis points East, and the <var>Z</var> axis points North.</p>
  *
  * {@note The upper case letters are intentional. By convention, (<var>X</var>, <var>Y</var>, <var>Z</var>)
  *        stand for <cite>geocentric</cite> coordinates while (<var>x</var>, <var>y</var>, <var>z</var>)
@@ -68,6 +71,7 @@ import org.apache.sis.internal.jdk7.Obje
  * <center><b>Geocentric coordinates transformation</b></center>
  * <center>from (<var>X</var><sub>s</sub>, <var>Y</var><sub>s</sub>, <var>Z</var><sub>s</sub>)
  *           to (<var>X</var><sub>t</sub>, <var>Y</var><sub>t</sub>, <var>Z</var><sub>t</sub>)</center>
+ * <center><font size="-1">(ignoring unit conversions)</font></center>
  *
  * <p><math display="block" alttext="MathML capable browser required">
  *   <mfenced open="[" close="]">
@@ -78,7 +82,7 @@ import org.apache.sis.internal.jdk7.Obje
  *     </mtable>
  *   </mfenced>
  *   <mo>=</mo>
- *   <mi>dS</mi>
+ *   <mo>(</mo><mn>1</mn><mo>+</mo><mi>dS</mi><mo>)</mo>
  *   <mo>⋅</mo>
  *   <mfenced open="[" close="]">
  *     <mtable>
@@ -142,6 +146,8 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.4 (derived from geotk-1.2)
  * @version 0.4
  * @module
+ *
+ * @see DefaultGeodeticDatum#getBursaWolfParameters()
  */
 @Immutable
 public class BursaWolfParameters extends FormattableObject implements Serializable {
@@ -258,7 +264,7 @@ public class BursaWolfParameters extends
      * @param  targetDatum The target datum (usually WGS 84) for this set of parameters, or {@code null} if unspecified.
      * @throws IllegalArgumentException if the specified matrix does not meet the conditions.
      *
-     * @see #getAffineTransform()
+     * @see #getPositionVectorTransformation(boolean)
      */
     public BursaWolfParameters(final Matrix matrix, final double tolerance, final GeodeticDatum targetDatum)
             throws IllegalArgumentException
@@ -307,18 +313,19 @@ public class BursaWolfParameters extends
 
     /**
      * Returns {@code true} if the {@linkplain #targetDatum target datum} is equals (at least on computation purpose)
-     * to the WGS84 datum. This method may conservatively returns {@code false} if the specified datum is uncertain.
+     * to the WGS84 datum. If the datum is unspecified, then this method returns {@code true} since WGS84 is the only
+     * datum supported by the WKT 1 format, and is what users often mean.
      *
      * @return {@code true} if the given datum is equal to WGS84 for computational purpose.
      */
     final boolean isToWGS84() {
-        return (targetDatum != null) &&
+        return (targetDatum == null) ||
                 (IdentifiedObjects.nameMatches(targetDatum, "WGS 84") ||
                  IdentifiedObjects.nameMatches(targetDatum, "WGS84"));
     }
 
     /**
-     * Returns {@code true} if this Bursa Wolf parameters performs no operation.
+     * Returns {@code true} if this Bursa-Wolf parameters performs no operation.
      * This is true when all parameters are set to zero.
      *
      * @return {@code true} if the parameters describe no operation.
@@ -330,7 +337,7 @@ public class BursaWolfParameters extends
     }
 
     /**
-     * Returns {@code true} if this Bursa Wolf parameters contains only translation terms.
+     * Returns {@code true} if this Bursa-Wolf parameters contains only translation terms.
      *
      * @return {@code true} if the parameters describe to a translation only.
      */
@@ -339,7 +346,7 @@ public class BursaWolfParameters extends
     }
 
     /**
-     * Returns an affine transform that can be used to define this Bursa Wolf parameters.
+     * Returns the position vector transformation (geocentric domain) as an affine transform.
      * The formula is as below, where {@code R} is a conversion factor from arc-seconds to radians:
      *
      * <blockquote><pre> R = toRadians(1″)
@@ -352,19 +359,28 @@ public class BursaWolfParameters extends
      * └    ┘    └                               ┘  └   ┘</pre></blockquote>
      *
      * This affine transform can be applied on <strong>geocentric</strong> coordinates.
+     * This is identified as operation method 1033 in the EPSG database.
      *
-     * @return An affine transform created from the parameters.
-     *
-     * @see DefaultGeodeticDatum#getAffineTransform(GeodeticDatum)
-     */
-    public Matrix getAffineTransform() {
-        final double  S = 1 + dS / PPM;
-        final double RS = TO_RADIANS * S;
+     * {@section Inverse transformation}
+     * The inverse transformation can be computed by reversing the sign of the 7 parameters before to use
+     * them in the above matrix. Note that both the direct and inverse transformations are approximations.
+     * Multiplication of direct and inverse transformation matrices results in a matrix close to the identity,
+     * but not necessarily strictly equals.
+     *
+     * @param  inverse If {@code true}, returns the inverse transformation instead.
+     * @return An affine transform in geocentric space created from this Bursa-Wolf parameters.
+     *
+     * @see DefaultGeodeticDatum#getPositionVectorTransformation(GeodeticDatum)
+     */
+    public Matrix getPositionVectorTransformation(final boolean inverse) {
+        final double sgn = inverse ? -1 : +1;
+        final double   S = 1 + sgn*dS / PPM;
+        final double  RS = sgn*TO_RADIANS * S;
         return new Matrix4(
-                 S,  -rZ*RS,  +rY*RS,  tX,
-            +rZ*RS,       S,  -rX*RS,  tY,
-            -rY*RS,  +rX*RS,       S,  tZ,
-                 0,       0,       0,   1);
+                 S,  -rZ*RS,  +rY*RS,  sgn*tX,
+            +rZ*RS,       S,  -rX*RS,  sgn*tY,
+            -rY*RS,  +rX*RS,       S,  sgn*tZ,
+                 0,       0,       0,      1);
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEllipsoid.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
-import java.util.Collections;
 import javax.measure.unit.SI;
 import javax.measure.unit.Unit;
 import javax.measure.quantity.Length;
@@ -232,24 +231,6 @@ public class DefaultEllipsoid extends Ab
     }
 
     /**
-     * Creates a new ellipsoid using the specified name and axis length in metres.
-     * This is a convenience method for {@link #createEllipsoid(Map, double, double, Unit) createEllipsoid(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property
-     * and the unit of measurement fixed to {@link SI#METRE}.
-     *
-     * @param name          The ellipsoid name.
-     * @param semiMajorAxis The equatorial radius in metres.
-     * @param semiMinorAxis The polar radius in metres.
-     * @return An ellipsoid with the given axis length.
-     */
-    public static DefaultEllipsoid createEllipsoid(final String name,
-                                                   final double semiMajorAxis,
-                                                   final double semiMinorAxis)
-    {
-        return createEllipsoid(Collections.singletonMap(NAME_KEY, name), semiMajorAxis, semiMinorAxis, SI.METRE);
-    }
-
-    /**
      * Creates a new ellipsoid using the specified properties and axis length.
      * The properties map is given unchanged to the
      * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
@@ -274,24 +255,6 @@ public class DefaultEllipsoid extends Ab
     }
 
     /**
-     * Creates a new ellipsoid using the specified name, axis length in metres and inverse flattening value. This is
-     * a convenience method for {@link #createFlattenedSphere(Map, double, double, Unit) createFlattenedSphere(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property and the unit
-     * of measurement fixed to {@link SI#METRE}.
-     *
-     * @param name              The ellipsoid name.
-     * @param semiMajorAxis     The equatorial radius in metres.
-     * @param inverseFlattening The inverse flattening value.
-     * @return An ellipsoid with the given axis length.
-     */
-    public static DefaultEllipsoid createFlattenedSphere(final String name,
-                                                         final double semiMajorAxis,
-                                                         final double inverseFlattening)
-    {
-        return createFlattenedSphere(Collections.singletonMap(NAME_KEY, name), semiMajorAxis, inverseFlattening, SI.METRE);
-    }
-
-    /**
      * Creates a new ellipsoid using the specified properties, axis length and inverse flattening value.
      * The properties map is given unchanged to the
      * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
@@ -423,6 +386,8 @@ public class DefaultEllipsoid extends Ab
      * The radius is expressed in {@linkplain #getAxisUnit() axis linear unit}.
      *
      * @return The radius of a sphere having the same surface than this ellipsoid.
+     *
+     * @see org.apache.sis.referencing.GeodeticObjects#SPHERE
      */
     public double getAuthalicRadius() {
         return Formulas.getAuthalicRadius(getSemiMajorAxis(), getSemiMinorAxis());

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
-import java.util.Collections;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.referencing.datum.EngineeringDatum;
@@ -47,17 +46,6 @@ public class DefaultEngineeringDatum ext
     private static final long serialVersionUID = 1498304918725248637L;
 
     /**
-     * Creates an engineering datum from a name. This is a convenience constructor for
-     * {@link #DefaultEngineeringDatum(Map)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property.
-     *
-     * @param name The datum name.
-     */
-    public DefaultEngineeringDatum(final String name) {
-        this(Collections.singletonMap(NAME_KEY, name));
-    }
-
-    /**
      * Creates an engineering datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      *

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -20,7 +20,6 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Collections;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
@@ -28,11 +27,8 @@ import org.opengis.referencing.datum.Ell
 import org.opengis.referencing.datum.PrimeMeridian;
 import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.operation.Matrix;
-import org.apache.sis.referencing.GeodeticObjects;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
-import org.apache.sis.referencing.operation.matrix.NoninvertibleMatrixException;
 import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.Immutable;
 import org.apache.sis.io.wkt.Formatter;
@@ -45,9 +41,36 @@ import org.apache.sis.internal.jdk7.Obje
 
 
 /**
- * Defines the location and precise orientation in 3-dimensional space of a defined ellipsoid
- * (or sphere) that approximates the shape of the earth. Used also for Cartesian coordinate
- * system centered in this ellipsoid (or sphere).
+ * Defines the location and orientation of an ellipsoid that approximates the shape of the earth.
+ * Geodetic datum are used together with ellipsoidal coordinate system, and also with Cartesian
+ * coordinate system centered in the ellipsoid (or sphere).
+ *
+ * {@section Bursa-Wolf parameters}
+ * One or many {@link BursaWolfParameters} can optionally be associated to each {@code DefaultGeodeticDatum} instance.
+ * This association is not part of the ISO 19111 model, but still a common practice (especially in older standards).
+ * Associating Bursa-Wolf parameters to geodetic datum is known as the <cite>early-binding</cite> approach.
+ * A recommended alternative, discussed below, is the <cite>late-binding</cite> approach.
+ *
+ * <p>There is different methods for transforming coordinates from one geodetic datum to an other datum,
+ * and Bursa-Wolf parameters are used with some of them. However different set of parameters may exist
+ * for the same pair of (<var>source</var>, <var>target</var>) datum, so it is often not sufficient to
+ * know those datum. The (<var>source</var>, <var>target</var>) pair of CRS are often necessary,
+ * sometime together with the geographic extent of the coordinates to transform.</p>
+ *
+ * <p>Apache SIS searches for datum shift methods (including Bursa-Wolf parameters) in the EPSG database when a
+ * {@link org.opengis.referencing.operation.CoordinateOperation} or a
+ * {@link org.opengis.referencing.operation.MathTransform} is requested for a pair of CRS.
+ * This is known as the <cite>late-binding</cite> approach.
+ * If a datum shift method is found in the database, it will have precedence over any {@code BursaWolfParameters}
+ * instance associated to this {@code DefaultGeodeticDatum}. Only if no datum shift method is found in the database,
+ * then the {@code BursaWolfParameters} associated to the datum may be used as a fallback.</p>
+ *
+ * <p>The Bursa-Wolf parameters association serves an other purpose: when a CRS is formatted in
+ * <cite>Well Known Text</cite> (WKT) format, the formatted string may contain a {@code TOWGS84[…]} element
+ * with the parameter values of the transformation to the WGS 84 datum. This element is provided as a help
+ * for other Geographic Information Systems that support only the <cite>early-binding</cite> approach.
+ * Apache SIS usually does not need the {@code TOWGS84} element, except as a fallback for datum that
+ * do not exist in the EPSG database.</p>
  *
  * {@section Creating new geodetic datum instances}
  * New instances can be created either directly by specifying all information to a factory method (choices 3
@@ -95,7 +118,7 @@ public class DefaultGeodeticDatum extend
 
     /**
      * The <code>{@value #BURSA_WOLF_KEY}</code> property for
-     * {@link #getAffineTransform(GeodeticDatum) datum shifts}.
+     * {@linkplain #getBursaWolfParameters(GeodeticDatum) Bursa-Wolf parameters}.
      */
     public static final String BURSA_WOLF_KEY = "bursaWolf";
 
@@ -117,24 +140,11 @@ public class DefaultGeodeticDatum extend
     private final PrimeMeridian primeMeridian;
 
     /**
-     * Bursa Wolf parameters for datum shifts, or {@code null} if none.
+     * Bursa-Wolf parameters for datum shifts, or {@code null} if none.
      */
     private final BursaWolfParameters[] bursaWolf;
 
     /**
-     * Creates a geodetic datum using the Greenwich prime meridian. This is a convenience constructor for
-     * {@link #DefaultGeodeticDatum(Map, Ellipsoid, PrimeMeridian) DefaultGeodeticDatum(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property
-     * and the {@link #getPrimeMeridian() prime meridian} fixed to Greenwich.
-     *
-     * @param name      The datum name.
-     * @param ellipsoid The ellipsoid.
-     */
-    public DefaultGeodeticDatum(final String name, final Ellipsoid ellipsoid) {
-        this(Collections.singletonMap(NAME_KEY, name), ellipsoid, GeodeticObjects.WGS84.primeMeridian());
-    }
-
-    /**
      * Creates a geodetic datum from the given properties. The properties map is given
      * unchanged to the {@link AbstractDatum#AbstractDatum(Map) super-class constructor}.
      * In addition to the properties documented in the parent constructor,
@@ -225,23 +235,25 @@ public class DefaultGeodeticDatum extend
     }
 
     /**
-     * Returns all Bursa Wolf parameters specified in the {@code properties} map at construction time.
+     * Returns all Bursa-Wolf parameters specified in the {@code properties} map at construction time.
+     * For a discussion about what Bursa-Wolf parameters are, see the class javadpc.
      *
-     * @return The Bursa Wolf parameters, or an empty array if none.
+     * @return The Bursa-Wolf parameters, or an empty array if none.
      */
     public BursaWolfParameters[] getBursaWolfParameters() {
         return (bursaWolf != null) ? bursaWolf.clone() : EMPTY_ARRAY;
     }
 
     /**
-     * Returns Bursa Wolf parameters for a datum shift toward the specified target, or {@code null} if none.
+     * Returns Bursa-Wolf parameters for a datum shift toward the specified target, or {@code null} if none.
      * This method searches only for Bursa-Wolf parameters explicitly specified in the {@code properties} map
      * given at construction time. This method doesn't try to infer a set of parameters from indirect informations.
      * For example it does not try to inverse the parameters specified in the {@code target} datum if none were found
-     * in this datum. If a more elaborated search is wanted, use {@link #getAffineTransform(GeodeticDatum)} instead.
+     * in this datum.
+     * If a more elaborated search is wanted, use {@link #getPositionVectorTransformation(GeodeticDatum)} instead.
      *
      * @param  target The target geodetic datum.
-     * @return Bursa Wolf parameters from this datum to the given target datum, or {@code null} if none.
+     * @return Bursa-Wolf parameters from this datum to the given target datum, or {@code null} if none.
      */
     public BursaWolfParameters getBursaWolfParameters(final GeodeticDatum target) {
         if (bursaWolf != null) {
@@ -263,26 +275,21 @@ public class DefaultGeodeticDatum extend
     }
 
     /**
-     * Returns a matrix that can be used to define a transformation to the specified datum.
+     * Returns the position vector transformation (geocentric domain) to the specified datum.
      * If no transformation path is found, then this method returns {@code null}.
+     * If non-null, then the representation is represented as an affine transform.
+     *
+     * {@note This is identified in the EPSG database as operation method 1033 -
+     *        <cite>Position Vector transformation (geocentric domain)</cite>.}
      *
      * @param  targetDatum The target datum.
-     * @return An affine transform from {@code this} to {@code target}, or {@code null} if none.
+     * @return An affine transform from {@code this} to {@code target} in geocentric space, or {@code null} if none.
      *
-     * @see BursaWolfParameters#getAffineTransform()
+     * @see BursaWolfParameters#getPositionVectorTransformation(boolean)
      */
-    public Matrix getAffineTransform(final GeodeticDatum targetDatum) {
+    public Matrix getPositionVectorTransformation(final GeodeticDatum targetDatum) {
         ensureNonNull("targetDatum", targetDatum);
-        try {
-            return getAffineTransform(this, targetDatum, null);
-        } catch (NoninvertibleMatrixException e) {
-            /*
-             * Should never happen, unless the user has overriden BursaWolfParameters.getAffineTransform()
-             * and create an invalid matrix. Returning 'null' is compliant with this method contract.
-             */
-            Logging.unexpectedException(DefaultGeodeticDatum.class, "getAffineTransform", e);
-            return null;
-        }
+        return getPositionVectorTransformation(this, targetDatum, null);
     }
 
     /**
@@ -295,14 +302,14 @@ public class DefaultGeodeticDatum extend
      *         This is used in order to avoid never-ending recursivity.
      * @return An affine transform from {@code source} to {@code target}, or {@code null} if none.
      */
-    private static Matrix getAffineTransform(final GeodeticDatum source, final GeodeticDatum target,
-            Set<GeodeticDatum> exclusion) throws NoninvertibleMatrixException
+    private static Matrix getPositionVectorTransformation(final GeodeticDatum source, final GeodeticDatum target,
+            Set<GeodeticDatum> exclusion)
     {
         final BursaWolfParameters[] sourceParam = bursaWolf(source);
         if (sourceParam != null) {
             for (final BursaWolfParameters candidate : sourceParam) {
                 if (deepEquals(target, candidate.targetDatum, ComparisonMode.IGNORE_METADATA)) {
-                    return candidate.getAffineTransform();
+                    return candidate.getPositionVectorTransformation(false);
                 }
             }
         }
@@ -314,7 +321,7 @@ public class DefaultGeodeticDatum extend
         if (targetParam != null) {
             for (final BursaWolfParameters candidate : targetParam) {
                 if (deepEquals(source, candidate.targetDatum, ComparisonMode.IGNORE_METADATA)) {
-                    return MatrixSIS.castOrCopy(candidate.getAffineTransform()).inverse();
+                    return candidate.getPositionVectorTransformation(true);
                 }
             }
         }
@@ -336,9 +343,9 @@ public class DefaultGeodeticDatum extend
                         }
                         if (exclusion.add(source)) {
                             if (exclusion.add(target)) {
-                                final Matrix step1 = getAffineTransform(source, sourceStep, exclusion);
+                                final Matrix step1 = getPositionVectorTransformation(source, sourceStep, exclusion);
                                 if (step1 != null) {
-                                    final Matrix step2 = getAffineTransform(targetStep, target, exclusion);
+                                    final Matrix step2 = getPositionVectorTransformation(targetStep, target, exclusion);
                                     if (step2 != null) {
                                         /*
                                          * MatrixSIS.multiply(MatrixSIS) is equivalent to AffineTransform.concatenate(…):
@@ -386,11 +393,11 @@ public class DefaultGeodeticDatum extend
                     return deepEquals(getEllipsoid(),     that.getEllipsoid(),     mode) &&
                            deepEquals(getPrimeMeridian(), that.getPrimeMeridian(), mode);
                     /*
-                     * HACK: We do not consider Bursa Wolf parameters as a non-metadata field.
+                     * HACK: We do not consider Bursa-Wolf parameters as a non-metadata field.
                      *       This is needed in order to get equalsIgnoreMetadata(...) to returns
                      *       'true' when comparing the WGS84 constant in this class with a WKT
                      *       DATUM element with a TOWGS84[0,0,0,0,0,0,0] element. Furthermore,
-                     *       the Bursa Wolf parameters are not part of ISO 19111 specification.
+                     *       the Bursa-Wolf parameters are not part of ISO 19111 specification.
                      *       We don't want two CRS to be considered as different because one has
                      *       more of those transformation informations (which is nice, but doesn't
                      *       change the CRS itself).

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultImageDatum.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
-import java.util.Collections;
 import javax.xml.bind.annotation.XmlType;
 import javax.xml.bind.annotation.XmlRootElement;
 import org.opengis.referencing.datum.ImageDatum;
@@ -57,18 +56,6 @@ public class DefaultImageDatum extends A
     private final PixelInCell pixelInCell;
 
     /**
-     * Creates an image datum from a name. This is a convenience constructor for
-     * {@link #DefaultImageDatum(Map, PixelInCell) DefaultImageDatum(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property.
-     *
-     * @param name The datum name.
-     * @param pixelInCell the way the image grid is associated with the image data attributes.
-     */
-    public DefaultImageDatum(final String name, final PixelInCell pixelInCell) {
-        this(Collections.singletonMap(NAME_KEY, name), pixelInCell);
-    }
-
-    /**
      * Creates an image datum from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractDatum#AbstractDatum(Map) super-class constructor}.
      *

Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java?rev=1535870&r1=1535869&r2=1535870&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DefaultPrimeMeridian.java [UTF-8] Fri Oct 25 21:07:47 2013
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.datum;
 
 import java.util.Map;
-import java.util.Collections;
 import javax.measure.unit.Unit;
 import javax.measure.unit.NonSI;
 import javax.measure.quantity.Angle;
@@ -97,20 +96,6 @@ public class DefaultPrimeMeridian extend
     private final Unit<Angle> angularUnit;
 
     /**
-     * Creates a prime meridian from a name and Greenwich longitude in degrees. This is a convenience
-     * constructor for {@link #DefaultPrimeMeridian(Map, double, Unit) DefaultPrimeMeridian(Map, …)}
-     * with a map containing only the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} property,
-     * and with the unit of measurement fixed to {@link NonSI#DEGREE_ANGLE}.
-     *
-     * @param name                The datum name.
-     * @param greenwichLongitude  The longitude value relative to the Greenwich Meridian, in degrees.
-     *                            Positive values are east of Greenwich.
-     */
-    public DefaultPrimeMeridian(final String name, final double greenwichLongitude) {
-        this(Collections.singletonMap(NAME_KEY, name), greenwichLongitude, NonSI.DEGREE_ANGLE);
-    }
-
-    /**
      * Creates a prime meridian from the given properties. The properties map is given
      * unchanged to the {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map)
      * super-class constructor}.



Mime
View raw message