sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 02/02: Make `CoordinateFormat` more robust to change of CRS.
Date Wed, 22 Apr 2020 17:16:17 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 0707557f01169941fd6aa69dc445dc2f0fedb829
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Apr 22 17:50:21 2020 +0200

    Make `CoordinateFormat` more robust to change of CRS.
---
 .../java/org/apache/sis/gui/map/StatusBar.java     |   3 +-
 .../gui/referencing/RecentReferenceSystems.java    |  10 +-
 .../org/apache/sis/geometry/CoordinateFormat.java  | 269 ++++++++++++++-------
 .../apache/sis/referencing/GeodeticCalculator.java |   3 +-
 .../apache/sis/geometry/CoordinateFormatTest.java  |   9 +-
 5 files changed, 201 insertions(+), 93 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
index ae8f08b..5f4abe9 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/StatusBar.java
@@ -51,6 +51,7 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.portrayal.RenderException;
 import org.apache.sis.internal.util.Strings;
+import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Exceptions;
@@ -371,7 +372,7 @@ public class StatusBar extends Widget implements EventHandler<MouseEvent>
{
             sourceCoordinates = targetCoordinates.coordinates;      // Okay to share array
if same dimension.
         }
         setDisplayCRS(crs);
-        format.setPrecision(resolution, unit);
+        format.setGroundPrecision(Quantities.create(resolution, unit));
         lastX = lastY = Double.NaN;
     }
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
index fd4b0da..cba3feb 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/referencing/RecentReferenceSystems.java
@@ -590,7 +590,7 @@ public class RecentReferenceSystems {
                      */
                     final ObservableList<ReferenceSystem> items = referenceSystems;
                     final ComparisonMode mode = duplicationCriterion.get();
-                    final int count = items.size() - NUM_OTHER_ITEMS;
+                    int count = items.size() - NUM_OTHER_ITEMS;
                     boolean found = false;
                     for (int i=0; i<count; i++) {
                         if (Utilities.deepEquals(newValue, items.get(i), mode)) {
@@ -608,7 +608,13 @@ public class RecentReferenceSystems {
                      */
                     if (!found) {
                         if (count >= NUM_SHOWN_ITEMS) {
-                            items.remove(count - 1);        // Remove the last item before
`OTHER`.
+                            final List<ReferenceSystem> selected = getSelectedItems();
+                            for (int i=count; --i >= NUM_CORE_ITEMS;) {
+                                if (!selected.contains(items.get(i))) {         // Do not
remove selected items.
+                                    items.remove(i);                            // Remove
an item before `OTHER`.
+                                    if (--count < NUM_SHOWN_ITEMS) break;
+                                }
+                            }
                         }
                         items.add(Math.min(items.size(), NUM_CORE_ITEMS), newValue);
                         notifyChanges();
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
index 4be8aaf..b1e925d 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/geometry/CoordinateFormat.java
@@ -32,6 +32,7 @@ import java.io.IOException;
 import java.io.UncheckedIOException;
 import javax.measure.Unit;
 import javax.measure.UnitConverter;
+import javax.measure.Quantity;
 import javax.measure.quantity.Time;
 import javax.measure.IncommensurableException;
 import org.opengis.geometry.DirectPosition;
@@ -58,6 +59,7 @@ import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Units;
+import org.apache.sis.measure.Quantities;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.io.CompoundFormat;
 
@@ -79,11 +81,10 @@ import org.apache.sis.io.CompoundFormat;
  * or by overriding the {@link #createFormat(Class)} protected method.
  *
  * <h2>Coordinate reference system</h2>
- * If the Coordinate Reference System (CRS) of the positions to format is known, it should
- * {@linkplain #setDefaultCRS(CoordinateReferenceSystem) be specified} before to invoke other
methods in this
- * class because that CRS may impact calculation of properties like the number of fraction
digits achieving a
- * {@linkplain #setPrecision(double, Unit) specified precision}.
- * That default CRS is used only if the {@link DirectPosition} does not specify its own CRS.
+ * {@code CoordinateFormat} uses the {@link DirectPosition#getCoordinateReferenceSystem()}
value for determining
+ * how to format each coordinate value. If the position does not specify a coordinate reference
system, then the
+ * {@linkplain #setDefaultCRS(CoordinateReferenceSystem) default CRS} is assumed. If no default
CRS has been
+ * specified, then all coordinates are formatted as decimal numbers.
  *
  * <p>{@code CoordinateFormat} does <strong>not</strong> transform the
given coordinates in a unique CRS.
  * If the coordinates need to be formatted in a specific CRS, then the caller should
@@ -122,6 +123,9 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     /**
      * The separator between each coordinate values to be formatted.
      * The default value is a space.
+     *
+     * @see #getSeparator()
+     * @see #setSeparator(String)
      */
     private String separator;
 
@@ -131,6 +135,13 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     private String parseSeparator;
 
     /**
+     * The desired ground precision, or {@code null} if unspecified.
+     *
+     * @see #setGroundPrecision(Quantity)
+     */
+    private Quantity<?> groundPrecision;
+
+    /**
      * The desired precisions for each coordinate, or {@code null} if unspecified.
      * The length of this array does not need to be equal to the number of dimensions;
      * extraneous values are ignored and missing values are assumed equal to 0.
@@ -145,14 +156,29 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     private double[] desiredPrecisions;
 
     /**
+     * Whether the {@link Format} instances have been configured for the precision specified
by
+     * {@link #groundPrecision} and {@link #desiredPrecisions}. We use a field separated
from
+     * {@link #lastCRS} because precision needs to be set only for formatting, not for parsing.
+     *
+     * @see #setPrecisions(double...)
+     * @see #setGroundPrecision(Quantity)
+     * @see #configure(CoordinateReferenceSystem)
+     */
+    private transient boolean isPrecisionApplied;
+
+    /**
      * The coordinate reference system to assume if no CRS is attached to the position to
format.
      * May be {@code null}.
+     *
+     * @see #setDefaultCRS(CoordinateReferenceSystem)
      */
     private CoordinateReferenceSystem defaultCRS;
 
     /**
-     * The coordinate reference system of the last {@link DirectPosition} that we formatted.
+     * The coordinate reference system of the last {@link DirectPosition} that we parsed
or formatted.
      * This is used for determining if we need to recompute all other transient fields in
this class.
+     *
+     * @see #createFormats(CoordinateReferenceSystem)
      */
     private transient CoordinateReferenceSystem lastCRS;
 
@@ -164,6 +190,8 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     /**
      * The type for each value in the {@code formats} array, or {@code null} if not yet computed.
      * Types are: 0=number, 1=longitude, 2=latitude, 3=other angle, 4=date, 5=elapsed time.
+     *
+     * @see #createFormats(CoordinateReferenceSystem)
      */
     private transient byte[] types;
 
@@ -172,6 +200,8 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * not been able to configure the precision. This is the same array than {@link #formats},
      * unless {@link #setPrecisions(double...)} has been invoked.
      * Values at different indices may reference the same {@link Format} instance.
+     *
+     * @see #createFormats(CoordinateReferenceSystem)
      */
     private transient Format[] sharedFormats;
 
@@ -179,6 +209,8 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * The formats to use for formatting each coordinate value, or {@code null} if not yet
computed.
      * The length of this array should be equal to the number of dimensions in {@link #lastCRS}.
      * Values at different indices may reference the same {@link Format} instance.
+     *
+     * @see #createFormats(CoordinateReferenceSystem)
      */
     private transient Format[] formats;
 
@@ -271,7 +303,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     /**
      * Returns the coordinate reference system to use if no CRS is explicitly associated
to a given {@code DirectPosition}.
      * This CRS determines the type of format to use for each coordinate (number, angle or
date) and the number of fraction
-     * digits to use for achieving a {@linkplain #setPrecision(double, Unit) specified precision}.
+     * digits to use for achieving a {@linkplain #setGroundPrecision(Quantity) specified
precision on ground}.
      *
      * @return the default coordinate reference system, or {@code null} if none.
      */
@@ -291,33 +323,44 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     }
 
     /**
-     * Computes the value of transient fields from the given CRS.
+     * Computes the values of transient fields from the given CRS. The {@link #lastCRS} field
is set to the given CRS
+     * for allowing callers to check if this method needs to be invoked again (this method
does not check by itself).
+     * This method does not configure the formats for precisions specified by {@link #setPrecisions(double...)}
and
+     * related methods; that work is done by {@link #configure(CoordinateReferenceSystem)}
at formatting time
+     * (it is not needed at parsing time).
+     *
+     * @param  crs  the CRS for which to create the {@link Format} instances.
+     *
+     * @see #configure(CoordinateReferenceSystem)
      */
-    private void configure(final CoordinateReferenceSystem crs) {
-        types         = null;
-        formats       = null;
-        sharedFormats = null;
-        units         = null;
-        toFormatUnit  = null;
-        unitSymbols   = null;
-        epochs        = null;
-        negate        = 0;
-        lastCRS       = crs;
-        if (crs == null) {
-            return;
-        }
+    private void createFormats(final CoordinateReferenceSystem crs) {
+        types              = null;
+        formats            = null;
+        sharedFormats      = null;
+        units              = null;
+        toFormatUnit       = null;
+        unitSymbols        = null;
+        epochs             = null;
+        negate             = 0L;
+        lastCRS            = crs;
+        isPrecisionApplied = false;
         /*
          * If no CRS were specified, we will format everything as numbers. Working with null
CRS
          * is sometime useful because null CRS are allowed in DirectPosition according ISO
19107.
-         * Otherwise (if a CRS is given), infer the format subclasses from the axes.
          */
+        if (crs == null) {
+            return;
+        }
         final CoordinateSystem cs = crs.getCoordinateSystem();
         if (cs == null) {
             return;                                    // Paranoiac check (should never be
null).
         }
-        final int dimension = cs.getDimension();
-        final byte[]   types   = new byte  [dimension];
-        final Format[] formats = new Format[dimension];
+        /*
+         * Otherwise (if a CRS is given), infer the format subclasses from the axes.
+         */
+        final int      dimension = cs.getDimension();
+        final byte[]   types     = new byte  [dimension];
+        final Format[] formats   = new Format[dimension];
         for (int i=0; i<dimension; i++) {
             final CoordinateSystemAxis axis = cs.getAxis(i);
             if (axis == null) {                                               // Paranoiac
check.
@@ -383,9 +426,9 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
                 }
             }
         }
-        this.types    = types;         // Assign only on success.
+        this.types    = types;          // Assign only on success.
         this.formats  = formats;
-        sharedFormats = formats;
+        sharedFormats = formats;        // `getFormatClone(int)` will separate arrays later
if needed.
     }
 
     /**
@@ -461,9 +504,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * @since 1.1
      */
     public double[] getPrecisions() {
-        if (lastCRS != defaultCRS) {
-            configure(defaultCRS);
-        }
+        configure(defaultCRS);
         Format[] cf = formats;
         if (cf == null) {
             cf = new Format[DEFAULT_DIMENSION];
@@ -492,21 +533,21 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * will be shown with two fraction digits when formatted as decimal numbers, or with
"D°MM"
      * pattern when formatted as angles.
      *
-     * <p>This precision does not have a clear relationship to the precision on the
ground.
+     * <p>This precision does not have a direct relationship to the precision on the
ground.
      * For example a precision of 0.01 could be one centimeter or 10 meters, depending if
      * the units of measurement in that dimension is meter or kilometer.
-     * For a precision related to the ground, use {@link #setPrecision(double, Unit)} instead.</p>
+     * For a precision related to the ground, use {@link #setGroundPrecision(Quantity)} instead.</p>
      *
      * <p>If any value in the given array is 0 or {@link Double#NaN}, then there is
a choice:
-     * if {@link #setPrecision(double, Unit)} has been invoked, the ground precision specified
to that
+     * if {@link #setGroundPrecision(Quantity)} has been invoked, the precision specified
to that
      * method will apply (if possible). Otherwise an implementation-specific default precision
is used.
-     * So it is possible to use {@link #setPrecision(double, Unit)} for specifying a precision
in "real
-     * world" units and to use this {@code setPrecisions(double...)} method for adjusting
the precision
-     * of only the vertical axis for example.</p>
+     * A typical use case is to use {@link #setGroundPrecision(Quantity)} for specifying
an horizontal
+     * precision in "real world" units and to use this {@code setPrecisions(double...)} method
for adjusting
+     * the precision of the vertical axis only.</p>
      *
      * @param  precisions  desired precision at which to format coordinate values in each
dimension
-     *                     (may have 0 values for unspecified precision in some of those
dimensions),
-     *                     or {@code null} for restoring the default values.
+     *                     (may have 0 or {@link Double#NaN} values for unspecified precisions
in some
+     *                     of those dimensions), or {@code null} for restoring the default
values.
      *
      * @see AngleFormat#setPrecision(double, boolean)
      * @see DecimalFormat#setMaximumFractionDigits(int)
@@ -514,20 +555,27 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * @since 1.1
      */
     public void setPrecisions(final double... precisions) {
+        /*
+         * Implementation note: this method configures (indirectly through calls to `applyPrecision(int)`)
+         * the formats given in the `formats` array but does not touch the `sharedFormats`
array. This is
+         * the opposite of `setGroundPrecision(…)` which performs a more global change
that affect formats
+         * in the `sharedFormats` array.
+         */
         if (precisions == null) {
             desiredPrecisions = null;
-            formats = sharedFormats;
+            formats = sharedFormats;        // `getFormatClone(int)` will separate arrays
later if needed.
         } else {
-            if (desiredPrecisions == null || desiredPrecisions.length != 0) {
+            if (desiredPrecisions == null || desiredPrecisions.length != precisions.length)
{
                 desiredPrecisions = new double[precisions.length];
-                Arrays.fill(desiredPrecisions, Double.NaN);
+                // Initial zero values mean "unspecified".
             }
+            isPrecisionApplied &= (formats != null);
             for (int i=0; i<precisions.length; i++) {
                 double p = Math.abs(precisions[i]);
                 if (!(p < Double.POSITIVE_INFINITY)) p = 0;                 // Use ! for
replacing NaN.
                 if (desiredPrecisions[i] != (desiredPrecisions[i] = p)) {
                     // Precision changed. Keep format up to date.
-                    if (formats != null && i < formats.length) {
+                    if (isPrecisionApplied && i < formats.length) {
                         applyPrecision(i);
                     }
                 }
@@ -544,7 +592,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     private void applyPrecision(final int dim) {
         final double precision = desiredPrecisions[dim];
         if (precision > 0) {
-            final Format format = formats[dim];
+            final Format format = formats[dim];                 // Will be cloned below if
needed.
             /*
              * Intentionally check the DecimalFormat subtype, not the more generic NumberFormat
type,
              * because the calculation below assumes base 10 and assumes that fraction digits
are for
@@ -562,13 +610,57 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
     }
 
     /**
+     * Computes the values of transient fields from the given CRS and configure the format
precisions.
+     * This method updates the {@link #lastCRS} and {@link #isPrecisionApplied} fields.
+     * This method does nothing if above-cited fields are already up to date.
+     *
+     * @param  crs  the CRS for which to create and configure the {@link Format} instances.
+     *
+     * @see #createFormats(CoordinateReferenceSystem)
+     */
+    private void configure(final CoordinateReferenceSystem crs) {
+        if (lastCRS != crs) {
+            createFormats(crs);             // This method sets the `lastCRS` field.
+        }
+        if (!isPrecisionApplied) {
+            if (groundPrecision != null) {
+                applyGroundPrecision(crs);
+            }
+            if (desiredPrecisions != null) {
+                if (sharedFormats == null) {
+                    formats = sharedFormats = new Format[desiredPrecisions.length];
+                    Arrays.fill(formats, getDefaultFormat());
+                    types = new byte[formats.length];
+                }
+                final int n = Math.min(desiredPrecisions.length, formats.length);
+                for (int i=0; i<n; i++) {
+                    applyPrecision(i);          // Will clone Format instances if needed.
+                }
+            }
+            isPrecisionApplied = true;
+        }
+    }
+
+    /**
+     * @deprecated Renamed {@link #setGroundPrecision(Quantity)}
+     * for avoiding confusion with {@link #setPrecisions(double...)}.
+     *
+     * @param  resolution  the desired resolution.
+     * @param  unit        unit of the desired resolution.
+     *
+     * @since 1.0
+     */
+    @Deprecated
+    public void setPrecision(double resolution, Unit<?> unit) {
+        setGroundPrecision(Quantities.create(resolution, unit));
+    }
+
+    /**
      * Adjusts the number of fraction digits to show in coordinates for achieving the given
precision.
      * The {@link NumberFormat} and {@link AngleFormat} are configured for coordinates expressed
in the
-     * {@linkplain #getDefaultCRS() default coordinate reference system} defined at the moment
this method is invoked.
-     * The number of fraction digits is <em>not</em> updated if a different CRS
is specified after this method call
-     * or if the coordinates to format are associated to a different CRS.
+     * coordinate reference system of the position to format.
      *
-     * <p>The given resolution will be converted to the units used by coordinate system
axes. For example if a 10 metres
+     * The given resolution will be converted to the units used by coordinate system axes.
For example if a 10 metres
      * resolution is specified but the {@linkplain #getDefaultCRS() default CRS} axes use
kilometres, then this method
      * converts the resolution to 0.01 kilometre and uses that value for inferring that coordinates
should be formatted
      * with 2 fraction digits. If the resolution is specified in an angular units such as
degrees, this method uses the
@@ -576,21 +668,41 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
      * computing an equivalent resolution in linear units. For example if the ellipsoid of
default CRS is WGS84,
      * then this method considers a resolution of 1 second of angle as equivalent to a resolution
of about 31 meters.
      * Conversions work also in the opposite direction (from linear to angular units) and
are also used for choosing
-     * which angle fields (degrees, minutes or seconds) to show.</p>
+     * which angle fields (degrees, minutes or seconds) to show.
      *
-     * @param  resolution  the desired resolution.
-     * @param  unit        unit of the desired resolution.
+     * <p>If both {@link #setPrecisions(double...)} and {@code setGroundPrecision(Quantity)}
are used,
+     * then the values specified with {@code setPrecisions(…)} have precedence and this
ground precision
+     * is used only as a fallback. A typical use case is to specify the ground precision
for horizontal
+     * dimensions, then to specify a different precision <var>dz</var> for the
vertical axis only with
+     * {@code setPrecisions(NaN, NaN, dz)}.</p>
+     *
+     * @param  precision  the desired precision together with its linear or angular unit.
      *
      * @see DecimalFormat#setMaximumFractionDigits(int)
      * @see AngleFormat#setPrecision(double, boolean)
      *
-     * @since 1.0
+     * @since 1.1
      */
     @SuppressWarnings("null")
-    public void setPrecision(double resolution, Unit<?> unit) {
-        ArgumentChecks.ensureFinite("resolution", resolution);
-        ArgumentChecks.ensureNonNull("unit", unit);
-        resolution = Math.abs(resolution);
+    public void setGroundPrecision(final Quantity<?> precision) {
+        ArgumentChecks.ensureNonNull("precision", precision);
+        groundPrecision = precision;
+        if (isPrecisionApplied) {
+            applyGroundPrecision(lastCRS);
+        }
+    }
+
+    /**
+     * Configures the formats for {@link #groundPrecision} value. Contrarily to {@link #applyPrecision(int)},
+     * this method modifies the default formats provided by {@link #getFormat(Class)}. They
are the formats
+     * stored in the {@link #sharedFormats} array. Those formats are used as fallback when
the {@link #formats}
+     * array does not provide more specific format.
+     *
+     * @param  crs  the target CRS in the conversion from ground units to CRS units.
+     */
+    private void applyGroundPrecision(final CoordinateReferenceSystem crs) {
+        final double resolution = Math.abs(groundPrecision.getValue().doubleValue());
+        final Unit<?> unit = groundPrecision.getUnit();
         if (Units.isTemporal(unit)) {
             return;                                 // Setting temporal resolution is not
yet implemented.
         }
@@ -603,7 +715,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
         Resolution related   = null;
         IncommensurableException error = null;
         if (specified.isAngular || Units.isLinear(unit)) try {
-            related = specified.related(ReferencingUtilities.getEllipsoid(defaultCRS));
+            related = specified.related(ReferencingUtilities.getEllipsoid(crs));
         } catch (IncommensurableException e) {
             error = e;
         }
@@ -613,8 +725,8 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
          * units which result in the finest resolution.
          */
         boolean relatedUsed = false;
-        if (defaultCRS != null) {
-            final CoordinateSystem cs = defaultCRS.getCoordinateSystem();
+        if (crs != null) {
+            final CoordinateSystem cs = crs.getCoordinateSystem();
             if (cs != null) {                                                   // Paranoiac
check (should never be null).
                 final int dimension = cs.getDimension();
                 for (int i=0; i<dimension; i++) {
@@ -636,7 +748,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
             }
         }
         if (error != null) {
-            Logging.unexpectedException(Logging.getLogger(Loggers.MEASURE), CoordinateFormat.class,
"setPrecision", error);
+            Logging.unexpectedException(Logging.getLogger(Loggers.MEASURE), CoordinateFormat.class,
"setGroundPrecision", error);
         }
         specified.setPrecision(this);
         if (relatedUsed) {
@@ -646,9 +758,9 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
 
     /**
      * Desired resolution in a given units, together with methods for converting to the units
of a coordinate system axis.
-     * This is a helper class for {@link CoordinateFormat#setPrecision(double, Unit)} implementation.
An execution of that
-     * method typically creates two instances of this {@code Resolution} class: one for the
resolution in metres and another
-     * one for the resolution in degrees.
+     * This is a helper class for {@link CoordinateFormat#setGroundPrecision(Quantity)} implementation.
An execution of
+     * that method typically creates two instances of this {@code Resolution} class: one
for the resolution in metres
+     * and another one for the resolution in degrees.
      */
     private static final class Resolution {
         /** The desired resolution in the unit of measurement given by {@link #unit}. */
@@ -726,7 +838,8 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
 
         /**
          * Configures the {@link NumberFormat} or {@link AngleFormat} for a number of fraction
digits
-         * sufficient for the given resolution.
+         * sufficient for the given resolution. This method configures the shared formats
returned by
+         * {@link #getFormat(Class)}. They are the formats stored in the {@link #sharedFormats}
array.
          */
         void setPrecision(final CoordinateFormat owner) {
             final Format format = owner.getFormat(isAngular ? Angle.class : Number.class);
@@ -872,26 +985,12 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
         if (crs == null) {
             crs = defaultCRS;                           // May still be null.
         }
-        if (crs != lastCRS) {
-            configure(crs);
-        }
         /*
-         * Unconditionally configure the formatters for the desired precisions because those
precisions
-         * may change for every point. Note that the formatters may not have been created
if the CRS is
-         * null (because `configure(…)` does not know which format to use), in which case
generic number
-         * formats will be used.
+         * Configure the formatters for the desired precision, which can potentially change
for each point.
+         * Note that the formatters may not have been created if the CRS is null (because
`createFormats(…)`
+         * does not know which format to use), in which case generic number formats will
be used.
          */
-        if (desiredPrecisions != null) {
-            if (sharedFormats == null) {
-                formats = sharedFormats = new Format[desiredPrecisions.length];
-                Arrays.fill(formats, getDefaultFormat());
-                types = new byte[formats.length];
-            }
-            final int n = Math.min(desiredPrecisions.length, formats.length);
-            for (int i=0; i<n; i++) {
-                applyPrecision(i);
-            }
-        }
+        configure(crs);
         /*
          * Standard java.text.Format API can only write into a StringBuffer. If the given
Appendable is not a
          * StringBuffer, then we will need to format in a temporary buffer before to copy
to the Appendable.
@@ -937,7 +1036,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
                     case LONGITUDE: object = new Longitude (value); break;
                     case LATITUDE:  object = new Latitude  (value); break;
                     case ANGLE:     object = new Angle     (value); break;
-                    case DATE:      object = new Date(Math.round(value) + epochs[i]); break;
+                    case DATE:      object = new Date(Math.addExact(Math.round(value), epochs[i]));
break;
                 }
             } else {
                 object = value;
@@ -1005,7 +1104,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
          * If no such CRS has been specified, then we will parse everything as plain numbers.
          */
         if (lastCRS != defaultCRS) {
-            configure(defaultCRS);
+            createFormats(defaultCRS);
         }
         final double[] coordinates;
         Format format;
@@ -1079,7 +1178,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
             if (object instanceof Angle) {
                 value = ((Angle) object).degrees();
             } else if (object instanceof Date) {
-                value = ((Date) object).getTime() - epochs[i];
+                value = Math.subtractExact(((Date) object).getTime(), epochs[i]);
             } else {
                 value = ((Number) object).doubleValue();
             }
@@ -1150,7 +1249,7 @@ public class CoordinateFormat extends CompoundFormat<DirectPosition>
{
         final CoordinateFormat clone = (CoordinateFormat) super.clone();
         clone.dummy  = null;
         clone.buffer = null;
-        clone.configure(null);
+        clone.createFormats(null);
         if (desiredPrecisions != null) {
             clone.desiredPrecisions = desiredPrecisions.clone();
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java
index 8ed2c3b..4dd9582 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/GeodeticCalculator.java
@@ -36,6 +36,7 @@ import org.opengis.geometry.DirectPosition;
 import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Units;
+import org.apache.sis.measure.Quantities;
 import org.apache.sis.geometry.CoordinateFormat;
 import org.apache.sis.internal.referencing.PositionTransformer;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -1109,7 +1110,7 @@ public class GeodeticCalculator {
                 final CoordinateFormat pointFormat = new CoordinateFormat(locale, null);
                 pointFormat.setSeparator("\t");      // For distributing coordinate values
on different columns.
                 pointFormat.setDefaultCRS(crs);
-                pointFormat.setPrecision(Formulas.LINEAR_TOLERANCE, Units.METRE);
+                pointFormat.setGroundPrecision(Quantities.create(Formulas.LINEAR_TOLERANCE,
Units.METRE));
                 final TableAppender table = new TableAppender(buffer, " │ ");
                 table.setCellAlignment(TableAppender.ALIGN_CENTER);
                 table.appendHorizontalSeparator();
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java
index d098362..60b1e38 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/geometry/CoordinateFormatTest.java
@@ -24,6 +24,7 @@ import java.text.ParseException;
 import java.io.IOException;
 import org.opengis.geometry.DirectPosition;
 import org.apache.sis.measure.Angle;
+import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.crs.HardCodedCRS;
 import org.apache.sis.test.mock.VerticalCRSMock;
@@ -257,16 +258,16 @@ public final strictfp class CoordinateFormatTest extends TestCase {
     }
 
     /**
-     * Tests {@link CoordinateFormat#setPrecision(double, Unit)}.
+     * Tests {@link CoordinateFormat#setGroundPrecision(Quantity)}.
      */
     @Test
-    public void testSetPrecision() {
+    public void testSetGroundPrecision() {
         final CoordinateFormat format = new CoordinateFormat(Locale.FRANCE, null);
         final DirectPosition2D pos = new DirectPosition2D(40.123456789, 9.87654321);
         format.setDefaultCRS(HardCodedCRS.WGS84_φλ);
-        format.setPrecision(0.01, Units.GRAD);
+        format.setGroundPrecision(Quantities.create(0.01, Units.GRAD));
         assertEquals("40°07,4′N 9°52,6′E", format.format(pos));
-        format.setPrecision(0.01, Units.METRE);
+        format.setGroundPrecision(Quantities.create(0.01, Units.METRE));
         assertEquals("40°07′24,4444″N 9°52′35,5556″E", format.format(pos));
     }
 


Mime
View raw message