sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1825822 - in /sis/branches/JDK8/core: sis-metadata/src/main/java/org/apache/sis/internal/metadata/ sis-metadata/src/main/java/org/apache/sis/io/wkt/ sis-referencing/src/main/java/org/apache/sis/geometry/ sis-referencing/src/main/java/org/a...
Date Sun, 04 Mar 2018 16:12:09 GMT
Author: desruisseaux
Date: Sun Mar  4 16:12:09 2018
New Revision: 1825822

URL: http://svn.apache.org/viewvc?rev=1825822&view=rev
Log:
Add a Formatter.append(double[][]) method for geometry coordinates, together with calculation of a default number of fraction digits and support for number alignment (for more readable WKT). This can be used as a basis for more extensive geometry WKT support in the future.

Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Symbols.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/package-info.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractDirectPositionTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WKTUtilitiesTest.java
    sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -79,7 +79,8 @@ public class ReferencingServices extends
 
     /**
      * The GRS80 {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius() authalic radius},
-     * which is {@value} metres.
+     * which is {@value} metres. This is close to the WGS84 authalic radius, which is about 6371007.180918474 when
+     * computed with {@code double} precision.
      */
     public static final double AUTHALIC_RADIUS = 6371007;
 

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/WKTKeywords.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -35,7 +35,7 @@ import org.apache.sis.util.Static;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 0.7
+ * @version 1.0
  *
  * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
  * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>
@@ -214,4 +214,10 @@ public final class WKTKeywords extends S
             spherical   = "spherical",
             temporal    = "temporal",
             vertical    = "vertical";
+
+    /**
+     * Geometries.
+     */
+    public static final String
+            Point       = "Point";
 }

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -36,6 +36,7 @@ import javax.measure.Unit;
 import javax.measure.Quantity;
 
 import org.opengis.util.InternationalString;
+import org.opengis.util.ControlledVocabulary;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.extent.Extent;
@@ -53,7 +54,6 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.CoordinateOperation;
 import org.opengis.referencing.operation.ConcatenatedOperation;
 import org.opengis.referencing.operation.MathTransform;
-import org.opengis.util.ControlledVocabulary;
 
 import org.apache.sis.measure.Units;
 import org.apache.sis.math.DecimalFunctions;
@@ -68,7 +68,9 @@ import org.apache.sis.util.CharSequences
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.collection.IntegerList;
 import org.apache.sis.internal.util.X364;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.internal.util.Citations;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.StandardDateFormat;
@@ -97,7 +99,7 @@ import org.apache.sis.metadata.iso.exten
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  *
  * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
  * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>
@@ -146,9 +148,9 @@ public class Formatter implements Locali
 
     /**
      * The value of {@link Symbols#getSeparator()} without trailing spaces, followed by the system line separator.
-     * Computed by {@link Symbols#lineSeparator()} and stored for reuse.
+     * Computed by {@link Symbols#separatorNewLine()} and stored for reuse.
      */
-    private final String lineSeparator;
+    private final String separatorNewLine;
 
     /**
      * The colors to use for this formatter, or {@code null} for no syntax coloring.
@@ -287,6 +289,14 @@ public class Formatter implements Locali
     private int margin;
 
     /**
+     * Indices where to insert additional margin, or {@code null} if none. The margin to insert will be
+     * the the width of the keyword (e.g. {@code "BOX"}), which is usually unknown to {@code Formatter}
+     * until {@link FormattableObject} finished to write the element. This field is usually {@code null},
+     * unless formatting geometries.
+     */
+    private IntegerList keywordSpaceAt;
+
+    /**
      * {@code true} if a new line were requested during the execution of {@link #append(FormattableObject)}.
      * This is used to determine if the next {@code UNIT} and {@code ID} elements shall appear on a new line.
      */
@@ -337,16 +347,16 @@ public class Formatter implements Locali
         ArgumentChecks.ensureNonNull("convention",  convention);
         ArgumentChecks.ensureNonNull("symbols",     symbols);
         ArgumentChecks.ensureBetween("indentation", WKTFormat.SINGLE_LINE, Byte.MAX_VALUE, indentation);
-        this.locale        = Locale.getDefault(Locale.Category.DISPLAY);
-        this.convention    = convention;
-        this.authority     = convention.getNameAuthority();
-        this.symbols       = symbols.immutable();
-        this.lineSeparator = this.symbols.lineSeparator();
-        this.indentation   = (byte) indentation;
-        this.numberFormat  = symbols.createNumberFormat();
-        this.dateFormat    = new StandardDateFormat(symbols.getLocale());
-        this.unitFormat    = new UnitFormat(symbols.getLocale());
-        this.buffer        = new StringBuffer();
+        this.locale           = Locale.getDefault(Locale.Category.DISPLAY);
+        this.convention       = convention;
+        this.authority        = convention.getNameAuthority();
+        this.symbols          = symbols.immutable();
+        this.separatorNewLine = this.symbols.separatorNewLine();
+        this.indentation      = (byte) indentation;
+        this.numberFormat     = symbols.createNumberFormat();
+        this.dateFormat       = new StandardDateFormat(symbols.getLocale());
+        this.unitFormat       = new UnitFormat(symbols.getLocale());
+        this.buffer           = new StringBuffer();
         unitFormat.setStyle(UnitFormat.Style.NAME);
         if (convention.usesCommonUnits) {
             unitFormat.setLocale(Locale.US);
@@ -360,15 +370,15 @@ public class Formatter implements Locali
     Formatter(final Locale locale, final Symbols symbols, final NumberFormat numberFormat,
             final DateFormat dateFormat, final UnitFormat unitFormat)
     {
-        this.locale        = locale;
-        this.convention    = Convention.DEFAULT;
-        this.authority     = Convention.DEFAULT.getNameAuthority();
-        this.symbols       = symbols;
-        this.lineSeparator = this.symbols.lineSeparator();
-        this.indentation   = Constants.DEFAULT_INDENTATION;
-        this.numberFormat  = numberFormat;                      // No clone needed.
-        this.dateFormat    = dateFormat;
-        this.unitFormat    = unitFormat;
+        this.locale           = locale;
+        this.convention       = Convention.DEFAULT;
+        this.authority        = Convention.DEFAULT.getNameAuthority();
+        this.symbols          = symbols;
+        this.separatorNewLine = symbols.separatorNewLine();
+        this.indentation      = Constants.DEFAULT_INDENTATION;
+        this.numberFormat     = numberFormat;                      // No clone needed.
+        this.dateFormat       = dateFormat;
+        this.unitFormat       = unitFormat;
         // Do not set the buffer. It will be set by WKTFormat.format(…).
     }
 
@@ -557,7 +567,7 @@ public class Formatter implements Locali
     private void appendSeparator() {
         if (buffer.length() != elementStart) {
             if (requestNewLine) {
-                buffer.append(lineSeparator).append(CharSequences.spaces(margin));
+                buffer.append(separatorNewLine).append(CharSequences.spaces(margin));
             } else {
                 buffer.append(symbols.getSeparator());
             }
@@ -638,7 +648,7 @@ public class Formatter implements Locali
             }
         }
         enclosingElements.add(object);
-        if (hasContextualUnit < 0) { // Test if leftmost bit is set to 1.
+        if (hasContextualUnit < 0) {                            // Test if leftmost bit is set to 1.
             throw new IllegalStateException(Errors.getResources(locale).getString(Errors.Keys.TreeDepthExceedsMaximum));
         }
         hasContextualUnit <<= 1;
@@ -682,6 +692,24 @@ public class Formatter implements Locali
         highlightError = false;
         buffer.insert(base, keyword);
         /*
+         * When formatting geometry coordinates, we may need to shift all numbers by the width
+         * of the keyword inserted above in order to keep numbers properly aligned. Exemple:
+         *
+         *     BOX[ 4.000 -10.000
+         *         50.000   2.000]
+         */
+        if (keywordSpaceAt != null) {
+            final int length = keyword.length();
+            final CharSequence additionalMargin = CharSequences.spaces(keyword.codePointCount(0, length));
+            final int n = keywordSpaceAt.size();
+            for (int i=0; i<n;) {
+                int p = keywordSpaceAt.getInt(i);
+                p += (++i * length);                    // Take in account spaces added previously.
+                buffer.insert(p, additionalMargin);
+            }
+            keywordSpaceAt.clear();
+        }
+        /*
          * Format the SCOPE["…"], AREA["…"] and other elements. Some of those information
          * are available only for Datum, CoordinateOperation and ReferenceSystem objects.
          */
@@ -692,6 +720,9 @@ public class Formatter implements Locali
             appendComplement(info, (stackDepth >= 1) ? enclosingElements.get(stackDepth - 1) : null,
                                    (stackDepth >= 2) ? enclosingElements.get(stackDepth - 2) : null);
         }
+        /*
+         * Close the bracket, then update the queue of enclosed elements by removing this element.
+         */
         buffer.appendCodePoint(symbols.getClosingBracket(0));
         indent(-1);
         enclosingElements.remove(stackDepth);
@@ -1170,7 +1201,7 @@ public class Formatter implements Locali
              * maximum of 8 fraction digits, which is more than enough.
              */
             numberFormat.setMaximumFractionDigits(DecimalFunctions.fractionDigitsForValue(number, 2));
-            numberFormat.setMinimumFractionDigits(1); // Must be after setMaximumFractionDigits(…).
+            numberFormat.setMinimumFractionDigits(1);   // Must be after setMaximumFractionDigits(…).
             numberFormat.setRoundingMode(RoundingMode.HALF_EVEN);
             numberFormat.format(number, buffer, dummy);
         }
