You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/10/01 17:25:11 UTC

[sis] branch geoapi-4.0 updated (7c07889 -> ea466fa)

This is an automated email from the ASF dual-hosted git repository.

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


    from 7c07889  Updates paths and properties to new syntax or location.
     new 6274488  Reduce the numbers of authorities declared in the ESRI-specific projections: limit to ESRI, OGC and Proj4.
     new d650236  feat (sis-referencing & internal) : implement cylindrical and conic satellite-tracking projections from Snyder's 'Map Projections - a working Manual'. Don't compute derivatives yet. Very few tests.
     new c9746c3  feat (sis-referencing & internal) : add test for cylindrical satellite-tracking projection and refactor of the ConicSatelliteTracking classe to use methods instead of lambda expressions
     new 2b94c39  feat (referencing) : add some tests for satellite-tracking projections from tables 38 and 39 of Snyder's Manual (page 238). Fix the condition of the while loop when applying Newton method.
     new a8a3962  feat&refactor (referencing- satellite tracking) : refactor to extract terms from transform/inverseTransform methods AND try to compute Jacobian Matrix. Tests not passed for the conic satellite tracking projection yet. Refactor should be lead to compute L coefficient and its derivative in a method.
     new 47873d4  fix (referencing satellite-tracking projections) : fix the Jacobian matrix in both satellite-tracking projections by inverting m01 and m10 in order to have the partial derivative of x on the matrix's first line and of y on the second line.
     new 8c2d72d  refactor (referencing satellite-tracking projections) : Method used by both (cylindrical and conic) direct transformation of the Satellite-tracking projections to  compute the L coefficient and its partial derivate dL_dφ if queried. Test passed for both transformation and testDerivativeOnSphere.
     new 4f9f695  Merge remote-tracking branch 'Satellite_Tracking_Projection' into geoapi-4.0. Some refactoring to come in next commits.
     new ea466fa  Reformatting: - Format copyright header in the same way than other files. - Rearrange some import order and remove some empty lines. - Set version number to 1.1.

The 9 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../sis/internal/referencing/provider/ESRI.java    |  59 ++-
 .../referencing/provider/MapProjection.java        |  55 +--
 .../provider/PolarStereographicSouth.java          |   4 +-
 .../referencing/provider/SatelliteTracking.java    | 149 +++++++
 .../referencing/provider/package-info.java         |   3 +-
 .../projection/ConicSatelliteTracking.java         | 442 +++++++++++++++++++++
 .../projection/CylindricalSatelliteTracking.java   | 141 +++++++
 .../operation/projection/package-info.java         |   3 +-
 .../referencing/provider/Descriptions.properties   |   5 +
 .../referencing/provider/ProvidersTest.java        |  13 +-
 .../projection/ConicSatelliteTrackingTest.java     | 269 +++++++++++++
 .../CylindricalSatelliteTrackingTest.java          | 228 +++++++++++
 .../sis/test/suite/ReferencingTestSuite.java       |   4 +-
 ide-project/NetBeans/build.xml                     |   1 +
 14 files changed, 1304 insertions(+), 72 deletions(-)
 create mode 100644 core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
 create mode 100644 core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
 create mode 100644 core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
 create mode 100644 core/sis-referencing/src/main/resources/org/apache/sis/internal/referencing/provider/Descriptions.properties
 create mode 100644 core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
 create mode 100644 core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java


[sis] 02/09: feat (sis-referencing & internal) : add test for cylindrical satellite-tracking projection and refactor of the ConicSatelliteTracking classe to use methods instead of lambda expressions

Posted by de...@apache.org.
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 c9746c3198d9cefcde6394e4089cfa5ac506e2b3
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Tue Jul 23 12:52:07 2019 +0200

    feat (sis-referencing & internal) : add test for cylindrical satellite-tracking projection and refactor of the ConicSatelliteTracking classe to use methods instead of lambda expressions
---
 .../referencing/provider/SatelliteTracking.java    |   3 -
 .../projection/ConicSatelliteTracking.java         | 110 +++++++++++++--------
 .../projection/CylindricalSatelliteTracking.java   |   2 +-
 .../projection/ConicSatelliteTrackingTest.java     |  59 ++++++++++-
 .../CylindricalSatelliteTrackingTest.java          | 103 +++++++++++++++++--
 5 files changed, 223 insertions(+), 54 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
index 3e9382d..6784123 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -19,10 +19,7 @@
 package org.apache.sis.internal.referencing.provider;
 
 import javax.xml.bind.annotation.XmlTransient;
-import static org.apache.sis.internal.referencing.provider.AbstractProvider.createZeroConstant;
-import static org.apache.sis.internal.referencing.provider.Mercator1SP.LATITUDE_OF_ORIGIN;
 import org.apache.sis.measure.Units;
-import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.operation.projection.ConicSatelliteTracking;
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index 3c8d556..f7247df 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -27,7 +27,6 @@ import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.OperationMethod;
 
 import static java.lang.Math.*;
-import java.util.function.DoubleUnaryOperator;
 import org.apache.sis.internal.referencing.Resources;
 import static org.apache.sis.internal.referencing.provider.SatelliteTracking.*;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
@@ -100,11 +99,14 @@ public class ConicSatelliteTracking extends NormalizedProjection{
      * Projection Cone's constant.
      */
     private final double n;
+    
     /**
-     * Approximation of the limiting lattitude to which the {@code L coefficient}
+     * Approximation of the Minimum latitude at infinite radius. 
+     * 
+     * Limiting latitude to which the {@code L coefficient}
      * is associated with a particular case {@code L equals -s0/n}.
      */
-    private final double lattitudeLimit;
+    private final double latitudeLimit;
     /**
      * Boolean attribute indicating if the projection cone's constant is positive.
      * */
@@ -174,31 +176,29 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             final double sin_φ0 = sin(φ0);
             final double φ2 = toRadians(initializer.getAndStore(STANDARD_PARALLEL_2));   //appropriated use of toRadians??
 
-//        final double F0 = atan( (p2_on_p1*cos2_φ0 - cos_i)/sqrt(cos2_φ0 - cos2_i) );
-//        final double F1 = atan( (p2_on_p1*cos2_φ1 - cos_i)/sqrt(cos2_φ1 - cos2_i) );
-//        final double F2 = atan( (p2_on_p1*cos2_φ2 - cos_i)/sqrt(cos2_φ2 - cos2_i) );
-//        final double dλ0 = -asin(sin_φ0/sin_i);
-//        final double dλ1 = -asin(sin_φ1/sin_i);
-//        final double dλ2 = -asin(sin_φ2/sin_i);   
-//        final double λt0 = atan( tan(dλ0) * cos_i );
-//        final double λt1 = atan( tan(dλ1) * cos_i );
-//        final double λt2 = atan( tan(dλ2) * cos_i );
-            final DoubleUnaryOperator computeFn = (cos2_φn) -> atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
-            final double F0 = computeFn.applyAsDouble(cos2_φ0);
+//            final DoubleUnaryOperator computeFn = (cos2_φn) -> atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
+//            final double F0 = computeFn.applyAsDouble(cos2_φ0);
+//            final double F1 = computeFn.applyAsDouble(cos2_φ1);
+//            final DoubleUnaryOperator computedλn = (sin_φn) -> -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
+//            final double dλ0 = computedλn.applyAsDouble(sin_φ0);
+//            final double dλ1 = computedλn.applyAsDouble(sin_φ1);
+//            final DoubleUnaryOperator computeλtn = (dλn) -> atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
+//            final double λt0 = computeλtn.applyAsDouble(dλ0);
+//            final double λt1 = computeλtn.applyAsDouble(dλ1);
+
+            final double F0 = computeFn(cos2_φ0);
             /*
-         *Inclination of the groundtrack to the meridian at latitude φ1
+             * Inclination of the groundtrack to the meridian at latitude φ1
              */
-            final double F1 = computeFn.applyAsDouble(cos2_φ1);
+            final double F1 = computeFn(cos2_φ1);
 
             cos_φ1xsin_F1 = cos_φ1 * sin(F1);
 
-            final DoubleUnaryOperator computedλn = (sin_φn) -> -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
-            final double dλ0 = computedλn.applyAsDouble(sin_φ0);
-            final double dλ1 = computedλn.applyAsDouble(sin_φ1);
+            final double dλ0 = computedλn(sin_φ0);
+            final double dλ1 = computedλn(sin_φ1);
 
-            final DoubleUnaryOperator computeλtn = (dλn) -> atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
-            final double λt0 = computeλtn.applyAsDouble(dλ0);
-            final double λt1 = computeλtn.applyAsDouble(dλ1);
+            final double λt0 = computeλtn(dλ0);
+            final double λt1 = computeλtn(dλ1);
 
             final double L0 = λt0 - p2_on_p1 * dλ0;
             final double L1 = λt1 - p2_on_p1 * dλ1;
@@ -211,10 +211,12 @@ public class ConicSatelliteTracking extends NormalizedProjection{
                 final double cos_φ2 = cos(φ2);
                 final double cos2_φ2 = cos_φ2 * cos_φ2;
                 final double sin_φ2 = sin(φ2);
-                final double dλ2 = computedλn.applyAsDouble(sin_φ2);
-                final double λt2 = computeλtn.applyAsDouble(dλ2);
+//                final double dλ2 = computedλn.applyAsDouble(sin_φ2);
+                final double dλ2 = computedλn(sin_φ2);
+                final double λt2 = computeλtn(dλ2);
                 final double L2 = λt2 - p2_on_p1 * dλ2;
-                final double F2 = computeFn.applyAsDouble(cos2_φ2);
+//                final double F2 = computeFn.applyAsDouble(cos2_φ2);
+                final double F2 = computeFn(cos2_φ2);
                 n = (F2 - F1) / (L2 - L1);
             } else {
                 n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder 
@@ -223,28 +225,24 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             ρ0 = cos_φ1xsin_F1 / (n * sin(n * L0 + s0)); // *R in eq.28-12 in Snyder 
 
             //======================== Unsure ======================================
-            // Aim to assess the limit lattitude associated with -s0/n L-value.
+            // Aim to assess the limit latitude associated with -s0/n L-value.
             //
-            lattitudeLimit = lattitudeFromNewtonMethod(-s0 / n);
+            latitudeLimit = latitudeFromNewtonMethod(-s0 / n);
             positiveN = (n >= 0);
             //======================================================================
 
 //            //Additionally we can compute the radius of the circle to which groundtracks
 //            //are tangent on the map :
 //            ρs = cos_φ1xsin_F1 / n; //*R
-
             //======================================================================        
             /*
          * At this point, all parameters have been processed. Now process to their
          * validation and the initialization of (de)normalize affine transforms.
              */
-        final MatrixSIS normalize   = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
-//        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
-        normalize  .convertAfter (0, n, null);  //For conic tracking
-//        denormalize.convertBefore(0, 1/PI,     null);
-//        denormalize.convertBefore(1, , null);
+            final MatrixSIS normalize = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
+            normalize.convertAfter(0, n, null);  //For conic tracking
         }else{
-            n = lattitudeLimit = cos_φ1xsin_F1 = s0 = ρ0 = NaN;
+            n = latitudeLimit = cos_φ1xsin_F1 = s0 = ρ0 = NaN;
             positiveN = false;
         }
     }
@@ -275,7 +273,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         /*
          * According to the Snyder (page 236) in those cases cannot or should not be plotted.
          */
