sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1425592 [1/2] - in /sis/trunk: ./ sis-referencing/src/main/java/org/apache/sis/geometry/ sis-referencing/src/test/java/org/apache/sis/geometry/ sis-utility/src/main/java/org/apache/sis/io/ sis-utility/src/main/java/org/apache/sis/math/ sis...
Date Mon, 24 Dec 2012 05:58:29 GMT
Author: desruisseaux
Date: Mon Dec 24 05:58:28 2012
New Revision: 1425592

URL: http://svn.apache.org/viewvc?rev=1425592&view=rev
Log:
Merge from the JDK6 branch.

Added:
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/Appender.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/main/java/org/apache/sis/io/Appender.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/LineAppender.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/main/java/org/apache/sis/io/LineAppender.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/TabularFormat.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/main/java/org/apache/sis/io/TabularFormat.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/AppenderTestCase.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/AppenderTestCase.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/LeftMarginTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/LeftMarginTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/LineAppenderTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/LineAppenderTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/TableAppenderTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/TableAppenderTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/TabulationExpansionTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/TabulationExpansionTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/WordWrapTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/WordWrapTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/io/WordWrapWithLineSeparatorTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/math/StatisticsFormatTest.java
      - copied unchanged from r1425591, sis/branches/JDK6/sis-utility/src/test/java/org/apache/sis/math/StatisticsFormatTest.java
Removed:
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/FilteredAppendable.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/LineFormatter.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/TableFormatter.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/FormatterTestCase.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/LineFormatterTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/TableFormatterTest.java
Modified:
    sis/trunk/   (props changed)
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition1D.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
    sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ImmutableEnvelope.java
    sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java
    sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/AppendableWriter.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/IO.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/io/package-info.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/ArgumentChecks.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/CharSequences.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/Characters.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/CheckedArrayList.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/CheckedHashMap.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/CheckedHashSet.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    sis/trunk/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/EchoAppendable.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/io/SingleCharAppendable.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/math/StatisticsTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/util/collection/CacheTest.java
    sis/trunk/sis-utility/src/test/java/org/apache/sis/util/collection/TreeTableFormatTest.java
    sis/trunk/src/main/docbook/fr/geometry.xml

Propchange: sis/trunk/
------------------------------------------------------------------------------
  Merged /sis/branches/JDK7:r1424819-1425590
  Merged /sis/branches/JDK6:r1424841-1425591

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java Mon Dec 24 05:58:28 2012
@@ -25,7 +25,9 @@ import java.util.Arrays;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.RangeMeaning;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
@@ -33,6 +35,7 @@ import org.apache.sis.util.resources.Err
 import static java.lang.Double.doubleToLongBits;
 import static org.apache.sis.util.Arrays.resize;
 import static org.apache.sis.util.StringBuilders.trimFractionalPart;
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
 
 // Related to JDK7
 import org.apache.sis.internal.util.Objects;
