sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Separated GeodeticCalculator.setDirection(double, double) in two methods: setStartingAzimuth(double) and setGeodesicDistance(double). The name of the previous method was vague, and it happens frequently in practice that we want to update only one of those two properties.
Date Tue, 14 May 2019 12:35:02 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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 11a4781  Separated GeodeticCalculator.setDirection(double, double) in two methods:
setStartingAzimuth(double) and setGeodesicDistance(double). The name of the previous method
was vague, and it happens frequently in practice that we want to update only one of those
two properties.
11a4781 is described below

commit 11a47812244b69450c7935aa41124c62c2e2db50
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue May 14 14:33:43 2019 +0200

    Separated GeodeticCalculator.setDirection(double, double) in two methods: setStartingAzimuth(double)
and setGeodesicDistance(double).
    The name of the previous method was vague, and it happens frequently in practice that
we want to update only one of those two properties.
---
 .../org/apache/sis/services/LocationServlet.java   |   3 +-
 .../sis/referencing/gazetteer/SimpleLocation.java  |   2 +-
 .../org/apache/sis/distance/LatLonPointRadius.java |   6 +-
 .../apache/sis/internal/referencing/Resources.java |  10 +-
 .../sis/internal/referencing/Resources.properties  |   2 +-
 .../internal/referencing/Resources_fr.properties   |   2 +-
 .../apache/sis/referencing/GeodeticCalculator.java | 199 ++++++++++++---------
 .../sis/referencing/GeodeticCalculatorTest.java    |   6 +-
 8 files changed, 135 insertions(+), 95 deletions(-)