-        if( ((positiveN)&&(φ<=lattitudeLimit)) || ((!positiveN)&&(φ>=lattitudeLimit)) ){
+        if( ((positiveN)&&(φ<=latitudeLimit)) || ((!positiveN)&&(φ>=latitudeLimit)) ){
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
         
@@ -285,11 +283,11 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         final double L      = λt - p2_on_p1 * dλ;
         
         /*
-         * As {@code lattitudeLimit} is an approximation we repeat the test here.
+         * As {@code latitudeLimit} is an approximation we repeat the test here.
          */
         if( ((positiveN)&&(L<=-s0/n)) || ((!positiveN)&&(L>=-s0/n)) ){
             //TODO if usefull, could we add :
-            // lattitudeLimit = φ;
+            // latitudeLimit = φ;
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
         
@@ -349,10 +347,10 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         
         //TODO ensure that λ0 will be added. ;  In eq. Snyder 28-23 : λ0 + θ/n
         dstPts[dstOff  ] =  θ ;//λ 
-        dstPts[dstOff+1] = lattitudeFromNewtonMethod(L); //φ
+        dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
     }
     
-    protected final double lattitudeFromNewtonMethod(final double l){
+    protected final double latitudeFromNewtonMethod(final double l){
         double dλ = -PI/2;
         double dλn = Double.MIN_VALUE;
         double A, A2, Δdλ;
@@ -382,10 +380,40 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     }
     
     
-    public double getLattitudeLimit(){
-        return lattitudeLimit;
+    public double getLatitudeLimit(){
+        return latitudeLimit;
     }
     
+    /**
+     * Method to compute the φn coefficient according to equation 28-9
+     * in Snyder's Map Projections manual.
+     * 
+     * @param cos2_φn : square of the φn 's cosinus.
+     * @return Fn  coefficient associated with the φn latittude.
+     */
+    private double computeFn(final double cos2_φn) {
+        return atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
+    }
+    /**
+     * Method to compute the φn coefficient according to equation 28-2a
+     * in Snyder's Map Projections manual.
+     * 
+     * @param sin_φn : φn 's sinus.
+     * @return dλn  coefficient associated with the φn latittude.
+     */
+    private double computedλn(final double sin_φn) {
+        return -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
+    }
+    /**
+     * Method to compute the φn coefficient according to equation 28-3a
+     * in Snyder's Map Projections manual.
+     * 
+     * @param dλn  coefficient associated with the φn latittude.
+     * @return λtn  coefficient associated with the φn latittude.
+     */
+    private double computeλtn(final double dλn) {
+        return atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
+    }
 //    /**
 //     * Radius of the circle radius of the circle to which groundtracks
 //     * are tangent on the map.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index 70ea6fe..1284583 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -191,7 +191,7 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         final double L   = y * dF1 / cos_φ1; // In eq. Snyder 28-19 : L = y * dF1 / R . cos_φ1
         
         dstPts[dstOff  ] =  x;
-        dstPts[dstOff+1] = lattitudeFromNewtonMethod(L); //φ
+        dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
     }
 
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
index 28d83df..0b20c88 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -18,8 +18,8 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
-import static java.lang.Double.NaN;
 import java.util.Collections;
+import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.referencing.NilReferencingObject;
 import org.apache.sis.internal.referencing.provider.SatelliteTracking;
 import org.apache.sis.measure.Units;
@@ -57,7 +57,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      * @param λ0 : central meridian.
      * @param φ1 : first parallel of conformality, with true scale.
      * @param φ2 : second parallel of conformality, without true scale.
-     * @param φ0 : lattitude_of_origin : latitude Crossing the central meridian
+     * @param φ0 : latitude_of_origin : latitude Crossing the central meridian
      * at the desired origin of rectangular coordinates (null or NaN for
      * cylindrical satellite tracking projection.)
      * @return
@@ -98,6 +98,9 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
     /**
      * Tests the projection of a few points on a sphere.
      *
+     * Test based on the numerical example given by Snyder p. 360 to 363 of 
+     * <cite> Map Projections - A working Manual</cite>
+     *
      * @throws FactoryException if an error occurred while creating the map
      * projection.
      * @throws TransformException if an error occurred while projecting a point.
@@ -112,7 +115,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
                 -90,     //central_meridian
                 45,      //standard_parallel_1
                 70,      //standard_parallel_2
-                30       //lattitude_of_origin
+                30       //latitude_of_origin
         );
         assertTrue(isInverseTransformSupported);
         verifyTransform(
@@ -123,5 +126,55 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
                     0.2001910, 0.2121685
                 });
     }
+    
+    /**
+     * Tests the projection of a few points on a sphere.
+     *
+     * Test based on the sample coordinates for several of the
+     * Satellite-Tracking Projections shown in table 39 from
+     * <cite> Map Projections - A working Manual</cite>
+     *
+     * @throws FactoryException if an error occurred while creating the map
+     * projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    public void testSampleCoordinates() throws FactoryException, TransformException {
+        
+        //Following tests don't pass with the former tolerance.
+        tolerance = Formulas.LINEAR_TOLERANCE; 
+        
+        //----------------------------------------------------------------------
+        // φ1 = 30° ; φ2 = 60°
+        //---------------------
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                -90,     //central_meridian
+                30,      //standard_parallel_1
+                60,      //standard_parallel_2
+                30       //latitude_of_origin
+        );
+//        double xConverterFactor=0.017453;
+//        verifyTransform(
+//                new double[]{ // (λ,φ) coordinates in degrees to project.
+//                      0,  0,
+////                     10,  0, 
+////                    -10, 10,
+////                     60, 40,
+////                     80, 70,
+////                    -120, 80.908  //Tracking limit
+//                },
+//                new double[]{ // Expected (x,y) results in metres.
+//                    0, 0,
+////                    xConverterFactor *   10,  0,
+////                    xConverterFactor *  -10,  0.17579,
+////                    xConverterFactor *   60,  0.79741,
+////                    xConverterFactor *   80,  2.34465,
+////                    xConverterFactor * -120,  7.23571 //Tracking limit
+//                });
+        
+    }
 
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
index 0a86ba6..c207af3 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -51,7 +51,7 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * @param λ0 : central meridian.
      * @param φ1 : first parallel of conformality, with true scale.
      * @param φ2 : second parallel of conformality, without true scale.
-     * @param φ0 : lattitude_of_origin : latitude Crossing the central meridian
+     * @param φ0 : latitude_of_origin : latitude Crossing the central meridian
      * at the desired origin of rectangular coordinates (null or NaN for
      * cylindrical satellite tracking projection.)
      * @return
@@ -66,6 +66,9 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
     /**
      * Tests the projection of a few points on a sphere.
      *
+     * Test based on the numerical example given by Snyder p. 360 to 363 of 
+     * <cite> Map Projections - A working Manual</cite>
+     * 
      * @throws FactoryException if an error occurred while creating the map
      * projection.
      * @throws TransformException if an error occurred while projecting a point.
@@ -89,7 +92,28 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                 new double[]{ // Expected (x,y) results in metres.
                     0.2267249, 0.6459071
                 });
+    }
+    
+    /**
+     * Tests the projection of a few points on a sphere.
+     *
+     * Test based on the sample coordinates for several of the
+     * Satellite-Tracking Projections shown in table 38 from
+     * <cite> Map Projections - A working Manual</cite>
+     *
+     * @throws FactoryException if an error occurred while creating the map
+     * projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    public void testSampleCoordinates() throws FactoryException, TransformException {
         
+        //Following tests don't pass with the former tolerance.
+        tolerance = Formulas.LINEAR_TOLERANCE; 
+        
+        //----------------------------------------------------------------------
+        // φ1 = 0°
+        //---------
         createProjection(
                 99.092,  //satellite_orbit_inclination
                 103.267, //satellite_orbital_period
@@ -97,21 +121,88 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                 0,       //central_meridian
                 0        //standard_parallel_1
         );
+       
+        double xConverterFactor=0.017453;
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                      0,  0,
+                     10,  0, 
+                    -10, 10,
+                     60, 40,
+                     80, 70,
+                    -120, 80.908  //Tracking limit
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    0, 0,
+                    xConverterFactor *   10,  0,
+                    xConverterFactor *  -10,  0.17579,
+                    xConverterFactor *   60,  0.79741,
+                    xConverterFactor *   80,  2.34465,
+                    xConverterFactor * -120,  7.23571 //Tracking limit
+                });
         
-        tolerance = Formulas.LINEAR_TOLERANCE; //Don't pass with the former tolerance.
         
-        final double xConverterFactor=0.017453;
+        //----------------------------------------------------------------------
+        // φ1 = +- 30°
+        //------------
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                0,       //central_meridian
+                -30        //standard_parallel_1
+        );
+       
+        xConverterFactor=0.015115;
         verifyTransform(
                 new double[]{ // (λ,φ) coordinates in degrees to project.
                       0,  0,
                      10,  0, 
-                    -10, 10
+                    -10, 10,
+                     60, 40,
+                     80, 70,
+                    -120, 80.908  //Tracking limit
                 },
                 new double[]{ // Expected (x,y) results in metres.
                     0, 0,
-                    xConverterFactor *  10,  0,
-                    xConverterFactor * -10,  0.17579,
+                    xConverterFactor *   10,  0,
+                    xConverterFactor *  -10,  0.14239,
+                    xConverterFactor *   60,  0.64591,
+                    xConverterFactor *   80,  1.89918,
+                    xConverterFactor * -120,  5.86095 //Tracking limit
                 });
+        
+        //----------------------------------------------------------------------
+        // φ1 = +- 45°
+        //------------
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                0,       //central_meridian
+                45        //standard_parallel_1
+        );
+       
+        xConverterFactor=0.012341;
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                      0,  0,
+                     10,  0, 
+                    -10, 10,
+                     60, 40,
+                     80, 70,
+                    -120, 80.908  //Tracking limit
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    0, 0,
+                    xConverterFactor *   10,  0,
+                    xConverterFactor *  -10,  0.10281,
+                    xConverterFactor *   60,  0.46636,
+                    xConverterFactor *   80,  1.37124,
+                    xConverterFactor * -120,  4.23171 //Tracking limit
+                });
+        
+        
     }
     
 }


[sis] 07/09: Reduce the numbers of authorities declared in the ESRI-specific projections: limit to ESRI, OGC and Proj4.

Posted by de...@apache.org.
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 6274488fad7169664133c5ee1fa8e8cca0a84533
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Oct 1 16:41:27 2019 +0200

    Reduce the numbers of authorities declared in the ESRI-specific projections: limit to ESRI, OGC and Proj4.
---
 .../sis/internal/referencing/provider/ESRI.java    | 59 ++++++++++++++--------
 .../referencing/provider/MapProjection.java        | 55 ++++----------------
 .../provider/PolarStereographicSouth.java          |  4 +-
 3 files changed, 50 insertions(+), 68 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
index 8469888..7510166 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
@@ -17,29 +17,49 @@
 package org.apache.sis.internal.referencing.provider;
 
 import org.opengis.parameter.ParameterDescriptor;
-import org.apache.sis.parameter.DefaultParameterDescriptor;
 import org.apache.sis.parameter.ParameterBuilder;
-import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.util.Static;
 
 
 /**
  * Constants for projections defined by ESRI but not by EPSG.
+ * Also used for some projections not defined by ESRI, but in which we reuse ESRI parameters.
+ * A characteristics of ESRI parameters is that they have the same name for all projections
+ * (at least all the ones supported by SIS). A similar pattern is observed with OGC parameters,
+ * which are close to ESRI ones.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
 final class ESRI extends Static {
     /**
-     * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
+     * The operation parameter descriptor for the <cite>Longitude of origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
      */
     static final ParameterDescriptor<Double> CENTRAL_MERIDIAN;
 
     /**
+     * The operation parameter descriptor for the <cite>Latitude of origin</cite> (φ₀) parameter value.
+     * Valid values range is (-90 … 90)° and default value is 0°.
+     */
+    static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> parameter value.
+     * Valid values range is [-90 … 90]° and default value is 0°.
+     */
+    static final ParameterDescriptor<Double> STANDARD_PARALLEL_1;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of 2nd standard parallel</cite> parameter value.
+     * Valid values range is [-90 … 90]° and default value is 0°.
+     */
+    static final ParameterDescriptor<Double> STANDARD_PARALLEL_2;
+
+    /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
      */
@@ -52,14 +72,12 @@ final class ESRI extends Static {
     static final ParameterDescriptor<Double> FALSE_NORTHING;
     static {
         final ParameterBuilder builder = MapProjection.builder();
-        FALSE_EASTING  = asPrimary(Equirectangular.FALSE_EASTING,  builder);
-        FALSE_NORTHING = asPrimary(Equirectangular.FALSE_NORTHING, builder);
-
-        final ParameterDescriptor<Double> template = Equirectangular.LONGITUDE_OF_ORIGIN;
-        CENTRAL_MERIDIAN = MapProjection.createLongitude(builder
-                .addName(MapProjection.sameNameAs(Citations.ESRI,  template))
-                .addName(MapProjection.sameNameAs(Citations.OGC,   template))
-                .addName(MapProjection.sameNameAs(Citations.PROJ4, template)));
+        CENTRAL_MERIDIAN    = MapProjection.createLongitude(copyNames(builder, Equirectangular.LONGITUDE_OF_ORIGIN));
+        LATITUDE_OF_ORIGIN  = MapProjection.createLatitude (copyNames(builder, Equirectangular.LATITUDE_OF_ORIGIN), true);
+        STANDARD_PARALLEL_1 = MapProjection.createLatitude (copyNames(builder, LambertConformal2SP.STANDARD_PARALLEL_1), true);
+        STANDARD_PARALLEL_2 = MapProjection.createLatitude (copyNames(builder, LambertConformal2SP.STANDARD_PARALLEL_2), true);
+        FALSE_EASTING       = MapProjection.createShift    (copyNames(builder, Equirectangular.FALSE_EASTING));
+        FALSE_NORTHING      = MapProjection.createShift    (copyNames(builder, Equirectangular.FALSE_NORTHING));
     }
 
     /**
@@ -69,17 +87,18 @@ final class ESRI extends Static {
     }
 
     /**
-     * Returns the same parameter than the given one, except that the alias of the ESRI authority
-     * is promoted as the primary name. The old primary name and identifiers (which are usually the
-     * EPSG ones) are discarded.
+     * Copies the ESRI, OGC and PROJ4 names from the given parameters to the given builder.
+     * Those parameters are selected because those authorities use the same names in all projections.
+     * The EPSG name is discarded because the name varies depending in the projection, in attempts to
+     * describe more precisely what they are for.
      *
+     * @param  builder   the builder where to add the names.
      * @param  template  the parameter from which to copy the names and identifiers.
-     * @param  builder   an initially clean builder where to add the names.
      * @return the given {@code builder}, for method call chaining.
      */
-    @SuppressWarnings("unchecked")
-    static ParameterDescriptor<Double> asPrimary(final ParameterDescriptor<Double> template, final ParameterBuilder builder) {
-        return MapProjection.alternativeAuthority(template, Citations.ESRI, builder).createBounded((MeasurementRange<Double>)
-                ((DefaultParameterDescriptor<Double>) template).getValueDomain(), template.getDefaultValue());
+    static ParameterBuilder copyNames(final ParameterBuilder builder, final ParameterDescriptor<Double> template) {
+        return builder.addName(MapProjection.sameNameAs(Citations.ESRI,  template))
+                      .addName(MapProjection.sameNameAs(Citations.OGC,   template))
+                      .addName(MapProjection.sameNameAs(Citations.PROJ4, template));
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
index 5efd1ea..d553ebf 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
@@ -59,7 +59,7 @@ import static org.opengis.metadata.Identifier.AUTHORITY_KEY;
  * {@linkplain ParameterDescriptorGroup descriptor group} named {@code PARAMETERS}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.6
  * @module
  */
@@ -292,36 +292,19 @@ public abstract class MapProjection extends AbstractProvider {
     static ParameterBuilder renameAlias(final ParameterDescriptor<Double> template, final Citation toRename,
             final ParameterDescriptor<Double> replacement, final ParameterBuilder builder)
     {
-        return copyAliases(template, toRename, sameNameAs(toRename, replacement),
-                IdentifiedObjects.getIdentifier(replacement, toRename), builder.addName(template.getName()));
-    }
-
-    /**
-     * Copies all aliases except the ones for the given authority. If the given replacement is non-null,
-     * then it will be used instead of the first occurrence of the omitted name.
-     *
-     * <p>This method does not copy the primary name. It is caller's responsibility to add it first.</p>
-     *
-     * @param  template     the parameter from which to copy the aliases.
-     * @param  exclude      the authority of the alias to omit. Can not be EPSG.
-     * @param  replacement  the alias to use instead of the omitted one, or {@code null} if none.
-     * @param  newCode      the identifier to use instead of the omitted one, or {@code null} if none.
-     * @param  builder      where to add the aliases.
-     * @return the given {@code builder}, for method call chaining.
-     */
-    private static ParameterBuilder copyAliases(final ParameterDescriptor<Double> template, final Citation exclude,
-            GenericName replacement, Identifier newCode, final ParameterBuilder builder)
-    {
+        builder.addName(template.getName());
+        GenericName newName = sameNameAs(toRename, replacement);
+        Identifier newCode = IdentifiedObjects.getIdentifier(replacement, toRename);
         for (GenericName alias : template.getAlias()) {
-            if (((Identifier) alias).getAuthority() == exclude) {
-                if (replacement == null) continue;
-                alias = replacement;
-                replacement = null;
+            if (((Identifier) alias).getAuthority() == toRename) {
+                if (newName == null) continue;
+                alias = newName;
+                newName = null;
             }
             builder.addName(alias);
         }
         for (Identifier id : template.getIdentifiers()) {
-            if (id.getAuthority() == exclude) {
+            if (id.getAuthority() == toRename) {
                 if (newCode == null) continue;
                 id = newCode;
                 newCode = null;
@@ -332,26 +315,6 @@ public abstract class MapProjection extends AbstractProvider {
     }
 
     /**
-     * Copies all aliases and all identifiers except EPSG, but using the alias specified by the given authority
-     * as the primary name. The old primary name (usually the EPSG name) is discarded.
-     *
-     * <p>This is a convenience method for defining the parameters of an ESRI-specific (or any other authority)
-     * projection using the EPSG parameters as template.</p>
-     *
-     * @param  template    the parameter from which to copy the names.
-     * @param  authority   the authority to use for the primary name.
-     * @param  builder     an initially clean builder where to add the names.
-     * @return the given {@code builder}, for method call chaining.
-     *
-     * @since 0.8
-     */
-    static ParameterBuilder alternativeAuthority(final ParameterDescriptor<Double> template,
-            final Citation authority, final ParameterBuilder builder)
-    {
-        return copyAliases(template, authority, null, null, builder.addName(sameNameAs(authority, template)));
-    }
-
-    /**
      * Creates a remarks for parameters that are not formally EPSG parameter.
      *
      * @param  origin  the name of the projection for where the parameter is formally used.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicSouth.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicSouth.java
index d06fbbe..86069eb 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicSouth.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicSouth.java
@@ -48,12 +48,12 @@ public final class PolarStereographicSouth extends AbstractStereographic {
     static {
         final ParameterBuilder builder = builder();
         final ParameterDescriptor<Double> standardParallel =
-                alternativeAuthority(PolarStereographicB.STANDARD_PARALLEL, Citations.ESRI, builder)
+                ESRI.copyNames(builder, PolarStereographicB.STANDARD_PARALLEL)
                 .createBounded(Latitude.MIN_VALUE, 0, Latitude.MIN_VALUE, Units.DEGREE);
 
         final ParameterDescriptor<?>[] parameters = {
             standardParallel,
-            ESRI.asPrimary(PolarStereographicB.LONGITUDE_OF_ORIGIN, builder),
+            ESRI.CENTRAL_MERIDIAN,
             PolarStereographicB.SCALE_FACTOR,                       // Not formally a parameter of this projection.
             ESRI.FALSE_EASTING,
             ESRI.FALSE_NORTHING


[sis] 03/09: feat (referencing) : add some tests for satellite-tracking projections from tables 38 and 39 of Snyder's Manual (page 238). Fix the condition of the while loop when applying Newton method.

Posted by de...@apache.org.
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 2b94c39aca000c337ff37b8c09a8e0a801ae00c2
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Wed Jul 24 14:33:59 2019 +0200

    feat (referencing) : add some tests for satellite-tracking projections from tables 38 and 39 of Snyder's Manual (page 238). Fix the condition of the while loop when applying Newton method.
---
 .../referencing/provider/SatelliteTracking.java    |   6 +-
 .../projection/ConicSatelliteTracking.java         | 139 +++++++++---------
 .../projection/CylindricalSatelliteTracking.java   |  44 +++---
 .../projection/ConicSatelliteTrackingTest.java     | 156 +++++++++++++++------
 .../CylindricalSatelliteTrackingTest.java          |  49 +++----
 .../sis/test/suite/ReferencingTestSuite.java       |   2 +
 6 files changed, 236 insertions(+), 160 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
index 6784123..787b404 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -151,7 +151,7 @@ public class SatelliteTracking extends MapProjection {
         SATELLITE_ORBIT_INCLINATION = builder
                 .addName("satellite_orbit_inclination")
                 .setDescription("Angle of inclination between the plane of the Earth's Equator and the plane of the satellite orbit")
-                .create(0, Units.RADIAN);
+                .create(0, Units.DEGREE);
 
         SATELLITE_ORBITAL_PERIOD = builder
                 .addName("satellite_orbital_period")
@@ -162,7 +162,7 @@ public class SatelliteTracking extends MapProjection {
                 .addName("ascending_node_period")
                 .setDescription("Length of Earth's rotation with respect to the precessed ascending node")
                 .createStrictlyPositive(98.884, Units.MINUTE);
-                
+
         PARAMETERS = builder.addName(NAME)
                 .createGroupForMapProjection(CENTRAL_MERIDIAN,
                         STANDARD_PARALLEL_1, STANDARD_PARALLEL_2,
@@ -187,7 +187,7 @@ public class SatelliteTracking extends MapProjection {
     protected NormalizedProjection createProjection(final Parameters parameters) throws ParameterNotFoundException {
         ArgumentChecks.ensureNonNull("Parameters", parameters);
 
-        if (parameters.getValue(STANDARD_PARALLEL_2) == -parameters.getValue(STANDARD_PARALLEL_1)) { 
+        if (parameters.getValue(STANDARD_PARALLEL_2) == -parameters.getValue(STANDARD_PARALLEL_1)) {
             return new org.apache.sis.referencing.operation.projection.CylindricalSatelliteTracking(this, parameters);
         } else {
             return new org.apache.sis.referencing.operation.projection.ConicSatelliteTracking(this, parameters);
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index f7247df..d650fa2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -34,13 +34,13 @@ import org.apache.sis.referencing.operation.transform.ContextualParameters;
 
 /**
  * <cite>Cylindrical Satellite-Tracking projections</cite>.
- * 
+ *
  * <cite>
  * - All groundtracks for satellites orbiting the Earth with the same orbital
  * parameters are shown as straight lines on the map.
  *
  * - Cylindrical {@link CylindricalSatelliteTracking}
- * or conical {@link ConicSatelliteTracking} form available. 
+ * or conical {@link ConicSatelliteTracking} form available.
  *
  * - Neither conformal nor equal-area.
  *
@@ -78,33 +78,35 @@ import org.apache.sis.referencing.operation.transform.ContextualParameters;
  * @version 1.0
  */
 public class ConicSatelliteTracking extends NormalizedProjection{
-    
+
     /**
      * {@code SATELLITE_ORBIT_INCLINATION}
      */
     final double i;
-    
+
     /**
      * Coefficients for both cylindrical and conic Satellite-Tracking Projection.
      */
     final double cos_i, sin_i, cos2_i, cos_φ1, p2_on_p1;
-    
+
 //    /**
 //     * Radius of the circle radius of the circle to which groundtracks
 //     * are tangent on the map.
 //     */
 //    private final double ρs;
-    
+
     /**
      * Projection Cone's constant.
      */
     private final double n;
-    
+
     /**
-     * Approximation of the Minimum latitude at infinite radius. 
-     * 
+     * Approximation of the Minimum latitude at infinite radius.
+     *
      * Limiting latitude to which the {@code L coefficient}
      * is associated with a particular case {@code L equals -s0/n}.
+     * In such a situation the coefficent ρ computed for transformations
+     * is infinite.
      */
     private final double latitudeLimit;
     /**
@@ -143,11 +145,11 @@ public class ConicSatelliteTracking extends NormalizedProjection{
 
     /**
      * Constructor for ConicSatelliteTracking.
-     * 
-     * Calculation are based on <cite>28 .SATTELITE-TRACKING PROJECTIONS </cite> 
+     *
+     * Calculation are based on <cite>28 .SATTELITE-TRACKING PROJECTIONS </cite>
      * in <cite> Map Projections - A Working Manual</cite> By John P. Snyder.
-     * 
-     * @param initializer 
+     *
+     * @param initializer
      */
     ConicSatelliteTracking(final Initializer initializer) {
         super(initializer);
@@ -155,7 +157,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         //======================================================================
         // Common for both cylindrical and conic sattelite tracking projections :
         //======================================================================
-        i        = toRadians(initializer.getAndStore(SATELLITE_ORBIT_INCLINATION)); // Radian input value.
+        i        = toRadians(initializer.getAndStore(SATELLITE_ORBIT_INCLINATION));
         cos_i    = cos(i);
         sin_i    = sin(i);
         cos2_i   = cos_i * cos_i;
@@ -164,11 +166,11 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         final double φ1      = toRadians(initializer.getAndStore(STANDARD_PARALLEL_1));  //appropriated use of toRadians??
         cos_φ1               = cos(φ1);
         final double cos2_φ1 = cos_φ1 * cos_φ1;
-        
+
         //======================================================================
         // For conic projection :
         //=======================
-        if(!(this instanceof CylindricalSatelliteTracking)) {
+        if (!(this instanceof CylindricalSatelliteTracking)) {
             final double sin_φ1 = sin(φ1);
             final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));   //appropriated use of toRadians??
             final double cos_φ0 = cos(φ0);
@@ -176,13 +178,13 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             final double sin_φ0 = sin(φ0);
             final double φ2 = toRadians(initializer.getAndStore(STANDARD_PARALLEL_2));   //appropriated use of toRadians??
 
-//            final DoubleUnaryOperator computeFn = (cos2_φn) -> atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
+//            final DoubleUnaryOperator computeFn = (cos2_φn) -> atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder
 //            final double F0 = computeFn.applyAsDouble(cos2_φ0);
 //            final double F1 = computeFn.applyAsDouble(cos2_φ1);
-//            final DoubleUnaryOperator computedλn = (sin_φn) -> -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
+//            final DoubleUnaryOperator computedλn = (sin_φn) -> -asin(sin_φn / sin_i); // eq.28-2a in Snyder
 //            final double dλ0 = computedλn.applyAsDouble(sin_φ0);
 //            final double dλ1 = computedλn.applyAsDouble(sin_φ1);
-//            final DoubleUnaryOperator computeλtn = (dλn) -> atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
+//            final DoubleUnaryOperator computeλtn = (dλn) -> atan(tan(dλn) * cos_i); // eq.28-3a in Snyder
 //            final double λt0 = computeλtn.applyAsDouble(dλ0);
 //            final double λt1 = computeλtn.applyAsDouble(dλ1);
 
@@ -203,10 +205,10 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             final double L0 = λt0 - p2_on_p1 * dλ0;
             final double L1 = λt1 - p2_on_p1 * dλ1;
 
-            if (φ1 == PI / 2 - i) { //tracking limit
+            if (φ1 == PI - i) { //tracking limit computed as 180 - i from Snyder's manual p.238
                 final double factor = (p2_on_p1 * cos_i - 1);
                 final double factor2 = factor * factor;
-                n = sin_i / (factor2); //eq. 28-18 in Snyder 
+                n = sin_i / (factor2); //eq. 28-18 in Snyder
             } else if (φ2 != φ1) {
                 final double cos_φ2 = cos(φ2);
                 final double cos2_φ2 = cos_φ2 * cos_φ2;
@@ -219,10 +221,10 @@ public class ConicSatelliteTracking extends NormalizedProjection{
                 final double F2 = computeFn(cos2_φ2);
                 n = (F2 - F1) / (L2 - L1);
             } else {
-                n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder 
+                n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder
             }
             s0 = F1 - n * L1;
-            ρ0 = cos_φ1xsin_F1 / (n * sin(n * L0 + s0)); // *R in eq.28-12 in Snyder 
+            ρ0 = cos_φ1xsin_F1 / (n * sin(n * L0 + s0)); // *R in eq.28-12 in Snyder
 
             //======================== Unsure ======================================
             // Aim to assess the limit latitude associated with -s0/n L-value.
@@ -234,14 +236,17 @@ public class ConicSatelliteTracking extends NormalizedProjection{
 //            //Additionally we can compute the radius of the circle to which groundtracks
 //            //are tangent on the map :
 //            ρs = cos_φ1xsin_F1 / n; //*R
-            //======================================================================        
+            //======================================================================
             /*
          * At this point, all parameters have been processed. Now process to their
          * validation and the initialization of (de)normalize affine transforms.
              */
             final MatrixSIS normalize = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
             normalize.convertAfter(0, n, null);  //For conic tracking
-        }else{
+
+            final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+            denormalize.convertBefore(1, 1, ρ0);  //For conic tracking
+        } else {
             n = latitudeLimit = cos_φ1xsin_F1 = s0 = ρ0 = NaN;
             positiveN = false;
         }
@@ -263,58 +268,59 @@ public class ConicSatelliteTracking extends NormalizedProjection{
      * @throws ProjectionException if the coordinates can not be converted.
      */
     @Override
-    public Matrix transform(double[] srcPts, int srcOff, 
+    public Matrix transform(double[] srcPts, int srcOff,
                             double[] dstPts, int dstOff,
                             boolean derivate) throws ProjectionException {
-        
+
         final double λ      = srcPts[srcOff];
         final double φ      = srcPts[srcOff + 1];
-        
+
         /*
          * According to the Snyder (page 236) in those cases cannot or should not be plotted.
          */
-        if( ((positiveN)&&(φ<=latitudeLimit)) || ((!positiveN)&&(φ>=latitudeLimit)) ){
+        if ( ((positiveN) && (φ<=latitudeLimit)) || ((!positiveN) && (φ>=latitudeLimit)) ){
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
-        
+
         final double sin_φ  = sin(φ);
         final double dλ     = -asin(sin_φ / sin_i);
         final double λt     = atan(tan(dλ) * cos_i);
         final double L      = λt - p2_on_p1 * dλ;
-        
+
         /*
          * As {@code latitudeLimit} is an approximation we repeat the test here.
          */
-        if( ((positiveN)&&(L<=-s0/n)) || ((!positiveN)&&(L>=-s0/n)) ){
+        if ( ((positiveN) && (L<=-s0/n)) || ((!positiveN) && (L>=-s0/n)) ){
             //TODO if usefull, could we add :
             // latitudeLimit = φ;
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
-        
-        final double ρ      = cos_φ1xsin_F1/(n*sin(n*L+s0)); 
+
+        final double ρ      = cos_φ1xsin_F1/(n*sin(n*L+s0));
         final double θ      = λ;      // extracted n
-        
+
         final double sinθ = sin(θ);
         final double cosθ = cos(θ);
         if (dstPts != null) {
             dstPts[dstOff    ] = ρ * sinθ;   // x
-            dstPts[dstOff + 1] = ρ0 - ρ*cosθ;  // y       //TODO : extract ρ0 when ensuring : λ = λ - λ0;
+//            dstPts[dstOff + 1] = ρ0 - ρ*cosθ;  // y       //TODO : extract ρ0 when ensuring : λ = λ - λ0;
+            dstPts[dstOff + 1] = - ρ*cosθ;
         }
-        
+
          if (!derivate) {
             return null;
         }
 
         //=========================TO Resolve =================================
-//        final double dx_dλ = ρ*n*cosθ; 
+//        final double dx_dλ = ρ*n*cosθ;
 //        final double dx_dφ =?;
 //
 //        final double dy_dλ =  ρ*n*sinθ;
 //        final double dy_dφ = ?;
         //======================================================================
-        
+
         throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
-        
+
         //Additionally we can compute the scale factors :
         // k = ρ*n/cos(φ); // /R
         // h = k*tan(F)/tan(n*L+s0);
@@ -330,41 +336,42 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     protected void inverseTransform(double[] srcPts, int srcOff,
                                     double[] dstPts, int dstOff)
                                     throws ProjectionException {
-        
+
         final double x   = srcPts[srcOff];
-        final double y   = srcPts[srcOff + 1] - ρ0;
-        
+//        final double y   = srcPts[srcOff + 1] - ρ0;
+        final double y   = srcPts[srcOff + 1];
+
         //TODO : extract - ρ0 : MatrixSIS convertBefore and convertAfter??
-        
-        if((x== 0) && (y == 0)) {
+
+        if ((x== 0) && (y == 0)) {
             //TODO : which values does it imply?
             throw new UnsupportedOperationException("Not supported yet for those coordinates."); //To change body of generated methods, choose Tools | Templates.
         }
-               
-        final double ρ = positiveN? hypot(x,y) : -hypot(x,y);
+
+        final double ρ = positiveN ? hypot(x,y) : -hypot(x,y);
         final double θ = atan(x/(-y) ); //undefined if x=y=0
         final double L = (asin(cos_φ1xsin_F1/(ρ*n)) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
-        
+
         //TODO ensure that λ0 will be added. ;  In eq. Snyder 28-23 : λ0 + θ/n
-        dstPts[dstOff  ] =  θ ;//λ 
+        dstPts[dstOff  ] =  θ ;//λ
         dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
     }
-    
+
     protected final double latitudeFromNewtonMethod(final double l){
         double dλ = -PI/2;
         double dλn = Double.MIN_VALUE;
         double A, A2, Δdλ;
-        
+
         int count=0;
         int maxIteration = 100; //TODO : check the value.
-                
-        while((count==0) || dλ-dλn >= (0.1*PI/180)){ //TODO : check the condition. It is considered here that convergence is reached when improvement is lower than 0.1°.
-            if(count >= maxIteration){
+
+        while ((count==0) || abs(dλ-dλn) >= (0.01*PI/180)){ //TODO : check the condition. It is considered here that convergence is reached when improvement is lower than 0.01°.
+            if (count >= maxIteration){
                 throw new RuntimeException(Resources.format(Resources.Keys.NoConvergence));
             }
             dλn = dλ;
 
-//            λt = l + p2_on_p1 * dλ_n; 
+//            λt = l + p2_on_p1 * dλ_n;
 //            dλ = atan(tan(λt) / cos_i);
 
             A = tan(l + p2_on_p1*dλn) / cos_i;
@@ -376,48 +383,48 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         }
 //        λt = L + p2_on_p1 * dλ;
         final double sin_dλ = sin(dλ);
-        return -asin(sin_dλ * sin_i); 
+        return -asin(sin_dλ * sin_i);
     }
-    
-    
+
+
     public double getLatitudeLimit(){
         return latitudeLimit;
     }
-    
+
     /**
      * Method to compute the φn coefficient according to equation 28-9
      * in Snyder's Map Projections manual.
-     * 
+     *
      * @param cos2_φn : square of the φn 's cosinus.
      * @return Fn  coefficient associated with the φn latittude.
      */
     private double computeFn(final double cos2_φn) {
-        return atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
+        return atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder
     }
     /**
      * Method to compute the φn coefficient according to equation 28-2a
      * in Snyder's Map Projections manual.
-     * 
+     *
      * @param sin_φn : φn 's sinus.
      * @return dλn  coefficient associated with the φn latittude.
      */
     private double computedλn(final double sin_φn) {
-        return -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
+        return -asin(sin_φn / sin_i); // eq.28-2a in Snyder
     }
     /**
      * Method to compute the φn coefficient according to equation 28-3a
      * in Snyder's Map Projections manual.
-     * 
+     *
      * @param dλn  coefficient associated with the φn latittude.
      * @return λtn  coefficient associated with the φn latittude.
      */
     private double computeλtn(final double dλn) {
-        return atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
+        return atan(tan(dλn) * cos_i); // eq.28-3a in Snyder
     }
 //    /**
 //     * Radius of the circle radius of the circle to which groundtracks
 //     * are tangent on the map.
-//     * 
+//     *
 //     * @return radius ρs.
 //     */
 //    public double getRadiusOfTangencyCircle(){
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index 1284583..13f8521 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -29,15 +29,15 @@ import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 
 /**
- * <cite>Cylindrical Satellite-Tracking projections</cite>. 
- * 
+ * <cite>Cylindrical Satellite-Tracking projections</cite>.
+ *
  * <cite>
  * - All groundtracks
  * for satellites orbiting the Earth with the same orbital parameters are shown
  * as straight lines on the map.
  *
  * - Cylindrical {@link CylindricalSatelliteTracking}
- * or conical {@link ConicSatelliteTracking} form available. 
+ * or conical {@link ConicSatelliteTracking} form available.
  *
  * - Neither conformal nor equal-area.
  *
@@ -76,13 +76,13 @@ import org.apache.sis.referencing.operation.transform.ContextualParameters;
  */
 public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
 
-    
+
     /**
-     * F1' in Snyder : tangente of the angle on both the globe and on the map between 
-     * the groundtrack and the meridian at latitude φ1. 
+     * F1' in Snyder : tangente of the angle on both the globe and on the map between
+     * the groundtrack and the meridian at latitude φ1.
      */
     final double dF1;
-    
+
     /**
      * Create a Cylindrical Satellite Tracking Projection from the given
      * parameters.
@@ -99,15 +99,15 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
 
     private CylindricalSatelliteTracking(final Initializer initializer) {
         super(initializer);
-        
+
         final double cos2_φ1 = cos_φ1 * cos_φ1;
         dF1 = (p2_on_p1 * cos2_φ1 - cos_i) / sqrt(cos2_φ1 - cos2_i);
-        
+
         final MatrixSIS normalize   = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
 //        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
 
         normalize  .convertAfter (0, cos_φ1, null);  //For conic tracking
-        
+
 //        denormalize.convertBefore(0, 1/PI,     null);
 //        denormalize.convertBefore(1, , null);
 
@@ -117,10 +117,10 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
      * Converts the specified (λ,φ) coordinate (units in radians) and stores the result in {@code dstPts}
      * (linear distance on a unit sphere). In addition, opportunistically computes the projection derivative
      * if {@code derivate} is {@code true}.
-     * 
+     *
      * <cite> The Yaxis lies along the central meridian λ0, y increasing northerly,
      * and X axis intersects perpendicularly at O_PARALLEL φ0, x increasing easterly.
-     * </cite> 
+     * </cite>
      *
      * @return the matrix of the projection derivative at the given source position,
      *         or {@code null} if the {@code derivate} argument is {@code false}.
@@ -130,12 +130,12 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
     public Matrix transform(double[] srcPts, int srcOff,
             double[] dstPts, int dstOff,
             boolean derivate) throws ProjectionException {
-        
+
         final double λ = srcPts[srcOff];
         final double φ = srcPts[srcOff + 1];
 
         // TODO : check the condition and the thrown exception.
-        if (abs(φ) > PI / 2 - abs(PI / 2 - i)) {  // Exceed tracking limit 
+        if (abs(φ) > PI / 2 - abs(PI / 2 - i)) {  // Exceed tracking limit
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
@@ -155,8 +155,8 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         /* =====================================================================
         * Uncomputed scale factors :
         *===========================
-        * F' : tangente of the angle on the globe between the groundtrack and 
-        * the meridian at latitude φ 
+        * F' : tangente of the angle on the globe between the groundtrack and
+        * the meridian at latitude φ
         * final double dF = (p2_on_p1 * cos2_φ - cos_i) / sqrt(cos2_φ - cos2_i);
         * k = cos_φ1/cos_φ;   // Parallel eq. Snyder 28-7
         * h = k* dF / dF1;    // Meridian eq. Snyder 28-8
@@ -170,7 +170,7 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
 //
 //        final double dy_dλ = 0;
 //        final double dy_dφ =
-                
+
         throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
     }
 
@@ -181,15 +181,15 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
      * @throws ProjectionException if the coordinates can not be converted.
      */
     @Override
-    protected void inverseTransform(double[] srcPts, int srcOff, 
-                                    double[] dstPts, int dstOff) 
+    protected void inverseTransform(double[] srcPts, int srcOff,
+                                    double[] dstPts, int dstOff)
             throws ProjectionException {
-        
+
         final double x   = srcPts[srcOff];
         final double y   = srcPts[srcOff + 1];
-        
+
         final double L   = y * dF1 / cos_φ1; // In eq. Snyder 28-19 : L = y * dF1 / R . cos_φ1
-        
+
         dstPts[dstOff  ] =  x;
         dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
     }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
index 0b20c88..7152538 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
+import static java.lang.Math.sin;
 import java.util.Collections;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.referencing.NilReferencingObject;
@@ -26,12 +27,14 @@ import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
 import org.apache.sis.referencing.operation.transform.MathTransformFactoryMock;
 import org.apache.sis.test.DependsOn;
-import static org.junit.Assert.assertTrue;
 import org.junit.Test;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
 
+import static java.lang.StrictMath.toRadians;
+import static org.junit.Assert.assertTrue;
+
 /**
  * Tests coordiantes computed by applying a conic satellite-tracking projection
  * with {@link ConicSatelliteTracking}.
@@ -41,7 +44,6 @@ import org.opengis.util.FactoryException;
  * @since 1.0
  * @module
  */
-@DependsOn(ConformalProjectionTest.class)
 public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
 
     /**
@@ -64,8 +66,8 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      */
     void createProjection(final double i,
             final double orbitalT, final double ascendingNodeT,
-            final double λ0,       final double φ1, 
-            final double φ2,       final double φ0) 
+            final double λ0, final double φ1,
+            final double φ2, final double φ0)
             throws FactoryException {
 
         final SatelliteTracking provider = new SatelliteTracking();
@@ -73,7 +75,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
         final DefaultEllipsoid sphere = DefaultEllipsoid.createEllipsoid(
                 Collections.singletonMap(DefaultEllipsoid.NAME_KEY, NilReferencingObject.UNNAMED),
                 1, 1, Units.METRE);
-        
+
         values.parameter("semi_major").setValue(sphere.getSemiMajorAxis());
         values.parameter("semi_minor").setValue(sphere.getSemiMinorAxis());
         values.parameter("satellite_orbit_inclination").setValue(i);
@@ -81,16 +83,16 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
         values.parameter("ascending_node_period").setValue(ascendingNodeT);
         values.parameter("central_meridian").setValue(λ0);
         values.parameter("standard_parallel_1").setValue(φ1);
-        
+
         if (!Double.isNaN(φ2)) {
             values.parameter("standard_parallel_2").setValue(φ2);
-        }else{
+        } else {
             values.parameter("standard_parallel_2").setValue(-φ1); //Cylindrical case
         }
         if (!Double.isNaN(φ0)) {
             values.parameter("latitude_of_origin").setValue(φ0);
         }
-        
+
         transform = new MathTransformFactoryMock(provider).createParameterizedTransform(values);
         validate();
     }
@@ -98,7 +100,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
     /**
      * Tests the projection of a few points on a sphere.
      *
-     * Test based on the numerical example given by Snyder p. 360 to 363 of 
+     * Test based on the numerical example given by Snyder p. 360 to 363 of
      * <cite> Map Projections - A working Manual</cite>
      *
      * @throws FactoryException if an error occurred while creating the map
@@ -109,13 +111,13 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
     public void testTransform() throws FactoryException, TransformException {
         tolerance = 1E-5;
         createProjection(
-                99.092,  //satellite_orbit_inclination
+                99.092, //satellite_orbit_inclination
                 103.267, //satellite_orbital_period
-                1440.0,  //ascending_node_period
-                -90,     //central_meridian
-                45,      //standard_parallel_1
-                70,      //standard_parallel_2
-                30       //latitude_of_origin
+                1440.0, //ascending_node_period
+                -90, //central_meridian
+                45, //standard_parallel_1
+                70, //standard_parallel_2
+                30 //latitude_of_origin
         );
         assertTrue(isInverseTransformSupported);
         verifyTransform(
@@ -126,7 +128,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
                     0.2001910, 0.2121685
                 });
     }
-    
+
     /**
      * Tests the projection of a few points on a sphere.
      *
@@ -134,47 +136,111 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      * Satellite-Tracking Projections shown in table 39 from
      * <cite> Map Projections - A working Manual</cite>
      *
+     * As this table only introduce the values for n, F1 and ρ coefficients, the
+     * test was realized by checking these coefficients in debugger mode and by
+     * extracting the computed results of the projection. Thus this method
+     * should be used as a non-regression test.
+     *
      * @throws FactoryException if an error occurred while creating the map
      * projection.
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
     public void testSampleCoordinates() throws FactoryException, TransformException {
-        
+
         //Following tests don't pass with the former tolerance.
-        tolerance = Formulas.LINEAR_TOLERANCE; 
-        
+        tolerance = Formulas.LINEAR_TOLERANCE;
+
         //----------------------------------------------------------------------
         // φ1 = 30° ; φ2 = 60°
         //---------------------
         createProjection(
-                99.092,  //satellite_orbit_inclination
+                99.092, //satellite_orbit_inclination
                 103.267, //satellite_orbital_period
-                1440.0,  //ascending_node_period
-                -90,     //central_meridian
-                30,      //standard_parallel_1
-                60,      //standard_parallel_2
-                30       //latitude_of_origin
+                1440.0, //ascending_node_period
+                -90, //central_meridian
+                30, //standard_parallel_1
+                60, //standard_parallel_2
+                0 //latitude_of_origin
         );
-//        double xConverterFactor=0.017453;
-//        verifyTransform(
-//                new double[]{ // (λ,φ) coordinates in degrees to project.
-//                      0,  0,
-////                     10,  0, 
-////                    -10, 10,
-////                     60, 40,
-////                     80, 70,
-////                    -120, 80.908  //Tracking limit
-//                },
-//                new double[]{ // Expected (x,y) results in metres.
-//                    0, 0,
-////                    xConverterFactor *   10,  0,
-////                    xConverterFactor *  -10,  0.17579,
-////                    xConverterFactor *   60,  0.79741,
-////                    xConverterFactor *   80,  2.34465,
-////                    xConverterFactor * -120,  7.23571 //Tracking limit
-//                });
-        
-    }
 
+        double n = 0.49073;
+
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                       0, -10,
+                       0, 0,
+                       0, 10,
+                       0, 70,
+                    -120, 80.908  //Tracking limit
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    2.67991 * sin(n * (toRadians(   0 - -90))),    0.46093,
+                    2.38332 * sin(n * (toRadians(   0 - -90))),    0.67369,
+                    2.14662 * sin(n * (toRadians(   0 - -90))),    0.84348,
+                    0.98470 * sin(n * (toRadians(   0 - -90))),    1.67697,
+                    0.50439 * sin(n * (toRadians(-120 - -90))),    1.89549 //Tracking limit
+                });
+
+        //----------------------------------------------------------------------
+        // φ1 = 45° ; φ2 = 70°
+        //---------------------
+        createProjection(
+                99.092, //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0, //ascending_node_period
+                -90, //central_meridian
+                45, //standard_parallel_1
+                70, //standard_parallel_2
+                0 //latitude_of_origin
+        );
+
+        n = 0.69478;
+
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                    0, -10,
+                    0, 0,
+                    0, 10,
+                    0, 70,
+                    -120, 80.908 //Tracking limit
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    2.92503 * sin(n * (toRadians(   0 - -90))), 0.90110,
+                    2.25035 * sin(n * (toRadians(   0 - -90))), 1.21232,
+                    1.82978 * sin(n * (toRadians(   0 - -90))), 1.40632,
+                    0.57297 * sin(n * (toRadians(   0 - -90))), 1.98605,
+                    0.28663 * sin(n * (toRadians(-120 - -90))), 1.982485 //Tracking limit
+                });
+        //----------------------------------------------------------------------
+        // φ1 = 45° ; φ2 = 70°
+        //---------------------
+        createProjection(
+                99.092, //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0, //ascending_node_period
+                -90, //central_meridian
+                45, //standard_parallel_1
+                80.908, //standard_parallel_2
+                0 //latitude_of_origin
+        );
+
+        n = 0.88475;
+
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                       0, -10,
+                       0,  0,
+                       0, 10,
+                       0, 70,
+                    -120, 80.908 //Tracking limit
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    4.79153 * sin(n * (toRadians(   0 - -90))), 1.80001,
+                    2.66270 * sin(n * (toRadians(   0 - -90))), 2.18329,
+                    1.84527 * sin(n * (toRadians(   0 - -90))), 2.33046,
+                    0.40484 * sin(n * (toRadians(   0 - -90))), 2.58980,
+                    0.21642 * sin(n * (toRadians(-120 - -90))), 2.46908 //Tracking limit
+                });
+    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
index c207af3..02c1b09 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -35,9 +35,9 @@ import org.opengis.util.FactoryException;
  * @module
  */
 public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest {
-    
-    
-    
+
+
+
     /**
      * Creates a new instance of {@link ConicSatelliteTracking} concatenated
      * with the (de)normalization matrices. The new instance is stored in the
@@ -58,17 +58,17 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      */
     private void createProjection(final double i,
             final double orbitalT, final double ascendingNodeT,
-            final double λ0,       final double φ1) 
+            final double λ0,       final double φ1)
             throws FactoryException {
         super.createProjection(i, orbitalT, ascendingNodeT, λ0, φ1, NaN, NaN);
     }
-    
+
     /**
      * Tests the projection of a few points on a sphere.
      *
-     * Test based on the numerical example given by Snyder p. 360 to 363 of 
+     * Test based on the numerical example given by Snyder p. 360 to 363 of
      * <cite> Map Projections - A working Manual</cite>
-     * 
+     *
      * @throws FactoryException if an error occurred while creating the map
      * projection.
      * @throws TransformException if an error occurred while projecting a point.
@@ -93,7 +93,7 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                     0.2267249, 0.6459071
                 });
     }
-    
+
     /**
      * Tests the projection of a few points on a sphere.
      *
@@ -106,11 +106,12 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
-    public void testSampleCoordinates() throws FactoryException, TransformException {
-        
+    public void testSampleCoordinatesCylindricalProjection()
+            throws FactoryException, TransformException {
+
         //Following tests don't pass with the former tolerance.
-        tolerance = Formulas.LINEAR_TOLERANCE; 
-        
+        tolerance = Formulas.LINEAR_TOLERANCE;
+
         //----------------------------------------------------------------------
         // φ1 = 0°
         //---------
@@ -121,12 +122,12 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                 0,       //central_meridian
                 0        //standard_parallel_1
         );
-       
+
         double xConverterFactor=0.017453;
         verifyTransform(
                 new double[]{ // (λ,φ) coordinates in degrees to project.
                       0,  0,
-                     10,  0, 
+                     10,  0,
                     -10, 10,
                      60, 40,
                      80, 70,
@@ -140,8 +141,8 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                     xConverterFactor *   80,  2.34465,
                     xConverterFactor * -120,  7.23571 //Tracking limit
                 });
-        
-        
+
+
         //----------------------------------------------------------------------
         // φ1 = +- 30°
         //------------
@@ -152,12 +153,12 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                 0,       //central_meridian
                 -30        //standard_parallel_1
         );
-       
+
         xConverterFactor=0.015115;
         verifyTransform(
                 new double[]{ // (λ,φ) coordinates in degrees to project.
                       0,  0,
-                     10,  0, 
+                     10,  0,
                     -10, 10,
                      60, 40,
                      80, 70,
@@ -171,7 +172,7 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                     xConverterFactor *   80,  1.89918,
                     xConverterFactor * -120,  5.86095 //Tracking limit
                 });
-        
+
         //----------------------------------------------------------------------
         // φ1 = +- 45°
         //------------
@@ -182,12 +183,12 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                 0,       //central_meridian
                 45        //standard_parallel_1
         );
-       
+
         xConverterFactor=0.012341;
         verifyTransform(
                 new double[]{ // (λ,φ) coordinates in degrees to project.
                       0,  0,
-                     10,  0, 
+                     10,  0,
                     -10, 10,
                      60, 40,
                      80, 70,
@@ -201,8 +202,8 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
                     xConverterFactor *   80,  1.37124,
                     xConverterFactor * -120,  4.23171 //Tracking limit
                 });
-        
-        
+
+
     }
-    
+
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index dec780f..aa68d1f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -180,6 +180,8 @@ import org.junit.BeforeClass;
     org.apache.sis.referencing.operation.projection.SinusoidalTest.class,
     org.apache.sis.referencing.operation.projection.PolyconicTest.class,
     org.apache.sis.referencing.operation.projection.MollweideTest.class,
+    org.apache.sis.referencing.operation.projection.ConicSatelliteTrackingTest.class,
+    org.apache.sis.referencing.operation.projection.CylindricalSatelliteTrackingTest.class,
 
     // Coordinate operation and derived Coordinate Reference Systems (cyclic dependency).
     org.apache.sis.referencing.operation.DefaultTransformationTest.class,


[sis] 06/09: refactor (referencing satellite-tracking projections) : Method used by both (cylindrical and conic) direct transformation of the Satellite-tracking projections to compute the L coefficient and its partial derivate dL_dφ if queried. Test passed for both transformation and testDerivativeOnSphere.

Posted by de...@apache.org.
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 8c2d72d85ff84114983ef2e1f62cd2cf5e538585
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Thu Jul 25 12:18:37 2019 +0200

    refactor (referencing satellite-tracking projections) : Method used by both (cylindrical and conic) direct transformation of the Satellite-tracking projections to  compute the L coefficient and its partial derivate dL_dφ if queried. Test passed for both transformation and testDerivativeOnSphere.
---
 .../projection/ConicSatelliteTracking.java         | 61 ++++++++++++++++------
 .../projection/CylindricalSatelliteTracking.java   | 17 +++---
 2 files changed, 50 insertions(+), 28 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index 9b0b0b0..d265ae2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -285,13 +285,9 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
-        final double sin_φ  = sin(φ);
-        final double sinφ_sini = sin_φ / sin_i;
-        final double dλ     = -asin(sinφ_sini);
-        final double tan_dλ = tan(dλ);
-        final double λt     = atan(tan_dλ * cos_i);
-        final double L      = λt - p2_on_p1 * dλ;
-
+        // compute an double array with {L} or {L, dL/dφ} if derivate recquired.
+        final double[] vector_L = computeLanddLdφForDirectTransform(φ, derivate);
+        final double L = vector_L[0];
         /*
          * As {@code latitudeLimit} is an approximation we repeat the test here.
          */
@@ -301,17 +297,15 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
-        final double nLandS0 = n*L+s0;
+        final double nLandS0     = n*L+s0;
         final double sin_nLandS0 = sin(nLandS0);
-
-        final double ρ      = cosφ1xsinF1_n/sin_nLandS0;
-//        final double ρ      = 1/sin(n*L+s0);
+        final double ρ           = cosφ1xsinF1_n/sin_nLandS0;
 
         final double sinλ = sin(λ);
         final double cosλ = cos(λ);
         if (dstPts != null) {
-            dstPts[dstOff    ] = ρ * sinλ;   // x
-            dstPts[dstOff + 1] = - ρ*cosλ;   // y
+            dstPts[dstOff    ] =   ρ * sinλ;   // x
+            dstPts[dstOff + 1] = - ρ * cosλ;   // y
         }
 
          if (!derivate) {
@@ -320,14 +314,12 @@ public class ConicSatelliteTracking extends NormalizedProjection{
 
         //=========================To check the resolution =====================
         final double dρ_dφ = cosφ1xsinF1_n  * (-1 / (sin_nLandS0*sin_nLandS0)) *cos(nLandS0)  * n
-                // dL/dφ :
-                * ((cos(φ) / sin_i) *(1/sqrt(1-sinφ_sini*sinφ_sini)))  // derivative of (-dλ/dφ)
-                * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
+                * vector_L[1]; // dL/dφ
 
         final double dx_dλ = ρ*cosλ;
         final double dx_dφ = sinλ * dρ_dφ;
 
-        final double dy_dλ =  ρ*sinλ;
+        final double dy_dλ = ρ*sinλ;
         final double dy_dφ = -cosλ * dρ_dφ;
         //======================================================================
 
@@ -445,4 +437,39 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         return atan(tan(dλn) * cos_i); // eq.28-3a in Snyder
     }
 
+    /**
+     * Method returning the L coefficient used for the direct transformation of
+     * the Satellite-tracking projections and its partial derivate dL_dφ if
+     * queried by a true value of the input parameter derivate.
+     *
+     * This method is used By :
+     *     {@link ConicSatelliteTracking#transform(double[], int, double[], int, boolean) }
+     * and {@link CylindricalSatelliteTracking#transform(double[], int, double[], int, boolean) }
+     *
+     * @param φ : input latitude of the projection's (direct)  method.
+     * @param derivate : boolean value indicating if the partial derivate dL_dφ
+     *                  must be computed.
+     * @return a double array. It contains :
+     *        - if derivate is false :  only the L coefficient value.
+     *        - if derivate is true :  the L coefficient value and the partial
+     *          derivate dL_dφ respectively at the at index 0 and 1 of the
+     *          resulting array.
+     */
+    double[] computeLanddLdφForDirectTransform(final double φ, final boolean derivate){
+
+        final double sinφ_sini = sin(φ) / sin_i;
+        final double dλ        = -asin(sinφ_sini);
+        final double tan_dλ    = tan(dλ);
+        final double λt        = atan(tan_dλ * cos_i);
+        final double L         = λt - p2_on_p1 * dλ;
+
+        if (derivate){
+            final double dL_dφ = ((cos(φ) / sin_i) *(1/sqrt(1-sinφ_sini*sinφ_sini)))  // first computed term associated with derivative of (-dλ/dφ)
+                               * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
+            return new double[] {L, dL_dφ};
+        } else {
+            return new double[] {L};
+        }
+    }
+
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index 9348834..4348993 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -127,21 +127,17 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         final double φ = srcPts[srcOff + 1];
 
         // TODO : check the condition and the thrown exception.
-        if (abs(φ) > PI / 2 - abs(PI / 2 - i)) {  // Exceed tracking limit
+            // if (abs(φ) > PI / 2 - abs(PI - i)) {  // Exceed tracking limit
+        if (φ > PI - i) {  // Exceed tracking limit
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
-        final double sin_φ  = sin(φ);
-        final double sinφ_sini = sin_φ / sin_i;
-
-        final double dλ     = -asin(sin_φ / sin_i);
-        final double tan_dλ = tan(dλ);
-        final double λt     = atan(tan(dλ) * cos_i);
-        final double L      = λt - p2_on_p1 * dλ;
+        // compute an double array with {L} or {L, dL/dφ} if derivate recquired.
+        final double[] vector_L = computeLanddLdφForDirectTransform(φ, derivate);
 
         if (dstPts != null) {
             dstPts[dstOff    ] = λ;   // In eq. Snyder 28-5 : R(λ - λ0) cos_φ1
-            dstPts[dstOff + 1] = L;   // In eq. Snyder 28-6 : R L cos(φ1)/F'1
+            dstPts[dstOff + 1] = vector_L[0];   // In eq. Snyder 28-6 : R L cos(φ1)/F'1
         }
 
         /* =====================================================================
@@ -162,8 +158,7 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         final double dx_dφ = 0;
 
         final double dy_dλ = 0;
-        final double dy_dφ = ((cos(φ) / sin_i) *(1/sqrt(1-sinφ_sini*sinφ_sini)))  // derivative of (-dλ/dφ)
-                * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
+        final double dy_dφ = vector_L[1]; //dL/dφ
         //======================================================================
 
         return new Matrix2(dx_dλ, dx_dφ,


[sis] 09/09: Reformatting: - Format copyright header in the same way than other files. - Rearrange some import order and remove some empty lines. - Set version number to 1.1.

Posted by de...@apache.org.
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 ea466fae64c884924c3fc650d8385b76d07c39b1
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Oct 1 11:52:57 2019 +0200

    Reformatting:
    - Format copyright header in the same way than other files.
    - Rearrange some import order and remove some empty lines.
    - Set version number to 1.1.
    
    Code change:
    - Add serialVersionUID.
    - Edit ParameterDescriptors.
    - Reduce the scope of some variables.
---
 .../referencing/provider/SatelliteTracking.java    | 204 ++++++++------------
 .../referencing/provider/package-info.java         |   3 +-
 .../projection/ConicSatelliteTracking.java         | 209 +++++++++------------
 .../projection/CylindricalSatelliteTracking.java   |  99 +++-------
 .../operation/projection/package-info.java         |   3 +-
 .../referencing/provider/Descriptions.properties   |   5 +
 .../referencing/provider/ProvidersTest.java        |  13 +-
 .../projection/ConicSatelliteTrackingTest.java     |  61 +++---
 .../CylindricalSatelliteTrackingTest.java          |  65 +++----
 .../sis/test/suite/ReferencingTestSuite.java       |   2 +-
 ide-project/NetBeans/build.xml                     |   1 +
 11 files changed, 270 insertions(+), 395 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
index 787b404..dd529b8 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -1,174 +1,116 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.sis.internal.referencing.provider;
 
 import javax.xml.bind.annotation.XmlTransient;
-import org.apache.sis.measure.Units;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
+import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.operation.projection.ConicSatelliteTracking;
 import org.apache.sis.referencing.operation.projection.CylindricalSatelliteTracking;
 import org.apache.sis.referencing.operation.projection.NormalizedProjection;
-import org.apache.sis.util.ArgumentChecks;
-import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.parameter.ParameterNotFoundException;
+import org.apache.sis.util.iso.ResourceInternationalString;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.measure.Units;
+
 
 /**
- * The provider for <cite>Satellite-Tracking projections"</cite>.
- *
- * <cite>
- * - All groundtracks for satellites orbiting the Earth with the same orbital
- * parameters are shown as straight lines on the map.
- *
- * - Cylindrical {@link CylindricalSatelliteTracking} or conical
- * {@link ConicSatelliteTracking} form available.
- *
- * - Neither conformal nor equal-area.
- *
- * - All meridians are equally spaced straight lines, parallel on cylindrical
- * form and converging to a common point on conical form.
- *
- * - All parallels are straight and parallel on cylindrical form and are
- * concentric circular arcs on conical form. Parallels are unequally spaced.
- *
- * - Conformality occurs along two chosen parallels. Scale is correct along one
- * of these parameters on the conical form and along both on the cylindrical
- * form.
- *
- * Developed 1977 by Snyder
- * </cite>
- *
- * <cite> These formulas are confined to circular orbits and the SPHERICAL
- * Earth.</cite>
- *
- * <cite>The ascending and descending groundtracks meet at the northern an
- * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
- * Projection does not extend closer to the poles.</cite>
+ * The provider for <cite>"Satellite-Tracking"</cite> projections.
+ * More details about this projection are documented in the {@link ConicSatelliteTracking} class.
  *
- * This projection method has no associated EPSG code.
- *
- * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
- *
- * =============================================================================
- * REMARK : The parameters associated with the satellite (and its orbit) could
- * be aggregate in class of the kind : Satellite or SatelliteOrbit.
- * =============================================================================
- *
- * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
- * @author Matthieu Bastianelli (Geomatys)
- * @version 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
  */
 @XmlTransient
 public class SatelliteTracking extends MapProjection {
-
     /**
-     * Name of this projection.
+     * For cross-version compatibility.
      */
-    public static final String NAME = "Satellite-Tracking";
+    private static final long serialVersionUID = -2406880538621713953L;
 
     /**
-     * The operation parameter descriptor for the <cite>Longitude of projection
-     * center</cite> (λ₀) parameter value. Valid values range is [-180 … 180]°
-     * and default value is 0°.
+     * The operation parameter descriptor for the <cite>central meridian</cite> (λ₀) parameter value.
+     * Valid values range is [-180 … 180]° and default value is 0°.
      */
     public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN = ESRI.CENTRAL_MERIDIAN;
 
     /**
-     * The operation parameter descriptor for the <cite>Latitude of 1st standard
-     * parallel</cite>. For conical satellite-tracking projection : first
-     * parallel of conformality with true scale.
-     *
-     * Valid values range is [-90 … 90]° and default value is the value given to
-     * the {@link #LATITUDE_OF_FALSE_ORIGIN} parameter.
+     * Latitude crossing the central meridian at the desired origin of rectangular coordinates.
      */
-    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1 = LambertConformal2SP.STANDARD_PARALLEL_1;
+    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = ESRI.LATITUDE_OF_ORIGIN;;
 
     /**
-     * The operation parameter descriptor for the <cite>second parallel of
-     * conformality but without true scale</cite> parameter value for conic
-     * projection. Valid values range is [-90 … 90]° and default value is the
-     * opposite value given to the {@link #STANDARD_PARALLEL_1} parameter.
+     * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite>.
+     * For conical satellite-tracking projection, this is the first parallel of conformality with true scale.
+     * Valid values range is [-90 … 90]° and default value is 0°.
      */
-    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2 = LambertConformal2SP.STANDARD_PARALLEL_2;
+    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1 = ESRI.STANDARD_PARALLEL_1;
 
     /**
-     * Latitude Crossing the central meridian at the desired origin of
-     * rectangular coordinates.
+     * The operation parameter descriptor for the <cite>second parallel of conformality but without true scale</cite>
+     * parameter value for conic projection. Valid values range is [-90 … 90]° and default value is the opposite value
+     * given to the {@link #STANDARD_PARALLEL_1} parameter.
      */
-    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = TransverseMercator.LATITUDE_OF_ORIGIN;;
+    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2 = ESRI.STANDARD_PARALLEL_2;
 
     /**
-     * The operation parameter descriptor for the <cite> Angle of inclination
-     * between the plane of the Earth's Equator and the plane of the satellite
-     * orbit</cite> parameter value. <cite> It's measured counterclockwise from
-     * the Equatorto the orbit plane at the ascending node (99.092° for Landsat
-     * 1, 2, 3</cite>
+     * The operation parameter descriptor for the angle of inclination between the plane of the Earth's
+     * Equator and the plane of the satellite orbit. It is measured counterclockwise from the Equator to
+     * the orbit plane at the ascending node. Examples: 99.092° for Landsat 1, 2 and 3.
      */
     public static final ParameterDescriptor<Double> SATELLITE_ORBIT_INCLINATION;
 
     /**
-     * The operation parameter descriptor for the <cite> time required for
-     * revolution of the satellite</cite> parameter value.
+     * The operation parameter descriptor for the time required for revolution of the satellite.
+     * Examples: 103.267 minutes for Landsat 1, 2 and 3; 98.884 minutes for Landsat 4 and 5.
      */
     public static final ParameterDescriptor<Double> SATELLITE_ORBITAL_PERIOD;
 
     /**
-     * The operation parameter descriptor for the <cite> length of earth's
-     * rotation with respect to the precessed ascending node</cite> parameter
-     * value. The ascending node is the point on the satellite orbit at which
-     * the satellite crosses the Earth's equatorial plane in a northerly
-     * direction.
+     * The operation parameter descriptor for the length of Earth's rotation with respect to the
+     * precessed ascending node. The ascending node is the point on the satellite orbit at which
+     * the satellite crosses the Earth's equatorial plane in a northerly direction.
+     * For Landsat (Sun-synchronous satellite orbit), this is 1440 minutes.
      */
-    public static final ParameterDescriptor<Double> ASCENDING_NODE_PERIOD; //Constant for landsat but is it the case for every satellite with circular orbital?
+    public static final ParameterDescriptor<Double> ASCENDING_NODE_PERIOD;
 
     /**
      * The group of all parameters expected by this coordinate operation.
      */
     static final ParameterDescriptorGroup PARAMETERS;
-
     static {
-        final ParameterBuilder builder = new ParameterBuilder();
-        builder.setCodeSpace(null, null)
-                .setRequired(true);
-
-        SATELLITE_ORBIT_INCLINATION = builder
-                .addName("satellite_orbit_inclination")
-                .setDescription("Angle of inclination between the plane of the Earth's Equator and the plane of the satellite orbit")
-                .create(0, Units.DEGREE);
-
-        SATELLITE_ORBITAL_PERIOD = builder
-                .addName("satellite_orbital_period")
-                .setDescription("Time required for revolution of the satellite")
-                .createStrictlyPositive(103.267, Units.MINUTE); //Or hours? Seconds?
-
-        ASCENDING_NODE_PERIOD = builder
-                .addName("ascending_node_period")
-                .setDescription("Length of Earth's rotation with respect to the precessed ascending node")
-                .createStrictlyPositive(98.884, Units.MINUTE);
-
-        PARAMETERS = builder.addName(NAME)
-                .createGroupForMapProjection(CENTRAL_MERIDIAN,
-                        STANDARD_PARALLEL_1, STANDARD_PARALLEL_2,
-                        SATELLITE_ORBIT_INCLINATION, SATELLITE_ORBITAL_PERIOD,
-                        ASCENDING_NODE_PERIOD, LATITUDE_OF_ORIGIN);
-
+        final ParameterBuilder builder = builder().setCodeSpace(Citations.SIS, Constants.SIS);
+        SATELLITE_ORBIT_INCLINATION = setNameAndDescription(builder, "satellite_orbit_inclination").create(0, Units.DEGREE);
+        SATELLITE_ORBITAL_PERIOD    = setNameAndDescription(builder, "satellite_orbital_period").createStrictlyPositive(Double.NaN, Units.DAY);
+        ASCENDING_NODE_PERIOD       = setNameAndDescription(builder, "ascending_node_period")   .createStrictlyPositive(Double.NaN, Units.DAY);
+        PARAMETERS = builder.addName("Satellite-Tracking").createGroupForMapProjection(
+                LATITUDE_OF_ORIGIN,
+                CENTRAL_MERIDIAN,
+                STANDARD_PARALLEL_1,
+                STANDARD_PARALLEL_2,
+                SATELLITE_ORBIT_INCLINATION,
+                SATELLITE_ORBITAL_PERIOD,
+                ASCENDING_NODE_PERIOD);
     }
 
     /**
@@ -179,19 +121,29 @@ public class SatelliteTracking extends MapProjection {
     }
 
     /**
+     * Sets the parameter name in the given builder, together with a description created from
+     * the resource bundle in this package. The resource key is the same as the parameter name.
+     *
+     * @param  builder  the builder where to set the parameter name and description.
+     * @param  name     the parameter name, also used as resource key.
+     * @return the builder, for method calls chaining.
+     */
+    private static ParameterBuilder setNameAndDescription(final ParameterBuilder builder, final String name) {
+        return builder.addName(name).setDescription(new ResourceInternationalString(
+                "org.apache.sis.internal.referencing.provider.Descriptions", name));
+    }
+
+    /**
      * {@inheritDoc}
      *
      * @return the map projection created from the given parameter values.
      */
     @Override
     protected NormalizedProjection createProjection(final Parameters parameters) throws ParameterNotFoundException {
-        ArgumentChecks.ensureNonNull("Parameters", parameters);
-
         if (parameters.getValue(STANDARD_PARALLEL_2) == -parameters.getValue(STANDARD_PARALLEL_1)) {
-            return new org.apache.sis.referencing.operation.projection.CylindricalSatelliteTracking(this, parameters);
+            return new CylindricalSatelliteTracking(this, parameters);
         } else {
-            return new org.apache.sis.referencing.operation.projection.ConicSatelliteTracking(this, parameters);
+            return new ConicSatelliteTracking(this, parameters);
         }
     }
-
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/package-info.java
index 45c7402..ecc9b95 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/package-info.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/package-info.java
@@ -21,7 +21,8 @@
  * discussion of parameters.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
  *
  * @see org.apache.sis.referencing.operation.transform.MathTransformProvider
  *
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index d265ae2..5bd5ff0 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -1,101 +1,94 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.sis.referencing.operation.projection;
 
-import static java.lang.Double.NaN;
 import java.util.EnumMap;
-import org.apache.sis.parameter.Parameters;
-import org.apache.sis.util.Workaround;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.OperationMethod;
-
-import static java.lang.Math.*;
-import java.util.Map;
+import org.apache.sis.util.Workaround;
 import org.apache.sis.internal.referencing.Resources;
-import static org.apache.sis.internal.referencing.provider.SatelliteTracking.*;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.parameter.Parameters;
+
+import static java.lang.Math.*;
+import static java.lang.Double.NaN;
+import static org.apache.sis.internal.referencing.provider.SatelliteTracking.*;
+
 
 /**
- * <cite>Cylindrical Satellite-Tracking projections</cite>.
- *
- * <cite>
- * - All groundtracks for satellites orbiting the Earth with the same orbital
- * parameters are shown as straight lines on the map.
- *
- * - Cylindrical {@link CylindricalSatelliteTracking}
- * or conical {@link ConicSatelliteTracking} form available.
- *
- * - Neither conformal nor equal-area.
- *
- * - All meridians are equally spaced straight lines, parallel on cylindrical
- * form and converging to a common point on conical form.
- *
- * - All parallels are straight and parallel on cylindrical form and are
- * concentric circular arcs on conical form. Parallels are unequally spaced.
+ * <cite>Satellite-Tracking</cite> projection.
+ * This projection has been developed in 1977 by Snyder and has no associated EPSG code.
+ * This projection is neither conformal or equal-area, but has the property that ground tracks
+ * for satellites orbiting the Earth with the same orbital parameters are shown as straight lines
+ * on the map. Other properties are (Snyder 1987):
  *
- * - Conformality occurs along two chosen parallels. Scale is correct along one
- * of these parameters on the conical form and along both on the cylindrical
- * form.
+ * <ul>
+ *   <li>All meridians are equally spaced straight lines.
+ *       They are parallel on cylindrical form and converging to a common point on conical form.</li>
+ *   <li>All parallels are straight but unequally spaced.
+ *       They are parallel on cylindrical form and are concentric circular arcs on conical form.</li>
+ *   <li>Conformality occurs along two chosen parallels. Scale is correct along one of these parameters
+ *       on the conical form and along both on the cylindrical form.</li>
+ * </ul>
  *
- * Developed 1977 by Snyder
- * </cite>
+ * <div class="section">Limitations</div>
+ * This map projection supports only circular orbits. The Earth is assumed spherical.
+ * Areas close to poles can not be mapped.
  *
- * <cite> These formulas are confined to circular orbits and the SPHERICAL
- * Earth.</cite>
+ * <div class="section">References</div>
+ * John P. Snyder., 1987. <u>Map Projections - A Working Manual</u>
+ * chapter 28: <cite>Satellite-tracking projections</cite>.
  *
- * <cite>The ascending and descending groundtracks meet at the northern an
- * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
- * Projection does not extend closer to the poles.</cite>
- *
- * This projection method has no associated EPSG code.
- *
- * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
- *
- * =============================================================================
- * REMARK : The parameters associated with the satellite (and its orbit) could
- * be aggregate in class of the kind : Satellite or SatelliteOrbit.
- * =============================================================================
- *
- * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
- * @author Matthieu Bastianelli (Geomatys)
- * @version 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
  */
-public class ConicSatelliteTracking extends NormalizedProjection{
+public class ConicSatelliteTracking extends NormalizedProjection {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 859940667477896653L;
 
     /**
-     * {@code SATELLITE_ORBIT_INCLINATION}
+     * Sines and cosines of inclination between the plane of the Earth's Equator and the plane
+     * of the satellite orbit. The angle variable name is <var>i</var> in Snyder's book.
+     *
+     * @see org.apache.sis.internal.referencing.provider.SatelliteTracking#SATELLITE_ORBIT_INCLINATION
      */
-    final double i;
+    final double cos_i, sin_i, cos2_i;
 
     /**
-     * Coefficients for both cylindrical and conic Satellite-Tracking Projection.
+     * Cosine of first standard parallel.
+     *
+     * @see org.apache.sis.internal.referencing.provider.SatelliteTracking#STANDARD_PARALLEL_1
      */
-    final double cos_i, sin_i, cos2_i, cos_φ1, p2_on_p1;
+    final double cos_φ1;
 
-//    /**
-//     * Radius of the circle radius of the circle to which groundtracks
-//     * are tangent on the map.
-//     */
-//    private final double ρs;
+    /**
+     * Ratio of satellite orbital period (P2) over ascending node period (P1).
+     *
+     * @see org.apache.sis.internal.referencing.provider.SatelliteTracking#SATELLITE_ORBITAL_PERIOD
+     * @see org.apache.sis.internal.referencing.provider.SatelliteTracking#ASCENDING_NODE_PERIOD
+     */
+    final double p2_on_p1;
 
     /**
      * Projection Cone's constant.
@@ -103,23 +96,17 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     private final double n;
 
     /**
-     * Approximation of the Minimum latitude at infinite radius.
-     *
-     * Limiting latitude to which the {@code L coefficient}
-     * is associated with a particular case {@code L equals -s0/n}.
-     * In such a situation the coefficent ρ computed for transformations
-     * is infinite.
+     * Approximation of the minimum latitude at infinite radius.
+     * This is the limiting latitude at which the {@code L} coefficient takes the {@code -s0/n} value.
+     * In such a situation the coefficient ρ computed for transformations is infinite.
      */
     private final double latitudeLimit;
-    /**
-     * Boolean attribute indicating if the projection cone's constant is positive.
-     */
-    private final boolean positiveN;
+
     /**
      * Coefficients for the Conic Satellite-Tracking Projection.
-     * cosφ1xsinF1_n = cos(φ1)*sin(F1)/n
+     * {@code cosφ1_sinF1_n} = cos(φ₁) × sin(F₁) / n.
      */
-    private final double cosφ1xsinF1_n, s0, ρ0;
+    private final double cosφ1_sinF1_n, s0;
 
     /**
      * Work around for RFE #4093999 in Sun's bug database ("Relax constraint on
@@ -133,26 +120,18 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     }
 
     /**
-     * Create a Cylindrical Satellite Tracking Projection from the given
-     * parameters.
-     *
-     * The parameters are described in <cite>Map Projections - A Working
-     * Manual</cite> By John P. Snyder.
+     * Creates a Satellite Tracking projection from the given parameters.
      *
-     * @param method : description of the projection parameters.
-     * @param parameters : the parameter values of the projection to create.
+     * @param method      description of the projection parameters.
+     * @param parameters  the parameter values of the projection to create.
      */
     public ConicSatelliteTracking(final OperationMethod method, final Parameters parameters) {
         this(initializer(method, parameters));
     }
 
     /**
-     * Constructor for ConicSatelliteTracking.
-     *
-     * Calculation are based on <cite>28 .SATTELITE-TRACKING PROJECTIONS </cite>
-     * in <cite> Map Projections - A Working Manual</cite> By John P. Snyder.
-     *
-     * @param initializer
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors").
      */
     ConicSatelliteTracking(final Initializer initializer) {
         super(initializer);
@@ -160,7 +139,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         //======================================================================
         // Common for both cylindrical and conic sattelite tracking projections :
         //======================================================================
-        i        = toRadians(initializer.getAndStore(SATELLITE_ORBIT_INCLINATION));
+        final double i = toRadians(initializer.getAndStore(SATELLITE_ORBIT_INCLINATION));
         cos_i    = cos(i);
         sin_i    = sin(i);
         cos2_i   = cos_i * cos_i;
@@ -224,16 +203,14 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             } else {
                 n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder
             }
-
-            cosφ1xsinF1_n = cos_φ1 * sin(F1)/n;
+            cosφ1_sinF1_n = cos_φ1 * sin(F1)/n;
             s0 = F1 - n * L1;
-            ρ0 = cosφ1xsinF1_n / sin(n * L0 + s0); // *R in eq.28-12 in Snyder
+            final double ρ0 = cosφ1_sinF1_n / sin(n * L0 + s0); // *R in eq.28-12 in Snyder
 
             //======================== Unsure ======================================
             // Aim to assess the limit latitude associated with -s0/n L-value.
             //
             latitudeLimit = latitudeFromNewtonMethod(-s0 / n);
-            positiveN = (n >= 0);
             //======================================================================
 
 //            //Additionally we can compute the radius of the circle to which groundtracks
@@ -250,8 +227,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
             denormalize.convertBefore(1, 1, ρ0);  //For conic tracking
         } else {
-            n = latitudeLimit = cosφ1xsinF1_n = s0 = ρ0 = NaN;
-            positiveN = false;
+            n = latitudeLimit = cosφ1_sinF1_n = s0 = NaN;
         }
     }
 
@@ -273,15 +249,14 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     @Override
     public Matrix transform(double[] srcPts, int srcOff,
                             double[] dstPts, int dstOff,
-                            boolean derivate) throws ProjectionException {
-
-        final double λ      = srcPts[srcOff];
-        final double φ      = srcPts[srcOff + 1];
-
+                            boolean derivate) throws ProjectionException
+    {
+        final double λ = srcPts[srcOff];
+        final double φ = srcPts[srcOff + 1];
         /*
          * According to the Snyder (page 236) in those cases cannot or should not be plotted.
          */
-        if ( ((positiveN) && (φ<=latitudeLimit)) || ((!positiveN) && (φ>=latitudeLimit)) ){
+        if ( ((n >= 0) && (φ<=latitudeLimit)) || ((!(n >= 0)) && (φ>=latitudeLimit)) ){
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
@@ -291,7 +266,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         /*
          * As {@code latitudeLimit} is an approximation we repeat the test here.
          */
-        if ( ((positiveN) && (L<=-s0/n)) || ((!positiveN) && (L>=-s0/n)) ){
+        if ( ((n >= 0) && (L<=-s0/n)) || ((!(n >= 0)) && (L>=-s0/n)) ){
             //TODO if usefull, could we add :
             // latitudeLimit = φ;
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
@@ -299,7 +274,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
 
         final double nLandS0     = n*L+s0;
         final double sin_nLandS0 = sin(nLandS0);
-        final double ρ           = cosφ1xsinF1_n/sin_nLandS0;
+        final double ρ           = cosφ1_sinF1_n/sin_nLandS0;
 
         final double sinλ = sin(λ);
         final double cosλ = cos(λ);
@@ -313,7 +288,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         }
 
         //=========================To check the resolution =====================
-        final double dρ_dφ = cosφ1xsinF1_n  * (-1 / (sin_nLandS0*sin_nLandS0)) *cos(nLandS0)  * n
+        final double dρ_dφ = cosφ1_sinF1_n  * (-1 / (sin_nLandS0*sin_nLandS0)) *cos(nLandS0)  * n
                 * vector_L[1]; // dL/dφ
 
         final double dx_dλ = ρ*cosλ;
@@ -353,10 +328,9 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             //TODO : which values does it imply?
             throw new UnsupportedOperationException("Not supported yet for those coordinates."); //To change body of generated methods, choose Tools | Templates.
         }
-
-        final double ρ = positiveN ? hypot(x,y) : -hypot(x,y);
+        final double ρ = copySign(hypot(x,y), n);
         final double θ = atan(x/(-y) ); //undefined if x=y=0
-        final double L = (asin(cosφ1xsinF1_n/ρ) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
+        final double L = (asin(cosφ1_sinF1_n/ρ) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
 
         dstPts[dstOff  ] =  θ ;//λ
         dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
@@ -401,11 +375,6 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         return -asin(sin_dλ * sin_i);
     }
 
-
-    public double getLatitudeLimit(){
-        return latitudeLimit;
-    }
-
     /**
      * Method to compute the φn coefficient according to equation 28-9
      * in Snyder's Map Projections manual.
@@ -456,7 +425,6 @@ public class ConicSatelliteTracking extends NormalizedProjection{
      *          resulting array.
      */
     double[] computeLanddLdφForDirectTransform(final double φ, final boolean derivate){
-
         final double sinφ_sini = sin(φ) / sin_i;
         final double dλ        = -asin(sinφ_sini);
         final double tan_dλ    = tan(dλ);
@@ -471,5 +439,4 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             return new double[] {L};
         }
     }
-
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index 4348993..0b98e4c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -1,85 +1,47 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.sis.referencing.operation.projection;
 
 import org.opengis.referencing.operation.Matrix;
-
-import org.apache.sis.parameter.Parameters;
 import org.opengis.referencing.operation.OperationMethod;
-
-import static java.lang.Math.*;
-import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 
+import static java.lang.Math.*;
+
+
 /**
- * <cite>Cylindrical Satellite-Tracking projections</cite>.
- *
- * <cite>
- * - All groundtracks
- * for satellites orbiting the Earth with the same orbital parameters are shown
- * as straight lines on the map.
- *
- * - Cylindrical {@link CylindricalSatelliteTracking}
- * or conical {@link ConicSatelliteTracking} form available.
- *
- * - Neither conformal nor equal-area.
- *
- * - All meridians are equally spaced straight lines, parallel on cylindrical
- * form and converging to a common point on conical form.
- *
- * - All parallels are straight and parallel on cylindrical form and are
- * concentric circular arcs on conical form. Parallels are unequally spaced.
+ * Special case of <cite>Satellite-Tracking</cite> projection when the standard parallels are opposite.
  *
- * - Conformality occurs along two chosen parallels. Scale is correct along one
- * of these parameters on the conical form and along both on the cylindrical
- * form.
- *
- * Developed 1977 by Snyder
- * </cite>
- *
- * <cite> These formulas are confined to circular orbits and the SPHERICAL
- * Earth.</cite>
- *
- * <cite>The ascending and descending groundtracks meet at the northern an
- * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
- * Projection does not extend closer to the poles.</cite>
- *
- * This projection method has no associated EPSG code.
- *
- * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
- *
- * =============================================================================
- * REMARK : The parameters associated with the satellite (and its orbit) could
- * be aggregate in class of the kind : Satellite or SatelliteOrbit.
- * =============================================================================
- *
- * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
- * @author Matthieu Bastianelli (Geomatys)
- * @version 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
  */
 public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5972958777067525602L;
 
     /**
-     * Create a Cylindrical Satellite Tracking Projection from the given
-     * parameters.
+     * Create a Cylindrical Satellite Tracking Projection from the given parameters.
      *
      * The parameters are described in <cite>Map Projections - A Working
      * Manual</cite> By John P. Snyder.
@@ -126,12 +88,6 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         final double λ = srcPts[srcOff];
         final double φ = srcPts[srcOff + 1];
 
-        // TODO : check the condition and the thrown exception.
-            // if (abs(φ) > PI / 2 - abs(PI - i)) {  // Exceed tracking limit
-        if (φ > PI - i) {  // Exceed tracking limit
-            throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
-        }
-
         // compute an double array with {L} or {L, dL/dφ} if derivate recquired.
         final double[] vector_L = computeLanddLdφForDirectTransform(φ, derivate);
 
@@ -163,8 +119,6 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
 
         return new Matrix2(dx_dλ, dx_dφ,
                            dy_dλ, dy_dφ);
-
-//        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
     }
 
     /**
@@ -184,5 +138,4 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         dstPts[dstOff  ] =  x;
         dstPts[dstOff+1] = latitudeFromNewtonMethod(y); //φ
     }
-
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
index 0fd27c9..d57f0c0 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/package-info.java
@@ -159,7 +159,8 @@
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Rémi Maréchal (Geomatys)
  * @author  Adrian Custer (Geomatys)
- * @version 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
  *
  * @see <a href="http://mathworld.wolfram.com/MapProjection.html">Map projections on MathWorld</a>
  *
diff --git a/core/sis-referencing/src/main/resources/org/apache/sis/internal/referencing/provider/Descriptions.properties b/core/sis-referencing/src/main/resources/org/apache/sis/internal/referencing/provider/Descriptions.properties
new file mode 100644
index 0000000..374d094
--- /dev/null
+++ b/core/sis-referencing/src/main/resources/org/apache/sis/internal/referencing/provider/Descriptions.properties
@@ -0,0 +1,5 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements;
+# and to You under the Apache License, Version 2.0.
+satellite_orbit_inclination = Angle of inclination between the plane of the Earth's Equator and the plane of the satellite orbit.
+satellite_orbital_period = Time required for revolution of the satellite.
+ascending_node_period = Length of Earth's rotation with respect to the precessed ascending node.
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
index 8888882..a78648b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
@@ -36,7 +36,7 @@ import static org.junit.Assert.*;
  * Tests {@link Providers} and some consistency rules of all providers defined in this package.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.6
  * @module
  */
@@ -104,6 +104,7 @@ public final strictfp class ProvidersTest extends TestCase {
             ObliqueMercatorTwoPoints.class,
             ObliqueMercatorTwoPointsCenter.class,
             ZonedTransverseMercator.class,
+            SatelliteTracking.class,
             Sinusoidal.class,
             Polyconic.class,
             Mollweide.class,
@@ -248,4 +249,14 @@ public final strictfp class ProvidersTest extends TestCase {
             }
         }
     }
+
+    /**
+     * Tests the description provided in some parameters.
+     */
+    @Test
+    public void testDescription() {
+        assertFalse(SatelliteTracking.SATELLITE_ORBIT_INCLINATION.getDescription().length() == 0);
+        assertFalse(SatelliteTracking.SATELLITE_ORBITAL_PERIOD   .getDescription().length() == 0);
+        assertFalse(SatelliteTracking.ASCENDING_NODE_PERIOD      .getDescription().length() == 0);
+    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
index 10ace56..077a12b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -1,52 +1,49 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.sis.referencing.operation.projection;
 
-import static java.lang.Math.sin;
 import java.util.Collections;
+import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.referencing.NilReferencingObject;
 import org.apache.sis.internal.referencing.provider.SatelliteTracking;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.datum.DefaultEllipsoid;
 import org.apache.sis.referencing.operation.transform.MathTransformFactoryMock;
-import org.apache.sis.test.DependsOn;
+import org.apache.sis.test.DependsOnMethod;
 import org.junit.Test;
-import org.opengis.parameter.ParameterValueGroup;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
 
+import static java.lang.StrictMath.sin;
 import static java.lang.StrictMath.toRadians;
-import org.apache.sis.test.DependsOnMethod;
 import static org.junit.Assert.assertTrue;
 
+
 /**
- * Tests coordiantes computed by applying a conic satellite-tracking projection
+ * Tests coordinates computed by applying a conic satellite-tracking projection
  * with {@link ConicSatelliteTracking}.
  *
- * @author Matthieu Bastianelli (Geomatys)
- * @version 1.0
- * @since 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
+ * @since   1.1
  * @module
  */
-public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
-
+public strictfp class ConicSatelliteTrackingTest extends MapProjectionTestCase {
     /**
      * Creates a new instance of {@link ConicSatelliteTracking} concatenated
      * with the (de)normalization matrices. The new instance is stored in the
@@ -63,7 +60,6 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      * @param φ0 : latitude_of_origin : latitude Crossing the central meridian
      * at the desired origin of rectangular coordinates (null or NaN for
      * cylindrical satellite tracking projection.)
-     * @return
      */
     void createProjection(final double i,
             final double orbitalT, final double ascendingNodeT,
@@ -80,8 +76,8 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
         values.parameter("semi_major").setValue(sphere.getSemiMajorAxis());
         values.parameter("semi_minor").setValue(sphere.getSemiMinorAxis());
         values.parameter("satellite_orbit_inclination").setValue(i);
-        values.parameter("satellite_orbital_period").setValue(orbitalT);
-        values.parameter("ascending_node_period").setValue(ascendingNodeT);
+        values.parameter("satellite_orbital_period").setValue(orbitalT, Units.MINUTE);
+        values.parameter("ascending_node_period").setValue(ascendingNodeT, Units.MINUTE);
         values.parameter("central_meridian").setValue(λ0);
         values.parameter("standard_parallel_1").setValue(φ1);
 
@@ -104,8 +100,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      * Test based on the numerical example given by Snyder p. 360 to 363 of
      * <cite> Map Projections - A working Manual</cite>
      *
-     * @throws FactoryException if an error occurred while creating the map
-     * projection.
+     * @throws FactoryException if an error occurred while creating the map projection.
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
@@ -142,8 +137,7 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
      * extracting the computed results of the projection. Thus this method
      * should be used as a non-regression test.
      *
-     * @throws FactoryException if an error occurred while creating the map
-     * projection.
+     * @throws FactoryException if an error occurred while creating the map projection.
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
@@ -245,7 +239,6 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
                 });
     }
 
-
     /**
      * Tests the derivatives at a few points on a sphere. This method compares the derivatives computed
      * by the projection with an estimation of derivatives computed by the finite differences method.
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
index 95b6832..5221dcb 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -1,44 +1,41 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
- *   http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 package org.apache.sis.referencing.operation.projection;
 
-import static java.lang.Double.NaN;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.test.DependsOnMethod;
-import static org.junit.Assert.assertTrue;
 import org.junit.Test;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.util.FactoryException;
+
+import static java.lang.Double.NaN;
+import static org.junit.Assert.assertTrue;
+
 
 /**
- * Tests coordiantes computed by applying a cylindrical satellite-tracking projection
+ * Tests coordinates computed by applying a cylindrical satellite-tracking projection
  * with {@link CylindricalSatelliteTracking}.
  *
- * @author Matthieu Bastianelli (Geomatys)
- * @version 1.0
- * @since 1.0
+ * @author  Matthieu Bastianelli (Geomatys)
+ * @version 1.1
+ * @since   1.1
  * @module
  */
-public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest {
-
-
-
+public final strictfp class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest {
     /**
      * Creates a new instance of {@link ConicSatelliteTracking} concatenated
      * with the (de)normalization matrices. The new instance is stored in the
@@ -51,16 +48,14 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * precessed ascending node.
      * @param λ0 : central meridian.
      * @param φ1 : first parallel of conformality, with true scale.
-     * @param φ2 : second parallel of conformality, without true scale.
-     * @param φ0 : latitude_of_origin : latitude Crossing the central meridian
      * at the desired origin of rectangular coordinates (null or NaN for
      * cylindrical satellite tracking projection.)
-     * @return
      */
     private void createProjection(final double i,
             final double orbitalT, final double ascendingNodeT,
             final double λ0,       final double φ1)
-            throws FactoryException {
+            throws FactoryException
+    {
         super.createProjection(i, orbitalT, ascendingNodeT, λ0, φ1, NaN, NaN);
     }
 
@@ -70,8 +65,7 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * Test based on the numerical example given by Snyder p. 360 to 363 of
      * <cite> Map Projections - A working Manual</cite>
      *
-     * @throws FactoryException if an error occurred while creating the map
-     * projection.
+     * @throws FactoryException if an error occurred while creating the map projection.
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
@@ -102,8 +96,7 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * Satellite-Tracking Projections shown in table 38 from
      * <cite> Map Projections - A working Manual</cite>
      *
-     * @throws FactoryException if an error occurred while creating the map
-     * projection.
+     * @throws FactoryException if an error occurred while creating the map projection.
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
@@ -214,8 +207,8 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
      * @throws TransformException if an error occurred while projecting a point.
      */
     @Test
-    @DependsOnMethod("testInverseDerivative")
     @Override
+    @DependsOnMethod("testInverseDerivative")
     public void testDerivativeOnSphere() throws FactoryException, TransformException {
         createProjection(
                 99.092,  //satellite_orbit_inclination
@@ -232,6 +225,4 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
         verifyDerivative( -56, 50);
         verifyDerivative( -20, 47);
     }
-
-
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index 28156b3..388b5a8 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -25,7 +25,7 @@ import org.junit.BeforeClass;
  * All tests from the {@code sis-referencing} module, in rough dependency order.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */
diff --git a/ide-project/NetBeans/build.xml b/ide-project/NetBeans/build.xml
index 64a2234..fcaedcf 100644
--- a/ide-project/NetBeans/build.xml
+++ b/ide-project/NetBeans/build.xml
@@ -97,6 +97,7 @@
         <include name="**/*.lst"/>
       </fileset>
       <fileset dir="${project.root}/core/sis-referencing/src/main/resources">
+        <include name="**/*.properties"/>
         <include name="**/*.sql"/>
       </fileset>
       <fileset dir="${project.root}/storage/sis-xmlstore/src/main/resources">


[sis] 05/09: fix (referencing satellite-tracking projections) : fix the Jacobian matrix in both satellite-tracking projections by inverting m01 and m10 in order to have the partial derivative of x on the matrix's first line and of y on the second line.

Posted by de...@apache.org.
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 47873d482b06c88e11d5d9d48266f84cec48a0f2
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Thu Jul 25 10:25:35 2019 +0200

    fix (referencing satellite-tracking projections) : fix the Jacobian matrix in both satellite-tracking projections by inverting m01 and m10 in order to have the partial derivative of x on the matrix's first line and of y on the second line.
---
 .../sis/referencing/operation/projection/ConicSatelliteTracking.java  | 4 ++--
 .../operation/projection/CylindricalSatelliteTracking.java            | 4 ++--
 .../referencing/operation/projection/ConicSatelliteTrackingTest.java  | 4 ++--
 .../operation/projection/CylindricalSatelliteTrackingTest.java        | 3 ++-
 4 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index 9b173f1..9b0b0b0 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -331,8 +331,8 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         final double dy_dφ = -cosλ * dρ_dφ;
         //======================================================================
 
-        return new Matrix2(dx_dλ, dy_dλ,
-                           dx_dφ, dy_dφ);
+        return new Matrix2(dx_dλ, dx_dφ,
+                           dy_dλ, dy_dφ);
 //        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
 
         /* =====================================================================
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index d68ec87..9348834 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -166,8 +166,8 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
                 * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
         //======================================================================
 
-        return new Matrix2(dx_dλ, dy_dλ,
-                           dx_dφ, dy_dφ);
+        return new Matrix2(dx_dλ, dx_dφ,
+                           dy_dλ, dy_dφ);
 
 //        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
     }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
index 242e024..10ace56 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -267,8 +267,8 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
         );
         final double delta = (1.0 / 60) / 1852;                 // Approximately 1 metre.
         derivativeDeltas = new double[] {delta, delta};
-        tolerance = Formulas.LINEAR_TOLERANCE / 10;
-        verifyDerivative(   0, -10);
+        tolerance = Formulas.LINEAR_TOLERANCE/100 ;
+        verifyDerivative( -75, 40);
         verifyDerivative(-100,  3);
         verifyDerivative( -56, 50);
         verifyDerivative( -20, 47);
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
index 844ba7f..95b6832 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -226,7 +226,8 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
         );
         final double delta = (1.0 / 60) / 1852;                 // Approximately 1 metre.
         derivativeDeltas = new double[] {delta, delta};
-        tolerance = Formulas.LINEAR_TOLERANCE / 10;
+        tolerance = Formulas.LINEAR_TOLERANCE / 100;
+        verifyDerivative(-75, 40);
         verifyDerivative(-100,  3);
         verifyDerivative( -56, 50);
         verifyDerivative( -20, 47);


[sis] 01/09: feat (sis-referencing & internal) : implement cylindrical and conic satellite-tracking projections from Snyder's 'Map Projections - a working Manual'. Don't compute derivatives yet. Very few tests.

Posted by de...@apache.org.
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 d6502364fe43513d836c23c2016844d2ca315531
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Tue Jul 23 09:31:22 2019 +0200

    feat (sis-referencing & internal) : implement cylindrical and conic satellite-tracking projections from Snyder's 'Map Projections - a working Manual'. Don't compute derivatives yet. Very few tests.
---
 .../referencing/provider/SatelliteTracking.java    | 200 +++++++++++
 .../projection/ConicSatelliteTracking.java         | 398 +++++++++++++++++++++
 .../projection/CylindricalSatelliteTracking.java   | 197 ++++++++++
 .../projection/ConicSatelliteTrackingTest.java     | 127 +++++++
 .../CylindricalSatelliteTrackingTest.java          | 117 ++++++
 5 files changed, 1039 insertions(+)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
new file mode 100644
index 0000000..3e9382d
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -0,0 +1,200 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sis.internal.referencing.provider;
+
+import javax.xml.bind.annotation.XmlTransient;
+import static org.apache.sis.internal.referencing.provider.AbstractProvider.createZeroConstant;
+import static org.apache.sis.internal.referencing.provider.Mercator1SP.LATITUDE_OF_ORIGIN;
+import org.apache.sis.measure.Units;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.parameter.ParameterBuilder;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.projection.ConicSatelliteTracking;
+import org.apache.sis.referencing.operation.projection.CylindricalSatelliteTracking;
+import org.apache.sis.referencing.operation.projection.NormalizedProjection;
+import org.apache.sis.util.ArgumentChecks;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.ParameterNotFoundException;
+
+/**
+ * The provider for <cite>Satellite-Tracking projections"</cite>.
+ *
+ * <cite>
+ * - All groundtracks for satellites orbiting the Earth with the same orbital
+ * parameters are shown as straight lines on the map.
+ *
+ * - Cylindrical {@link CylindricalSatelliteTracking} or conical
+ * {@link ConicSatelliteTracking} form available.
+ *
+ * - Neither conformal nor equal-area.
+ *
+ * - All meridians are equally spaced straight lines, parallel on cylindrical
+ * form and converging to a common point on conical form.
+ *
+ * - All parallels are straight and parallel on cylindrical form and are
+ * concentric circular arcs on conical form. Parallels are unequally spaced.
+ *
+ * - Conformality occurs along two chosen parallels. Scale is correct along one
+ * of these parameters on the conical form and along both on the cylindrical
+ * form.
+ *
+ * Developed 1977 by Snyder
+ * </cite>
+ *
+ * <cite> These formulas are confined to circular orbits and the SPHERICAL
+ * Earth.</cite>
+ *
+ * <cite>The ascending and descending groundtracks meet at the northern an
+ * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
+ * Projection does not extend closer to the poles.</cite>
+ *
+ * This projection method has no associated EPSG code.
+ *
+ * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
+ *
+ * =============================================================================
+ * REMARK : The parameters associated with the satellite (and its orbit) could
+ * be aggregate in class of the kind : Satellite or SatelliteOrbit.
+ * =============================================================================
+ *
+ * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
+ * @author Matthieu Bastianelli (Geomatys)
+ * @version 1.0
+ */
+@XmlTransient
+public class SatelliteTracking extends MapProjection {
+
+    /**
+     * Name of this projection.
+     */
+    public static final String NAME = "Satellite-Tracking";
+
+    /**
+     * The operation parameter descriptor for the <cite>Longitude of projection
+     * center</cite> (λ₀) parameter value. Valid values range is [-180 … 180]°
+     * and default value is 0°.
+     */
+    public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN = ESRI.CENTRAL_MERIDIAN;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of 1st standard
+     * parallel</cite>. For conical satellite-tracking projection : first
+     * parallel of conformality with true scale.
+     *
+     * Valid values range is [-90 … 90]° and default value is the value given to
+     * the {@link #LATITUDE_OF_FALSE_ORIGIN} parameter.
+     */
+    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1 = LambertConformal2SP.STANDARD_PARALLEL_1;
+
+    /**
+     * The operation parameter descriptor for the <cite>second parallel of
+     * conformality but without true scale</cite> parameter value for conic
+     * projection. Valid values range is [-90 … 90]° and default value is the
+     * opposite value given to the {@link #STANDARD_PARALLEL_1} parameter.
+     */
+    public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2 = LambertConformal2SP.STANDARD_PARALLEL_2;
+
+    /**
+     * Latitude Crossing the central meridian at the desired origin of
+     * rectangular coordinates.
+     */
+    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = TransverseMercator.LATITUDE_OF_ORIGIN;;
+
+    /**
+     * The operation parameter descriptor for the <cite> Angle of inclination
+     * between the plane of the Earth's Equator and the plane of the satellite
+     * orbit</cite> parameter value. <cite> It's measured counterclockwise from
+     * the Equatorto the orbit plane at the ascending node (99.092° for Landsat
+     * 1, 2, 3</cite>
+     */
+    public static final ParameterDescriptor<Double> SATELLITE_ORBIT_INCLINATION;
+
+    /**
+     * The operation parameter descriptor for the <cite> time required for
+     * revolution of the satellite</cite> parameter value.
+     */
+    public static final ParameterDescriptor<Double> SATELLITE_ORBITAL_PERIOD;
+
+    /**
+     * The operation parameter descriptor for the <cite> length of earth's
+     * rotation with respect to the precessed ascending node</cite> parameter
+     * value. The ascending node is the point on the satellite orbit at which
+     * the satellite crosses the Earth's equatorial plane in a northerly
+     * direction.
+     */
+    public static final ParameterDescriptor<Double> ASCENDING_NODE_PERIOD; //Constant for landsat but is it the case for every satellite with circular orbital?
+
+    /**
+     * The group of all parameters expected by this coordinate operation.
+     */
+    static final ParameterDescriptorGroup PARAMETERS;
+
+    static {
+        final ParameterBuilder builder = new ParameterBuilder();
+        builder.setCodeSpace(null, null)
+                .setRequired(true);
+
+        SATELLITE_ORBIT_INCLINATION = builder
+                .addName("satellite_orbit_inclination")
+                .setDescription("Angle of inclination between the plane of the Earth's Equator and the plane of the satellite orbit")
+                .create(0, Units.RADIAN);
+
+        SATELLITE_ORBITAL_PERIOD = builder
+                .addName("satellite_orbital_period")
+                .setDescription("Time required for revolution of the satellite")
+                .createStrictlyPositive(103.267, Units.MINUTE); //Or hours? Seconds?
+
+        ASCENDING_NODE_PERIOD = builder
+                .addName("ascending_node_period")
+                .setDescription("Length of Earth's rotation with respect to the precessed ascending node")
+                .createStrictlyPositive(98.884, Units.MINUTE);
+                
+        PARAMETERS = builder.addName(NAME)
+                .createGroupForMapProjection(CENTRAL_MERIDIAN,
+                        STANDARD_PARALLEL_1, STANDARD_PARALLEL_2,
+                        SATELLITE_ORBIT_INCLINATION, SATELLITE_ORBITAL_PERIOD,
+                        ASCENDING_NODE_PERIOD, LATITUDE_OF_ORIGIN);
+
+    }
+
+    /**
+     * Constructs a new provider.
+     */
+    public SatelliteTracking() {
+        super(PARAMETERS);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the map projection created from the given parameter values.
+     */
+    @Override
+    protected NormalizedProjection createProjection(final Parameters parameters) throws ParameterNotFoundException {
+        ArgumentChecks.ensureNonNull("Parameters", parameters);
+
+        if (parameters.getValue(STANDARD_PARALLEL_2) == -parameters.getValue(STANDARD_PARALLEL_1)) { 
+            return new org.apache.sis.referencing.operation.projection.CylindricalSatelliteTracking(this, parameters);
+        } else {
+            return new org.apache.sis.referencing.operation.projection.ConicSatelliteTracking(this, parameters);
+        }
+    }
+
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
new file mode 100644
index 0000000..3c8d556
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import static java.lang.Double.NaN;
+import java.util.EnumMap;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.util.Workaround;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+
+import static java.lang.Math.*;
+import java.util.function.DoubleUnaryOperator;
+import org.apache.sis.internal.referencing.Resources;
+import static org.apache.sis.internal.referencing.provider.SatelliteTracking.*;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+
+/**
+ * <cite>Cylindrical Satellite-Tracking projections</cite>.
+ * 
+ * <cite>
+ * - All groundtracks for satellites orbiting the Earth with the same orbital
+ * parameters are shown as straight lines on the map.
+ *
+ * - Cylindrical {@link CylindricalSatelliteTracking}
+ * or conical {@link ConicSatelliteTracking} form available. 
+ *
+ * - Neither conformal nor equal-area.
+ *
+ * - All meridians are equally spaced straight lines, parallel on cylindrical
+ * form and converging to a common point on conical form.
+ *
+ * - All parallels are straight and parallel on cylindrical form and are
+ * concentric circular arcs on conical form. Parallels are unequally spaced.
+ *
+ * - Conformality occurs along two chosen parallels. Scale is correct along one
+ * of these parameters on the conical form and along both on the cylindrical
+ * form.
+ *
+ * Developed 1977 by Snyder
+ * </cite>
+ *
+ * <cite> These formulas are confined to circular orbits and the SPHERICAL
+ * Earth.</cite>
+ *
+ * <cite>The ascending and descending groundtracks meet at the northern an
+ * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
+ * Projection does not extend closer to the poles.</cite>
+ *
+ * This projection method has no associated EPSG code.
+ *
+ * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
+ *
+ * =============================================================================
+ * REMARK : The parameters associated with the satellite (and its orbit) could
+ * be aggregate in class of the kind : Satellite or SatelliteOrbit.
+ * =============================================================================
+ *
+ * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
+ * @author Matthieu Bastianelli (Geomatys)
+ * @version 1.0
+ */
+public class ConicSatelliteTracking extends NormalizedProjection{
+    
+    /**
+     * {@code SATELLITE_ORBIT_INCLINATION}
+     */
+    final double i;
+    
+    /**
+     * Coefficients for both cylindrical and conic Satellite-Tracking Projection.
+     */
+    final double cos_i, sin_i, cos2_i, cos_φ1, p2_on_p1;
+    
+//    /**
+//     * Radius of the circle radius of the circle to which groundtracks
+//     * are tangent on the map.
+//     */
+//    private final double ρs;
+    
+    /**
+     * Projection Cone's constant.
+     */
+    private final double n;
+    /**
+     * Approximation of the limiting lattitude to which the {@code L coefficient}
+     * is associated with a particular case {@code L equals -s0/n}.
+     */
+    private final double lattitudeLimit;
+    /**
+     * Boolean attribute indicating if the projection cone's constant is positive.
+     * */
+    private final boolean positiveN;
+    /**
+     * Coefficients for the Conic Satellite-Tracking Projection.
+     */
+    private final double cos_φ1xsin_F1, s0, ρ0;
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database ("Relax constraint on
+     * placement of this()/super() call in constructors").
+     */
+    @Workaround(library = "JDK", version = "1.8")
+    static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+        final EnumMap<NormalizedProjection.ParameterRole, ParameterDescriptor<Double>> roles = new EnumMap<>(NormalizedProjection.ParameterRole.class);
+        roles.put(NormalizedProjection.ParameterRole.CENTRAL_MERIDIAN, CENTRAL_MERIDIAN);
+        return new Initializer(method, parameters, roles, (byte) 0);
+    }
+
+    /**
+     * Create a Cylindrical Satellite Tracking Projection from the given
+     * parameters.
+     *
+     * The parameters are described in <cite>Map Projections - A Working
+     * Manual</cite> By John P. Snyder.
+     *
+     * @param method : description of the projection parameters.
+     * @param parameters : the parameter values of the projection to create.
+     */
+    public ConicSatelliteTracking(final OperationMethod method, final Parameters parameters) {
+        this(initializer(method, parameters));
+    }
+
+    /**
+     * Constructor for ConicSatelliteTracking.
+     * 
+     * Calculation are based on <cite>28 .SATTELITE-TRACKING PROJECTIONS </cite> 
+     * in <cite> Map Projections - A Working Manual</cite> By John P. Snyder.
+     * 
+     * @param initializer 
+     */
+    ConicSatelliteTracking(final Initializer initializer) {
+        super(initializer);
+
+        //======================================================================
+        // Common for both cylindrical and conic sattelite tracking projections :
+        //======================================================================
+        i        = toRadians(initializer.getAndStore(SATELLITE_ORBIT_INCLINATION)); // Radian input value.
+        cos_i    = cos(i);
+        sin_i    = sin(i);
+        cos2_i   = cos_i * cos_i;
+        p2_on_p1 = initializer.getAndStore(SATELLITE_ORBITAL_PERIOD) / initializer.getAndStore(ASCENDING_NODE_PERIOD);
+
+        final double φ1      = toRadians(initializer.getAndStore(STANDARD_PARALLEL_1));  //appropriated use of toRadians??
+        cos_φ1               = cos(φ1);
+        final double cos2_φ1 = cos_φ1 * cos_φ1;
+        
+        //======================================================================
+        // For conic projection :
+        //=======================
+        if(!(this instanceof CylindricalSatelliteTracking)) {
+            final double sin_φ1 = sin(φ1);
+            final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));   //appropriated use of toRadians??
+            final double cos_φ0 = cos(φ0);
+            final double cos2_φ0 = cos_φ0 * cos_φ0;
+            final double sin_φ0 = sin(φ0);
+            final double φ2 = toRadians(initializer.getAndStore(STANDARD_PARALLEL_2));   //appropriated use of toRadians??
+
+//        final double F0 = atan( (p2_on_p1*cos2_φ0 - cos_i)/sqrt(cos2_φ0 - cos2_i) );
+//        final double F1 = atan( (p2_on_p1*cos2_φ1 - cos_i)/sqrt(cos2_φ1 - cos2_i) );
+//        final double F2 = atan( (p2_on_p1*cos2_φ2 - cos_i)/sqrt(cos2_φ2 - cos2_i) );
+//        final double dλ0 = -asin(sin_φ0/sin_i);
+//        final double dλ1 = -asin(sin_φ1/sin_i);
+//        final double dλ2 = -asin(sin_φ2/sin_i);   
+//        final double λt0 = atan( tan(dλ0) * cos_i );
+//        final double λt1 = atan( tan(dλ1) * cos_i );
+//        final double λt2 = atan( tan(dλ2) * cos_i );
+            final DoubleUnaryOperator computeFn = (cos2_φn) -> atan((p2_on_p1 * cos2_φn - cos_i) / sqrt(cos2_φn - cos2_i)); // eq.28-9 in Snyder 
+            final double F0 = computeFn.applyAsDouble(cos2_φ0);
+            /*
+         *Inclination of the groundtrack to the meridian at latitude φ1
+             */
+            final double F1 = computeFn.applyAsDouble(cos2_φ1);
+
+            cos_φ1xsin_F1 = cos_φ1 * sin(F1);
+
+            final DoubleUnaryOperator computedλn = (sin_φn) -> -asin(sin_φn / sin_i); // eq.28-2a in Snyder 
+            final double dλ0 = computedλn.applyAsDouble(sin_φ0);
+            final double dλ1 = computedλn.applyAsDouble(sin_φ1);
+
+            final DoubleUnaryOperator computeλtn = (dλn) -> atan(tan(dλn) * cos_i); // eq.28-3a in Snyder 
+            final double λt0 = computeλtn.applyAsDouble(dλ0);
+            final double λt1 = computeλtn.applyAsDouble(dλ1);
+
+            final double L0 = λt0 - p2_on_p1 * dλ0;
+            final double L1 = λt1 - p2_on_p1 * dλ1;
+
+            if (φ1 == PI / 2 - i) { //tracking limit
+                final double factor = (p2_on_p1 * cos_i - 1);
+                final double factor2 = factor * factor;
+                n = sin_i / (factor2); //eq. 28-18 in Snyder 
+            } else if (φ2 != φ1) {
+                final double cos_φ2 = cos(φ2);
+                final double cos2_φ2 = cos_φ2 * cos_φ2;
+                final double sin_φ2 = sin(φ2);
+                final double dλ2 = computedλn.applyAsDouble(sin_φ2);
+                final double λt2 = computeλtn.applyAsDouble(dλ2);
+                final double L2 = λt2 - p2_on_p1 * dλ2;
+                final double F2 = computeFn.applyAsDouble(cos2_φ2);
+                n = (F2 - F1) / (L2 - L1);
+            } else {
+                n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder 
+            }
+            s0 = F1 - n * L1;
+            ρ0 = cos_φ1xsin_F1 / (n * sin(n * L0 + s0)); // *R in eq.28-12 in Snyder 
+
+            //======================== Unsure ======================================
+            // Aim to assess the limit lattitude associated with -s0/n L-value.
+            //
+            lattitudeLimit = lattitudeFromNewtonMethod(-s0 / n);
+            positiveN = (n >= 0);
+            //======================================================================
+
+//            //Additionally we can compute the radius of the circle to which groundtracks
+//            //are tangent on the map :
+//            ρs = cos_φ1xsin_F1 / n; //*R
+
+            //======================================================================        
+            /*
+         * At this point, all parameters have been processed. Now process to their
+         * validation and the initialization of (de)normalize affine transforms.
+             */
+        final MatrixSIS normalize   = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
+//        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+        normalize  .convertAfter (0, n, null);  //For conic tracking
+//        denormalize.convertBefore(0, 1/PI,     null);
+//        denormalize.convertBefore(1, , null);
+        }else{
+            n = lattitudeLimit = cos_φ1xsin_F1 = s0 = ρ0 = NaN;
+            positiveN = false;
+        }
+    }
+
+    /**
+     * Converts the specified (λ,φ) coordinate (units in radians) and stores the result in {@code dstPts}
+     * (linear distance on a unit sphere). In addition, opportunistically computes the projection derivative
+     * if {@code derivate}
+     * is {@code true}.
+     *
+     * <cite> The Yaxis lies along the central meridian λ0, y increasing
+     * northerly, and X axis intersects perpendicularly at LATITUDE_OF_ORIGIN
+     * φ0, x increasing easterly.
+     * </cite>
+     *
+     * @return the matrix of the projection derivative at the given source position,
+     *         or {@code null} if the {@code derivate} argument is {@code false}.
+     * @throws ProjectionException if the coordinates can not be converted.
+     */
+    @Override
+    public Matrix transform(double[] srcPts, int srcOff, 
+                            double[] dstPts, int dstOff,
+                            boolean derivate) throws ProjectionException {
+        
+        final double λ      = srcPts[srcOff];
+        final double φ      = srcPts[srcOff + 1];
+        
+        /*
+         * According to the Snyder (page 236) in those cases cannot or should not be plotted.
+         */
+        if( ((positiveN)&&(φ<=lattitudeLimit)) || ((!positiveN)&&(φ>=lattitudeLimit)) ){
+            throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
+        }
+        
+        final double sin_φ  = sin(φ);
+        final double dλ     = -asin(sin_φ / sin_i);
+        final double λt     = atan(tan(dλ) * cos_i);
+        final double L      = λt - p2_on_p1 * dλ;
+        
+        /*
+         * As {@code lattitudeLimit} is an approximation we repeat the test here.
+         */
+        if( ((positiveN)&&(L<=-s0/n)) || ((!positiveN)&&(L>=-s0/n)) ){
+            //TODO if usefull, could we add :
+            // lattitudeLimit = φ;
+            throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
+        }
+        
+        final double ρ      = cos_φ1xsin_F1/(n*sin(n*L+s0)); 
+        final double θ      = λ;      // extracted n
+        
+        final double sinθ = sin(θ);
+        final double cosθ = cos(θ);
+        if (dstPts != null) {
+            dstPts[dstOff    ] = ρ * sinθ;   // x
+            dstPts[dstOff + 1] = ρ0 - ρ*cosθ;  // y       //TODO : extract ρ0 when ensuring : λ = λ - λ0;
+        }
+        
+         if (!derivate) {
+            return null;
+        }
+
+        //=========================TO Resolve =================================
+//        final double dx_dλ = ρ*n*cosθ; 
+//        final double dx_dφ =?;
+//
+//        final double dy_dλ =  ρ*n*sinθ;
+//        final double dy_dφ = ?;
+        //======================================================================
+        
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        
+        //Additionally we can compute the scale factors :
+        // k = ρ*n/cos(φ); // /R
+        // h = k*tan(F)/tan(n*L+s0);
+    }
+
+    /**
+     * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
+     * and stores the result in {@code dstPts} (angles in radians).
+     *
+     * @throws ProjectionException if the coordinates can not be converted.
+     */
+    @Override
+    protected void inverseTransform(double[] srcPts, int srcOff,
+                                    double[] dstPts, int dstOff)
+                                    throws ProjectionException {
+        
+        final double x   = srcPts[srcOff];
+        final double y   = srcPts[srcOff + 1] - ρ0;
+        
+        //TODO : extract - ρ0 : MatrixSIS convertBefore and convertAfter??
+        
+        if((x== 0) && (y == 0)) {
+            //TODO : which values does it imply?
+            throw new UnsupportedOperationException("Not supported yet for those coordinates."); //To change body of generated methods, choose Tools | Templates.
+        }
+               
+        final double ρ = positiveN? hypot(x,y) : -hypot(x,y);
+        final double θ = atan(x/(-y) ); //undefined if x=y=0
+        final double L = (asin(cos_φ1xsin_F1/(ρ*n)) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
+        
+        //TODO ensure that λ0 will be added. ;  In eq. Snyder 28-23 : λ0 + θ/n
+        dstPts[dstOff  ] =  θ ;//λ 
+        dstPts[dstOff+1] = lattitudeFromNewtonMethod(L); //φ
+    }
+    
+    protected final double lattitudeFromNewtonMethod(final double l){
+        double dλ = -PI/2;
+        double dλn = Double.MIN_VALUE;
+        double A, A2, Δdλ;
+        
+        int count=0;
+        int maxIteration = 100; //TODO : check the value.
+                
+        while((count==0) || dλ-dλn >= (0.1*PI/180)){ //TODO : check the condition. It is considered here that convergence is reached when improvement is lower than 0.1°.
+            if(count >= maxIteration){
+                throw new RuntimeException(Resources.format(Resources.Keys.NoConvergence));
+            }
+            dλn = dλ;
+
+//            λt = l + p2_on_p1 * dλ_n; 
+//            dλ = atan(tan(λt) / cos_i);
+
+            A = tan(l + p2_on_p1*dλn) / cos_i;
+            A2=A*A;
+            Δdλ = -(dλn-atan(A)) / (1- (A2 + 1/cos2_i) * (p2_on_p1*cos_i/(A2+1)) );
+            dλ = dλn + Δdλ ;
+
+            count++;
+        }
+//        λt = L + p2_on_p1 * dλ;
+        final double sin_dλ = sin(dλ);
+        return -asin(sin_dλ * sin_i); 
+    }
+    
+    
+    public double getLattitudeLimit(){
+        return lattitudeLimit;
+    }
+    
+//    /**
+//     * Radius of the circle radius of the circle to which groundtracks
+//     * are tangent on the map.
+//     * 
+//     * @return radius ρs.
+//     */
+//    public double getRadiusOfTangencyCircle(){
+//        return ρs;
+//    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
new file mode 100644
index 0000000..70ea6fe
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import org.opengis.referencing.operation.Matrix;
+
+import org.apache.sis.parameter.Parameters;
+import org.opengis.referencing.operation.OperationMethod;
+
+import static java.lang.Math.*;
+import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+
+/**
+ * <cite>Cylindrical Satellite-Tracking projections</cite>. 
+ * 
+ * <cite>
+ * - All groundtracks
+ * for satellites orbiting the Earth with the same orbital parameters are shown
+ * as straight lines on the map.
+ *
+ * - Cylindrical {@link CylindricalSatelliteTracking}
+ * or conical {@link ConicSatelliteTracking} form available. 
+ *
+ * - Neither conformal nor equal-area.
+ *
+ * - All meridians are equally spaced straight lines, parallel on cylindrical
+ * form and converging to a common point on conical form.
+ *
+ * - All parallels are straight and parallel on cylindrical form and are
+ * concentric circular arcs on conical form. Parallels are unequally spaced.
+ *
+ * - Conformality occurs along two chosen parallels. Scale is correct along one
+ * of these parameters on the conical form and along both on the cylindrical
+ * form.
+ *
+ * Developed 1977 by Snyder
+ * </cite>
+ *
+ * <cite> These formulas are confined to circular orbits and the SPHERICAL
+ * Earth.</cite>
+ *
+ * <cite>The ascending and descending groundtracks meet at the northern an
+ * southern tracking limits, lats. 80.9°N and S for landsat 1, 2 and 3. The map
+ * Projection does not extend closer to the poles.</cite>
+ *
+ * This projection method has no associated EPSG code.
+ *
+ * Earth radius is normalized. Its value is 1 and is'nt an input parameter.
+ *
+ * =============================================================================
+ * REMARK : The parameters associated with the satellite (and its orbit) could
+ * be aggregate in class of the kind : Satellite or SatelliteOrbit.
+ * =============================================================================
+ *
+ * @see <cite>Map Projections - A Working Manual</cite> By John P. Snyder
+ * @author Matthieu Bastianelli (Geomatys)
+ * @version 1.0
+ */
+public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
+
+    
+    /**
+     * F1' in Snyder : tangente of the angle on both the globe and on the map between 
+     * the groundtrack and the meridian at latitude φ1. 
+     */
+    final double dF1;
+    
+    /**
+     * Create a Cylindrical Satellite Tracking Projection from the given
+     * parameters.
+     *
+     * The parameters are described in <cite>Map Projections - A Working
+     * Manual</cite> By John P. Snyder.
+     *
+     * @param method : description of the projection parameters.
+     * @param parameters : the parameter values of the projection to create.
+     */
+    public CylindricalSatelliteTracking(final OperationMethod method, final Parameters parameters) {
+        this(initializer(method, parameters));
+    }
+
+    private CylindricalSatelliteTracking(final Initializer initializer) {
+        super(initializer);
+        
+        final double cos2_φ1 = cos_φ1 * cos_φ1;
+        dF1 = (p2_on_p1 * cos2_φ1 - cos_i) / sqrt(cos2_φ1 - cos2_i);
+        
+        final MatrixSIS normalize   = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
+//        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+
+        normalize  .convertAfter (0, cos_φ1, null);  //For conic tracking
+        
+//        denormalize.convertBefore(0, 1/PI,     null);
+//        denormalize.convertBefore(1, , null);
+
+    }
+
+    /**
+     * Converts the specified (λ,φ) coordinate (units in radians) and stores the result in {@code dstPts}
+     * (linear distance on a unit sphere). In addition, opportunistically computes the projection derivative
+     * if {@code derivate} is {@code true}.
+     * 
+     * <cite> The Yaxis lies along the central meridian λ0, y increasing northerly,
+     * and X axis intersects perpendicularly at O_PARALLEL φ0, x increasing easterly.
+     * </cite> 
+     *
+     * @return the matrix of the projection derivative at the given source position,
+     *         or {@code null} if the {@code derivate} argument is {@code false}.
+     * @throws ProjectionException if the coordinates can not be converted.
+     */
+    @Override
+    public Matrix transform(double[] srcPts, int srcOff,
+            double[] dstPts, int dstOff,
+            boolean derivate) throws ProjectionException {
+        
+        final double λ = srcPts[srcOff];
+        final double φ = srcPts[srcOff + 1];
+
+        // TODO : check the condition and the thrown exception.
+        if (abs(φ) > PI / 2 - abs(PI / 2 - i)) {  // Exceed tracking limit 
+            throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
+        }
+
+//        final double cos_φ  = cos(φ);
+//        final double cos2_φ = cos_φ * cos_φ;
+        final double sin_φ  = sin(φ);
+
+        final double dλ     = -asin(sin_φ / sin_i);
+        final double λt     = atan(tan(dλ) * cos_i);
+        final double L      = λt - p2_on_p1 * dλ;
+
+        if (dstPts != null) {
+            dstPts[dstOff    ] = λ;   // In eq. Snyder 28-5 : R(λ - λ0) cos_φ1
+            dstPts[dstOff + 1] = L * cos_φ1 / dF1;  // In eq. Snyder 28-6 : R L cos(φ1)/F'1
+        }
+
+        /* =====================================================================
+        * Uncomputed scale factors :
+        *===========================
+        * F' : tangente of the angle on the globe between the groundtrack and 
+        * the meridian at latitude φ 
+        * final double dF = (p2_on_p1 * cos2_φ - cos_i) / sqrt(cos2_φ - cos2_i);
+        * k = cos_φ1/cos_φ;   // Parallel eq. Snyder 28-7
+        * h = k* dF / dF1;    // Meridian eq. Snyder 28-8
+        ===================================================================== */
+        if (!derivate) {
+            return null;
+        }
+
+//        final double dx_dλ = cos_φ1; //*R
+//        final double dx_dφ = 0;
+//
+//        final double dy_dλ = 0;
+//        final double dy_dφ =
+                
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    /**
+     * Transforms the specified (<var>x</var>,<var>y</var>) coordinates
+     * and stores the result in {@code dstPts} (angles in radians).
+     *
+     * @throws ProjectionException if the coordinates can not be converted.
+     */
+    @Override
+    protected void inverseTransform(double[] srcPts, int srcOff, 
+                                    double[] dstPts, int dstOff) 
+            throws ProjectionException {
+        
+        final double x   = srcPts[srcOff];
+        final double y   = srcPts[srcOff + 1];
+        
+        final double L   = y * dF1 / cos_φ1; // In eq. Snyder 28-19 : L = y * dF1 / R . cos_φ1
+        
+        dstPts[dstOff  ] =  x;
+        dstPts[dstOff+1] = lattitudeFromNewtonMethod(L); //φ
+    }
+
+}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
new file mode 100644
index 0000000..28d83df
--- /dev/null
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import static java.lang.Double.NaN;
+import java.util.Collections;
+import org.apache.sis.internal.referencing.NilReferencingObject;
+import org.apache.sis.internal.referencing.provider.SatelliteTracking;
+import org.apache.sis.measure.Units;
+import org.apache.sis.referencing.datum.DefaultEllipsoid;
+import org.apache.sis.referencing.operation.transform.MathTransformFactoryMock;
+import org.apache.sis.test.DependsOn;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ * Tests coordiantes computed by applying a conic satellite-tracking projection
+ * with {@link ConicSatelliteTracking}.
+ *
+ * @author Matthieu Bastianelli (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+@DependsOn(ConformalProjectionTest.class)
+public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
+
+    /**
+     * Creates a new instance of {@link ConicSatelliteTracking} concatenated
+     * with the (de)normalization matrices. The new instance is stored in the
+     * inherited {@link #transform} field.
+     *
+     * @param i : Angle of inclination between the plane of the Earth's Equator
+     * and the plane of the satellite orbit.
+     * @param orbitalT : Time required for revolution of the satellite.
+     * @param ascendingNodeT : Length of Earth's rotation with respect to the
+     * precessed ascending node.
+     * @param λ0 : central meridian.
+     * @param φ1 : first parallel of conformality, with true scale.
+     * @param φ2 : second parallel of conformality, without true scale.
+     * @param φ0 : lattitude_of_origin : latitude Crossing the central meridian
+     * at the desired origin of rectangular coordinates (null or NaN for
+     * cylindrical satellite tracking projection.)
+     * @return
+     */
+    void createProjection(final double i,
+            final double orbitalT, final double ascendingNodeT,
+            final double λ0,       final double φ1, 
+            final double φ2,       final double φ0) 
+            throws FactoryException {
+
+        final SatelliteTracking provider = new SatelliteTracking();
+        final ParameterValueGroup values = provider.getParameters().createValue();
+        final DefaultEllipsoid sphere = DefaultEllipsoid.createEllipsoid(
+                Collections.singletonMap(DefaultEllipsoid.NAME_KEY, NilReferencingObject.UNNAMED),
+                1, 1, Units.METRE);
+        
+        values.parameter("semi_major").setValue(sphere.getSemiMajorAxis());
+        values.parameter("semi_minor").setValue(sphere.getSemiMinorAxis());
+        values.parameter("satellite_orbit_inclination").setValue(i);
+        values.parameter("satellite_orbital_period").setValue(orbitalT);
+        values.parameter("ascending_node_period").setValue(ascendingNodeT);
+        values.parameter("central_meridian").setValue(λ0);
+        values.parameter("standard_parallel_1").setValue(φ1);
+        
+        if (!Double.isNaN(φ2)) {
+            values.parameter("standard_parallel_2").setValue(φ2);
+        }else{
+            values.parameter("standard_parallel_2").setValue(-φ1); //Cylindrical case
+        }
+        if (!Double.isNaN(φ0)) {
+            values.parameter("latitude_of_origin").setValue(φ0);
+        }
+        
+        transform = new MathTransformFactoryMock(provider).createParameterizedTransform(values);
+        validate();
+    }
+
+    /**
+     * Tests the projection of a few points on a sphere.
+     *
+     * @throws FactoryException if an error occurred while creating the map
+     * projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    public void testTransform() throws FactoryException, TransformException {
+        tolerance = 1E-5;
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                -90,     //central_meridian
+                45,      //standard_parallel_1
+                70,      //standard_parallel_2
+                30       //lattitude_of_origin
+        );
+        assertTrue(isInverseTransformSupported);
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                    -75, 40
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    0.2001910, 0.2121685
+                });
+    }
+
+}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
new file mode 100644
index 0000000..0a86ba6
--- /dev/null
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import static java.lang.Double.NaN;
+import org.apache.sis.internal.referencing.Formulas;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.util.FactoryException;
+
+/**
+ * Tests coordiantes computed by applying a cylindrical satellite-tracking projection
+ * with {@link CylindricalSatelliteTracking}.
+ *
+ * @author Matthieu Bastianelli (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest {
+    
+    
+    
+    /**
+     * Creates a new instance of {@link ConicSatelliteTracking} concatenated
+     * with the (de)normalization matrices. The new instance is stored in the
+     * inherited {@link #transform} field.
+     *
+     * @param i : Angle of inclination between the plane of the Earth's Equator
+     * and the plane of the satellite orbit.
+     * @param orbitalT : Time required for revolution of the satellite.
+     * @param ascendingNodeT : Length of Earth's rotation with respect to the
+     * precessed ascending node.
+     * @param λ0 : central meridian.
+     * @param φ1 : first parallel of conformality, with true scale.
+     * @param φ2 : second parallel of conformality, without true scale.
+     * @param φ0 : lattitude_of_origin : latitude Crossing the central meridian
+     * at the desired origin of rectangular coordinates (null or NaN for
+     * cylindrical satellite tracking projection.)
+     * @return
+     */
+    private void createProjection(final double i,
+            final double orbitalT, final double ascendingNodeT,
+            final double λ0,       final double φ1) 
+            throws FactoryException {
+        super.createProjection(i, orbitalT, ascendingNodeT, λ0, φ1, NaN, NaN);
+    }
+    
+    /**
+     * Tests the projection of a few points on a sphere.
+     *
+     * @throws FactoryException if an error occurred while creating the map
+     * projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    @Override
+    public void testTransform() throws FactoryException, TransformException {
+        tolerance = 1E-5;
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                -90,     //central_meridian
+                30       //standard_parallel_1
+        );
+        assertTrue(isInverseTransformSupported);
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                    -75, 40
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    0.2267249, 0.6459071
+                });
+        
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                0,       //central_meridian
+                0        //standard_parallel_1
+        );
+        
+        tolerance = Formulas.LINEAR_TOLERANCE; //Don't pass with the former tolerance.
+        
+        final double xConverterFactor=0.017453;
+        verifyTransform(
+                new double[]{ // (λ,φ) coordinates in degrees to project.
+                      0,  0,
+                     10,  0, 
+                    -10, 10
+                },
+                new double[]{ // Expected (x,y) results in metres.
+                    0, 0,
+                    xConverterFactor *  10,  0,
+                    xConverterFactor * -10,  0.17579,
+                });
+    }
+    
+}


[sis] 08/09: Merge remote-tracking branch 'Satellite_Tracking_Projection' into geoapi-4.0. Some refactoring to come in next commits.

Posted by de...@apache.org.
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 4f9f6950404554c20e1600171a537bf883ba0f83
Merge: 6274488 8c2d72d
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Oct 1 16:50:56 2019 +0200

    Merge remote-tracking branch 'Satellite_Tracking_Projection' into geoapi-4.0.
    Some refactoring to come in next commits.

 .../referencing/provider/SatelliteTracking.java    | 197 +++++++++
 .../projection/ConicSatelliteTracking.java         | 475 +++++++++++++++++++++
 .../projection/CylindricalSatelliteTracking.java   | 188 ++++++++
 .../projection/ConicSatelliteTrackingTest.java     | 276 ++++++++++++
 .../CylindricalSatelliteTrackingTest.java          | 237 ++++++++++
 .../sis/test/suite/ReferencingTestSuite.java       |   2 +
 6 files changed, 1375 insertions(+)



[sis] 04/09: feat&refactor (referencing- satellite tracking) : refactor to extract terms from transform/inverseTransform methods AND try to compute Jacobian Matrix. Tests not passed for the conic satellite tracking projection yet. Refactor should be lead to compute L coefficient and its derivative in a method.

Posted by de...@apache.org.
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 a8a3962f58f1a4d401448a6e7758b99e31357c99
Author: Matthieu_Bastianelli <ma...@geomatys.com>
AuthorDate: Wed Jul 24 18:31:59 2019 +0200

    feat&refactor (referencing- satellite tracking) : refactor to extract terms from transform/inverseTransform methods AND try to compute Jacobian Matrix. Tests not passed for the conic satellite tracking projection yet. Refactor should be lead to compute L coefficient and its derivative in a method.
---
 .../projection/ConicSatelliteTracking.java         | 97 +++++++++++++---------
 .../projection/CylindricalSatelliteTracking.java   | 48 +++++------
 .../projection/ConicSatelliteTrackingTest.java     | 30 +++++++
 .../CylindricalSatelliteTrackingTest.java          | 27 ++++++
 4 files changed, 135 insertions(+), 67 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
index d650fa2..9b173f1 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTracking.java
@@ -27,8 +27,10 @@ import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.OperationMethod;
 
 import static java.lang.Math.*;
+import java.util.Map;
 import org.apache.sis.internal.referencing.Resources;
 import static org.apache.sis.internal.referencing.provider.SatelliteTracking.*;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 
@@ -111,12 +113,13 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     private final double latitudeLimit;
     /**
      * Boolean attribute indicating if the projection cone's constant is positive.
-     * */
+     */
     private final boolean positiveN;
     /**
      * Coefficients for the Conic Satellite-Tracking Projection.
+     * cosφ1xsinF1_n = cos(φ1)*sin(F1)/n
      */
-    private final double cos_φ1xsin_F1, s0, ρ0;
+    private final double cosφ1xsinF1_n, s0, ρ0;
 
     /**
      * Work around for RFE #4093999 in Sun's bug database ("Relax constraint on
@@ -194,8 +197,6 @@ public class ConicSatelliteTracking extends NormalizedProjection{
              */
             final double F1 = computeFn(cos2_φ1);
 
-            cos_φ1xsin_F1 = cos_φ1 * sin(F1);
-
             final double dλ0 = computedλn(sin_φ0);
             final double dλ1 = computedλn(sin_φ1);
 
@@ -223,8 +224,10 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             } else {
                 n = sin_φ1 * (p2_on_p1 * (2 * cos2_i - cos2_φ1) - cos_i) / (p2_on_p1 * cos2_φ1 - cos_i); //eq. 28-17 in Snyder
             }
+
+            cosφ1xsinF1_n = cos_φ1 * sin(F1)/n;
             s0 = F1 - n * L1;
-            ρ0 = cos_φ1xsin_F1 / (n * sin(n * L0 + s0)); // *R in eq.28-12 in Snyder
+            ρ0 = cosφ1xsinF1_n / sin(n * L0 + s0); // *R in eq.28-12 in Snyder
 
             //======================== Unsure ======================================
             // Aim to assess the limit latitude associated with -s0/n L-value.
@@ -247,7 +250,7 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
             denormalize.convertBefore(1, 1, ρ0);  //For conic tracking
         } else {
-            n = latitudeLimit = cos_φ1xsin_F1 = s0 = ρ0 = NaN;
+            n = latitudeLimit = cosφ1xsinF1_n = s0 = ρ0 = NaN;
             positiveN = false;
         }
     }
@@ -283,8 +286,10 @@ public class ConicSatelliteTracking extends NormalizedProjection{
         }
 
         final double sin_φ  = sin(φ);
-        final double dλ     = -asin(sin_φ / sin_i);
-        final double λt     = atan(tan(dλ) * cos_i);
+        final double sinφ_sini = sin_φ / sin_i;
+        final double dλ     = -asin(sinφ_sini);
+        final double tan_dλ = tan(dλ);
+        final double λt     = atan(tan_dλ * cos_i);
         final double L      = λt - p2_on_p1 * dλ;
 
         /*
@@ -296,34 +301,46 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
-        final double ρ      = cos_φ1xsin_F1/(n*sin(n*L+s0));
-        final double θ      = λ;      // extracted n
+        final double nLandS0 = n*L+s0;
+        final double sin_nLandS0 = sin(nLandS0);
+
+        final double ρ      = cosφ1xsinF1_n/sin_nLandS0;
+//        final double ρ      = 1/sin(n*L+s0);
 
-        final double sinθ = sin(θ);
-        final double cosθ = cos(θ);
+        final double sinλ = sin(λ);
+        final double cosλ = cos(λ);
         if (dstPts != null) {
-            dstPts[dstOff    ] = ρ * sinθ;   // x
-//            dstPts[dstOff + 1] = ρ0 - ρ*cosθ;  // y       //TODO : extract ρ0 when ensuring : λ = λ - λ0;
-            dstPts[dstOff + 1] = - ρ*cosθ;
+            dstPts[dstOff    ] = ρ * sinλ;   // x
+            dstPts[dstOff + 1] = - ρ*cosλ;   // y
         }
 
          if (!derivate) {
             return null;
         }
 
-        //=========================TO Resolve =================================
-//        final double dx_dλ = ρ*n*cosθ;
-//        final double dx_dφ =?;
-//
-//        final double dy_dλ =  ρ*n*sinθ;
-//        final double dy_dφ = ?;
+        //=========================To check the resolution =====================
+        final double dρ_dφ = cosφ1xsinF1_n  * (-1 / (sin_nLandS0*sin_nLandS0)) *cos(nLandS0)  * n
+                // dL/dφ :
+                * ((cos(φ) / sin_i) *(1/sqrt(1-sinφ_sini*sinφ_sini)))  // derivative of (-dλ/dφ)
+                * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
+
+        final double dx_dλ = ρ*cosλ;
+        final double dx_dφ = sinλ * dρ_dφ;
+
+        final double dy_dλ =  ρ*sinλ;
+        final double dy_dφ = -cosλ * dρ_dφ;
         //======================================================================
 
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        return new Matrix2(dx_dλ, dy_dλ,
+                           dx_dφ, dy_dφ);
+//        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
 
-        //Additionally we can compute the scale factors :
-        // k = ρ*n/cos(φ); // /R
-        // h = k*tan(F)/tan(n*L+s0);
+        /* =====================================================================
+        * Uncomputed scale factors :
+        *===========================
+        * k = ρ*n/cos(φ); // /R
+        * h = k*tan(F)/tan(n*L+s0);
+        ===================================================================== */
     }
 
     /**
@@ -338,11 +355,8 @@ public class ConicSatelliteTracking extends NormalizedProjection{
                                     throws ProjectionException {
 
         final double x   = srcPts[srcOff];
-//        final double y   = srcPts[srcOff + 1] - ρ0;
         final double y   = srcPts[srcOff + 1];
 
-        //TODO : extract - ρ0 : MatrixSIS convertBefore and convertAfter??
-
         if ((x== 0) && (y == 0)) {
             //TODO : which values does it imply?
             throw new UnsupportedOperationException("Not supported yet for those coordinates."); //To change body of generated methods, choose Tools | Templates.
@@ -350,13 +364,22 @@ public class ConicSatelliteTracking extends NormalizedProjection{
 
         final double ρ = positiveN ? hypot(x,y) : -hypot(x,y);
         final double θ = atan(x/(-y) ); //undefined if x=y=0
-        final double L = (asin(cos_φ1xsin_F1/(ρ*n)) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
+        final double L = (asin(cosφ1xsinF1_n/ρ) -s0)/n; //undefined if x=y=0  //eq.28-26 in Snyder with R=1
 
-        //TODO ensure that λ0 will be added. ;  In eq. Snyder 28-23 : λ0 + θ/n
         dstPts[dstOff  ] =  θ ;//λ
         dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
     }
 
+    /**
+     * Return an approximation of the latitude associated with L coefficient
+     * by applying Newton-Raphson method.
+     *
+     * Eq. 28-24, 28-25 and then 28-22 in Snyder's Manual.
+     *
+     * @param l the L coefficient used in (cylindrical and conic)
+     * satellite-tracking projections.
+     * @return Approximation of the associated latitude.
+     */
     protected final double latitudeFromNewtonMethod(final double l){
         double dλ = -PI/2;
         double dλn = Double.MIN_VALUE;
@@ -371,17 +394,17 @@ public class ConicSatelliteTracking extends NormalizedProjection{
             }
             dλn = dλ;
 
+            // Alternative calculation with Snyder's eq.  28-20 and 28-21
 //            λt = l + p2_on_p1 * dλ_n;
 //            dλ = atan(tan(λt) / cos_i);
 
-            A = tan(l + p2_on_p1*dλn) / cos_i;
+            A = tan(l + p2_on_p1 * dλn) / cos_i;
             A2=A*A;
             Δdλ = -(dλn-atan(A)) / (1- (A2 + 1/cos2_i) * (p2_on_p1*cos_i/(A2+1)) );
             dλ = dλn + Δdλ ;
 
             count++;
         }
-//        λt = L + p2_on_p1 * dλ;
         final double sin_dλ = sin(dλ);
         return -asin(sin_dλ * sin_i);
     }
@@ -421,13 +444,5 @@ public class ConicSatelliteTracking extends NormalizedProjection{
     private double computeλtn(final double dλn) {
         return atan(tan(dλn) * cos_i); // eq.28-3a in Snyder
     }
-//    /**
-//     * Radius of the circle radius of the circle to which groundtracks
-//     * are tangent on the map.
-//     *
-//     * @return radius ρs.
-//     */
-//    public double getRadiusOfTangencyCircle(){
-//        return ρs;
-//    }
+
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
index 13f8521..d68ec87 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTracking.java
@@ -25,6 +25,7 @@ import org.opengis.referencing.operation.OperationMethod;
 
 import static java.lang.Math.*;
 import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
 
@@ -76,13 +77,6 @@ import org.apache.sis.referencing.operation.transform.ContextualParameters;
  */
 public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
 
-
-    /**
-     * F1' in Snyder : tangente of the angle on both the globe and on the map between
-     * the groundtrack and the meridian at latitude φ1.
-     */
-    final double dF1;
-
     /**
      * Create a Cylindrical Satellite Tracking Projection from the given
      * parameters.
@@ -101,15 +95,13 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         super(initializer);
 
         final double cos2_φ1 = cos_φ1 * cos_φ1;
-        dF1 = (p2_on_p1 * cos2_φ1 - cos_i) / sqrt(cos2_φ1 - cos2_i);
+        final double cosφ1_dF1 = sqrt(cos2_φ1 - cos2_i) *cos_φ1 / (p2_on_p1 * cos2_φ1 - cos_i);
 
         final MatrixSIS normalize   = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION);
-//        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
-
         normalize  .convertAfter (0, cos_φ1, null);  //For conic tracking
 
-//        denormalize.convertBefore(0, 1/PI,     null);
-//        denormalize.convertBefore(1, , null);
+        final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+        denormalize.convertBefore(1, cosφ1_dF1,     null);
 
     }
 
@@ -139,17 +131,17 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
             throw new ProjectionException(Resources.format(Resources.Keys.CanNotTransformCoordinates_2));
         }
 
-//        final double cos_φ  = cos(φ);
-//        final double cos2_φ = cos_φ * cos_φ;
         final double sin_φ  = sin(φ);
+        final double sinφ_sini = sin_φ / sin_i;
 
         final double dλ     = -asin(sin_φ / sin_i);
+        final double tan_dλ = tan(dλ);
         final double λt     = atan(tan(dλ) * cos_i);
         final double L      = λt - p2_on_p1 * dλ;
 
         if (dstPts != null) {
             dstPts[dstOff    ] = λ;   // In eq. Snyder 28-5 : R(λ - λ0) cos_φ1
-            dstPts[dstOff + 1] = L * cos_φ1 / dF1;  // In eq. Snyder 28-6 : R L cos(φ1)/F'1
+            dstPts[dstOff + 1] = L;   // In eq. Snyder 28-6 : R L cos(φ1)/F'1
         }
 
         /* =====================================================================
@@ -159,19 +151,25 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
         * the meridian at latitude φ
         * final double dF = (p2_on_p1 * cos2_φ - cos_i) / sqrt(cos2_φ - cos2_i);
         * k = cos_φ1/cos_φ;   // Parallel eq. Snyder 28-7
-        * h = k* dF / dF1;    // Meridian eq. Snyder 28-8
+        * h = k* dF / cosφ1_dF1;    // Meridian eq. Snyder 28-8
         ===================================================================== */
         if (!derivate) {
             return null;
         }
 
-//        final double dx_dλ = cos_φ1; //*R
-//        final double dx_dφ = 0;
-//
-//        final double dy_dλ = 0;
-//        final double dy_dφ =
+        //=========================To check the resolution =====================
+        final double dx_dλ = 1; //*R
+        final double dx_dφ = 0;
+
+        final double dy_dλ = 0;
+        final double dy_dφ = ((cos(φ) / sin_i) *(1/sqrt(1-sinφ_sini*sinφ_sini)))  // derivative of (-dλ/dφ)
+                * ( p2_on_p1 - ((1+tan_dλ*tan_dλ)*cos_i/(1+λt*λt) ) );
+        //======================================================================
 
-        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+        return new Matrix2(dx_dλ, dy_dλ,
+                           dx_dφ, dy_dφ);
+
+//        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
     }
 
     /**
@@ -186,12 +184,10 @@ public class CylindricalSatelliteTracking extends ConicSatelliteTracking {
             throws ProjectionException {
 
         final double x   = srcPts[srcOff];
-        final double y   = srcPts[srcOff + 1];
-
-        final double L   = y * dF1 / cos_φ1; // In eq. Snyder 28-19 : L = y * dF1 / R . cos_φ1
+        final double y   = srcPts[srcOff + 1]; // In eq. Snyder 28-19 : y = yinit * cosφ1_dF1 / R . cos_φ1
 
         dstPts[dstOff  ] =  x;
-        dstPts[dstOff+1] = latitudeFromNewtonMethod(L); //φ
+        dstPts[dstOff+1] = latitudeFromNewtonMethod(y); //φ
     }
 
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
index 7152538..242e024 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/ConicSatelliteTrackingTest.java
@@ -33,6 +33,7 @@ import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
 
 import static java.lang.StrictMath.toRadians;
+import org.apache.sis.test.DependsOnMethod;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -243,4 +244,33 @@ public class ConicSatelliteTrackingTest extends MapProjectionTestCase {
                     0.21642 * sin(n * (toRadians(-120 - -90))), 2.46908 //Tracking limit
                 });
     }
+
+
+    /**
+     * Tests the derivatives at a few points on a sphere. This method compares the derivatives computed
+     * by the projection with an estimation of derivatives computed by the finite differences method.
+     *
+     * @throws FactoryException if an error occurred while creating the map projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    @DependsOnMethod("testInverseDerivative")
+    public void testDerivativeOnSphere() throws FactoryException, TransformException {
+        createProjection(
+                99.092, //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0, //ascending_node_period
+                -90, //central_meridian
+                45, //standard_parallel_1
+                70, //standard_parallel_2
+                30 //latitude_of_origin
+        );
+        final double delta = (1.0 / 60) / 1852;                 // Approximately 1 metre.
+        derivativeDeltas = new double[] {delta, delta};
+        tolerance = Formulas.LINEAR_TOLERANCE / 10;
+        verifyDerivative(   0, -10);
+        verifyDerivative(-100,  3);
+        verifyDerivative( -56, 50);
+        verifyDerivative( -20, 47);
+    }
 }
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
index 02c1b09..844ba7f 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/CylindricalSatelliteTrackingTest.java
@@ -20,6 +20,7 @@ package org.apache.sis.referencing.operation.projection;
 
 import static java.lang.Double.NaN;
 import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.test.DependsOnMethod;
 import static org.junit.Assert.assertTrue;
 import org.junit.Test;
 import org.opengis.referencing.operation.TransformException;
@@ -205,5 +206,31 @@ public class CylindricalSatelliteTrackingTest extends ConicSatelliteTrackingTest
 
 
     }
+     /**
+     * Tests the derivatives at a few points on a sphere. This method compares the derivatives computed
+     * by the projection with an estimation of derivatives computed by the finite differences method.
+     *
+     * @throws FactoryException if an error occurred while creating the map projection.
+     * @throws TransformException if an error occurred while projecting a point.
+     */
+    @Test
+    @DependsOnMethod("testInverseDerivative")
+    @Override
+    public void testDerivativeOnSphere() throws FactoryException, TransformException {
+        createProjection(
+                99.092,  //satellite_orbit_inclination
+                103.267, //satellite_orbital_period
+                1440.0,  //ascending_node_period
+                -90,     //central_meridian
+                30       //standard_parallel_1
+        );
+        final double delta = (1.0 / 60) / 1852;                 // Approximately 1 metre.
+        derivativeDeltas = new double[] {delta, delta};
+        tolerance = Formulas.LINEAR_TOLERANCE / 10;
+        verifyDerivative(-100,  3);
+        verifyDerivative( -56, 50);
+        verifyDerivative( -20, 47);
+    }
+
 
 }