@@ -69,6 +72,21 @@ public abstract class AbstractDirectPosi
     }
 
     /**
+     * Returns a sequence of numbers that hold the coordinate of this position in its
+     * reference system.
+     *
+     * @return The coordinates.
+     */
+    @Override
+    public double[] getCoordinate() {
+        final double[] ordinates = new double[getDimension()];
+        for (int i=0; i<ordinates.length; i++) {
+            ordinates[i] = getOrdinate(i);
+        }
+        return ordinates;
+    }
+
+    /**
      * Sets this direct position to the given position. If the given position is
      * {@code null}, then all ordinate values are set to {@link Double#NaN NaN}.
      *
@@ -86,7 +104,7 @@ public abstract class AbstractDirectPosi
     {
         final int dimension = getDimension();
         if (position != null) {
-            ensureDimensionMatch("position", position.getDimension(), dimension);
+            ensureDimensionMatches("position", dimension, position);
             final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
             if (crs != null) {
                 final CoordinateReferenceSystem other = position.getCoordinateReferenceSystem();
@@ -105,58 +123,55 @@ public abstract class AbstractDirectPosi
     }
 
     /**
-     * Returns a sequence of numbers that hold the coordinate of this position in its
-     * reference system.
-     *
-     * @return The coordinates.
-     */
-    @Override
-    public double[] getCoordinate() {
-        final double[] ordinates = new double[getDimension()];
-        for (int i=0; i<ordinates.length; i++) {
-            ordinates[i] = getOrdinate(i);
-        }
-        return ordinates;
-    }
-
-    /**
-     * Ensures that the given CRS, if non-null, has the expected number of dimensions.
-     * This method presumes that the argument name is {@code "crs"}.
-     *
-     * @param  crs The coordinate reference system to check, or {@code null}.
-     * @param  expected The expected number of dimensions.
-     * @throws MismatchedDimensionException if the CRS dimension is not valid.
-     */
-    static void ensureDimensionMatch(final CoordinateReferenceSystem crs,
-            final int expected) throws MismatchedDimensionException
-    {
+     * Ensures that the position is contained in the coordinate system domain.
+     * For each dimension, this method compares the ordinate values against the
+     * limits of the coordinate system axis for that dimension.
+     * If some ordinates are out of range, then there is a choice depending on the
+     * {@linkplain CoordinateSystemAxis#getRangeMeaning() axis range meaning}:
+     *
+     * <ul>
+     *   <li>If {@link RangeMeaning#EXACT} (typically <em>latitudes</em> ordinates), then values
+     *       greater than the {@linkplain CoordinateSystemAxis#getMaximumValue() axis maximal value}
+     *       are replaced by the axis maximum, and values smaller than the
+     *       {@linkplain CoordinateSystemAxis#getMinimumValue() axis minimal value}
+     *       are replaced by the axis minimum.</li>
+     *
+     *   <li>If {@link RangeMeaning#WRAPAROUND} (typically <em>longitudes</em> ordinates), then
+     *       a multiple of the axis range (e.g. 360° for longitudes) is added or subtracted.</li>
+     * </ul>
+     *
+     * @return {@code true} if this position has been modified as a result of this method call,
+     *         or {@code false} if no change has been done.
+     */
+    public boolean normalize() {
+        boolean changed = false;
+        final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
         if (crs != null) {
+            final int dimension = getDimension();
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            if (cs != null) { // Should never be null, but let be safe.
-                final int dimension = cs.getDimension();
-                if (dimension != expected) {
-                    throw new MismatchedDimensionException(Errors.format(
-                            Errors.Keys.MismatchedDimension_3, "crs", dimension, expected));
+            for (int i=0; i<dimension; i++) {
+                double ordinate = getOrdinate(i);
+                final CoordinateSystemAxis axis = cs.getAxis(i);
+                final double  minimum = axis.getMinimumValue();
+                final double  maximum = axis.getMaximumValue();
+                final RangeMeaning rm = axis.getRangeMeaning();
+                if (RangeMeaning.EXACT.equals(rm)) {
+                         if (ordinate < minimum) ordinate = minimum;
+                    else if (ordinate > maximum) ordinate = maximum;
+                    else continue;
+                } else if (RangeMeaning.WRAPAROUND.equals(rm)) {
+                    final double csSpan = maximum - minimum;
+                    final double shift  = Math.floor((ordinate - minimum) / csSpan) * csSpan;
+                    if (shift == 0) {
+                        continue;
+                    }
+                    ordinate -= shift;
                 }
+                setOrdinate(i, ordinate);
+                changed = true;
             }
         }
-    }
-
-    /**
-     * Ensures that the given number of dimensions is equals to the expected value.
-     *
-     * @param  name The name of the argument to check.
-     * @param  dimension The object dimension.
-     * @param  expectedDimension The Expected dimension for the object.
-     * @throws MismatchedDimensionException if the object doesn't have the expected dimension.
-     */
-    static void ensureDimensionMatch(final String name, final int dimension,
-            final int expectedDimension) throws MismatchedDimensionException
-    {
-        if (dimension != expectedDimension) {
-            throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3,
-                        name, dimension, expectedDimension));
-        }
+        return changed;
     }
 
     /**

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java Mon Dec 24 05:58:28 2012
@@ -38,6 +38,7 @@ import org.apache.sis.util.resources.Err
 import static java.lang.Double.doubleToLongBits;
 import static org.apache.sis.internal.util.Utilities.SIGN_BIT_MASK;
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
 import static org.apache.sis.util.StringBuilders.trimFractionalPart;
 import static org.apache.sis.math.MathFunctions.epsilonEqual;
 import static org.apache.sis.math.MathFunctions.isNegative;
@@ -85,6 +86,12 @@ import org.apache.sis.internal.util.Obje
  * </ul>
  * </td></tr></table>
  *
+ * {@section Choosing the range of longitude values}
+ * Geographic CRS typically have longitude values in the [-180 … +180]° range, but the [0 … 360]°
+ * range is also occasionally used. Users of this class need to ensure that this envelope CRS is
+ * associated to axes having the desired {@linkplain CoordinateSystemAxis#getMinimumValue() minimum}
+ * and {@linkplain CoordinateSystemAxis#getMaximumValue() maximum value}.
+ *
  * {@section Note on positive and negative zeros}
  * The IEEE 754 standard defines two different values for positive zero and negative zero.
  * When used with SIS envelopes and keeping in mind the above discussion, those zeros have
@@ -561,7 +568,7 @@ public abstract class AbstractEnvelope i
     public boolean contains(final DirectPosition position) throws MismatchedDimensionException {
         ensureNonNull("position", position);
         final int dimension = getDimension();
-        AbstractDirectPosition.ensureDimensionMatch("point", position.getDimension(), dimension);
+        ensureDimensionMatches("point", dimension, position);
         assert equalsIgnoreMetadata(getCoordinateReferenceSystem(),
                 position.getCoordinateReferenceSystem(), true) : position;
         for (int i=0; i<dimension; i++) {
@@ -617,7 +624,7 @@ public abstract class AbstractEnvelope i
     public boolean contains(final Envelope envelope, final boolean edgesInclusive) throws MismatchedDimensionException {
         ensureNonNull("envelope", envelope);
         final int dimension = getDimension();
-        AbstractDirectPosition.ensureDimensionMatch("envelope", envelope.getDimension(), dimension);
+        ensureDimensionMatches("envelope", dimension, envelope);
         assert equalsIgnoreMetadata(getCoordinateReferenceSystem(),
                 envelope.getCoordinateReferenceSystem(), true) : envelope;
         final DirectPosition lowerCorner = envelope.getLowerCorner();
@@ -707,7 +714,7 @@ public abstract class AbstractEnvelope i
     public boolean intersects(final Envelope envelope, final boolean edgesInclusive) throws MismatchedDimensionException {
         ensureNonNull("envelope", envelope);
         final int dimension = getDimension();
-        AbstractDirectPosition.ensureDimensionMatch("envelope", envelope.getDimension(), dimension);
+        ensureDimensionMatches("envelope", dimension, envelope);
         assert equalsIgnoreMetadata(getCoordinateReferenceSystem(),
                 envelope.getCoordinateReferenceSystem(), true) : envelope;
         final DirectPosition lowerCorner = envelope.getLowerCorner();

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java Mon Dec 24 05:58:28 2012
@@ -85,7 +85,7 @@ class ArrayEnvelope extends AbstractEnve
     {
         crs = getCommonCRS(lowerCorner, upperCorner); // This performs also an argument check.
         final int dimension = lowerCorner.getDimension();
-        AbstractDirectPosition.ensureDimensionMatch(crs, dimension);
+        ensureDimensionMatches("crs", dimension, crs);
         ensureSameDimension(dimension, upperCorner.getDimension());
         ordinates = new double[dimension * 2];
         for (int i=0; i<dimension; i++) {

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition1D.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition1D.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition1D.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition1D.java Mon Dec 24 05:58:28 2012
@@ -27,6 +27,8 @@ import org.opengis.geometry.DirectPositi
 import org.opengis.geometry.MismatchedDimensionException;
 import org.apache.sis.util.resources.Errors;
 
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
+
 
 /**
  * Holds the coordinates for a one-dimensional position within some coordinate reference system.
@@ -68,7 +70,7 @@ public class DirectPosition1D extends Ab
      * @param crs The coordinate reference system.
      */
     public DirectPosition1D(final CoordinateReferenceSystem crs) {
-        ensureDimensionMatch(crs, 1);
+        ensureDimensionMatches("crs", 1, crs);
         this.crs = crs;
     }
 
@@ -103,7 +105,7 @@ public class DirectPosition1D extends Ab
             throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.UnparsableStringForClass_2, "POINT", wkt));
         }
-        ensureDimensionMatch("wkt", ordinates.length, 1);
+        ensureDimensionMatches("wkt", 1, ordinates);
         ordinate = ordinates[0];
     }
 
@@ -136,7 +138,7 @@ public class DirectPosition1D extends Ab
      * @param crs The new coordinate reference system, or {@code null}.
      */
     public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs) {
-        ensureDimensionMatch(crs, 1);
+        ensureDimensionMatches("crs", 1, crs);
         this.crs = crs;
     }
 
@@ -199,7 +201,7 @@ public class DirectPosition1D extends Ab
      */
     @Override
     public void setLocation(final DirectPosition position) throws MismatchedDimensionException {
-        ensureDimensionMatch("position", position.getDimension(), 1);
+        ensureDimensionMatches("position", 1, position);
         setCoordinateReferenceSystem(position.getCoordinateReferenceSystem());
         ordinate = position.getOrdinate(0);
     }

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java Mon Dec 24 05:58:28 2012
@@ -25,11 +25,7 @@ import org.apache.sis.util.resources.Err
 
 import static java.lang.Double.doubleToLongBits;
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
-
-// Following imports are needed because we can't extend AbstractDirectPosition.
-// We want to write this class as if it was an AbstractDirectPosition subclass.
-import static org.apache.sis.geometry.AbstractDirectPosition.ensureDimensionMatch;
-import static org.apache.sis.geometry.AbstractDirectPosition.parse;
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
 
 // Related to JDK7
 import org.apache.sis.internal.util.Objects;
@@ -94,7 +90,7 @@ public class DirectPosition2D extends Po
      * @param crs The coordinate reference system, or {@code null}.
      */
     public DirectPosition2D(final CoordinateReferenceSystem crs) {
-        ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 
@@ -127,7 +123,7 @@ public class DirectPosition2D extends Po
     public DirectPosition2D(final double x, final double y, final CoordinateReferenceSystem crs) {
         super(x, y);
         this.crs = crs;
-        ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
     }
 
     /**
@@ -140,7 +136,7 @@ public class DirectPosition2D extends Po
      */
     public DirectPosition2D(final DirectPosition position) throws MismatchedDimensionException {
         ensureNonNull("position", position);
-        ensureDimensionMatch("position", position.getDimension(), 2);
+        ensureDimensionMatches("position", 2, position);
         x   = position.getOrdinate(0);
         y   = position.getOrdinate(1);
         crs = position.getCoordinateReferenceSystem();
@@ -163,12 +159,12 @@ public class DirectPosition2D extends Po
      * @see org.apache.sis.measure.CoordinateFormat
      */
     public DirectPosition2D(final CharSequence wkt) throws IllegalArgumentException {
-        final double[] ordinates = parse(wkt);
+        final double[] ordinates = AbstractDirectPosition.parse(wkt);
         if (ordinates == null) {
             throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.UnparsableStringForClass_2, "POINT", wkt));
         }
-        ensureDimensionMatch("wkt", ordinates.length, 2);
+        ensureDimensionMatches("wkt", 2, ordinates);
         x = ordinates[0];
         y = ordinates[1];
     }
@@ -211,7 +207,7 @@ public class DirectPosition2D extends Po
      * @param crs The new coordinate reference system, or {@code null}.
      */
     public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs) {
-        ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/Envelope2D.java Mon Dec 24 05:58:28 2012
@@ -36,6 +36,7 @@ import static java.lang.Double.doubleToL
 import static org.apache.sis.math.MathFunctions.isPositive;
 import static org.apache.sis.math.MathFunctions.isNegative;
 import static org.apache.sis.math.MathFunctions.isSameSign;
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
 import static org.apache.sis.internal.referencing.Utilities.isPoleToPole;
 
 // Following imports are needed because we can't extend AbstractEnvelope.
@@ -151,7 +152,7 @@ public class Envelope2D extends Rectangl
          */
         this(lowerCorner.getOrdinate(0), lowerCorner.getOrdinate(1),
              upperCorner.getOrdinate(0), upperCorner.getOrdinate(1));
-        AbstractDirectPosition.ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 
@@ -223,7 +224,7 @@ public class Envelope2D extends Rectangl
             throws MismatchedDimensionException
     {
         super(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); // Really 'super', not 'this'.
-        AbstractDirectPosition.ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 
@@ -246,7 +247,7 @@ public class Envelope2D extends Rectangl
             final CoordinateReferenceSystem crs) throws MismatchedDimensionException
     {
         super(x, y, width, height); // Really 'super', not 'this'.
-        AbstractDirectPosition.ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 
@@ -269,7 +270,7 @@ public class Envelope2D extends Rectangl
      * @param crs The new coordinate reference system, or {@code null}.
      */
     public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs) {
-        AbstractDirectPosition.ensureDimensionMatch(crs, 2);
+        ensureDimensionMatches("crs", 2, crs);
         this.crs = crs;
     }
 

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java Mon Dec 24 05:58:28 2012
@@ -29,6 +29,8 @@ import org.opengis.geometry.MismatchedDi
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.util.resources.Errors;
 
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
+
 // JDK7 related
 import org.apache.sis.internal.util.Objects;
 
@@ -118,7 +120,7 @@ public class GeneralDirectPosition exten
     public GeneralDirectPosition(final DirectPosition point) {
         ordinates = point.getCoordinate(); // Should already be cloned.
         crs = point.getCoordinateReferenceSystem();
-        ensureDimensionMatch(crs, ordinates.length);
+        ensureDimensionMatches("crs", ordinates.length, crs);
     }
 
     /**
@@ -178,7 +180,7 @@ public class GeneralDirectPosition exten
     public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs)
             throws MismatchedDimensionException
     {
-        ensureDimensionMatch(crs, getDimension());
+        ensureDimensionMatches("crs", getDimension(), crs);
         this.crs = crs;
     }
 
@@ -208,7 +210,7 @@ public class GeneralDirectPosition exten
         if (ordinates == null) {
             Arrays.fill(this.ordinates, Double.NaN);
         } else {
-            ensureDimensionMatch("ordinates", ordinates.length, this.ordinates.length);
+            ensureDimensionMatches("ordinates", this.ordinates.length, ordinates);
             System.arraycopy(ordinates, 0, this.ordinates, 0, ordinates.length);
         }
     }
@@ -254,7 +256,7 @@ public class GeneralDirectPosition exten
         if (position == null) {
             Arrays.fill(ordinates, Double.NaN);
         } else {
-            ensureDimensionMatch("position", position.getDimension(), ordinates.length);
+            ensureDimensionMatches("position", ordinates.length, position);
             setCoordinateReferenceSystem(position.getCoordinateReferenceSystem());
             for (int i=0; i<ordinates.length; i++) {
                 ordinates[i] = position.getOrdinate(i);

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralEnvelope.java Mon Dec 24 05:58:28 2012
@@ -24,6 +24,9 @@ package org.apache.sis.geometry;
 import java.util.Arrays;
 import java.io.Serializable;
 import java.lang.reflect.Field;
+import org.opengis.referencing.cs.RangeMeaning;
+import org.opengis.referencing.cs.CoordinateSystem;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.geometry.Envelope;
 import org.opengis.geometry.DirectPosition;
@@ -254,7 +257,7 @@ public class GeneralEnvelope extends Arr
     public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs)
             throws MismatchedDimensionException
     {
-        AbstractDirectPosition.ensureDimensionMatch(crs, getDimension());
+        ensureDimensionMatches("crs", getDimension(), crs);
         this.crs = crs;
     }
 
@@ -293,11 +296,11 @@ public class GeneralEnvelope extends Arr
             throw new IllegalArgumentException(Errors.format(
                     Errors.Keys.OddArrayLength_1, ordinates.length));
         }
-        final int dimension  = ordinates.length >>> 1;
-        final int check = this.ordinates.length >>> 1;
-        if (dimension != check) {
+        final int dimension = ordinates.length >>> 1;
+        final int expected  = this.ordinates.length >>> 1;
+        if (dimension != expected) {
             throw new MismatchedDimensionException(Errors.format(
-                    Errors.Keys.MismatchedDimension_3, "ordinates", dimension, check));
+                    Errors.Keys.MismatchedDimension_3, "ordinates", expected, dimension));
         }
         System.arraycopy(ordinates, 0, this.ordinates, 0, ordinates.length);
     }
@@ -314,7 +317,7 @@ public class GeneralEnvelope extends Arr
     public void setEnvelope(final Envelope envelope) throws MismatchedDimensionException {
         ensureNonNull("envelope", envelope);
         final int dimension = ordinates.length >>> 1;
-        AbstractDirectPosition.ensureDimensionMatch("envelope", envelope.getDimension(), dimension);
+        ensureDimensionMatches("envelope", dimension, envelope);
         if (envelope instanceof ArrayEnvelope) {
             System.arraycopy(((ArrayEnvelope) envelope).ordinates, 0, ordinates, 0, ordinates.length);
         } else {
@@ -343,22 +346,6 @@ public class GeneralEnvelope extends Arr
         final int mid = ordinates.length >>> 1;
         Arrays.fill(ordinates, 0,   mid,              Double.NEGATIVE_INFINITY);
         Arrays.fill(ordinates, mid, ordinates.length, Double.POSITIVE_INFINITY);
-        assert isInfinite() : this;
-    }
-
-    /**
-     * Returns {@code true} if at least one ordinate has an
-     * {@linkplain Double#isInfinite infinite} value.
-     *
-     * @return {@code true} if this envelope has infinite value.
-     */
-    public boolean isInfinite() {
-        for (int i=0; i<ordinates.length; i++) {
-            if (Double.isInfinite(ordinates[i])) {
-                return true;
-            }
-        }
-        return false;
     }
 
     /**
@@ -420,7 +407,7 @@ public class GeneralEnvelope extends Arr
     public void add(final DirectPosition position) throws MismatchedDimensionException {
         ensureNonNull("position", position);
         final int dim = ordinates.length >>> 1;
-        AbstractDirectPosition.ensureDimensionMatch("position", position.getDimension(), dim);
+        ensureDimensionMatches("position", dim, position);
         assert equalsIgnoreMetadata(crs, position.getCoordinateReferenceSystem(), true) : position;
         for (int i=0; i<dim; i++) {
             final double value = position.getOrdinate(i);
@@ -490,7 +477,7 @@ public class GeneralEnvelope extends Arr
     public void add(final Envelope envelope) throws MismatchedDimensionException {
         ensureNonNull("envelope", envelope);
         final int dim = ordinates.length >>> 1;
-        AbstractDirectPosition.ensureDimensionMatch("envelope", envelope.getDimension(), dim);
+        ensureDimensionMatches("envelope", dim, envelope);
         assert equalsIgnoreMetadata(crs, envelope.getCoordinateReferenceSystem(), true) : envelope;
         final DirectPosition lower = envelope.getLowerCorner();
         final DirectPosition upper = envelope.getUpperCorner();
@@ -609,7 +596,7 @@ public class GeneralEnvelope extends Arr
     public void intersect(final Envelope envelope) throws MismatchedDimensionException {
         ensureNonNull("envelope", envelope);
         final int dim = ordinates.length >>> 1;
-        AbstractDirectPosition.ensureDimensionMatch("envelope", envelope.getDimension(), dim);
+        ensureDimensionMatches("envelope", dim, envelope);
         assert equalsIgnoreMetadata(crs, envelope.getCoordinateReferenceSystem(), true) : envelope;
         final DirectPosition lower = envelope.getLowerCorner();
         final DirectPosition upper = envelope.getUpperCorner();
@@ -719,6 +706,156 @@ public class GeneralEnvelope extends Arr
     }
 
     /**
+     * Ensures that the envelope is contained in the coordinate system domain.
+     * For each dimension, this method compares the ordinate values against the
+     * limits of the coordinate system axis for that dimension.
+     * If some ordinates are out of range, then there is a choice depending on the
+     * {@linkplain CoordinateSystemAxis#getRangeMeaning() axis range meaning}:
+     *
+     * <ul class="verbose">
+     *   <li>If {@link RangeMeaning#EXACT} (typically <em>latitudes</em> ordinates), then values
+     *       greater than the {@linkplain CoordinateSystemAxis#getMaximumValue() axis maximal value}
+     *       are replaced by the axis maximum, and values smaller than the
+     *       {@linkplain CoordinateSystemAxis#getMinimumValue() axis minimal value}
+     *       are replaced by the axis minimum.</li>
+     *
+     *   <li>If {@link RangeMeaning#WRAPAROUND} (typically <em>longitudes</em> ordinates), then
+     *       a multiple of the axis range (e.g. 360° for longitudes) is added or subtracted.
+     *       Example:
+     *       <ul>
+     *         <li>the [190 … 200]° longitude range is converted to [-170 … -160]°,</li>
+     *         <li>the [170 … 200]° longitude range is converted to [+170 … -160]°.</li>
+     *       </ul>
+     *       See <cite>Spanning the anti-meridian of a Geographic CRS</cite> in the
+     *       class javadoc for more information about the meaning of such range.</li>
+     * </ul>
+     *
+     * {@section Spanning the anti-meridian of a Geographic CRS}
+     * If the envelope is spanning the anti-meridian, then some {@linkplain #getLower(int) lower}
+     * ordinate values may become greater than their {@linkplain #getUpper(int) upper} counterpart
+     * as a result of this method call. If such effect is undesirable, then this method may be
+     * combined with {@link #simplify()} as below:
+     *
+     * {@preformat java
+     *     if (envelope.normalize()) {
+     *         envelope.simplify();
+     *     }
+     * }
+     *
+     * {@section Choosing the range of longitude values}
+     * Geographic CRS typically have longitude values in the [-180 … +180]° range, but the [0 … 360]°
+     * range is also occasionally used. Callers need to ensure that this envelope CRS is associated
+     * to axes having the desired {@linkplain CoordinateSystemAxis#getMinimumValue() minimum} and
+     * {@linkplain CoordinateSystemAxis#getMaximumValue() maximum value}.
+     *
+     * {@section Usage}
+     * This method is sometime useful before to compute the {@linkplain #add(Envelope) union}
+     * or {@linkplain #intersect(Envelope) intersection} of envelopes, in order to ensure that
+     * both envelopes are defined in the same domain. This method may also be invoked before
+     * to project an envelope, since some projections produce {@link Double#NaN} numbers when
+     * given an ordinate value out of bounds.
+     *
+     * @return {@code true} if this envelope has been modified as a result of this method call,
+     *         or {@code false} if no change has been done.
+     */
+    public boolean normalize() {
+        boolean changed = false;
+        if (crs != null) {
+            final int dimension = ordinates.length >>> 1;
+            final CoordinateSystem cs = crs.getCoordinateSystem();
+            for (int i=0; i<dimension; i++) {
+                final int j = i + dimension;
+                final CoordinateSystemAxis axis = cs.getAxis(i);
+                final double  minimum = axis.getMinimumValue();
+                final double  maximum = axis.getMaximumValue();
+                final RangeMeaning rm = axis.getRangeMeaning();
+                if (RangeMeaning.EXACT.equals(rm)) {
+                    if (ordinates[i] < minimum) {ordinates[i] = minimum; changed = true;}
+                    if (ordinates[j] > maximum) {ordinates[j] = maximum; changed = true;}
+                } else if (RangeMeaning.WRAPAROUND.equals(rm)) {
+                    final double csSpan = maximum - minimum;
+                    if (csSpan > 0 && csSpan < Double.POSITIVE_INFINITY) {
+                        double o1 = ordinates[i];
+                        double o2 = ordinates[j];
+                        if (Math.abs(o2-o1) >= csSpan) {
+                            /*
+                             * If the range exceed the CS span, then we have to replace it by the
+                             * full span, otherwise the range computed by the "else" block is too
+                             * small. The full range will typically be [-180 … 180]°.  However we
+                             * make a special case if the two bounds are multiple of the CS span,
+                             * typically [0 … 360]°. In this case the [0 … -0]° range matches the
+                             * original values and is understood by GeneralEnvelope as a range
+                             * spanning all the world.
+                             */
+                            if (o1 != minimum || o2 != maximum) {
+                                if ((o1 % csSpan) == 0 && (o2 % csSpan) == 0) {
+                                    ordinates[i] = +0.0;
+                                    ordinates[j] = -0.0;
+                                } else {
+                                    ordinates[i] = minimum;
+                                    ordinates[j] = maximum;
+                                }
+                                changed = true;
+                            }
+                        } else {
+                            o1 = Math.floor((o1 - minimum) / csSpan) * csSpan;
+                            o2 = Math.floor((o2 - minimum) / csSpan) * csSpan;
+                            if (o1 != 0) {ordinates[i] -= o1; changed = true;}
+                            if (o2 != 0) {ordinates[j] -= o2; changed = true;}
+                        }
+                    }
+                }
+            }
+        }
+        return changed;
+    }
+
+    // Note: As of JDK 1.6.0_31, using {@linkplain #getLower(int)} in the first line crash the
+    // Javadoc tools, maybe because getLower/getUpper are defined in a non-public parent class.
+    /**
+     * Ensures that <var>lower</var> &lt;= <var>upper</var> for every dimensions.
+     * If a {@linkplain #getUpper(int) upper ordinate value} is less than a
+     * {@linkplain #getLower(int) lower ordinate value}, then there is a choice:
+     *
+     * <ul>
+     *   <li>If the axis has {@link RangeMeaning#WRAPAROUND}, then the lower ordinate value is
+     *       set to the {@linkplain CoordinateSystemAxis#getMinimumValue() axis minimum value}
+     *       and the upper ordinate value is set to the
+     *       {@linkplain CoordinateSystemAxis#getMaximumValue() axis maimum value}.</li>
+     *   <li>Otherwise an {@link IllegalStateException} is thrown.</li>
+     * </ul>
+     *
+     * This method is useful when the envelope needs to be used with libraries that do not support
+     * envelopes spanning the anti-meridian.
+     *
+     * @return {@code true} if this envelope has been modified as a result of this method call,
+     *         or {@code false} if no change has been done.
+     * @throws IllegalStateException If a upper ordinate value is less than a lower ordinate
+     *         value on an axis which does not have the {@code WRAPAROUND} range meaning.
+     */
+    public boolean simplify() throws IllegalStateException {
+        boolean changed = false;
+        final int dimension = ordinates.length >>> 1;
+        for (int i=0; i<dimension; i++) {
+            final int j = i+dimension;
+            final double lower = ordinates[i];
+            final double upper = ordinates[j];
+            if (isNegative(upper - lower)) {
+                final CoordinateSystemAxis axis = getAxis(crs, i);
+                if (axis != null && RangeMeaning.WRAPAROUND.equals(axis.getRangeMeaning())) {
+                    ordinates[i] = axis.getMinimumValue();
+                    ordinates[j] = axis.getMaximumValue();
+                    changed = true;
+                } else {
+                    throw new IllegalStateException(Errors.format(Errors.Keys.IllegalOrdinateRange_3,
+                            (axis != null) ? axis.getName() : i, lower, upper));
+                }
+            }
+        }
+        return changed;
+    }
+
+    /**
      * Returns a deep copy of this envelope.
      *
      * @return A clone of this envelope.

Modified: sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ImmutableEnvelope.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ImmutableEnvelope.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ImmutableEnvelope.java (original)
+++ sis/trunk/sis-referencing/src/main/java/org/apache/sis/geometry/ImmutableEnvelope.java Mon Dec 24 05:58:28 2012
@@ -29,6 +29,8 @@ import org.opengis.geometry.MismatchedDi
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.metadata.extent.GeographicBoundingBox;
 
+import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
+
 
 /**
  * Immutable representation of an {@linkplain Envelope envelope}.
@@ -77,7 +79,7 @@ public final class ImmutableEnvelope ext
     {
         super(lowerCorner, upperCorner);
         this.crs = crs;
-        AbstractDirectPosition.ensureDimensionMatch(crs, getDimension());
+        ensureDimensionMatches("crs", getDimension(), crs);
     }
 
     /**
@@ -123,7 +125,7 @@ public final class ImmutableEnvelope ext
     {
         super(envelope);
         this.crs = crs;
-        AbstractDirectPosition.ensureDimensionMatch(crs, getDimension());
+        ensureDimensionMatches("crs", getDimension(), crs);
     }
 
     /**
@@ -150,7 +152,7 @@ public final class ImmutableEnvelope ext
     {
         super(wkt);
         this.crs = crs;
-        AbstractDirectPosition.ensureDimensionMatch(crs, getDimension());
+        ensureDimensionMatches("crs", getDimension(), crs);
     }
 
     /**

Modified: sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java (original)
+++ sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java Mon Dec 24 05:58:28 2012
@@ -19,10 +19,12 @@ package org.apache.sis.geometry;
 import java.util.Arrays;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOn;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.apache.sis.test.Assert.*;
 import static org.opengis.test.Validators.*;
+import static org.apache.sis.geometry.AbstractEnvelopeTest.WGS84;
 
 
 /**
@@ -36,6 +38,19 @@ import static org.opengis.test.Validator
 @DependsOn(AbstractDirectPositionTest.class)
 public final strictfp class GeneralDirectPositionTest extends TestCase {
     /**
+     * Tests the {@link GeneralDirectPosition#normalize()} method.
+     */
+    @Test
+    @Ignore("The tested position needs to be associated to CRS:84")
+    public void testNormalize() {
+        final GeneralDirectPosition position = new GeneralDirectPosition(WGS84);
+        position.setCoordinate(300, -100);
+        assertTrue(position.normalize());
+        assertEquals(-90.0, position.getOrdinate(1), 0.0);
+        assertEquals(-60.0, position.getOrdinate(0), 0.0);
+    }
+
+    /**
      * Tests the {@link GeneralDirectPosition#toString()} method.
      */
     @Test

Modified: sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java (original)
+++ sis/trunk/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralEnvelopeTest.java Mon Dec 24 05:58:28 2012
@@ -356,6 +356,64 @@ public final strictfp class GeneralEnvel
     }
 
     /**
+     * Tests the {@link GeneralEnvelope#normalize()} method.
+     */
+    @Test
+    @Ignore("The tested envelope needs to be associated to CRS:84")
+    public void testNormalize() {
+        GeneralEnvelope e = create(-100, -100, +100, +100);
+        assertTrue(e.normalize());
+        assertEnvelopeEquals(e, -100, -90, +100, +90);
+
+        e = create(185, 10, 190, 20);
+        assertTrue(e.normalize());
+        assertEnvelopeEquals(e, -175, 10, -170, 20);
+
+        e = create(175, 10, 185, 20);
+        assertTrue(e.normalize());
+        assertEnvelopeEquals(e, 175, 10, -175, 20);
+
+        e = create(0, 10, 360, 20);
+        assertTrue(e.normalize());
+        assertEquals("Expect positive zero", Double.doubleToLongBits(+0.0), Double.doubleToLongBits(e.getLower(0)));
+        assertEquals("Expect negative zero", Double.doubleToLongBits(-0.0), Double.doubleToLongBits(e.getUpper(0)));
+    }
+
+    /**
+     * Tests the {@link GeneralEnvelope#normalize()} method
+     * with an envelope having more then 360° of longitude.
+     */
+    @Test
+    @Ignore("The tested envelope needs to be associated to CRS:84")
+    public void testNormalizeWorld() {
+        GeneralEnvelope e = create(-195, -90, +170, +90); // -195° is equivalent to 165°
+        assertTrue(e.normalize());
+        assertEnvelopeEquals(e, -180, -90, +180, +90);
+    }
+
+    /**
+     * Tests the {@link GeneralEnvelope#simplify()}.
+     */
+    @Test
+    @Ignore("The tested envelope needs to be associated to CRS:84")
+    public void testSimplify() {
+        // Normal envelope: no change expected.
+        GeneralEnvelope e = create(-100, -10, +100, +10);
+        assertFalse(e.simplify());
+        assertEnvelopeEquals(e, -100, -10, +100, +10);
+
+        // Anti-meridian spanning: should substitute [-180 … 180]°
+        e = create(30, -10, -60, 10);
+        assertTrue(e.simplify());
+        assertEnvelopeEquals(e, -180, -10, 180, 10);
+
+        // Anti-meridian spanning using positive and negative zero.
+        e = create(0.0, -10, -0.0, 10);
+        assertTrue(e.simplify());
+        assertEnvelopeEquals(e, -180, -10, 180, 10);
+    }
+
+    /**
      * Tests modifying the corner of an envelope.
      */
     @Test

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/io/AppendableWriter.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/io/AppendableWriter.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/io/AppendableWriter.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/io/AppendableWriter.java Mon Dec 24 05:58:28 2012
@@ -55,8 +55,8 @@ final class AppendableWriter extends Wri
      * are often synchronized on themselves.
      */
     private static Object getLock(Appendable out) {
-        while (out instanceof FilteredAppendable) {
-            out = ((FilteredAppendable) out).out;
+        while (out instanceof Appender) {
+            out = ((Appender) out).out;
         }
         // StringWriter performs its synchronizations on its StringBuffer.
         if (out instanceof StringWriter) {
@@ -139,7 +139,7 @@ final class AppendableWriter extends Wri
     }
 
     /**
-     * If {@link #out} implements {@link Flushable}, or is a {@link FilteredAppendable} wrapper
+     * If {@link #out} implements {@link Flushable}, or is a {@link Appender} wrapper
      * around a flushable object, delegates to that object. Otherwise do nothing.
      */
     @Override
@@ -150,7 +150,7 @@ final class AppendableWriter extends Wri
     }
 
     /**
-     * If {@link #out} implements {@link Closeable}, or is a {@link FilteredAppendable} wrapper
+     * If {@link #out} implements {@link Closeable}, or is a {@link Appender} wrapper
      * around a closeable object, delegates to that object. Otherwise just flush (if possible).
      */
     @Override

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/io/CompoundFormat.java Mon Dec 24 05:58:28 2012
@@ -39,9 +39,6 @@ import org.apache.sis.util.ArgumentCheck
 import org.apache.sis.util.collection.BackingStoreException;
 import org.apache.sis.internal.util.LocalizedParseException;
 
-// Related to JDK7
-import org.apache.sis.internal.util.Objects;
-
 
 /**
  * Base class of {@link Format} implementations which delegate part of their work to other
@@ -412,31 +409,11 @@ public abstract class CompoundFormat<T> 
         return clone;
     }
 
-    /**
-     * Compares this format with the given object for equality.
-     *
-     * @param  other The other object to compare with this.
-     * @return {@code true} if the other object is a format of the same class than this
-     *         and having the same configuration.
-     */
-    @Override
-    public boolean equals(final Object other) {
-        if (other == this) {
-            return true;
-        }
-        if (other != null && getClass() == other.getClass()) {
-            final CompoundFormat<?> that = (CompoundFormat<?>) other;
-            return Objects.equals(locale,   that.locale) &&
-                   Objects.equals(timezone, that.timezone);
-        }
-        return false;
-    }
-
-    /**
-     * Returns a hash code value for this format.
+    /*
+     * Do not override equals(Object) and hashCode(). They are unlikely to be needed since we
+     * do not expect CompoundFormats to be used as keys in HashMap, especially since they are
+     * mutable. Furthermore it is difficult to check for equality since the values in the
+     * 'formats' map are created only when needed and we don't know how subclasses will
+     * configure them.
      */
-    @Override
-    public int hashCode() {
-        return Objects.hash(locale, timezone);
-    }
 }

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/io/IO.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/io/IO.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/io/IO.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/io/IO.java Mon Dec 24 05:58:28 2012
@@ -54,10 +54,10 @@ public final class IO extends Static {
      */
     public static void flush(Appendable out) throws IOException {
         while (!(out instanceof Flushable)) {
-            if (!(out instanceof FilteredAppendable)) {
+            if (!(out instanceof Appender)) {
                 return;
             }
-            out = ((FilteredAppendable) out).out;
+            out = ((Appender) out).out;
         }
         ((Flushable) out).flush();
     }
@@ -82,10 +82,10 @@ public final class IO extends Static {
                 ((Flushable) out).flush();
                 isFlushed = true;
             }
-            if (!(out instanceof FilteredAppendable)) {
+            if (!(out instanceof Appender)) {
                 return;
             }
-            out = ((FilteredAppendable) out).out;
+            out = ((Appender) out).out;
         }
         ((Closeable) out).close();
     }
@@ -111,7 +111,7 @@ public final class IO extends Static {
      *
      * <p>It may be necessary to invoke {@link #flush(Appendable)} before this method in order
      * to get proper content. In particular, this is necessary if the chain of {@code Appendable}s
-     * contains {@link TableFormatter} or {@link LineFormatter} instances.</p>
+     * contains {@link TableAppender} or {@link LineAppender} instances.</p>
      *
      * @param  out The stream or buffer from which to get the content, or {@code null}.
      * @return The content of the given stream of buffer, or {@code null} if unavailable.
@@ -120,7 +120,7 @@ public final class IO extends Static {
      */
     public static CharSequence content(Appendable out) {
         while (!(out instanceof CharSequence)) {
-            if (!(out instanceof FilteredAppendable)) {
+            if (!(out instanceof Appender)) {
                 if (out instanceof StringWriter) {
                     return ((StringWriter) out).getBuffer();
                 }
@@ -129,7 +129,7 @@ public final class IO extends Static {
                 }
                 return null;
             }
-            out = ((FilteredAppendable) out).out;
+            out = ((Appender) out).out;
         }
         return (CharSequence) out;
     }

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/io/package-info.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/io/package-info.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/io/package-info.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/io/package-info.java Mon Dec 24 05:58:28 2012
@@ -19,8 +19,8 @@
  * Extensions to standard Java I/O ({@link java.io.Reader}, {@link java.io.Writer},
  * {@link java.lang.Appendable}).
  * Many classes defined in this package are filters applying on-the-fly formatting while writing
- * text to the output device. For example {@link org.apache.sis.io.LineFormatter} can wrap lines
- * to some maximal line length (e.g. 80 characters), and {@link org.apache.sis.io.TableFormatter}
+ * text to the output device. For example {@link org.apache.sis.io.LineAppender} can wrap lines
+ * to some maximal line length (e.g. 80 characters), and {@link org.apache.sis.io.TableAppender}
  * replaces all occurrence of {@code '\t'} by the amount of spaces needed for producing a tabular
  * output.
  *

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/math/MathFunctions.java Mon Dec 24 05:58:28 2012
@@ -692,6 +692,8 @@ testNextNumber:         while (true) { /
      * @param  number The number for which to find the next prime.
      * @return The given number if it is a prime number, or the next prime number otherwise.
      * @throws IllegalArgumentException If the given value is outside the supported range.
+     *
+     * @see java.math.BigInteger#isProbablePrime(int)
      */
     public static int nextPrimeNumber(final int number) throws IllegalArgumentException {
         ArgumentChecks.ensureBetween("number", 2, HIGHEST_SUPPORTED_PRIME_NUMBER, number);

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/math/Statistics.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/math/Statistics.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/math/Statistics.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/math/Statistics.java Mon Dec 24 05:58:28 2012
@@ -17,13 +17,18 @@
 package org.apache.sis.math;
 
 import java.io.Serializable;
+import org.opengis.util.InternationalString;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.iso.Types;
 
 import static java.lang.Math.*;
 import static java.lang.Double.NaN;
 import static java.lang.Double.isNaN;
 import static java.lang.Double.doubleToLongBits;
 
+// Related to JDK7
+import org.apache.sis.internal.util.Objects;
+
 
 /**
  * Holds some statistics derived from a series of sample values.
@@ -57,7 +62,7 @@ import static java.lang.Double.doubleToL
  * is defined. A simple usage is:
  *
  * {@preformat java
- *     Statistics stats = new Statistics();
+ *     Statistics stats = new Statistics("y");
  *     for (int i=0; i<numberOfValues; i++) {
  *         stats.add(f(i));
  *     }
@@ -70,7 +75,7 @@ import static java.lang.Double.doubleToL
  * {@preformat java
  *     final double x₀ = ...; // Put here the x value at i=0
  *     final double Δx = ...; // Put here the interval between x values
- *     Statistics stats = Statistics.forSeries(2);
+ *     Statistics stats = Statistics.forSeries("y", "∂y/∂x", "∂²y/∂x²");
  *     for (int i=0; i<numberOfValues; i++) {
  *         stats.add(f(x₀ + i*Δx));
  *     }
@@ -90,6 +95,15 @@ public class Statistics implements Clone
     private static final long serialVersionUID = -22884277805533726L;
 
     /**
+     * The name of the phenomenon for which this object is collecting statistics.
+     * If non-null, then this name will be shown as column header in the table formatted
+     * by {@link StatisticsFormat}.
+     *
+     * @see #name()
+     */
+    private final InternationalString name;
+
+    /**
      * The minimal value given to the {@link #add(double)} method.
      */
     private double minimum = NaN;
@@ -138,10 +152,15 @@ public class Statistics implements Clone
      * and all other statistical values are initialized to {@link Double#NaN}.
      *
      * <p>Instances created by this constructor do not compute differences between sample values.
-     * If differences or discrete derivatives are wanted, use the {@link #forSeries(int)} method
-     * instead.</p>
+     * If differences or discrete derivatives are wanted, use the {@link #forSeries forSeries(…)}
+     * method instead.</p>
+     *
+     * @param name The phenomenon for which this object is collecting statistics, or {@code null}
+     *             if none. If non-null, then this name will be shown as column header in the table
+     *             formatted by {@link StatisticsFormat}.
      */
-    public Statistics() {
+    public Statistics(final CharSequence name) {
+        this.name = Types.toInternationalString(name);
     }
 
     /**
@@ -159,34 +178,53 @@ public class Statistics implements Clone
      *     statistics.differences().scale(1/Δx);
      * }
      *
-     * The {@code order} argument can be interpreted as below:
+     * The maximal "derivative" order is determined by the length of the {@code differenceNames} array:
      *
      * <ul>
-     *   <li>0 if no differences are needed;</li>
+     *   <li>0 if no differences are needed (equivalent to direct instantiation of a new
+     *       {@code Statistics} object).</li>
      *   <li>1 for computing the statistics on the differences between consecutive samples
      *       (proportional to the statistics on the first discrete derivatives) in addition
-     *       to the sample statistics;</li>
+     *       to the sample statistics.</li>
      *   <li>2 for computing also the statistics on the differences between consecutive differences
      *       (proportional to the statistics on the second discrete derivatives) in addition to the
-     *       above;</li>
+     *       above.</li>
      *   <li><i>etc</i>.</li>
      * </ul>
      *
-     * @param  order The discrete derivative order, as a value equals or greater than 0.
+     *
+     *
+     * @param  name  The phenomenon for which this object is collecting statistics, or {@code null}
+     *               if none. If non-null, then this name will be shown as column header in the table
+     *               formatted by {@link StatisticsFormat}.
+     * @param  differenceNames The names of the statistics on differences.
+     *         The given array can not be null, but can contain null elements.
      * @return The newly constructed, initially empty, set of statistics.
      *
      * @see #differences()
      */
-    public static Statistics forSeries(int order) {
-        ArgumentChecks.ensurePositive("order", order);
-        Statistics stats = new Statistics();
-        while (--order >= 0) {
-            stats = new WithDelta(stats);
+    public static Statistics forSeries(final CharSequence name, final CharSequence... differenceNames) {
+        ArgumentChecks.ensureNonNull("differenceNames", differenceNames);
+        Statistics stats = null;
+        for (int i=differenceNames.length; --i >= -1;) {
+            final CharSequence n = (i >= 0) ? differenceNames[i] : name;
+            stats = (stats == null) ? new Statistics(n) : new WithDelta(n, stats);
         }
         return stats;
     }
 
     /**
+     * Returns the name of the phenomenon for which this object is collecting statistics.
+     * If non-null, then this name will be shown as column header in the table formatted
+     * by {@link StatisticsFormat}.
+     *
+     * @return The phenomenon for which this object is collecting statistics, or {@code null} if none.
+     */
+    public InternationalString name() {
+        return name;
+    }
+
+    /**
      * Resets this object state as if it was just created.
      * The {@linkplain #count()} and the {@link #sum()} are set to zero
      * and all other statistical values are set to {@link Double#NaN}.
@@ -286,7 +324,8 @@ public class Statistics implements Clone
      * {@code add(…)} had been first multiplied by the given factor.
      *
      * <p>This method is useful for computing discrete derivatives from the differences between
-     * sample values. See {@link #differences()} or {@link #forSeries(int)} for more information.</p>
+     * sample values. See {@link #differences()} or {@link #forSeries forSeries(…)} for more
+     * information.</p>
      *
      * @param factor The factor by which to multiply the statistics.
      */
@@ -431,15 +470,15 @@ public class Statistics implements Clone
      *     // Do not invoke scale(1/Δx) again.
      * }
      *
-     * This method is supported only if this {@code Statistics} instance has been created by a
-     * call to the {@link #forSeries(int)} method with an {@code order} argument greater than zero.
-     * More generally, calls to this method can be chained up to {@code order} time for
+     * This method returns a non-null value only if this {@code Statistics} instance has been created by a
+     * call to the {@link #forSeries forSeries(…)} method with a non-empty {@code differenceNames} array.
+     * More generally, calls to this method can be chained up to {@code differenceNames.length} times for
      * fetching second or higher order derivatives, as in the above example.
      *
      * @return The statistics on the differences between consecutive sample values,
      *         or {@code null} if not calculated by this object.
      *
-     * @see #forSeries(int)
+     * @see #forSeries(CharSequence, CharSequence[])
      * @see #scale(double)
      */
     public Statistics differences() {
@@ -460,6 +499,8 @@ public class Statistics implements Clone
      * }
      *
      * @return A string representation of this statistics object.
+     *
+     * @see StatisticsFormat
      */
     @Override
     public String toString() {
@@ -503,7 +544,8 @@ public class Statistics implements Clone
     public boolean equals(final Object object) {
         if (object != null && getClass() == object.getClass()) {
             final Statistics cast = (Statistics) object;
-            return count == cast.count && countNaN == cast.countNaN
+            return Objects.equals(name, cast.name)
+                    && count == cast.count && countNaN == cast.countNaN
                     && doubleToLongBits(minimum)   == doubleToLongBits(cast.minimum)
                     && doubleToLongBits(maximum)   == doubleToLongBits(cast.maximum)
                     && doubleToLongBits(sum)       == doubleToLongBits(cast.sum)
@@ -565,9 +607,11 @@ public class Statistics implements Clone
          * consecutive sample values. Other kinds of {@link Statistics} object could be
          * chained as well.
          *
+         * @param name  The phenomenon for which this object is collecting statistics, or {@code null}.
          * @param delta The object where to stores delta statistics.
          */
-        public WithDelta(final Statistics delta) {
+        WithDelta(final CharSequence name, final Statistics delta) {
+            super(name);
             this.delta = delta;
             delta.decrementCountNaN(); // Do not count the first NaN, which will always be the first value.
         }

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/math/StatisticsFormat.java Mon Dec 24 05:58:28 2012
@@ -16,8 +16,8 @@
  */
 package org.apache.sis.math;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.ArrayList;
 import java.io.IOException;
 import java.util.Locale;
 import java.util.TimeZone;
@@ -26,29 +26,37 @@ import java.text.NumberFormat;
 import java.text.DecimalFormat;
 import java.text.ParsePosition;
 import java.text.ParseException;
-
-import org.apache.sis.io.TableFormatter;
-import org.apache.sis.io.CompoundFormat;
+import org.opengis.util.InternationalString;
+import org.apache.sis.io.TableAppender;
+import org.apache.sis.io.TabularFormat;
 import org.apache.sis.util.resources.Vocabulary;
 
 import static java.lang.Math.*;
-
-// Related to JDK7
-import org.apache.sis.internal.util.JDK7;
+import java.text.FieldPosition;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.collection.BackingStoreException;
 
 
 /**
  * Formats a {@link Statistics} object.
- * This is a package-private class for now - if we want to make it public, we may need to make it
- * a full-featured {@link java.text.Format} implementation.
+ * By default, newly created {@code StatisticsFormat} instances will format statistical values
+ * in a tabular format using spaces as the column separator. This default configuration matches
+ * the {@link Statistics#toString()} format.
+ *
+ * {@section Limitations}
+ * The current implementation can only format statistics - parsing is not yet implemented.
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
  * @since   0.3 (derived from geotk-1.0)
  * @version 0.3
  * @module
  */
-@SuppressWarnings("serial")
-final class StatisticsFormat extends CompoundFormat<Statistics> {
+public class StatisticsFormat extends TabularFormat<Statistics> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7393669354879347985L;
+
     /**
      * Number of additional digits, to be added to the number of digits computed from the
      * range and the number of sample values. This is an arbitrary parameter.
@@ -56,58 +64,59 @@ final class StatisticsFormat extends Com
     private static final int ADDITIONAL_DIGITS = 2;
 
     /**
-     * The locale for row labels. This is usually the same than the format locale,
-     * but not necessarily.
+     * The locale for row and column headers.
+     * This is usually the same than the format locale, but not necessarily.
+     */
+    private final Locale headerLocale;
+
+    /**
+     * The "width" of the border to drawn around the table, in number of lines.
+     *
+     * @see #getBorderWidth()
+     * @see #setBorderWidth(int)
      */
-    private final Locale labelLocale;
+    private byte borderWidth;
 
     /**
      * {@code true} if the sample values given to {@code Statistics.add(…)} methods were the
      * totality of the population under study, or {@code false} if they were only a sampling.
      *
+     * @see #isForAllPopulation()
+     * @see #setForAllPopulation(boolean)
      * @see Statistics#standardDeviation(boolean)
      */
     private boolean allPopulation;
 
     /**
-     * The column separator, typically as a semicolon or tabulation character.
-     * If 0, then the values will be written in a tabular format using {@link TableFormatter}.
-     */
-    private char columnSeparator;
-
-    /**
      * Returns an instance for the current system default locale.
      *
      * @return A statistics format instance for the current default locale.
      */
     public static StatisticsFormat getInstance() {
-        return new StatisticsFormat(
-                Locale.getDefault(), null);
+        return getInstance(Locale.getDefault());
     }
 
     /**
-     * Constructs a new format for the given locales.
-     * The timezone is used only if the values added to the {@link Statistics} are dates.
+     * Returns an instance for the given locale.
      *
-     * @param locale      The locale, or {@code null} for unlocalized format.
-     * @param labelLocale The locale for row labels. Usually, but not necessarily, same as {@code locale}.
-     * @param timezone    The timezone, or {@code null} for UTC.
+     * @param  locale The locale for which to get a {@code StatisticsFormat} instance.
+     * @return A statistics format instance for the given locale.
      */
-    private StatisticsFormat(final Locale locale, final Locale labelLocale, final TimeZone timezone) {
-        super(locale, timezone);
-        this.labelLocale = labelLocale;
+    public static StatisticsFormat getInstance(final Locale locale) {
+        return new StatisticsFormat(locale, locale, null);
     }
 
     /**
-     * Constructs a new format for the given locale.
+     * Constructs a new format for the given numeric and header locales.
      * The timezone is used only if the values added to the {@link Statistics} are dates.
      *
-     * @param locale   The locale, or {@code null} for unlocalized format.
-     * @param timezone The timezone, or {@code null} for UTC.
+     * @param locale       The locale for numeric values, or {@code null} for unlocalized format.
+     * @param headerLocale The locale for row and column headers. Usually same as {@code locale}.
+     * @param timezone     The timezone, or {@code null} for UTC.
      */
-    public StatisticsFormat(final Locale locale, final TimeZone timezone) {
+    public StatisticsFormat(final Locale locale, final Locale headerLocale, final TimeZone timezone) {
         super(locale, timezone);
-        labelLocale = locale;
+        this.headerLocale = headerLocale;
     }
 
     /**
@@ -119,6 +128,60 @@ final class StatisticsFormat extends Com
     }
 
     /**
+     * Returns {@code true} if this formatter shall consider that the statistics where computed
+     * using the totality of the populations under study. This information impacts the standard
+     * deviation values to be formatted.
+     *
+     * @return {@code true} if the statistics to format where computed using the totality of
+     *         the populations under study.
+     *
+     * @see Statistics#standardDeviation(boolean)
+     */
+    public boolean isForAllPopulation() {
+        return allPopulation;
+    }
+
+    /**
+     * Sets whether this formatter shall consider that the statistics where computed using
+     * the totality of the populations under study. The default value is {@code false}.
+     *
+     * @param allPopulation {@code true} if the statistics to format where computed
+     *        using the totality of the populations under study.
+     *
+     * @see Statistics#standardDeviation(boolean)
+     */
+    public void setForAllPopulation(final boolean allPopulation) {
+        this.allPopulation = allPopulation;
+    }
+
+    /**
+     * Returns the "width" of the border to drawn around the table, in number of lines.
+     * The default width is 0, which stands for no border.
+     *
+     * @return The border "width" in number of lines.
+     */
+    public int getBorderWidth() {
+        return borderWidth;
+    }
+
+    /**
+     * Sets the "width" of the border to drawn around the table, in number of lines.
+     * The value can be any of the following:
+     *
+     * <ul>
+     *  <li>0 (the default) for no border</li>
+     *  <li>1 for single line ({@code │},{@code ─})</li>
+     *  <li>2 for double lines ({@code ║},{@code ═})</li>
+     * </ul>
+     *
+     * @param borderWidth The border width, in number of lines.
+     */
+    public void setBorderWidth(final int borderWidth) {
+        ArgumentChecks.ensureBetween("borderWidth", 0, 2, borderWidth);
+        this.borderWidth = (byte) borderWidth;
+    }
+
+    /**
      * Not yet implemented.
      */
     @Override
@@ -127,23 +190,40 @@ final class StatisticsFormat extends Com
     }
 
     /**
-     * The resource keys of the rows to formats. Array index must be consistent with
-     * the switch statements inside the {@link #format(Statistics)} method.
+     * Formats the given statistics. This method will delegates to one of the following methods,
+     * depending on the type of the given object:
+     *
+     * <ul>
+     *   <li>{@link #format(Statistics, Appendable)}</li>
+     *   <li>{@link #format(Statistics[], Appendable)}</li>
+     * </ul>
+     *
+     * @param  object      The object to format.
+     * @param  toAppendTo  Where to format the object.
+     * @param  pos         Ignored in current implementation.
+     * @return             The given buffer, returned for convenience.
      */
-    private static final int[] KEYS = {
-        Vocabulary.Keys.NumberOfValues,
-        Vocabulary.Keys.NumberOfNaN,
-        Vocabulary.Keys.MinimumValue,
-        Vocabulary.Keys.MaximumValue,
-        Vocabulary.Keys.MeanValue,
-        Vocabulary.Keys.RootMeanSquare,
-        Vocabulary.Keys.StandardDeviation
-    };
+    @Override
+    public StringBuffer format(final Object object, final StringBuffer toAppendTo, final FieldPosition pos) {
+        if (object instanceof Statistics[]) try {
+            format((Statistics[]) object, toAppendTo);
+            return toAppendTo;
+        } catch (IOException e) {
+            // Same exception handling than in the super-class.
+            throw new BackingStoreException(e);
+        } else {
+            return super.format(object, toAppendTo, pos);
+        }
+    }
 
     /**
      * Formats a localized string representation of the given statistics.
      * If statistics on {@linkplain Statistics#differences() differences}
      * are associated to the given object, they will be formatted too.
+     *
+     * @param  stats       The statistics to format.
+     * @param  toAppendTo  Where to format the statistics.
+     * @throws IOException If an error occurred while writing in the given appender.
      */
     @Override
     public void format(Statistics stats, final Appendable toAppendTo) throws IOException {
@@ -159,31 +239,56 @@ final class StatisticsFormat extends Com
      * Formats the given statistics in a tabular format. This method does not check
      * for the statistics on {@linkplain Statistics#differences() differences} - if
      * such statistics are wanted, they must be included in the given array.
+     *
+     * @param  stats       The statistics to format.
+     * @param  toAppendTo  Where to format the statistics.
+     * @throws IOException If an error occurred while writing in the given appender.
      */
-    private void format(final Statistics[] stats, Appendable toAppendTo) throws IOException {
-        final String lineSeparator = JDK7.lineSeparator();
+    public void format(final Statistics[] stats, final Appendable toAppendTo) throws IOException {
         /*
-         * Verify if we can omit the count of NaN values.
+         * Inspect the given statistics in order to determine if we shall omit the headers,
+         * and if we shall omit the count of NaN values.
          */
+        final String[] headers = new String[stats.length];
+        boolean showHeaders  = false;
         boolean showNaNCount = false;
-        for (final Statistics s : stats) {
-            if (s.countNaN() != 0) {
-                showNaNCount = true;
-                break;
+        for (int i=0; i<stats.length; i++) {
+            final Statistics s = stats[i];
+            showNaNCount |= (s.countNaN() != 0);
+            final InternationalString header = s.name();
+            if (header != null) {
+                headers[i] = header.toString(headerLocale);
+                showHeaders |= (headers[i] != null);
             }
         }
+        char horizontalLine = 0;
+        String separator = columnSeparator;
+        switch (borderWidth) {
+            case 1: horizontalLine = '─'; separator += "│ "; break;
+            case 2: horizontalLine = '═'; separator += "║ "; break;
+        }
+        final TableAppender table = new TableAppender(toAppendTo, separator);
+        final Vocabulary resources = Vocabulary.getResources(headerLocale);
         /*
-         * This formatter can optionally use a column separator, typically a semi-colon.
-         * If no column separator was specified (which is the usual case), then we will
-         * format the values in a tabular format.
+         * If there is a header for at least one statistics, write the full headers row.
          */
-        TableFormatter table = null;
-        char separator = columnSeparator;
-        if (separator == 0) {
-            toAppendTo = table = new TableFormatter(toAppendTo, " ");
-            separator = '\t'; // Will be handled especially by TableFormatter.
+        if (horizontalLine != 0) {
+            table.nextLine(horizontalLine);
+        }
+        if (showHeaders) {
+            table.nextColumn();
+            for (final String header : headers) {
+                if (header != null) {
+                    table.append(header);
+                    table.setCellAlignment(TableAppender.ALIGN_CENTER);
+                }
+                table.nextColumn();
+            }
+            table.append(lineSeparator);
+            if (horizontalLine != 0) {
+                table.nextLine(horizontalLine);
+            }
         }
-        final Vocabulary resources = Vocabulary.getResources(labelLocale);
         /*
          * Initialize the NumberFormat for formatting integers without scientific notation.
          * This is necessary since the format may have been modified by a previous execution
@@ -211,10 +316,8 @@ final class StatisticsFormat extends Com
                 case 2: needsConfigure = true; break;
                 case 3: needsConfigure = (stats[0].differences() != null); break;
             }
-            if (table != null) {
-                table.setCellAlignment(TableFormatter.ALIGN_LEFT);
-            }
-            toAppendTo.append(resources.getString(KEYS[i])).append(':');
+            table.setCellAlignment(TableAppender.ALIGN_LEFT);
+            table.append(resources.getString(KEYS[i])).append(':');
             for (final Statistics s : stats) {
                 final Number value;
                 switch (i) {
@@ -230,22 +333,38 @@ final class StatisticsFormat extends Com
                 if (needsConfigure) {
                     configure(format, s);
                 }
-                toAppendTo.append(separator).append(format.format(value));
-                if (table != null) {
-                    table.setCellAlignment(TableFormatter.ALIGN_RIGHT);
-                }
+                table.append(beforeFill);
+                table.nextColumn(fillCharacter);
+                table.append(format.format(value));
+                table.setCellAlignment(TableAppender.ALIGN_RIGHT);
             }
-            toAppendTo.append(lineSeparator);
+            table.append(lineSeparator);
+        }
+        if (horizontalLine != 0) {
+            table.nextLine(horizontalLine);
         }
         /*
-         * TableFormatter needs to be explicitly flushed in order to format the values.
+         * TableAppender needs to be explicitly flushed in order to format the values.
          */
-        if (table != null) {
-            table.flush();
-        }
+        table.flush();
     }
 
     /**
+     * The resource keys of the rows to formats. Array index must be consistent with the
+     * switch statements inside the {@link #format(Statistics[], Appendable)} method
+     * (we define this static field close to the format methods for this purpose).
+     */
+    private static final int[] KEYS = {
+        Vocabulary.Keys.NumberOfValues,
+        Vocabulary.Keys.NumberOfNaN,
+        Vocabulary.Keys.MinimumValue,
+        Vocabulary.Keys.MaximumValue,
+        Vocabulary.Keys.MeanValue,
+        Vocabulary.Keys.RootMeanSquare,
+        Vocabulary.Keys.StandardDeviation
+    };
+
+    /**
      * Configures the given formatter for writing a set of data described by the given statistics.
      * This method configures the formatter using heuristic rules based on the range of values and
      * their standard deviation. It can be used for reasonable default formatting when the user

Modified: sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java
URL: http://svn.apache.org/viewvc/sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java?rev=1425592&r1=1425591&r2=1425592&view=diff
==============================================================================
--- sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java (original)
+++ sis/trunk/sis-utility/src/main/java/org/apache/sis/measure/AngleFormat.java Mon Dec 24 05:58:28 2012
@@ -460,7 +460,7 @@ scan:   for (int i=0; i<length;) {
                 }
                 if (parseFinished || (field != expectedField && field != FRACTION_FIELD)) {
                     throw new IllegalArgumentException(Errors.format(
-                            Errors.Keys.IllegalFormatPatternForClass_2, pattern, Angle.class));
+                            Errors.Keys.IllegalFormatPatternForClass_2, Angle.class, pattern));
                 }
                 if (isIntegerField) {
                     /*
@@ -493,7 +493,7 @@ scan:   for (int i=0; i<length;) {
                         final int b = pattern.codePointAt(endPreviousField);
                         if (b != decimalSeparator || endPreviousField + Character.charCount(b) != i) {
                             throw new IllegalArgumentException(Errors.format(
-                                    Errors.Keys.IllegalFormatPatternForClass_2, pattern, Angle.class));
+                                    Errors.Keys.IllegalFormatPatternForClass_2, Angle.class, pattern));
                         }
                     }
                     int width = 1;
@@ -531,7 +531,7 @@ scan:   for (int i=0; i<length;) {
                 default: {
                     // Happen if no symbol has been recognized in the pattern.
                     throw new IllegalArgumentException(Errors.format(
-                            Errors.Keys.IllegalFormatPatternForClass_2, pattern, Angle.class));
+                            Errors.Keys.IllegalFormatPatternForClass_2, Angle.class, pattern));
                 }
             }
         }
@@ -981,7 +981,7 @@ scan:   for (int i=0; i<length;) {
         }
         ArgumentChecks.ensureNonNull("value", value);
         throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentClass_3,
-                "value", value.getClass(), Angle.class));
+                "value", Angle.class, value.getClass()));
     }
 
     /**
@@ -1102,11 +1102,10 @@ scan:   for (int i=0; i<length;) {
                 default: throw new AssertionError(field);
             }
             if (toSkip != null) {
-                final int toSkipLength = toSkip.length();
                 int c;
                 do {
-                    if (source.regionMatches(index, toSkip, 0, toSkipLength)) {
-                        pos.setIndex(index + toSkipLength);
+                    if (source.startsWith(toSkip, index)) {
+                        pos.setIndex(index + toSkip.length());
                         return field;
                     }
                     if (index >= length) break;



Mime
View raw message