diff --git a/application/sis-webapp/src/main/java/org/apache/sis/services/LocationServlet.java
b/application/sis-webapp/src/main/java/org/apache/sis/services/LocationServlet.java
index df4533d..419fe4d 100644
--- a/application/sis-webapp/src/main/java/org/apache/sis/services/LocationServlet.java
+++ b/application/sis-webapp/src/main/java/org/apache/sis/services/LocationServlet.java
@@ -308,12 +308,13 @@ public class LocationServlet extends HttpServlet {
         double radiusKM = Double.parseDouble(radius);
         final GeodeticCalculator calculator = GeodeticCalculator.create(CommonCRS.SPHERE.geographic());
         calculator.setStartPoint(point.y, point.x);
+        calculator.setGeodesicDistance(radiusKM);
 
         StringBuilder regionStr = new StringBuilder();
 
         try {
           for (int i = 0; i <= 360; i += 10) {
-            calculator.setDirection(i, radiusKM);
+            calculator.setStartingAzimuth(i);
             DirectPosition pt = calculator.getEndPoint();
             regionStr.append(pt.getOrdinate(1)).append(',').append(pt.getOrdinate(0)).append(',');
           }
diff --git a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
index 03d803d..276f8f5 100644
--- a/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
+++ b/core/sis-referencing-by-identifiers/src/main/java/org/apache/sis/referencing/gazetteer/SimpleLocation.java
@@ -133,7 +133,7 @@ class SimpleLocation extends AbstractLocation implements DirectPosition,
Envelop
     /**
      * Returns the coordinate reference system the envelope and the position.
      * Default implementation returns {@link CommonCRS#defaultGeographic()}.
-     * Subclasses must override this method if the another CRS.
+     * Subclasses must override this method if they use another CRS.
      */
     @Override
     public CoordinateReferenceSystem getCoordinateReferenceSystem() {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/distance/LatLonPointRadius.java
b/core/sis-referencing/src/main/java/org/apache/sis/distance/LatLonPointRadius.java
index 74b0cab..0dc59dc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/distance/LatLonPointRadius.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/distance/LatLonPointRadius.java
@@ -84,11 +84,12 @@ public class LatLonPointRadius {
 
     final GeodeticCalculator calculator = GeodeticCalculator.create(CommonCRS.SPHERE.geographic());
     calculator.setStartPoint(center.getOrdinate(1), center.getOrdinate(0));
+    calculator.setGeodesicDistance(radius);
 
     try {
       for (int i = 0; i < numberOfPoints; i++)
       {
-        calculator.setDirection(i * bearingIncrement, radius);
+        calculator.setStartingAzimuth(i * bearingIncrement);
         DirectPosition p = calculator.getEndPoint();
         points[i] = new DirectPosition2D(p.getOrdinate(1), p.getOrdinate(0));
       }
@@ -119,9 +120,10 @@ public class LatLonPointRadius {
 
     Path2D path = new Path2D.Double();
     double initX = Double.NaN, previousX = Double.NaN;
+    calculator.setGeodesicDistance(radius);
     try {
       for (int i = 0; i < 360; i++) {
-        calculator.setDirection(i, radius);
+        calculator.setStartingAzimuth(i);
         DirectPosition pt = calculator.getEndPoint();
         double x = pt.getOrdinate(1) + 180.0;
         double y = pt.getOrdinate(0) + 90.0;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
index 4af6b0b..5a419f7 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
@@ -202,11 +202,6 @@ public final class Resources extends IndexedResourceBundle {
         public static final short EllipsoidalHeightNotAllowed_1 = 77;
 
         /**
-         * The destination coordinates have not been specified.
-         */
-        public static final short EndPointNotSet = 88;
-
-        /**
          * There is no local registry for version {1} of “{0}” authority. Fallback on
default version
          * for objects creation.
          */
@@ -478,6 +473,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short SingularMatrix = 63;
 
         /**
+         * The {0,choice,0#start|1#end} point has not been specified.
+         */
+        public static final short StartOrEndPointNotSet_1 = 88;
+
+        /**
          * Combined URI contains unexpected components.
          */
         public static final short UnexpectedComponentInURI = 80;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
index 7d78b45..755e08b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
@@ -62,7 +62,6 @@ CanNotUseGeodeticParameters_2     = Can not use the {0} geodetic parameters:
{1}
 ColinearAxisDirections_2          = Axis directions {0} and {1} are colinear.
 CoordinateOperationNotFound_2     = Coordinate conversion of transformation from system \u201c{0}\u201d
to \u201c{1}\u201d has not been found.
 DatumOriginShallBeDate            = Origin of temporal datum shall be a date.
-EndPointNotSet                    = The destination coordinates have not been specified.
 DuplicatedParameterName_4         = Name or alias for parameter \u201c{0}\u201d at index
{1} conflict with name \u201c{2}\u201d at index {3}.
 DuplicatedSpatialComponents_1     = Compound coordinate reference systems can not contain
two {0,choice,1#horizontal|2#vertical} components.
 EllipsoidalHeightNotAllowed_1     = Compound coordinate reference systems should not contain
ellipsoidal height. Use a three-dimensional {0,choice,0#geographic|1#projected} system instead.
@@ -106,6 +105,7 @@ NoSuchOperationMethod_1           = No operation method found for name
or identi
 ParameterNotFound_2               = No parameter named \u201c{1}\u201d has been found in
\u201c{0}\u201d.
 RecursiveCreateCallForCode_2      = Recursive call while creating an object of type \u2018{0}\u2019
for code \u201c{1}\u201d.
 SingularMatrix                    = Matrix is singular.
+StartOrEndPointNotSet_1           = The {0,choice,0#start|1#end} point has not been specified.
 UnexpectedComponentInURI          = Combined URI contains unexpected components.
 UnexpectedDimensionForCS_1        = Unexpected dimension for a coordinate system of type
\u2018{0}\u2019.
 UnitlessParameter_1               = Parameter \u201c{0}\u201d does not expect unit.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
index 7f624ba..aca7f24 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
@@ -67,7 +67,6 @@ CanNotUseGeodeticParameters_2     = Ne peut pas utiliser les param\u00e8tres
g\u
 ColinearAxisDirections_2          = Les directions d\u2019axes {0} et {1} sont colin\u00e9aires.
 CoordinateOperationNotFound_2     = La conversion ou transformation des coordonn\u00e9es
du syst\u00e8me \u00ab\u202f{0}\u202f\u00bb vers \u00ab\u202f{1}\u202f\u00bb n\u2019a pas
\u00e9t\u00e9 trouv\u00e9e.
 DatumOriginShallBeDate            = L\u2019origine d\u2019un r\u00e9f\u00e9rentiel temporel
doit \u00eatre une date.
-EndPointNotSet                    = Les coordonn\u00e9es de la destination n\u2019ont pas
\u00e9t\u00e9 d\u00e9finies.
 DuplicatedParameterName_4         = Le nom ou un alias pour le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb
\u00e0 l\u2019index {1} duplique le nom \u00ab\u202f{2}\u202f\u00bb \u00e0 l\u2019index {3}.
 DuplicatedSpatialComponents_1     = Un syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
ne peut pas contenir deux composantes {0,choice,1#horizontales|2#verticales}.
 EllipsoidalHeightNotAllowed_1     = Un syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es
ne devrait pas contenir une hauteur ellipso\u00efdale. Utilisez plut\u00f4t un syst\u00e8me
{0,choice,0#g\u00e9ographique|1#projet\u00e9} \u00e0 trois dimensions.
@@ -111,6 +110,7 @@ NoSuchOperationMethod_1           = Aucune m\u00e9thode n\u2019a \u00e9t\u00e9
t
 ParameterNotFound_2               = Aucun param\u00e8tre nomm\u00e9 \u00ab\u202f{1}\u202f\u00bb
n\u2019a \u00e9t\u00e9 trouv\u00e9 dans \u00ab\u202f{0}\u202f\u00bb.
 RecursiveCreateCallForCode_2      = Appels r\u00e9cursifs lors de la cr\u00e9ation d\u2019un
objet de type \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb.
 SingularMatrix                    = La matrice est singuli\u00e8re.
+StartOrEndPointNotSet_1           = Le point {0,choice,0#de d\u00e9part|1#d\u2019arriv\u00e9}
n\u2019a pas \u00e9t\u00e9 d\u00e9fini.
 UnexpectedComponentInURI          = L\u2019URI combin\u00e9 contient des composantes qui
n\u2019\u00e9taient pas attendues.
 UnexpectedDimensionForCS_1        = Dimension inattendue pour un syst\u00e8me de coordonn\u00e9es
de type \u2018{0}\u2019.
 UnitlessParameter_1               = Le param\u00e8tre \u00ab\u202f{0}\u202f\u00bb n\u2019attend
pas d\u2019unit\u00e9.
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 6ad9e73..b671b0d 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
@@ -52,12 +52,13 @@ import static java.lang.Math.*;
  *
  * <p>This class uses the following information:</p>
  * <ul>
- *   <li>The {@linkplain #setStartPoint(Position) start point}, which is always considered
valid.
- *     It is initially set at (0,0) and can only be changed to another valid value.</li>
- *   <li>One of the followings (the latest specified property overrides other properties
and determines what will be calculated):
+ *   <li>The {@linkplain #setStartPoint(Position) start point}, which is always considered
valid after the first call
+ *     to {@code setStartPoint(…)}. Its value can only be changed by another call to {@code
setStartPoint(…)}.</li>
+ *   <li>One of the followings (the latest specified properties override other properties
and determines what will be calculated):
  *     <ul>
  *       <li>the {@linkplain #setEndPoint(Position) end point}, or</li>
- *       <li>an {@linkplain #setDirection(double, double) azimuth and distance}.</li>
+ *       <li>the {@linkplain #setStartingAzimuth(double) azimuth at start point} together
with
+ *           the {@linkplain #setGeodesicDistance(double) geodesic distance} from that point.</li>
  *     </ul>
  *   </li>
  * </ul>
@@ -104,18 +105,25 @@ public class GeodeticCalculator {
     /**
      * The (<var>latitude</var>, <var>longitude</var>) coordinates
of the start point <strong>in radians</strong>.
      * This point is set by {@link #setStartPoint(double, double)}.
+     *
+     * @see #START_POINT
      */
     private double φ1, λ1;
 
     /**
      * The (<var>latitude</var>, <var>longitude</var>) coordinates
of the end point <strong>in radians</strong>.
      * This point is set by {@link #setEndPoint(double, double)}.
+     *
+     * @see #END_POINT
      */
     private double φ2, λ2;
 
     /**
      * The azimuth at start point and end point, in radians between -π and +π.
      * 0° point toward North and values are increasing clockwise.
+     *
+     * @see #STARTING_AZIMUTH
+     * @see #ENDING_AZIMUTH
      */
     private double α1, α2;
 
@@ -123,21 +131,23 @@ public class GeodeticCalculator {
      * The distance from the starting point ({@link #φ1},{@link #λ1}) to the end point
({@link #φ2},{@link #λ2}).
      * The distance is in the same units than ellipsoid axes and the azimuth is in radians.
      *
+     * @see #GEODESIC_DISTANCE
      * @see #getDistanceUnit()
      */
-    private double distance;
+    private double geodesicDistance;
 
     /**
-     * Tells if the end point is valid.
-     * This is {@code false} if {@link #φ2} and {@link #λ2} need to be computed.
+     * A bitmask specifying which information are valid. For example if the {@link #END_POINT}
bit is not set,
+     * then {@link #φ2} and {@link #λ2} need to be computed, which implies the computation
of {@link #α1} and
+     * {@link #α2} as well. If the {@link #GEODESIC_DISTANCE} bit is not set, then {@link
#geodesicDistance}
+     * needs to be computed, which implies recomputation of {@link #α1} and {@link #α2}
as well.
      */
-    private boolean isEndPointValid;
+    private int validity;
 
     /**
-     * Tells if the azimuths and the distance are valid.
-     * If {@code false} then {@link #distance}, {@link #α1} and {@link #α2} need to be
computed.
+     * Bitmask specifying which information are valid.
      */
-    private boolean isCourseValid;
+    private static final int START_POINT = 1, END_POINT = 2, STARTING_AZIMUTH = 4, ENDING_AZIMUTH
= 8, GEODESIC_DISTANCE = 16;
 
     /**
      * Constructs a new geodetic calculator expecting coordinates in the supplied CRS.
@@ -225,8 +235,7 @@ public class GeodeticCalculator {
         ArgumentChecks.ensureFinite("longitude", longitude);
         φ1 = toRadians(latitude);
         λ1 = toRadians(longitude);
-        isEndPointValid = false;
-        isCourseValid = false;
+        validity = START_POINT;
     }
 
     /**
@@ -252,11 +261,16 @@ public class GeodeticCalculator {
      *
      * @return the starting point represented in the CRS specified at construction time.
      * @throws TransformException if the coordinates can not be transformed to {@linkplain
#getPositionCRS() position CRS}.
+     * @throws IllegalStateException if the start point has not been specified.
      *
      * @see #getEndPoint()
      */
     public DirectPosition getStartPoint() throws TransformException {
-        return geographic(φ1, λ1).inverseTransform();
+        if ((validity & START_POINT) != 0) {
+            return geographic(φ1, λ1).inverseTransform();
+        } else {
+            throw new IllegalStateException(Resources.format(Resources.Keys.StartOrEndPointNotSet_1,
0));
+        }
     }
 
     /**
@@ -278,9 +292,8 @@ public class GeodeticCalculator {
 
     /**
      * Sets the destination as geographic (<var>latitude</var>, <var>longitude</var>)
coordinates.
-     * The azimuth and geodesic distance values will be updated as an effect of this call.
-     * They will be recomputed next time that {@link #getStartingAzimuth()}, {@link #getEndingAzimuth()}
-     * or {@link #getGeodesicDistance()} is invoked.
+     * The {@linkplain #getStartingAzimuth() starting azimuth}, {@linkplain #getEndingAzimuth()
ending azimuth}
+     * and {@linkplain #getGeodesicDistance() geodesic distance} will be updated as an effect
of this call.
      *
      * @param  latitude   the latitude in degrees between {@value Latitude#MIN_VALUE}° and
{@value Latitude#MAX_VALUE}°.
      * @param  longitude  the longitude in degrees.
@@ -293,8 +306,8 @@ public class GeodeticCalculator {
         ArgumentChecks.ensureFinite("longitude", longitude);
         φ2 = toRadians(latitude);
         λ2 = toRadians(longitude);
-        isEndPointValid = true;
-        isCourseValid = false;
+        validity |= END_POINT;
+        validity &= ~(STARTING_AZIMUTH | ENDING_AZIMUTH | GEODESIC_DISTANCE);
     }
 
     /**
@@ -314,11 +327,12 @@ public class GeodeticCalculator {
     }
 
     /**
-     * Returns or computes the destination in the CRS specified at construction time. This
method returns the
-     * point specified in the last call to a {@link #setEndPoint(double, double) setEndPoint(…)}
method, unless
-     * {@link #setDirection(double, double) setDirection(…)} has been invoked more recently.
In the later case,
-     * the end point will be computed from the {@linkplain #getStartPoint() starting point}
and the specified
-     * azimuth and distance.
+     * Returns or computes the destination in the CRS specified at construction time. This
method returns
+     * the point specified in the last call to a {@link #setEndPoint(Position) setEndPoint(…)}
method,
+     * unless the {@linkplain #setStartingAzimuth(double) starting azimuth} and
+     * {@linkplain #setGeodesicDistance(double) geodesic distance} have been set more recently.
+     * In the later case, the end point will be computed from the {@linkplain #getStartPoint()
start point}
+     * and the current azimuth and distance.
      *
      * @return the destination (end point) represented in the CRS specified at construction
time.
      * @throws TransformException if the coordinates can not be transformed to {@linkplain
#getPositionCRS() position CRS}.
@@ -327,90 +341,97 @@ public class GeodeticCalculator {
      * @see #getStartPoint()
      */
     public DirectPosition getEndPoint() throws TransformException {
-        if (!isEndPointValid) {
+        if ((validity & END_POINT) == 0) {
             computeEndPoint();
         }
         return geographic(φ2, λ2).inverseTransform();
     }
 
     /**
-     * Sets the azimuth and the distance from the {@linkplain #getStartPoint() starting point}.
-     * The direction is relative to geographic North, with values increasing clockwise.
-     * The distance is usually a positive number, but negative numbers are accepted
-     * as courses in direction opposite to the given azimuth.
-     *
-     * <p>The {@linkplain #getEndPoint() end point} will be updated as an effect of
this method call;
-     * it will be recomputed the next time that {@link #getEndPoint()} is invoked.</p>
+     * Sets the angular heading (relative to geographic North) at the starting point.
+     * Azimuth is relative to geographic North with values increasing clockwise.
+     * The {@linkplain #getEndPoint() end point} and {@linkplain #getEndingAzimuth() ending
azimuth}
+     * will be updated as an effect of this method call.
      *
-     * @param  azimuth   the starting azimuth in degrees, with 0° toward north and values
increasing clockwise.
-     * @param  distance  the geodesic distance in unit of measurement given by {@link #getDistanceUnit()}.
+     * @param  azimuth  the starting azimuth in degrees, with 0° toward north and values
increasing clockwise.
      *
-     * @see #getStartingAzimuth()
-     * @see #getGeodesicDistance()
+     * @see #setGeodesicDistance(double)
      */
-    public void setDirection(double azimuth, double distance) {
-        ArgumentChecks.ensureFinite("azimuth",  azimuth);
-        ArgumentChecks.ensureFinite("distance", distance);
-        if (distance < 0) {
-            distance = -distance;
-            azimuth += 180;
-        }
-        α1              = toRadians(IEEEremainder(azimuth, 360));
-        this.distance   = distance;
-        isEndPointValid = false;
-        isCourseValid   = true;
+    public void setStartingAzimuth(final double azimuth) {
+        ArgumentChecks.ensureFinite("azimuth", azimuth);
+        α1 = toRadians(IEEEremainder(azimuth, 360));
+        validity |= STARTING_AZIMUTH;
+        validity &= ~(END_POINT | ENDING_AZIMUTH);
     }
 
     /**
-     * Returns the angular heading (relative to geographic North) at the starting point.
+     * Returns or computes the angular heading (relative to geographic North) at the starting
point.
      * This method returns the azimuth normalized to [-180 … +180]° range given in last
call to
-     * <code>{@linkplain #setDirection(double, double) setDirection}(azimuth, …)</code>
method,
-     * unless the {@link #setEndPoint(double, double) setEndPoint(…)} method has been invoked
more recently.
-     * In the later case, the azimuth will be computed from the {@linkplain #getStartPoint
start point}.
+     * {@link #setStartingAzimuth(double)} method, unless the {@link #setEndPoint(Position)
setEndPoint(…)}
+     * method has been invoked more recently. In the later case, the azimuth will be computed
from the
+     * {@linkplain #getStartPoint() start point} and the current end point.
      *
      * @return the azimuth in degrees from -180° to +180°. 0° is toward North and values
are increasing clockwise.
-     * @throws IllegalStateException if the destination point, azimuth or distance have not
been set.
+     * @throws IllegalStateException if the end point, azimuth or distance have not been
set.
      */
     public double getStartingAzimuth() {
-        if (!isCourseValid) {
-            computeCourse();
+        if ((validity & STARTING_AZIMUTH) == 0) {
+            computeTrack();
         }
         return toDegrees(α1);
     }
 
     /**
-     * Returns the angular heading (relative to geographic North) at the ending point.
+     * Computes the angular heading (relative to geographic North) at the ending point. This
method computes the azimuth
+     * from the current {@linkplain #setStartPoint(Position) start point} and {@linkplain
#setEndPoint(Position) end point},
+     * or from start point and the current {@linkplain #setStartingAzimuth(double) starting
azimuth} and
+     * {@linkplain #setGeodesicDistance(double) geodesic distance}.
      *
      * @return the azimuth in degrees from -180° to +180°. 0° is toward North and values
are increasing clockwise.
      * @throws IllegalStateException if the destination point, azimuth or distance have not
been set.
      */
     public double getEndingAzimuth() {
-        if (!isCourseValid) {
-            computeCourse();
-        } else if (!isEndPointValid) {
-            computeEndPoint();
+        if ((validity & ENDING_AZIMUTH) == 0) {
+            if ((validity & END_POINT) == 0) {
+                computeEndPoint();                      // Compute also ending azimuth from
start point and distance.
+            } else {
+                computeTrack();                         // Compute also ending azimuth from
start point and end point.
+            }
         }
         return toDegrees(α2);
     }
 
     /**
-     * Returns the shortest distance from start point to end point.
-     * This is sometime called "great circle" or "orthodromic" distance.
-     * This method returns the absolute distance value set by the last call to
-     * {@link #setDirection(double,double) setDirection(…, distance)} method,
-     * unless the {@link #setEndPoint(double, double) setEndPoint(…)} method has been invoked
more recently.
-     * In the later case, the distance will be computed from the {@linkplain #getStartPoint
start point} to the end point.
-     *
-     * @return The shortest distance in the unit of measurement given by {@link #getDistanceUnit()}.
+     * Sets the geodesic distance from the start point to the end point. The {@linkplain
#getEndPoint() end point}
+     * and {@linkplain #getEndingAzimuth() ending azimuth} will be updated as an effect of
this method call.
+     *
+     * @param  distance  the geodesic distance in unit of measurement given by {@link #getDistanceUnit()}.
+     *
+     * @see #setStartingAzimuth(double)
+     */
+    public void setGeodesicDistance(final double distance) {
+        ArgumentChecks.ensurePositive("distance", distance);
+        geodesicDistance = distance;
+        validity |= GEODESIC_DISTANCE;
+        validity &= ~(END_POINT | ENDING_AZIMUTH);
+    }
+
+    /**
+     * Returns or computes the shortest distance from start point to end point. This is sometime
called "great circle"
+     * or "orthodromic" distance. This method returns the value given in last call to {@link
#setGeodesicDistance(double)},
+     * unless the {@link #setEndPoint(Position) setEndPoint(…)} method has been invoked
more recently. In the later case,
+     * the distance will be computed from the {@linkplain #getStartPoint() start point} and
current end point.
+     *
+     * @return the shortest distance in the unit of measurement given by {@link #getDistanceUnit()}.
      * @throws IllegalStateException if the destination point has not been set.
      *
      * @see #getDistanceUnit()
      */
     public double getGeodesicDistance() {
-        if (!isCourseValid) {
-            computeCourse();
+        if ((validity & GEODESIC_DISTANCE) == 0) {
+            computeTrack();
         }
-        return distance;
+        return geodesicDistance;
     }
 
     /**
@@ -428,7 +449,7 @@ public class GeodeticCalculator {
     /**
      * Computes the end point from the start point, the azimuth and the geodesic distance.
      * This method should be invoked if the end point or ending azimuth is requested while
-     * {@link #isEndPointValid} is {@code false}.
+     * {@link #END_POINT} validity flag is not set.
      *
      * <p>The default implementation computes {@link #φ2}, {@link #λ2} and {@link
#α2} using
      * spherical formulas. Subclasses should override if they can provide ellipsoidal formulas.</p>
@@ -436,10 +457,14 @@ public class GeodeticCalculator {
      * @throws IllegalStateException if the azimuth and the distance have not been set.
      */
     void computeEndPoint() {
-        if (!isCourseValid) {
-            throw new IllegalStateException(Resources.format(Resources.Keys.AzimuthAndDistanceNotSet));
+        if ((validity & (START_POINT | STARTING_AZIMUTH | GEODESIC_DISTANCE))
+                     != (START_POINT | STARTING_AZIMUTH | GEODESIC_DISTANCE))
+        {
+            throw new IllegalStateException((validity & START_POINT) == 0
+                    ? Resources.format(Resources.Keys.StartOrEndPointNotSet_1, 0)
+                    : Resources.format(Resources.Keys.AzimuthAndDistanceNotSet));
         }
-        final double Δσ    = distance / radius;
+        final double Δσ    = geodesicDistance / radius;
         final double sinΔσ = sin(Δσ);
         final double cosΔσ = cos(Δσ);
         final double sinφ1 = sin(φ1);
@@ -456,19 +481,29 @@ public class GeodeticCalculator {
         φ2 = atan(sinφ2 / hypot(Δλx, Δλy));                         // Improve accuracy
close to poles.
         λ2 = IEEEremainder(λ1 + Δλ, 2*PI);
         α2 = atan2(sinα1, cosΔσ*cosα1 - sinφ1/cosφ1 * sinΔσ);
-        isEndPointValid = true;
+        validity |= END_POINT;
     }
 
     /**
      * Computes the geodetic distance and azimuths from the start point and end point.
      * This method should be invoked if the distance or an azimuth is requested while
-     * {@link #isCourseValid} is {@code false}.
+     * {@link #STARTING_AZIMUTH}, {@link #ENDING_AZIMUTH} or {@link #GEODESIC_DISTANCE}
+     * validity flag is not set.
+     *
+     * <p>Note on terminology:</p>
+     * <ul>
+     *   <li><b>Course:</b> the intended path of travel.</li>
+     *   <li><b>Track:</b>  the actual path traveled over ground.</li>
+     * </ul>
      *
      * @throws IllegalStateException if the distance or azimuth has not been set.
      */
-    private void computeCourse() {
-        if (!isEndPointValid) {
-            throw new IllegalStateException(Resources.format(Resources.Keys.EndPointNotSet));
+    private void computeTrack() {
+        if ((validity & (START_POINT | END_POINT))
+                     != (START_POINT | END_POINT))
+        {
+            throw new IllegalStateException(Resources.format(
+                    Resources.Keys.StartOrEndPointNotSet_1, Integer.signum(validity &
START_POINT)));
         }
         final double Δλ    = λ2 - λ1;           // No need to reduce to −π … +π
range.
         final double sinΔλ = sin(Δλ);
@@ -491,8 +526,8 @@ public class GeodeticCalculator {
          */
         double Δσ = sinφ1*sinφ2 + cosφ1*cosφ2*cosΔλ;        // Actually Δσ = acos(…).
         Δσ = atan2(hypot(α1x, α1y), Δσ);
-        distance = radius * Δσ;
-        isCourseValid = true;
+        geodesicDistance = radius * Δσ;
+        validity |= (STARTING_AZIMUTH | ENDING_AZIMUTH | GEODESIC_DISTANCE);
     }
 
     /**
@@ -541,7 +576,7 @@ public class GeodeticCalculator {
         } catch (IOException e) {
             throw new UncheckedIOException(e);      // Should never happen since we are writting
in a StringBuilder.
         }
-        buffer.append(String.format(locale, " %f %s", distance, getDistanceUnit()));
+        buffer.append(String.format(locale, " %f %s", geodesicDistance, getDistanceUnit()));
         return buffer.toString();
     }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
b/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
index 9c31de6..512a00b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/GeodeticCalculatorTest.java
@@ -128,7 +128,8 @@ public final strictfp class GeodeticCalculatorTest extends TestCase {
          * Keep start point unchanged, but set above azimuth and distance.
          * Verify that we get the Shanghai coordinates.
          */
-        c.setDirection(-94.41, 18743000);
+        c.setStartingAzimuth(-94.41);
+        c.setGeodesicDistance(18743000);
         assertEquals("α₁",        -94.41, c.getStartingAzimuth(), 1E-12);           //
Should be the specified value.
         assertEquals("α₂",        -78.42, c.getEndingAzimuth(),   0.01);
         assertEquals("distance",   18743, c.getGeodesicDistance() / 1000, STRICT);  // Should
be the specified value.
@@ -164,7 +165,8 @@ public final strictfp class GeodeticCalculatorTest extends TestCase {
         c.setStartPoint(new DirectPosition2D(λ, φ));
         assertPositionEquals(λ, φ, c.getStartPoint(), Formulas.ANGULAR_TOLERANCE);
 
-        c.setDirection(-94.41, 18743000);
+        c.setStartingAzimuth(-94.41);
+        c.setGeodesicDistance(18743000);
         assertPositionEquals(121.8, 31.4, c.getEndPoint(), 0.01);
     }
 }


Mime
View raw message