@@ -1178,6 +1209,124 @@ public class Formatter implements Locali
     }
 
     /**
+     * Appends rows of numbers. Each number is separated by a space, and each row is separated by a comma.
+     * This method is mostly for formatting geometries, but it could be used for other objects like matrix
+     * as well. Each row usually have the same length, but this is not mandatory.
+     *
+     * @param  rows            rows to append, or {@code null} if none.
+     * @param  fractionDigits  the number of fraction digits for each number in a row, or {@code null} for default.
+     *         If a row contains more numbers than {@code fractionDigits.length}, then the last value in this array
+     *         is repeated for all remaining row numbers.
+     *
+     * @since 1.0
+     */
+    public void append(final double[][] rows, int... fractionDigits) {
+        if (rows == null || rows.length == 0) {
+            return;
+        }
+        if (fractionDigits == null || fractionDigits.length == 0) {
+            fractionDigits = Numerics.suggestFractionDigits(rows);
+        }
+        numberFormat.setRoundingMode(RoundingMode.HALF_EVEN);
+        /*
+         * If the rows are going to be formatted on many lines, then we will need to put some margin before each row.
+         * If the first row starts on its own line, then the margin will be the usual indentation. But if the first
+         * row starts on the same line than previous elements (or the keyword of this element, e.g. "BOX["), then we
+         * will need a different amount of spaces if we want to have the numbers properly aligned.
+         */
+        final boolean isMultiLines = (indentation > WKTFormat.SINGLE_LINE) && (rows.length > 1);
+        final boolean needsAlignment = !requestNewLine;
+        final CharSequence marginBeforeRow;
+        if (isMultiLines) {
+            int currentLineLength = margin;
+            if (needsAlignment) {
+                final int length = buffer.length();
+                int i = length;
+                while (i > 0) {                                         // Locate beginning of current line.
+                    final int c = buffer.codePointBefore(i);
+                    if (Characters.isLineOrParagraphSeparator(c)) break;
+                    i -= Character.charCount(c);
+                }
+                currentLineLength = buffer.codePointCount(i, length);
+            }
+            marginBeforeRow = CharSequences.spaces(currentLineLength);
+        } else {
+            marginBeforeRow = "";
+        }
+        /*
+         * 'formattedNumberMarks' contains, for each number in each row, positions in the 'buffer' where
+         * the number starts and position where it ends. Those positions are stored as (start,end) pairs.
+         * We compute those marks unconditionally for simplicity, but will ignore them if formatting on
+         * a single line.
+         */
+        final int[][] formattedNumberMarks = new int[rows.length][];
+        int numColumns = 0;
+        for (int j=0; j<rows.length; j++) {
+            if (j == 0) {
+                appendSeparator();      // It is up to the caller to decide if we begin with a new line.
+            } else {
+                buffer.append(separatorNewLine).append(marginBeforeRow);
+            }
+            final double[] numbers = rows[j];
+            numColumns = Math.max(numColumns, numbers.length);      // Store the length of longest row.
+            final int[] marks = new int[numbers.length << 1];       // Positions where numbers are formatted.
+            formattedNumberMarks[j] = marks;
+            for (int i=0; i<numbers.length; i++) {
+                if (i != 0) buffer.append(Symbols.NUMBER_SEPARATOR);
+                if (i < fractionDigits.length) {                    // Otherwise, same than previous number.
+                    final int f = fractionDigits[i];
+                    numberFormat.setMaximumFractionDigits(f);
+                    numberFormat.setMinimumFractionDigits(f);
+                }
+                marks[i << 1] = buffer.length();                    // Store the start position where number is formatted.
+                setColor(ElementKind.NUMBER);
+                numberFormat.format(numbers[i], buffer, dummy);
+                resetColor();
+                marks[(i << 1) | 1] = buffer.length();              // Store the end position where number is formatted.
+            }
+        }
+        /*
+         * If formatting on more than one line, insert the amount of spaces required for aligning numbers.
+         * This is possible because we wrote the coordinate values with fixed number of fraction digits.
+         */
+        if (isMultiLines) {
+            final int base = elementStart;
+            final String toWrite = buffer.substring(base);          // Save what we formatted in above loop.
+            buffer.setLength(base);                                 // Discard what we formatted - we will rewrite.
+            final int[] columnWidths = new int[numColumns];
+            for (final int[] marks : formattedNumberMarks) {        // Compute the maximal width of each column.
+                for (int i=0; i<marks.length; i += 2) {
+                    final int k = i >> 1;
+                    final int w = toWrite.codePointCount(marks[i  ] -= base,
+                                                         marks[i+1] -= base);
+                    if (w > columnWidths[k]) columnWidths[k] = w;
+                }
+            }
+            if (needsAlignment && keywordSpaceAt == null) {
+                keywordSpaceAt = new IntegerList(formattedNumberMarks.length, Integer.MAX_VALUE);
+            }
+            boolean requestAlignment = false;
+            int lastPosition = 0;
+            for (int[] marks : formattedNumberMarks) {              // Recopy the formatted text, with more spaces.
+                for (int i = 0; i<marks.length;) {
+                    final int w = columnWidths[i >> 1];
+                    final int s = marks[i++];
+                    final int e = marks[i++];
+                    buffer.append(toWrite, lastPosition, s)
+                          .append(CharSequences.spaces(w - toWrite.codePointCount(s, e)));
+                    if (requestAlignment) {
+                        requestAlignment = false;
+                        keywordSpaceAt.add(buffer.length());
+                    }
+                    buffer.append(toWrite, s, e);
+                    lastPosition = e;
+                }
+                requestAlignment = needsAlignment;
+            }
+        }
+    }
+
+    /**
      * Appends the given number without any change to the {@link NumberFormat} setting.
      * Caller shall ensure that the following method has been invoked prior this method call:
      *
@@ -1693,6 +1842,7 @@ public class Formatter implements Locali
         elementStart      = 0;
         colorApplied      = 0;
         margin            = 0;
+        keywordSpaceAt    = null;
         requestNewLine    = false;
         isComplement      = false;
         highlightError    = false;

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Symbols.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Symbols.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Symbols.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -67,7 +67,7 @@ import static org.apache.sis.util.Argume
  * Users can create their own {@code Symbols} instance for parsing or formatting a WKT with different symbols.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.6
+ * @version 1.0
  *
  * @see WKTFormat#getSymbols()
  * @see WKTFormat#setSymbols(Symbols)
@@ -92,6 +92,12 @@ public class Symbols implements Localize
     static final boolean SCIENTIFIC_NOTATION = true;
 
     /**
+     * Separator between numbers in a sequence of numbers.
+     * This is used for example between the coordinates of a point.
+     */
+    static final char NUMBER_SEPARATOR = ' ';
+
+    /**
      * The prefix character for the value of a WKT fragment.
      */
     static final char FRAGMENT_VALUE = '$';
@@ -541,7 +547,7 @@ public class Symbols implements Localize
      * Returns the value of {@link #getSeparator()} without trailing spaces,
      * followed by the system line separator.
      */
-    final String lineSeparator() {
+    final String separatorNewLine() {
         final String separator = getSeparator();
         return separator.substring(0, CharSequences.skipTrailingWhitespaces(separator, 0, separator.length()))
                 .concat(System.lineSeparator());

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -106,7 +106,7 @@ import org.apache.sis.internal.util.Stan
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Rémi Eve (IRD)
- * @version 0.8
+ * @version 1.0
  *
  * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
  * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/package-info.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -82,7 +82,7 @@
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Rémi Eve (IRD)
  * @author  Rueben Schulz (UBC)
- * @version 0.6
+ * @version 1.0
  *
  * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
  * @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractDirectPosition.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -35,6 +35,10 @@ import org.apache.sis.util.Utilities;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.internal.metadata.WKTKeywords;
+import org.apache.sis.internal.referencing.WKTUtilities;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.io.wkt.Formatter;
 
 import static java.lang.Double.doubleToLongBits;
 import static org.apache.sis.util.StringBuilders.trimFractionalPart;
@@ -53,11 +57,11 @@ import static org.apache.sis.util.Argume
  * serializable, is left to subclasses.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.0
  * @since   0.3
  * @module
  */
-public abstract class AbstractDirectPosition implements DirectPosition {
+public abstract class AbstractDirectPosition extends FormattableObject implements DirectPosition {
     /**
      * Constructs a direct position.
      */
@@ -65,6 +69,24 @@ public abstract class AbstractDirectPosi
     }
 
     /**
+     * Returns the given position as an {@code AbstractDirectPosition} instance.
+     * If the given position is already an instance of {@code AbstractDirectPosition},
+     * then it is returned unchanged. Otherwise the coordinate values and the CRS
+     * of the given position are copied in a new position.
+     *
+     * @param  position  the position to cast, or {@code null}.
+     * @return the values of the given position as an {@code AbstractDirectPosition} instance.
+     *
+     * @since 1.0
+     */
+    public static AbstractDirectPosition castOrCopy(final DirectPosition position) {
+        if (position == null || position instanceof AbstractDirectPosition) {
+            return (AbstractDirectPosition) position;
+        }
+        return new GeneralDirectPosition(position);
+    }
+
+    /**
      * Returns always {@code this}, the direct position for this
      * {@linkplain org.opengis.geometry.coordinate.Position position}.
      *
@@ -181,22 +203,28 @@ public abstract class AbstractDirectPosi
     }
 
     /**
-     * Returns {@code true} if every values in the given {@code double} array could be casted
-     * to the {@code float} type without precision lost. This method treats all {@code NaN} values
-     * as equal.
-     *
-     * @param  values  the value to test for their precision.
-     * @return {@code true} if every values can be casted to the {@code float} type without precision lost.
-     *
-     * @see #toString(DirectPosition, boolean)
-     */
-    static boolean isSimplePrecision(final double... values) {
-        for (final double value : values) {
-            if (Double.doubleToLongBits(value) != Double.doubleToLongBits((float) value)) {
-                return false;
-            }
-        }
-        return true;
+     * Formats this position in the <cite>Well Known Text</cite> (WKT) format.
+     * The format is like below, where {@code x₀}, {@code x₁}, {@code x₂}, <i>etc.</i>
+     * are the ordinate values at index 0, 1, 2, <i>etc.</i>:
+     *
+     * {@preformat wkt
+     *   POINT[x₀ x₁ x₂ …]
+     * }
+     *
+     * If the coordinate reference system is geodetic or projected, then coordinate values are formatted
+     * with a precision equivalent to one centimetre on Earth (the actual number of fraction digits is
+     * adjusted for the axis unit of measurement and the planet size if different than Earth).
+     *
+     * @param  formatter  the formatter where to format the inner content of this point.
+     * @return the WKT keyword, which is {@code "Point"} for this element.
+     *
+     * @since 1.0
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        final double[][] points = new double[][] {getCoordinate()};
+        formatter.append(points, WKTUtilities.suggestFractionDigits(getCoordinateReferenceSystem(), points));
+        return WKTKeywords.Point;
     }
 
     /**
@@ -208,10 +236,11 @@ public abstract class AbstractDirectPosi
      *   POINT(x₀ x₁ x₂ …)
      * }
      *
+     * This method formats the numbers as with {@link Double#toString(double)} (i.e. without fixed number of fraction digits).
      * The string returned by this method can be {@linkplain GeneralDirectPosition#GeneralDirectPosition(CharSequence) parsed}
      * by the {@code GeneralDirectPosition} constructor.
      *
-     * @return This position as a {@code POINT} in <cite>Well Known Text</cite> (WKT) format.
+     * @return this position as a {@code POINT} in <cite>Well Known Text</cite> (WKT) format.
      */
     @Override
     public String toString() {
@@ -227,7 +256,7 @@ public abstract class AbstractDirectPosi
      * @param  isSimplePrecision  {@code true} if every ordinate values can be casted to {@code float}.
      * @return the point as a {@code POINT} in WKT format.
      *
-     * @see #isSimplePrecision(double[])
+     * @see Numerics#isSimplePrecision(double[])
      */
     static String toString(final DirectPosition position, final boolean isSimplePrecision) {
         final StringBuilder buffer = new StringBuilder(32).append("POINT");

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/AbstractEnvelope.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -40,6 +40,9 @@ import org.apache.sis.util.Emptiable;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.io.wkt.Formatter;
+import org.apache.sis.io.wkt.FormattableObject;
+import org.apache.sis.internal.referencing.WKTUtilities;
 
 import static java.lang.Double.doubleToLongBits;
 import static org.apache.sis.internal.util.Numerics.SIGN_BIT_MASK;
@@ -109,12 +112,12 @@ import static org.apache.sis.math.MathFu
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
 @XmlTransient
-public abstract class AbstractEnvelope implements Envelope, Emptiable {
+public abstract class AbstractEnvelope extends FormattableObject implements Envelope, Emptiable {
     /**
      * An empty array of envelopes, to be returned by {@link #toSimpleEnvelopes()}
      * when en envelope is empty.
@@ -1133,6 +1136,7 @@ public abstract class AbstractEnvelope i
      * The {@code BOX} element is not part of the standard <cite>Well Known Text</cite> (WKT) format.
      * However it is understood by many software libraries, for example GDAL and PostGIS.</div>
      *
+     * This method formats the numbers as with {@link Double#toString(double)} (i.e. without fixed number of fraction digits).
      * The string returned by this method can be {@linkplain GeneralEnvelope#GeneralEnvelope(CharSequence) parsed}
      * by the {@code GeneralEnvelope} constructor.
      *
@@ -1185,6 +1189,41 @@ public abstract class AbstractEnvelope i
     }
 
     /**
+     * Formats this envelope as a "{@code BOX}" element.
+     * The output is of the form "{@code BOX}<var>n</var>{@code D[}{@linkplain #getLowerCorner()
+     * lower corner}{@code ,}{@linkplain #getUpperCorner() upper corner}{@code ]}"
+     * where <var>n</var> is the {@linkplain #getDimension() number of dimensions}.
+     * The number of dimension is written only if different than 2.
+     *
+     * <div class="note"><b>Note:</b>
+     * The {@code BOX} element is not part of the standard <cite>Well Known Text</cite> (WKT) format.
+     * However it is understood by many software libraries, for example GDAL and PostGIS.</div>
+     *
+     * If the coordinate reference system is geodetic or projected, then coordinate values are formatted
+     * with a precision equivalent to one centimetre on Earth (the actual number of fraction digits is
+     * adjusted for the axis unit of measurement and the planet size if different than Earth).
+     *
+     * @param  formatter  the formatter where to format the inner content of this envelope.
+     * @return the pseudo-WKT keyword, which is {@code "Box"} for this element.
+     *
+     * @since 1.0
+     */
+    @Override
+    protected String formatTo(final Formatter formatter) {
+        final double[][] points = new double[][] {
+            getLowerCorner().getCoordinate(),
+            getUpperCorner().getCoordinate()
+        };
+        formatter.append(points, WKTUtilities.suggestFractionDigits(getCoordinateReferenceSystem(), points));
+        final int dimension = getDimension();
+        String keyword = "Box";
+        if (dimension != 2) {
+            keyword = new StringBuilder(keyword).append(dimension).append('D').toString();
+        }
+        return keyword;
+    }
+
+    /**
      * Base class for unmodifiable direct positions backed by the enclosing envelope.
      * Subclasses must override the {@link #getOrdinate(int)} method in order to delegate
      * the work to the appropriate {@link AbstractEnvelope} method.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/ArrayEnvelope.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -37,6 +37,7 @@ import org.apache.sis.referencing.Common
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Numerics;
 
 import static org.apache.sis.util.ArgumentChecks.*;
 import static org.apache.sis.math.MathFunctions.isNegative;
@@ -56,7 +57,7 @@ import static org.apache.sis.internal.re
  * in {@link SubEnvelope}.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -593,6 +594,6 @@ scanNumber: while ((i += Character.charC
      */
     @Override
     public String toString() {
-        return toString(this, AbstractDirectPosition.isSimplePrecision(ordinates));
+        return toString(this, Numerics.isSimplePrecision(ordinates));
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/DirectPosition2D.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -292,7 +292,7 @@ public class DirectPosition2D extends Po
      */
     @Override
     public String toString() {
-        return AbstractDirectPosition.toString(this, AbstractDirectPosition.isSimplePrecision(x, y));
+        return AbstractDirectPosition.toString(this, Numerics.isSimplePrecision(x, y));
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/GeneralDirectPosition.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -30,6 +30,7 @@ import java.security.PrivilegedAction;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.internal.util.Numerics;
 import org.apache.sis.util.resources.Errors;
 
 import static org.apache.sis.util.ArgumentChecks.ensureDimensionMatches;
@@ -51,7 +52,7 @@ import static org.apache.sis.util.Argume
  * on the value of the containing object's {@code CoordinateReferenceSystem}.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.0
  *
  * @see DirectPosition1D
  * @see DirectPosition2D
@@ -282,7 +283,7 @@ public class GeneralDirectPosition exten
      */
     @Override
     public String toString() {
-        return toString(this, isSimplePrecision(ordinates));
+        return toString(this, Numerics.isSimplePrecision(ordinates));
     }
 
     /**

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/package-info.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/package-info.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/geometry/package-info.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -84,7 +84,7 @@
  * @see org.apache.sis.setup.GeometryLibrary
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Formulas.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -19,6 +19,7 @@ package org.apache.sis.internal.referenc
 import org.apache.sis.util.Static;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.internal.util.Numerics;
+import org.opengis.referencing.datum.Ellipsoid;
 
 import static java.lang.Math.*;
 import static org.apache.sis.math.MathFunctions.atanh;
@@ -31,7 +32,7 @@ import static org.apache.sis.internal.me
  * do not want to expose publicly those arbitrary values (or at least not in a too direct way).
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -59,6 +60,13 @@ public final class Formulas extends Stat
     public static final double ANGULAR_TOLERANCE = LINEAR_TOLERANCE / (NAUTICAL_MILE * 60);
 
     /**
+     * Default tolerance threshold for comparing ordinate values in temporal CRS,
+     * assuming that the unit of measurement is second. Current value is arbitrary
+     * and may change in any future Apache SIS version.
+     */
+    public static final double TEMPORAL_TOLERANCE = 60;             // One minute.
+
+    /**
      * The maximal longitude value before normalization if a centimetric precision is desired.
      * This is about 4×10⁸ degrees.
      *
@@ -87,6 +95,19 @@ public final class Formulas extends Stat
     }
 
     /**
+     * Returns the size of a planet described by the given ellipsoid compared to earth.
+     * This method returns a ratio of given planet authalic radius compared to WGS84.
+     * This can be used for adjusting {@link #LINEAR_TOLERANCE} and {@link #ANGULAR_TOLERANCE} to another planet.
+     *
+     * @param  planet  ellipsoid of the other planet to compare to Earth.
+     * @return ratio of planet authalic radius on WGS84 authalic radius.
+     */
+    public static double scaleComparedToEarth(final Ellipsoid planet) {
+        return getAuthalicRadius(planet.getSemiMajorAxis(),
+                                 planet.getSemiMinorAxis()) / 6371007.180918474;
+    }
+
+    /**
      * Returns 3ⁿ for very small (less than 10) positive values of <var>n</var>.
      * Note that this method overflow for any value equals or greater than 20.
      *

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ReferencingUtilities.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -31,8 +31,10 @@ import org.opengis.parameter.GeneralPara
 import org.opengis.referencing.cs.*;
 import org.opengis.referencing.crs.*;
 import org.opengis.referencing.IdentifiedObject;
+import org.opengis.referencing.datum.Datum;
 import org.opengis.referencing.datum.Ellipsoid;
 import org.opengis.referencing.datum.PrimeMeridian;
+import org.opengis.referencing.datum.GeodeticDatum;
 import org.opengis.referencing.datum.VerticalDatum;
 import org.opengis.referencing.datum.VerticalDatumType;
 import org.opengis.referencing.operation.CoordinateOperationFactory;
@@ -60,7 +62,7 @@ import static java.util.Collections.sing
  * <p><strong>Do not rely on this API!</strong> It may change in incompatible way in any future release.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.5
  * @module
  */
@@ -110,7 +112,7 @@ public final class ReferencingUtilities
         if (cs != null) {
             for (int i=cs.getDimension(); --i>=0;) {
                 final CoordinateSystemAxis axis = cs.getAxis(i);
-                if (axis != null) {  // Paranoiac check.
+                if (axis != null) {                                         // Paranoiac check.
                     final Unit<?> candidate = axis.getUnit();
                     if (candidate != null) {
                         if (unit == null) {
@@ -136,7 +138,7 @@ public final class ReferencingUtilities
     public static int getDimension(final CoordinateReferenceSystem crs) {
         if (crs != null) {
             final CoordinateSystem cs = crs.getCoordinateSystem();
-            if (cs != null) {  // Paranoiac check.
+            if (cs != null) {                                               // Paranoiac check.
                 return cs.getDimension();
             }
         }
@@ -199,6 +201,31 @@ public final class ReferencingUtilities
     }
 
     /**
+     * Returns the ellipsoid used by the given coordinate reference system, or {@code null} if none.
+     *
+     * @param  crs  the coordinate reference system for which to get the ellipsoid.
+     * @return the ellipsoid, or {@code null} if none.
+     */
+    public static Ellipsoid getEllipsoid(final CoordinateReferenceSystem crs) {
+        if (crs != null) {
+            if (crs instanceof SingleCRS) {
+                final Datum datum = ((SingleCRS) crs).getDatum();
+                if (datum instanceof GeodeticDatum) {
+                    final Ellipsoid e = ((GeodeticDatum) datum).getEllipsoid();
+                    if (e != null) return e;
+                }
+            }
+            if (crs instanceof CompoundCRS) {
+                for (final CoordinateReferenceSystem c : ((CompoundCRS) crs).getComponents()) {
+                    final Ellipsoid e = getEllipsoid(c);
+                    if (e != null) return e;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the ellipsoid used by the specified coordinate reference system, provided that the two first dimensions
      * use an instance of {@link GeographicCRS}. Otherwise (i.e. if the two first dimensions are not geographic),
      * returns {@code null}.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/WKTUtilities.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -49,6 +49,8 @@ import org.apache.sis.util.Static;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.math.DecimalFunctions;
 
 
 /**
@@ -60,7 +62,7 @@ import org.apache.sis.internal.util.Cons
  * We need to be specific in order to select the right "aspect" of the given object.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -72,6 +74,39 @@ public final class WKTUtilities extends
     }
 
     /**
+     * Returns the WKT type of the given interface.
+     *
+     * For {@link CoordinateSystem} base type, the returned value shall be one of
+     * {@code affine}, {@code Cartesian}, {@code cylindrical}, {@code ellipsoidal}, {@code linear},
+     * {@code parametric}, {@code polar}, {@code spherical}, {@code temporal} or {@code vertical}.
+     *
+     * @param  base  the abstract base interface.
+     * @param  type  the interface or classes for which to get the WKT type.
+     * @return the WKT type for the given class or interface, or {@code null} if none.
+     *
+     * @see ReferencingUtilities#toPropertyName(Class, Class)
+     */
+    public static String toType(final Class<?> base, final Class<?> type) {
+        if (type != base) {
+            final StringBuilder name = ReferencingUtilities.toPropertyName(base, type);
+            if (name != null) {
+                int end = name.length() - 2;
+                if (CharSequences.regionMatches(name, end, "CS")) {
+                    name.setLength(end);
+                    if ("time".contentEquals(name)) {
+                        return "temporal";
+                    }
+                    if (CharSequences.regionMatches(name, 0, "cartesian")) {
+                        name.setCharAt(0, 'C');     // "Cartesian"
+                    }
+                    return name.toString();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the given coordinate reference system as a formattable object.
      *
      * @param  object  the coordinate reference system, or {@code null}.
@@ -275,35 +310,44 @@ public final class WKTUtilities extends
     }
 
     /**
-     * Returns the WKT type of the given interface.
-     *
-     * For {@link CoordinateSystem} base type, the returned value shall be one of
-     * {@code affine}, {@code Cartesian}, {@code cylindrical}, {@code ellipsoidal}, {@code linear},
-     * {@code parametric}, {@code polar}, {@code spherical}, {@code temporal} or {@code vertical}.
+     * Suggests an amount of fraction digits to use for formatting numbers in each column of the given sequence
+     * of points. The number of fraction digits may be negative if we could round the numbers to 10, <i>etc</i>.
      *
-     * @param  base  the abstract base interface.
-     * @param  type  the interface or classes for which to get the WKT type.
-     * @return the WKT type for the given class or interface, or {@code null} if none.
-     *
-     * @see ReferencingUtilities#toPropertyName(Class, Class)
-     */
-    public static String toType(final Class<?> base, final Class<?> type) {
-        if (type != base) {
-            final StringBuilder name = ReferencingUtilities.toPropertyName(base, type);
-            if (name != null) {
-                int end = name.length() - 2;
-                if (CharSequences.regionMatches(name, end, "CS")) {
-                    name.setLength(end);
-                    if ("time".contentEquals(name)) {
-                        return "temporal";
-                    }
-                    if (CharSequences.regionMatches(name, 0, "cartesian")) {
-                        name.setCharAt(0, 'C');     // "Cartesian"
-                    }
-                    return name.toString();
+     * @param  crs     the coordinate reference system for each points, or {@code null} if unknown.
+     * @param  points  the sequence of points. It is not required that each point has the same dimension.
+     * @return suggested amount of fraction digits as an array as long as the longest row.
+     */
+    public static int[] suggestFractionDigits(final CoordinateReferenceSystem crs, final double[]... points) {
+        final int[] fractionDigits = Numerics.suggestFractionDigits(points);
+        final Ellipsoid ellipsoid = ReferencingUtilities.getEllipsoid(crs);
+        if (ellipsoid != null) {
+            /*
+             * Use heuristic precisions for geodetic or projected CRS. We do not apply those heuristics
+             * for other kind of CRS (e.g. engineering) because we do not know what could be the size
+             * of the object attached to the CRS.
+             */
+            final CoordinateSystem cs = crs.getCoordinateSystem();
+            final int dimension = Math.min(cs.getDimension(), fractionDigits.length);
+            final double scale = Formulas.scaleComparedToEarth(ellipsoid);
+            for (int i=0; i<dimension; i++) {
+                final Unit<?> unit = cs.getAxis(i).getUnit();
+                double precision;
+                if (Units.isLinear(unit)) {
+                    precision = Formulas.LINEAR_TOLERANCE * scale;                          // In metres
+                } else if (Units.isAngular(unit)) {
+                    precision = Formulas.ANGULAR_TOLERANCE * (Math.PI / 180) * scale;       // In radians
+                } else if (Units.isTemporal(unit)) {
+                    precision = Formulas.TEMPORAL_TOLERANCE;                                // In seconds
+                } else {
+                    continue;
+                }
+                precision /= Units.toStandardUnit(unit);                // In units used by the coordinates.
+                final int f = DecimalFunctions.fractionDigitsForDelta(precision, false);
+                if (f > fractionDigits[i]) {
+                    fractionDigits[i] = f;                              // Use at least the heuristic precision.
                 }
             }
         }
-        return null;
+        return fractionDigits;
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -174,6 +174,16 @@ class SpecializableTransform extends Abs
             }
             return false;
         }
+
+        /**
+         * Formats this envelope as a "{@code DOMAIN}" element (non-standard).
+         * This is used by {@link SpecializableTransform#formatTo(Formatter)}.
+         */
+        @Override
+        protected String formatTo(final Formatter formatter) {
+            super.formatTo(formatter);
+            return "Domain";
+        }
     }
 
     /**
@@ -485,11 +495,9 @@ next:   for (final Map.Entry<Envelope,Ma
     @Override
     protected String formatTo(final Formatter formatter) {
         for (final SubArea domain : domains) {
-            formatter.newLine();
-            formatter.append(generic);
-            formatter.newLine();
-            // TODO: format BBOX.
-            formatter.append(domain.transform);
+            formatter.newLine(); formatter.append(generic);
+            formatter.newLine(); formatter.append(domain);
+            formatter.newLine(); formatter.append(domain.transform);
         }
         formatter.setInvalidWKT(SpecializableTransform.class, null);
         return "Specializable_MT";

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractDirectPositionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractDirectPositionTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractDirectPositionTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/AbstractDirectPositionTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -26,17 +26,52 @@ import static org.apache.sis.test.Assert
  * Tests the static methods provided in {@link AbstractDirectPosition}.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.0
  * @since   0.3
  * @module
  */
 public final strictfp class AbstractDirectPositionTest extends TestCase {
     /**
-     * Tests {@link AbstractDirectPosition#isSimplePrecision(double[])}.
+     * Tests {@link AbstractDirectPosition#parse(CharSequence)}.
      */
     @Test
-    public void testIsSimplePrecision() {
-        assertTrue (AbstractDirectPosition.isSimplePrecision(2, 0.5, 0.25, Double.NaN, Double.POSITIVE_INFINITY));
-        assertFalse(AbstractDirectPosition.isSimplePrecision(2, 0.5, 1.0 / 3));
+    public void testParse() {
+        assertArrayEquals(new double[] {6, 10, 2}, AbstractDirectPosition.parse("POINT(6 10 2)"),       STRICT);
+        assertArrayEquals(new double[] {3, 14, 2}, AbstractDirectPosition.parse("POINT M [ 3 14 2 ] "), STRICT);
+        assertArrayEquals(new double[] {2, 10, 8}, AbstractDirectPosition.parse("POINT Z 2 10 8"),      STRICT);
+        assertArrayEquals(new double[] {},         AbstractDirectPosition.parse("POINT()"),             STRICT);
+        assertArrayEquals(new double[] {},         AbstractDirectPosition.parse("POINT ( ) "),          STRICT);
+    }
+
+    /**
+     * Tests {@link AbstractDirectPosition#parse(CharSequence)} with invalid input strings.
+     */
+    @Test
+    public void testParsingFailures() {
+        try {
+            AbstractDirectPosition.parse("POINT(6 10 2");
+            fail("Parsing should fails because of missing parenthesis.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("POINT(6 10 2"));
+            assertTrue(message, message.contains("‘)’"));
+        }
+        try {
+            AbstractDirectPosition.parse("POINT 6 10 2)");
+            fail("Parsing should fails because of missing parenthesis.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+        }
+        try {
+            AbstractDirectPosition.parse("POINT(6 10 2) x");
+            fail("Parsing should fails because of extra characters.");
+        } catch (IllegalArgumentException e) {
+            // This is the expected exception.
+            final String message = e.getMessage();
+            assertTrue(message, message.contains("POINT(6 10 2) x"));
+            assertTrue(message, message.contains("“x”") ||                  // English locale
+                                message.contains("« x »"));                 // French locale
+        }
     }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/ArrayEnvelopeTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -16,11 +16,12 @@
  */
 package org.apache.sis.geometry;
 
+import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.apache.sis.test.Assert.*;
 
 
 /**
@@ -28,7 +29,7 @@ import static org.junit.Assert.*;
  * This is the base class of {@link GeneralEnvelope} and {@link ImmutableEnvelope}.
  *
  * @author  Michael Hausegger
- * @version 0.8
+ * @version 1.0
  * @since   0.8
  * @module
  */
@@ -64,6 +65,21 @@ public final strictfp class ArrayEnvelop
     }
 
     /**
+     * Tests the {@link ArrayEnvelope#formatTo(Formatter)} method.
+     * Contrarily to {@code toString()}, the precision depends on the CRS.
+     */
+    @Test
+    public void testFormatWKT() {
+        ArrayEnvelope envelope = new ArrayEnvelope(new double[] {4, -10, 50, 2});
+        assertMultilinesEquals("BOX[ 4 -10,\n" +
+                               "    50   2]", envelope.toWKT());
+        envelope.crs = AbstractEnvelopeTest.WGS84;
+        assertMultilinesEquals("BOX[ 4.00000000 -10.00000000,\n" +
+                               "    50.00000000   2.00000000]", envelope.toWKT());
+
+    }
+
+    /**
      * Tests envelope construction from a the pseudo-Well Known Text (WKT) representation of a Bounding Box (BBOX).
      */
     @Test

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/geometry/GeneralDirectPositionTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -17,6 +17,7 @@
 package org.apache.sis.geometry;
 
 import java.util.Arrays;
+import org.apache.sis.io.wkt.Formatter;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
@@ -30,7 +31,7 @@ import static org.apache.sis.geometry.Ab
  * Tests the {@link GeneralDirectPosition} class.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.3
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -49,10 +50,23 @@ public final strictfp class GeneralDirec
     }
 
     /**
+     * Tests the {@link GeneralDirectPosition#formatTo(Formatter)} method.
+     * Contrarily to {@code toString()}, the precision depends on the CRS.
+     */
+    @Test
+    public void testFormatWKT() {
+        final GeneralDirectPosition position = new GeneralDirectPosition(6, 10);
+        assertEquals("POINT[6 10]", position.toWKT());
+        position.setCoordinateReferenceSystem(WGS84);
+        assertEquals("POINT[6.00000000 10.00000000]", position.toWKT());        // 1 cm precision on Earth.
+        validate(position);
+    }
+
+    /**
      * Tests the {@link GeneralDirectPosition#toString()} method.
      */
     @Test
-    public void testWktFormatting() {
+    public void testToString() {
         final GeneralDirectPosition position = new GeneralDirectPosition(6, 10, 2);
         assertEquals("POINT(6 10 2)", position.toString());
         validate(position);
@@ -62,7 +76,7 @@ public final strictfp class GeneralDirec
      * Tests the {@link GeneralDirectPosition#GeneralDirectPosition(CharSequence)} constructor.
      */
     @Test
-    public void testWktParsing() {
+    public void testConstructor() {
         assertEquals("POINT(6 10 2)", new GeneralDirectPosition("POINT(6 10 2)").toString());
         assertEquals("POINT(3 14 2)", new GeneralDirectPosition("POINT M [ 3 14 2 ] ").toString());
         assertEquals("POINT(2 10 8)", new GeneralDirectPosition("POINT Z 2 10 8").toString());
@@ -71,40 +85,6 @@ public final strictfp class GeneralDirec
     }
 
     /**
-     * Tests the {@link GeneralDirectPosition#GeneralDirectPosition(CharSequence)} constructor
-     * with invalid input strings.
-     */
-    @Test
-    @SuppressWarnings("ResultOfObjectAllocationIgnored")
-    public void testWktParsingFailures() {
-        try {
-            new GeneralDirectPosition("POINT(6 10 2");
-            fail("Parsing should fails because of missing parenthesis.");
-        } catch (IllegalArgumentException e) {
-            // This is the expected exception.
-            final String message = e.getMessage();
-            assertTrue(message, message.contains("POINT(6 10 2"));
-            assertTrue(message, message.contains("‘)’"));
-        }
-        try {
-            new GeneralDirectPosition("POINT 6 10 2)");
-            fail("Parsing should fails because of missing parenthesis.");
-        } catch (IllegalArgumentException e) {
-            // This is the expected exception.
-        }
-        try {
-            new GeneralDirectPosition("POINT(6 10 2) x");
-            fail("Parsing should fails because of extra characters.");
-        } catch (IllegalArgumentException e) {
-            // This is the expected exception.
-            final String message = e.getMessage();
-            assertTrue(message, message.contains("POINT(6 10 2) x"));
-            assertTrue(message, message.contains("“x”") ||  // English locale
-                                message.contains("« x »")); // French locale
-        }
-    }
-
-    /**
      * Tests {@link GeneralDirectPosition#clone()}.
      */
     @Test

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/FormulasTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -18,6 +18,7 @@ package org.apache.sis.internal.referenc
 
 import org.apache.sis.internal.metadata.ReferencingServices;
 import org.apache.sis.measure.Longitude;
+import org.apache.sis.referencing.datum.HardCodedDatum;
 import org.apache.sis.test.TestCase;
 import org.junit.Test;
 
@@ -28,7 +29,7 @@ import static org.junit.Assert.*;
  * Tests {@link Formulas}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.0
  * @since   0.4
  * @module
  */
@@ -90,6 +91,14 @@ public final strictfp class FormulasTest
     }
 
     /**
+     * Tests {@link Formulas#scaleComparedToEarth(Ellipsoid)}.
+     */
+    @Test
+    public void testScaleComparedToEarth() {
+        assertEquals(1, Formulas.scaleComparedToEarth(HardCodedDatum.WGS84.getEllipsoid()), 1E-14);
+    }
+
+    /**
      * Tests {@link Formulas#getSemiMinor(double, double)}.
      */
     @Test

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/ReferencingUtilitiesTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -39,7 +39,7 @@ import static org.apache.sis.internal.re
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
- * @since   0.5 (derived from 0.4)
+ * @since   0.4
  * @module
  */
 public final strictfp class ReferencingUtilitiesTest extends TestCase {

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WKTUtilitiesTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WKTUtilitiesTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WKTUtilitiesTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/WKTUtilitiesTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.referencing;
 
 import org.opengis.referencing.cs.*;
+import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -30,7 +31,7 @@ import static org.apache.sis.internal.re
  * Tests {@link WKTUtilities}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.0
  * @since   0.7
  * @module
  */
@@ -49,10 +50,21 @@ public final strictfp class WKTUtilities
         assertEquals(WKTKeywords.cylindrical, toType(CoordinateSystem.class, CylindricalCS   .class));
         assertEquals(WKTKeywords.ellipsoidal, toType(CoordinateSystem.class, EllipsoidalCS   .class));
         assertEquals(WKTKeywords.linear,      toType(CoordinateSystem.class, LinearCS        .class));
-//      assertEquals(WKTKeywords.parametric,  toType(CoordinateSystem.class, ParametricCS    .class));
+        assertEquals(WKTKeywords.parametric,  toType(CoordinateSystem.class, ParametricCS    .class));
         assertEquals(WKTKeywords.polar,       toType(CoordinateSystem.class, PolarCS         .class));
         assertEquals(WKTKeywords.spherical,   toType(CoordinateSystem.class, SphericalCS     .class));
         assertEquals(WKTKeywords.temporal,    toType(CoordinateSystem.class, TimeCS          .class));
         assertEquals(WKTKeywords.vertical,    toType(CoordinateSystem.class, VerticalCS      .class));
     }
+
+    /**
+     * Tests {@link WKTUtilities#suggestFractionDigits(CoordinateReferenceSystem, double[]...)}.
+     */
+    @Test
+    public void testSuggestFractionDigits() {
+        assertArrayEquals(new int[] {8, 9}, WKTUtilities.suggestFractionDigits(HardCodedCRS.WGS84, new double[][] {
+            {40, -10},
+            {50, -10.000000001}
+        }));
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/SpecializableTransformTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -71,4 +71,27 @@ public final strictfp class Specializabl
         isInverseTransformSupported = false;
         verifyInDomain(CoordinateDomain.RANGE_10, -672445632505596619L);
     }
+
+    /**
+     * Tests the pseudo Well-Known Text formatting.
+     * The format used by this transform is non-standard and may change in any future Apache SIS version.
+     *
+     * @throws InvalidGeodeticParameterException if {@link SpecializableTransform} constructor reject a parameter.
+     */
+    @Test
+    public void testWKT() throws InvalidGeodeticParameterException {
+        transform = create();
+        assertWktEquals(
+                "SPECIALIZABLE_MT[\n" +
+                "  PARAM_MT[“Affine”,\n" +
+                "    PARAMETER[“elt_0_0”, 10.0],\n" +
+                "    PARAMETER[“elt_1_1”, 10.0]],\n" +
+                "  DOMAIN[-5 -4,\n" +
+                "          5  3],\n" +
+                "  PARAM_MT[“Affine”,\n" +
+                "    PARAMETER[“elt_0_0”, 10.0],\n" +
+                "    PARAMETER[“elt_0_2”, 0.1],\n" +
+                "    PARAMETER[“elt_1_1”, 10.0],\n" +
+                "    PARAMETER[“elt_1_2”, 0.1]]]");
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Numerics.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -21,6 +21,8 @@ import java.util.HashMap;
 import org.apache.sis.util.Debug;
 import org.apache.sis.util.Static;
 import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.math.DecimalFunctions;
+import org.opengis.referencing.operation.Matrix;    // For javadoc
 
 import static java.lang.Math.max;
 import static java.lang.Math.abs;
@@ -30,7 +32,7 @@ import static java.lang.Math.abs;
  * Miscellaneous utilities methods working on floating point numbers.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -156,6 +158,22 @@ public final class Numerics extends Stat
     }
 
     /**
+     * Returns {@code true} if every values in the given {@code double} array could be casted to the
+     * {@code float} type without precision lost. This method treats all {@code NaN} values as equal.
+     *
+     * @param  values  the value to test for their precision.
+     * @return {@code true} if every values can be casted to the {@code float} type without precision lost.
+     */
+    public static boolean isSimplePrecision(final double... values) {
+        for (final double value : values) {
+            if (Double.doubleToLongBits(value) != Double.doubleToLongBits((float) value)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Returns a copy of the given array where each value has been casted to the {@code float} type.
      *
      * @param  data  the array to copy, or {@code null}.
@@ -428,4 +446,51 @@ public final class Numerics extends Stat
         }
         return bits;
     }
+
+    /**
+     * Suggests an amount of fraction digits to use for formatting numbers in each column of the given matrix.
+     * The number of fraction digits may be negative if we could round the numbers to 10, 100, <i>etc</i>.
+     *
+     * @param  rows  the matrix rows. It is not required that each row has the same length.
+     * @return suggested amount of fraction digits as an array as long as the longest row.
+     *
+     * @see org.apache.sis.referencing.operation.matrix.Matrices#toString(Matrix)
+     */
+    public static int[] suggestFractionDigits(final double[][] rows) {
+        int length = 0;
+        final int n = rows.length - 1;
+        for (int j=0; j <= n; j++) {
+            final int rl = rows[j].length;
+            if (rl > length) length = rl;
+        }
+        final int[] fractionDigits = new int[length];
+        for (int i=0; i<length; i++) {
+            double min = Double.POSITIVE_INFINITY;
+            double max = Double.NEGATIVE_INFINITY;
+            boolean isInteger = true;
+            for (final double[] row : rows) {
+                if (row.length > i) {
+                    final double value = row[i];
+                    if (value < min) min = value;
+                    if (value > max) max = value;
+                    if (isInteger && Math.floor(value) != value && !Double.isNaN(value)) {
+                        isInteger = false;
+                    }
+                }
+            }
+            if (!isInteger) {
+                final double  delta;
+                final boolean strict;
+                if (min < max) {
+                    delta  = (max - min) / n;
+                    strict = (n == 1);
+                } else {
+                    delta  = Math.max(Math.abs(min), Math.abs(max)) * 1E-6;     // The 1E-6 factor is arbitrary.
+                    strict = false;
+                }
+                fractionDigits[i] = DecimalFunctions.fractionDigitsForDelta(delta, strict);
+            }
+        }
+        return fractionDigits;
+    }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java?rev=1825822&r1=1825821&r2=1825822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/internal/util/NumericsTest.java [UTF-8] Sun Mar  4 16:12:09 2018
@@ -34,7 +34,7 @@ import static org.junit.Assert.*;
  * Tests the {@link Numerics} class.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.0
  * @since   0.3
  * @module
  */
@@ -69,6 +69,15 @@ public final strictfp class NumericsTest
     }
 
     /**
+     * Tests {@link Numerics#isSimplePrecision(double[])}.
+     */
+    @Test
+    public void testIsSimplePrecision() {
+        assertTrue (Numerics.isSimplePrecision(2, 0.5, 0.25, Double.NaN, Double.POSITIVE_INFINITY));
+        assertFalse(Numerics.isSimplePrecision(2, 0.5, 1.0 / 3));
+    }
+
+    /**
      * Tests the {@link Numerics#epsilonEqual(double, double, ComparisonMode)} method.
      */
     @Test
@@ -162,4 +171,17 @@ public final strictfp class NumericsTest
         final float recomposed = StrictMath.scalb((float) expected, e);
         assertEquals(value, StrictMath.copySign(recomposed, value), 0f);
     }
+
+    /**
+     * Tests {@link Numerics#suggestFractionDigits(double[][])}.
+     */
+    @Test
+    public void testSuggestFractionDigits() {
+        final int[] f = Numerics.suggestFractionDigits(new double[][] {
+            {10, 100,   0.1, 1000.1},
+            {15, 140.1, 0.4, Double.NaN},
+            {20, 400,   0.5, Double.NaN}
+        });
+        assertArrayEquals(new int[] {0, -2, 1, 3}, f);
+    }
 }



Mime
View raw message