Author: desruisseaux
Date: Thu Jul 30 15:27:52 2015
New Revision: 1693439

URL: http://svn.apache.org/r1693439
Log:
Moved the code used for map projection initialisation in a separated 
Initializer class (package private).
Opportunistically use more double-double arithmetic. While the digits modified 
by this extra-precision are
not significant, the intend is not to increase map projection accuracy but 
rather to improve the result of
concatenations of MathTransforms (through an attempt to improve the result of 
matrix multiplications and inversions).

Added:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
      - copied, changed from r1693283, 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
Modified:
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/PolarStereographic.java
    
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/MercatorTest.java
    
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/NoOp.java

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java?rev=1693439&r1=1693438&r2=1693439&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ConformalProjection.java
 [UTF-8] Thu Jul 30 15:27:52 2015
@@ -16,13 +16,9 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
-import java.util.Map;
 import java.io.IOException;
 import java.io.ObjectInputStream;
-import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.referencing.operation.OperationMethod;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.parameter.Parameters;
 
 import static java.lang.Math.*;
 
@@ -107,17 +103,12 @@ abstract class ConformalProjection exten
     private transient boolean useIterations;
 
     /**
-     * Constructs a new map projection from the supplied parameters.
+     * Creates a new normalized projection from the parameters computed by the 
given initializer.
      *
-     * @param method     Description of the map projection parameters.
-     * @param parameters The parameters of the projection to be created.
-     * @param roles Parameters to look for <cite>central meridian</cite>, 
<cite>scale factor</cite>,
-     *        <cite>false easting</cite>, <cite>false northing</cite> and 
other values.
-     */
-    protected ConformalProjection(final OperationMethod method, final 
Parameters parameters,
-            final Map<ParameterRole, ? extends ParameterDescriptor<Double>> 
roles)
-    {
-        super(method, parameters, roles);
+     * @param initializer The initializer for computing map projection 
internal parameters.
+     */
+    ConformalProjection(final Initializer initializer) {
+        super(initializer);
         initialize();
     }
 

Copied: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
 (from r1693283, 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java)
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java?p2=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java&p1=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java&r1=1693283&r2=1693439&rev=1693439&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
 [UTF-8] Thu Jul 30 15:27:52 2015
@@ -17,179 +17,44 @@
 package org.apache.sis.referencing.operation.projection;
 
 import java.util.Map;
-import java.util.HashMap;
-import java.io.Serializable;
-import java.lang.reflect.Modifier;
-import org.opengis.metadata.Identifier;
-import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptor;
-import org.opengis.parameter.ParameterDescriptorGroup;
-import org.opengis.referencing.operation.Matrix;
-import org.opengis.referencing.operation.MathTransform;
-import org.opengis.referencing.operation.MathTransform2D;
 import org.opengis.referencing.operation.OperationMethod;
-import org.opengis.referencing.operation.TransformException;
-import org.opengis.referencing.operation.MathTransformFactory;
-import org.opengis.util.FactoryException;
-import org.apache.sis.util.Debug;
-import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.internal.referencing.provider.MapProjection;
 import org.apache.sis.parameter.Parameters;
-import org.apache.sis.parameter.ParameterBuilder;
-import org.apache.sis.metadata.iso.citation.Citations;
-import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
-import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
-import org.apache.sis.internal.referencing.provider.MapProjection;
-import org.apache.sis.internal.referencing.Formulas;
-import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.internal.util.DoubleDouble;
-import org.apache.sis.internal.util.Constants;
-import org.apache.sis.internal.util.Utilities;
-import org.apache.sis.internal.util.Numerics;
+import 
org.apache.sis.referencing.operation.projection.NormalizedProjection.ParameterRole;
 
 import static java.lang.Math.*;
 import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 
-// Branch-dependent imports
-import java.util.Objects;
-
 
 /**
- * Base class for conversion services between ellipsoidal and cartographic 
projections.
- * This conversion works on a normalized spaces, where angles are expressed in 
radians and
- * computations are performed for a sphere having a semi-major axis of 1. More 
specifically:
- *
- * <ul class="verbose">
- *   <li>On input, the {@link #transform(double[], int, double[], int, 
boolean) transform(…)} method
- *   expects (<var>longitude</var>, <var>latitude</var>) angles in 
<strong>radians</strong>.
- *   Longitudes have the <cite>central meridian</cite> (λ₀) removed before the 
transform method is invoked.
- *   The conversion from degrees to radians and the longitude rotation are 
applied by the
- *   {@linkplain ContextualParameters#normalizeGeographicInputs normalization} 
affine transform.</li>
- *
- *   <li>On output, the {@link #transform(double[],int,double[],int,boolean) 
transform(…)} method returns
- *   (<var>x</var>, <var>y</var>) values on a sphere or ellipse having a 
semi-major axis length (<var>a</var>) of 1.
- *   The multiplication by the scale factor (<var>k</var>₀) and the 
translation by false easting (FE) and false
- *   northing (FN) are applied by the {@linkplain 
ContextualParameters#getMatrix denormalization} affine transform.</li>
- * </ul>
- *
- * The normalization and denormalization steps are represented below by the 
matrices immediately on the left and right
- * sides of {@code NormalizedProjection} respectively. Those matrices show 
only the basic parameters common to most projections.
- * Some projections will put more elements in those matrices.
- *
- * <center>
- *   <table class="compact" style="td {vertical-align: middle}" 
summary="Decomposition of a map projection">
- *     <tr>
- *       <td>{@include ../transform/formulas.html#SwapAxes}</td>
- *       <td>→</td>
- *       <td>{@include ../transform/formulas.html#NormalizeGeographic}</td>
- *       <td>→</td>
- *       <td>{@code NormalizedProjection}</td>
- *       <td>→</td>
- *       <td>{@include ../transform/formulas.html#DenormalizeCartesian}</td>
- *     </tr>
- *   </table>
- * </center>
- *
- * <div class="note"><b>Note:</b>
- * The first matrix on the left side is for {@linkplain 
org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes
- * swapping axes} from (<var>latitude</var>, <var>longitude</var>) to 
(<var>longitude</var>, <var>latitude</var>) order.
- * This matrix is shown here for completeness, but is not managed by this 
projection package. Axes swapping is managed
- * at a {@linkplain 
org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#createBaseToDerived(
- * org.opengis.referencing.cs.CoordinateSystem, 
org.opengis.referencing.operation.MathTransform,
- * org.opengis.referencing.cs.CoordinateSystem) higher level}.</div>
- *
- * {@code NormalizedProjection} does not store the above cited parameters 
(central meridian, scale factor, <i>etc.</i>)
- * on intend (except indirectly), in order to make clear that those parameters 
are not used by subclasses.
- * The ability to recognize two {@code NormalizedProjection}s as {@linkplain 
#equals(Object, ComparisonMode) equivalent}
- * without consideration for the scale factor (among other) allow more 
efficient concatenation in some cases
- * (typically some combinations of inverse projection followed by a direct 
projection).
- *
- * <p>All angles (either fields, method parameters or return values) in this 
class and subclasses are
- * in radians. This is the opposite of {@link Parameters} where all angles are 
in CRS-dependent units,
- * typically decimal degrees.</p>
- *
- * <div class="section">Serialization</div>
- * Serialization of this class is appropriate for short-term storage or RMI 
use, but may not be compatible
- * with future versions. For long term storage, WKT (Well Know Text) or XML 
are more appropriate.
+ * Helper class for map projection constructions, providing formulas normally 
needed only at construction time.
+ * Since map projection constructions should not happen very often, we afford 
using double-double arithmetic here.
+ * The main intend is not to provide more accurate coordinate conversions 
(while it may be a nice side-effect),
+ * but rather to increase the chances that the concatenations of 
(de)normalization matrices with the matrices of
+ * other transforms give back identity matrices when such result is expected.
  *
- * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
- * @author  André Gosselin (MPO)
- * @author  Rueben Schulz (UBC)
- * @author  Rémi Maréchal (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
  * @since   0.6
  * @version 0.6
  * @module
- *
- * @see ContextualParameters
- * @see <a href="http://mathworld.wolfram.com/MapProjection.html";>Map 
projections on MathWorld</a>
  */
-public abstract class NormalizedProjection extends AbstractMathTransform2D 
implements Serializable {
-    /**
-     * For cross-version compatibility.
-     */
-    private static final long serialVersionUID = 1969740225939106310L;
-
+final class Initializer {
     /**
-     * Maximum difference allowed when comparing longitudes or latitudes in 
radians.
-     * The current value takes the system-wide angular tolerance value 
(equivalent to
-     * about 1 cm on Earth) converted to radians.
+     * The parameters used for creating the map projection.
+     * This object will be stored in the map projection.
      *
-     * <p>Some formulas use this tolerance value for testing sines or cosines 
of an angle.
-     * In the sine case, this is justified because sin(θ) ≅ θ when θ is small.
-     * Similar reasoning applies to cosine with cos(θ) ≅ θ + π/2 when θ is 
small.</p>
-     *
-     * <p>Some formulas may use this tolerance value as a <em>linear</em> 
tolerance on the unit sphere.
-     * This is okay because the arc length for an angular tolerance θ is r⋅θ, 
but in this class r=1.</p>
-     */
-    static final double ANGULAR_TOLERANCE = Formulas.ANGULAR_TOLERANCE * 
(PI/180);
-    // Note: an alternative way to compute this value could be 
Formulas.LINEAR_TOLERANCE / AUTHALIC_RADIUS.
-    // But the later is only 0.07% lower than the current value.
-
-    /**
-     * Desired accuracy for the result of iterative computations, in radians.
-     * This constant defines the desired accuracy of methods like {@link 
ConformalProjection#φ(double)}.
-     *
-     * <p>The current value is 0.25 time the accuracy derived from {@link 
Formulas#LINEAR_TOLERANCE}.
-     * So if the linear tolerance is 1 cm, then the accuracy that we will seek 
for is 0.25 cm (about
-     * 4E-10 radians). The 0.25 factor is a safety margin for meeting the 1 cm 
accuracy.</p>
-     */
-    static final double ITERATION_TOLERANCE = ANGULAR_TOLERANCE * 0.25;
-
-    /**
-     * Maximum number of iterations for iterative computations.
-     * The iterative methods used in subclasses should converge quickly (in 3 
or 4 iterations)
-     * when used for a planet with an excentricity similar to Earth. But we 
allow a high limit
-     * in case someone uses SIS for some planet with higher excentricity.
-     */
-    static final int MAXIMUM_ITERATIONS = 15;
-
-    /**
-     * The internal parameter descriptors. Keys are implementation classes.  
Values are parameter descriptor groups
-     * containing at least a parameter for the {@link #excentricity} value, 
and optionally other internal parameter
-     * added by some subclasses.
-     *
-     * <p>Entries are created only when first needed. Those descriptors are 
usually never created since they are
-     * used only by {@link #getParameterDescriptors()}, which is itself 
invoked mostly for debugging purpose.</p>
-     */
-    @Debug
-    private static final Map<Class<?>,ParameterDescriptorGroup> DESCRIPTORS = 
new HashMap<>();
-
-    /**
-     * The parameters used for creating this projection. They are used for 
formatting <cite>Well Known Text</cite> (WKT)
-     * and error messages. Subclasses shall not use the values defined in this 
object for computation purpose, except at
-     * construction time.
-     *
-     * @see #getContextualParameters()
+     * @see NormalizedProjection#getContextualParameters()
      */
     final ContextualParameters context;
 
     /**
-     * Ellipsoid excentricity, equals to <code>sqrt({@linkplain 
#excentricitySquared})</code>.
-     * Value 0 means that the ellipsoid is spherical.
+     * The user-supplied parameters, stored temporarily while we transfer the 
information to {@link #context}.
      */
-    protected final double excentricity;
+    final Parameters parameters;
 
     /**
      * The square of excentricity: ℯ² = (a²-b²)/a² where
@@ -197,215 +62,27 @@ public abstract class NormalizedProjecti
      * <var>a</var> is the <cite>semi-major</cite> axis length and
      * <var>b</var> is the <cite>semi-minor</cite> axis length.
      */
-    protected final double excentricitySquared;
+    final DoubleDouble excentricitySquared;
 
     /**
-     * The inverse of this map projection.
-     */
-    private final MathTransform2D inverse;
-
-    /**
-     * Maps the parameters to be used for initializing {@link 
NormalizedProjection} and its
-     * {@linkplain ContextualParameters#getMatrix(boolean) normalization / 
denormalization} matrices.
-     * This is an enumeration of parameters found in almost every map 
projections, but under different names.
-     * This enumeration allows {@code NormalizedProjection} subclasses to 
specify which parameter names, ranges
-     * and default values should be used by the
-     * {@linkplain NormalizedProjection#NormalizedProjection(OperationMethod, 
Parameters, Map) projection constructor}.
-     *
-     * <p>{@code NormalizedProjection} subclasses will typically provide 
values only for the following keys:
-     * {@link #CENTRAL_MERIDIAN}, {@link #SCALE_FACTOR}, {@link 
#FALSE_EASTING} and {@link #FALSE_NORTHING}.</p>
-     *
-     * @author  Martin Desruisseaux (Geomatys)
-     * @since   0.6
-     * @version 0.6
-     * @module
-     *
-     * @see NormalizedProjection#NormalizedProjection(OperationMethod, 
Parameters, Map)
+     * Map projection variant. This is a convenience field left at
+     * the discretion of {@link NormalizedProjection} subclasses.
      */
-    protected static enum ParameterRole {
-        /**
-         * Maps the <cite>semi-major axis length</cite> parameter (symbol: 
<var>a</var>).
-         * This value is used for computing {@link 
NormalizedProjection#excentricity},
-         * and is also a multiplication factor for the denormalization matrix.
-         *
-         * <p>Unless specified otherwise, this is always mapped to a parameter 
named {@code "semi_major"}.
-         * {@code NormalizedProjection} subclasses typically do not need to 
provide a value for this key.</p>
-         */
-        SEMI_MAJOR,
-
-        /**
-         * Maps the <cite>semi-minor axis length</cite> parameter (symbol: 
<var>b</var>).
-         * This value is used for computing {@link 
NormalizedProjection#excentricity}.
-         *
-         * <p>Unless specified otherwise, this is always mapped to a parameter 
named {@code "semi_minor"}.
-         * {@code NormalizedProjection} subclasses typically do not need to 
provide a value for this key.</p>
-         */
-        SEMI_MINOR,
-
-        /**
-         * Maps the parameter for the latitude where to compute the 
<cite>radius of conformal sphere</cite>
-         * (symbol: <var>R</var><sub>c</sub>). If this parameter is provided, 
then the radius of the conformal
-         * sphere at latitude φ will be used instead than the semi-major axis 
length in the denormalisation matrix.
-         * In other words, if provided then <var>a</var> is replaced by 
<var>R</var><sub>c</sub> below:
-         *
-         * <center>{@include 
../transform/formulas.html#DenormalizeCartesian}</center>
-         *
-         * <p>This enumeration shall be used <strong>only</strong> when the 
user requested explicitely spherical
-         * formulas, for example the <cite>"Mercator (Spherical)"</cite> 
projection (EPSG:1026), but the figure
-         * of the Earth may be an ellipsoid rather than a sphere. In the 
majority of cases, this enumeration should
-         * not be used.</p>
-         */
-        LATITUDE_OF_CONFORMAL_SPHERE_RADIUS,
-
-        /**
-         * Maps the <cite>central meridian</cite> parameter (symbol: λ₀).
-         * This value is subtracted from the longitude values before the map 
projections.
-         *
-         * <p>Some common names for this parameter are:</p>
-         * <ul>
-         *   <li>Longitude of origin</li>
-         *   <li>Longitude of false origin</li>
-         *   <li>Longitude of natural origin</li>
-         *   <li>Spherical longitude of origin</li>
-         *   <li>Longitude of projection centre</li>
-         * </ul>
-         */
-        CENTRAL_MERIDIAN,
-
-        /**
-         * Maps the <cite>scale factor</cite> parameter (symbol: 
<var>k</var>₀).
-         * This is a multiplication factor for the (<var>x</var>,<var>y</var>) 
values obtained after map projections.
-         *
-         * <p>Some common names for this parameter are:</p>
-         * <ul>
-         *   <li>Scale factor at natural origin</li>
-         *   <li>Scale factor on initial line</li>
-         *   <li>Scale factor on pseudo standard parallel</li>
-         * </ul>
-         */
-        SCALE_FACTOR,
-
-        /**
-         * Maps the <cite>false easting</cite> parameter (symbol: 
<var>FE</var>).
-         * This is a translation term for the <var>x</var> values obtained 
after map projections.
-         *
-         * <p>Some common names for this parameter are:</p>
-         * <ul>
-         *   <li>False easting</li>
-         *   <li>Easting at false origin</li>
-         *   <li>Easting at projection centre</li>
-         * </ul>
-         */
-        FALSE_EASTING,
-
-        /**
-         * Maps the <cite>false westing</cite> parameter (symbol: 
<var>FW</var>).
-         * This is the same <var>x</var> translation than {@link 
#FALSE_EASTING}, but of opposite sign.
-         *
-         * <p>Actually, there is usually no parameter named "false westing" in 
a map projection.
-         * But some projections like <cite>"Lambert Conic Conformal (West 
Orientated)"</cite> are
-         * defined in such a way that their "false easting" parameter is 
effectively a "false westing".
-         * This enumeration value can be used for informing {@link 
NormalizedProjection} about that fact.</p>
-         */
-        FALSE_WESTING,
-
-        /**
-         * Maps the <cite>false northing</cite> parameter (symbol: 
<var>FN</var>).
-         * This is a translation term for the <var>y</var> values obtained 
after map projections.
-         *
-         * <p>Some common names for this parameter are:</p>
-         * <ul>
-         *   <li>False northing</li>
-         *   <li>Northing at false origin</li>
-         *   <li>Northing at projection centre</li>
-         * </ul>
-         */
-        FALSE_NORTHING,
-
-        /**
-         * Maps the <cite>false southing</cite> parameter (symbol: 
<var>FS</var>).
-         * This is the same <var>y</var> translation than {@link 
#FALSE_NORTHING}, but of opposite sign.
-         *
-         * <p>Actually, there is usually no parameter named "false southing" 
in a map projection.
-         * But some projections like <cite>"Transverse Mercator (South 
Orientated)"</cite> are
-         * defined in such a way that their "false northing" parameter is 
effectively a "false southing".
-         * This enumeration value can be used for informing {@link 
NormalizedProjection} about that fact.</p>
-         */
-        FALSE_SOUTHING
-    }
+    final byte variant;
 
     /**
-     * Constructs a new map projection from the supplied parameters.
-     * This constructor applies the following operations on the {@link 
ContextualParameter}:
-     *
-     * <ul>
-     *   <li>On the <b>normalization</b> matrix (to be applied before {@code 
this} transform):
-     *     <ul>
-     *       <li>{@linkplain 
ContextualParameters#normalizeGeographicInputs(double) Subtract}
-     *           the <cite>central meridian</cite> value.</li>
-     *       <li>Convert from degrees to radians.</li>
-     *     </ul>
-     *   </li>
-     *   <li>On the <b>denormalization</b> matrix (to be applied after {@code 
this} transform):
-     *     <ul>
-     *       <li>{@linkplain MatrixSIS#convertAfter(int, Number, Number) 
Scale} by the <cite>semi-major</cite> axis length.</li>
-     *       <li>If a scale factor is present (not all map projections have a 
scale factor), apply that scale.</li>
-     *       <li>Translate by the <cite>false easting</cite> and <cite>false 
northing</cite> (after the scale).</li>
-     *     </ul>
-     *   </li>
-     *   <li>On the <b>contextual parameters</b> (not the parameters of {@code 
this} transform):
-     *     <ul>
-     *       <li>Store the values for <cite>semi-major</cite> axis length, 
<cite>semi-minor</cite> axis length,
-     *         <cite>scale factor</cite> (if present), <cite>central 
meridian</cite>,
-     *         <cite>false easting</cite> and <cite>false northing</cite> 
values.</li>
-     *     </ul>
-     *   </li>
-     * </ul>
-     *
-     * In matrix form, this constructor creates the following matrices 
(subclasses are free to modify):
-     * <table class="sis">
-     *   <caption>Initial matrix coefficients after construction</caption>
-     *   <tr>
-     *     <th>Normalization</th>
-     *     <th class="sep">Denormalization</th>
-     *   </tr>
-     *   <tr>
-     *     <td>{@include ../transform/formulas.html#NormalizeGeographic}</td>
-     *     <td class="sep">{@include 
../transform/formulas.html#DenormalizeCartesian}</td>
-     *   </tr>
-     * </table>
-     *
-     * <div class="section">Which parameters are considered</div>
-     * The {@code roles} map specifies which parameters to look for 
<cite>central meridian</cite>,
-     * <cite>scale factor</cite>, <cite>false easting</cite>, <cite>false 
northing</cite> and other values.
-     * All entries in the {@code roles} map are optional.
-     * All descriptors in the map shall comply to the following constraints:
-     *
-     * <ul>
-     *   <li>Descriptors associated to {@link ParameterRole#SEMI_MAJOR}, 
{@link ParameterRole#SEMI_MINOR SEMI_MINOR},
-     *     {@link ParameterRole#FALSE_EASTING FALSE_EASTING} and {@link 
ParameterRole#FALSE_NORTHING FALSE_NORTHING}
-     *     shall have the same linear unit of measurement (usually metre).</li>
-     *   <li>Descriptors associated to angular measures ({@link 
ParameterRole#CENTRAL_MERIDIAN} and
-     *     {@link ParameterRole#LATITUDE_OF_CONFORMAL_SPHERE_RADIUS 
LATITUDE_OF_CONFORMAL_SPHERE_RADIUS})
-     *     shall use degrees.</li>
-     * </ul>
-     *
-     * Note that users can still use units of their choice in the {@link 
Parameters} object given in argument to
-     * this constructor. But those values will be converted to the units of 
measurement specified by the parameter
-     * descriptors in the {@code roles} map, which must be the above-cited 
units.
-     *
-     * @param method     Description of the map projection parameters.
-     * @param parameters The parameters of the projection to be created.
-     * @param roles Parameters to look for <cite>central meridian</cite>, 
<cite>scale factor</cite>,
-     *        <cite>false easting</cite>, <cite>false northing</cite> and 
other values.
+     * Creates a new initializer.
      */
-    protected NormalizedProjection(final OperationMethod method, final 
Parameters parameters,
-            final Map<ParameterRole, ? extends ParameterDescriptor<Double>> 
roles)
+    Initializer(final OperationMethod method, final Parameters parameters,
+            final Map<ParameterRole, ? extends ParameterDescriptor<Double>> 
roles,
+            final byte variant)
     {
         ensureNonNull("method",     method);
         ensureNonNull("parameters", parameters);
         ensureNonNull("roles",      roles);
-        context = new ContextualParameters(method);
+        this.context    = new ContextualParameters(method);
+        this.parameters = parameters;
+        this.variant    = variant;
         /*
          * Note: we do not use Map.getOrDefault(K,V) below because the user 
could have explicitly associated
          * a null value to keys (we are paranoiac...) and because it conflicts 
with the "? extends" part of
@@ -416,17 +93,34 @@ public abstract class NormalizedProjecti
         if (semiMajor == null) semiMajor = MapProjection.SEMI_MAJOR;
         if (semiMinor == null) semiMinor = MapProjection.SEMI_MINOR;
 
-              double a  = getAndStore(parameters, semiMajor);
-        final double b  = getAndStore(parameters, semiMinor);
-        final double λ0 = getAndStore(parameters, 
roles.get(ParameterRole.CENTRAL_MERIDIAN));
-        final double fe = getAndStore(parameters, 
roles.get(ParameterRole.FALSE_EASTING))
-                        - getAndStore(parameters, 
roles.get(ParameterRole.FALSE_WESTING));
-        final double fn = getAndStore(parameters, 
roles.get(ParameterRole.FALSE_NORTHING))
-                        - getAndStore(parameters, 
roles.get(ParameterRole.FALSE_SOUTHING));
-        final double rs = b / a;
-        excentricitySquared = 1 - (rs * rs);
-        excentricity = sqrt(excentricitySquared);
-        if (excentricitySquared != 0) {
+        final double a  = getAndStore(semiMajor);
+        final double b  = getAndStore(semiMinor);
+        final double λ0 = 
getAndStore(roles.get(ParameterRole.CENTRAL_MERIDIAN));
+        final double fe = getAndStore(roles.get(ParameterRole.FALSE_EASTING))
+                        - getAndStore(roles.get(ParameterRole.FALSE_WESTING));
+        final double fn = getAndStore(roles.get(ParameterRole.FALSE_NORTHING))
+                        - getAndStore(roles.get(ParameterRole.FALSE_SOUTHING));
+
+        excentricitySquared = new DoubleDouble();
+        final DoubleDouble k = new DoubleDouble(a);  // The value by which to 
multiply all results of normalized projection.
+        if (a != b) {
+            /*
+             * Equivalent Java code for the following lines:
+             *
+             *     final double rs = b / a;
+             *     excentricitySquared = 1 - (rs * rs);
+             *
+             * Test show that double-double arithmetic here makes a difference 
in the 3 last digits for WGS84 ellipsoid.
+             * Those 3 digits are not significant since the parameter are not 
so accurate (furthermore the 'b' parameter
+             * used below may have been computed from the inverse flattening 
factor).
+             */
+            final DoubleDouble rs = new DoubleDouble(b);
+            final double eb = rs.error;
+            rs.divide(k);    // rs = b/a
+            rs.multiply(rs);
+            excentricitySquared.value = 1;
+            excentricitySquared.subtract(rs);
+
             final ParameterDescriptor<Double> radius = 
roles.get(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS);
             if (radius != null) {
                 /*
@@ -439,61 +133,29 @@ public abstract class NormalizedProjecti
                  * Radius of conformal sphere Rc = a √(1 – ℯ²) / (1 – ℯ²⋅sin²φ)
                  *
                  * Using √(1 – ℯ²) = b/a we rewrite as: Rc = b / (1 – ℯ²⋅sin²φ)
+                 *
+                 * Equivalent Java code:
+                 *
+                 *     final double sinφ = 
sin(toRadians(parameters.doubleValue(radius)));
+                 *     k = b / (1 - excentricitySquared * (sinφ*sinφ));
                  */
-                final double sinφ = 
sin(toRadians(parameters.doubleValue(radius)));
-                a = b / (1 - excentricitySquared * (sinφ*sinφ));
+                final DoubleDouble t = new 
DoubleDouble(sin(toRadians(parameters.doubleValue(radius))), 0);
+                t.multiply(t);
+                t.multiply(excentricitySquared);
+                k.clear();
+                k.value = 1;
+                k.subtract(t);
+                k.inverseDivide(b, eb);
             }
         }
         context.normalizeGeographicInputs(λ0);
-        final DoubleDouble k = new DoubleDouble(a);
         final ParameterDescriptor<Double> scaleFactor = 
roles.get(ParameterRole.SCALE_FACTOR);
         if (scaleFactor != null) {
-            k.multiply(getAndStore(parameters, scaleFactor));
+            k.multiply(getAndStore(scaleFactor));
         }
         final MatrixSIS denormalize = context.getMatrix(false);
         denormalize.convertAfter(0, k, new DoubleDouble(fe));
         denormalize.convertAfter(1, k, new DoubleDouble(fn));
-        inverse = new Inverse();
-    }
-
-    /**
-     * Creates a new projection initialized to the values of the given one. 
This constructor may be invoked after
-     * we determined that the default implementation can be replaced by an 
other one, for example using spherical
-     * formulas instead than the ellipsoidal ones. This constructor allows to 
transfer all parameters to the new
-     * instance without recomputing them.
-     */
-    NormalizedProjection(final NormalizedProjection other) {
-        context             = other.context;
-        excentricity        = other.excentricity;
-        excentricitySquared = other.excentricitySquared;
-        inverse             = new Inverse();
-    }
-
-    /**
-     * Returns {@code true} if the projection specified by the given method 
has the given keyword or identifier.
-     * If non-null, the given identifier is presumed in the EPSG namespace and 
has precedence over the keyword.
-     *
-     * <div class="note"><b>Implementation note:</b>
-     * Since callers usually give a constant string for the {@code regex} 
argument, it would be more efficient to
-     * compile the {@link java.util.regex.Pattern} once for all. However the 
regular expression is used only as a
-     * fallback if the descriptor does not contain EPSG identifier, which 
should be rare. Usually, the regular
-     * expression will never be compiled.</div>
-     *
-     * @param  parameters The user-specified parameters.
-     * @param  regex      The regular expression to use when using the 
operation name as the criterion.
-     * @param  identifier The identifier to compare against the operation 
method name.
-     * @return {@code true} if the name of the given operation method contains 
the given keyword
-     *         or has an EPSG identifier equals to the given identifier.
-     */
-    static boolean identMatch(final OperationMethod method, final String 
regex, final String identifier) {
-        if (identifier != null) {
-            for (final Identifier id : method.getIdentifiers()) {
-                if (Constants.EPSG.equals(id.getCodeSpace())) {
-                    return identifier.equals(id.getCode());
-                }
-            }
-        }
-        return method.getName().getCode().replace('_',' ').matches(regex);
     }
 
     /**
@@ -507,10 +169,8 @@ public abstract class NormalizedProjecti
      *   <li>Ensure that the value is contained in the range specified by the 
descriptor.</li>
      *   <li>Store the value only if different than the default value.</li>
      * </ul>
-     *
-     * This method shall be invoked at construction time only.
      */
-    final double getAndStore(final Parameters parameters, final 
ParameterDescriptor<Double> descriptor) {
+    final double getAndStore(final ParameterDescriptor<Double> descriptor) {
         if (descriptor == null) {
             return 0;   // Default value for all parameters except scale 
factor.
         }
@@ -528,9 +188,7 @@ public abstract class NormalizedProjecti
      * if the parameter is not specified.  This method shall be used only for 
parameters having a default
      * value more complex than what we can represent in {@link 
ParameterDescriptor#getDefaultValue()}.
      */
-    final double getAndStore(final Parameters parameters, final 
ParameterDescriptor<Double> descriptor,
-            final double defaultValue)
-    {
+    final double getAndStore(final ParameterDescriptor<Double> descriptor, 
final double defaultValue) {
         final Double value = parameters.getValue(descriptor);   // Apply a 
unit conversion if needed.
         if (value == null) {
             return defaultValue;
@@ -540,382 +198,6 @@ public abstract class NormalizedProjecti
         return value;
     }
 
-    /**
-     * Returns the sequence of <cite>normalization</cite> → {@code this} → 
<cite>denormalization</cite> transforms
-     * as a whole. The transform returned by this method except 
(<var>longitude</var>, <var>latitude</var>)
-     * coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) 
coordinates in <em>metres</em>.
-     * Conversion to other units and {@linkplain 
org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes
-     * changes in axis order} are <strong>not</strong> managed by the returned 
transform.
-     *
-     * <p>The default implementation is as below:</p>
-     * {@preformat java
-     *     return getContextualParameters().completeTransform(factory, this);
-     * }
-     *
-     * Subclasses can override this method if they wish to use alternative 
implementations under some circumstances.
-     * For example many subclasses will replace {@code this} by a specialized 
implementation if they detect that the
-     * ellipsoid is actually spherical.
-     *
-     * @param  factory The factory to use for creating the transform.
-     * @return The map projection from (λ,φ) to (<var>x</var>,<var>y</var>) 
coordinates.
-     * @throws FactoryException if an error occurred while creating a 
transform.
-     *
-     * @see ContextualParameters#completeTransform(MathTransformFactory, 
MathTransform)
-     */
-    public MathTransform createMapProjection(final MathTransformFactory 
factory) throws FactoryException {
-        return context.completeTransform(factory, this);
-    }
-
-    /**
-     * Returns the parameters used for creating the complete map projection. 
Those parameters describe a sequence of
-     * <cite>normalize</cite> → {@code this} → <cite>denormalize</cite> 
transforms, <strong>not</strong> including
-     * {@linkplain 
org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes axis swapping}.
-     * Those parameters are used for formatting <cite>Well Known Text</cite> 
(WKT) and error messages.
-     * Subclasses shall not use the values defined in the returned object for 
computation purpose,
-     * except at construction time.
-     *
-     * @return The parameters values for the sequence of 
<cite>normalize</cite> → {@code this} → <cite>denormalize</cite>
-     *         transforms, or {@code null} if unspecified.
-     */
-    @Override
-    protected final ContextualParameters getContextualParameters() {
-        return context;
-    }
-
-    /**
-     * Returns a copy of non-linear internal parameter values of this {@code 
NormalizedProjection}.
-     * The returned group contained at least the {@link #excentricity} 
parameter value.
-     * Some subclasses add more non-linear parameters, but most of them do not 
because many parameters
-     * like the <cite>scale factor</cite> or the <cite>false 
easting/northing</cite> are handled by the
-     * {@linkplain ContextualParameters#getMatrix(boolean) (de)normalization 
affine transforms} instead.
-     *
-     * <div class="note"><b>Note:</b>
-     * This method is mostly for {@linkplain 
org.apache.sis.io.wkt.Convention#INTERNAL debugging purposes}
-     * since the isolation of non-linear parameters in this class is highly 
implementation dependent.
-     * Most GIS applications will instead be interested in the {@linkplain 
#getContextualParameters()
-     * contextual parameters}.</div>
-     *
-     * @return A copy of the internal parameter values for this normalized 
projection.
-     */
-    @Debug
-    @Override
-    public ParameterValueGroup getParameterValues() {
-        final ParameterValueGroup group = 
getParameterDescriptors().createValue();
-        group.parameter("excentricity").setValue(excentricity);
-        final String[] names  = getInternalParameterNames();
-        final double[] values = getInternalParameterValues();
-        for (int i=0; i<names.length; i++) {
-            group.parameter(names[i]).setValue(values[i]);
-        }
-        return group;
-    }
-
-    /**
-     * Returns a description of the non-linear internal parameters of this 
{@code NormalizedProjection}.
-     * The returned group contained at least a descriptor for the {@link 
#excentricity} parameter.
-     * Subclasses may add more parameters.
-     *
-     * <p>This method is for inspecting the parameter values of this 
non-linear kernel only,
-     * not for inspecting the {@linkplain #getContextualParameters() 
contextual parameters}.
-     * Inspecting the kernel parameter values is usually for debugging purpose 
only.</p>
-     *
-     * @return A description of the internal parameters.
-     */
-    @Debug
-    @Override
-    public ParameterDescriptorGroup getParameterDescriptors() {
-        Class<?> type = getClass();
-        while (!Modifier.isPublic(type.getModifiers())) {
-            type = type.getSuperclass();
-        }
-        ParameterDescriptorGroup group;
-        synchronized (DESCRIPTORS) {
-            group = DESCRIPTORS.get(type);
-            if (group == null) {
-                final ParameterBuilder builder = new 
ParameterBuilder().setRequired(true);
-                if (Utilities.isSIS(type)) {
-                    builder.setCodeSpace(Citations.SIS, "SIS");
-                }
-                final String[] names = getInternalParameterNames();
-                final ParameterDescriptor<?>[] parameters = new 
ParameterDescriptor<?>[names.length + 1];
-                for (int i=0; i<parameters.length; i++) {
-                    final ParameterDescriptor<?> p;
-                    if (i == 0) {
-                        final ParameterDescriptorGroup existing = 
CollectionsExt.first(DESCRIPTORS.values());
-                        if (existing != null) {
-                            p = (ParameterDescriptor<?>) 
existing.descriptor("excentricity");
-                        } else {
-                            p = builder.addName(Citations.SIS, 
"excentricity").createBounded(0, 1, Double.NaN, null);
-                        }
-                    } else {
-                        p = builder.addName(names[i-1]).create(Double.class, 
null);
-                    }
-                    parameters[i] = p;
-                }
-                group = 
builder.addName(CharSequences.camelCaseToSentence(type.getSimpleName())).createGroup(1,
 1, parameters);
-                DESCRIPTORS.put(type, group);
-            }
-        }
-        return group;
-    }
-
-    /**
-     * Returns the names of any additional internal parameters (other than 
{@link #excentricity})
-     * that this projection has. The length of this array must be the same 
than the length of the
-     * {@link #getInternalParameterValues()} array, if the later is non-null.
-     */
-    String[] getInternalParameterNames() {
-        return CharSequences.EMPTY_ARRAY;
-    }
-
-    /**
-     * Returns the values of any additional internal parameters (other than 
{@link #excentricity}) that
-     * this projection has. Those values are also compared by {@link 
#equals(Object, ComparisonMode)}.
-     */
-    double[] getInternalParameterValues() {
-        return null;
-    }
-
-    /**
-     * Converts a single coordinate in {@code srcPts} at the given offset and 
stores the result
-     * in {@code dstPts} at the given offset. In addition, opportunistically 
computes the
-     * transform derivative if requested.
-     *
-     * <div class="section">Normalization</div>
-     * The input ordinates are (<var>λ</var>,<var>φ</var>) (the variable names 
for <var>longitude</var> and
-     * <var>latitude</var> respectively) angles in radians.
-     * Input coordinate shall have the <cite>central meridian</cite> removed 
from the longitude by the caller
-     * before this method is invoked. After this method is invoked, the caller 
will need to multiply the output
-     * coordinate by the global <cite>scale factor</cite>
-     * and apply the (<cite>false easting</cite>, <cite>false northing</cite>) 
offset.
-     * This means that projections that implement this method are performed on 
a sphere or ellipse
-     * having a semi-major axis length of 1.
-     *
-     * <div class="note"><b>Note:</b> in <a 
href="http://trac.osgeo.org/proj/";>Proj.4</a>, the same standardization,
-     * described above, is handled by {@code pj_fwd.c}.</div>
-     *
-     * <div class="section">Argument checks</div>
-     * The input longitude and latitude are usually (but not always) in the 
range [-π … π] and [-π/2 … π/2] respectively.
-     * However values outside those ranges are accepted on the assumption that 
most implementations use those values
-     * only in trigonometric functions like {@linkplain Math#sin(double) sine} 
and {@linkplain Math#cos(double) cosine}.
-     * If this assumption is not applicable to a particular subclass, then it 
is implementor's responsibility to check
-     * the range.
-     *
-     * @param srcPts   The array containing the source point coordinate, as 
(<var>longitude</var>, <var>latitude</var>)
-     *                 angles in <strong>radians</strong>.
-     * @param srcOff   The offset of the single coordinate to be converted in 
the source array.
-     * @param dstPts   The array into which the converted coordinate is 
returned (may be the same than {@code srcPts}).
-     *                 Ordinates will be expressed in a dimensionless unit, as 
a linear distance on a unit sphere or ellipse.
-     * @param dstOff   The offset of the location of the converted coordinate 
that is stored in the destination array.
-     * @param derivate {@code true} for computing the derivative, or {@code 
false} if not needed.
-     * @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 coordinate can not be converted.
-     */
-    @Override
-    public abstract Matrix transform(double[] srcPts, int srcOff, double[] 
dstPts, int dstOff, boolean derivate)
-            throws ProjectionException;
-
-    /**
-     * Inverse converts the single coordinate in {@code srcPts} at the given 
offset and stores the result in
-     * {@code ptDst} at the given offset. The output ordinates are 
(<var>longitude</var>, <var>latitude</var>)
-     * angles in radians, usually (but not necessarily) in the range [-π … π] 
and [-π/2 … π/2] respectively.
-     *
-     * <div class="section">Normalization</div>
-     * Input coordinate shall have the (<cite>false easting</cite>, 
<cite>false northing</cite>) removed
-     * by the caller and the result divided by the global <cite>scale 
factor</cite> before this method is invoked.
-     * After this method is invoked, the caller will need to add the 
<cite>central meridian</cite> to the longitude
-     * in the output coordinate. This means that projections that implement 
this method are performed on a sphere
-     * or ellipse having a semi-major axis of 1.
-     *
-     * <div class="note"><b>Note:</b> in <a 
href="http://trac.osgeo.org/proj/";>Proj.4</a>, the same standardization,
-     * described above, is handled by {@code pj_inv.c}.</div>
-     *
-     * @param srcPts The array containing the source point coordinate, as 
linear distance on a unit sphere or ellipse.
-     * @param srcOff The offset of the point to be converted in the source 
array.
-     * @param dstPts The array into which the converted point coordinate is 
returned (may be the same than {@code srcPts}).
-     *               Ordinates will be (<var>longitude</var>, 
<var>latitude</var>) angles in <strong>radians</strong>.
-     * @param dstOff The offset of the location of the converted point that is 
stored in the destination array.
-     * @throws ProjectionException if the point can not be converted.
-     */
-    protected abstract void inverseTransform(double[] srcPts, int srcOff, 
double[] dstPts, int dstOff)
-            throws ProjectionException;
-
-    /**
-     * Returns the inverse of this map projection.
-     * Subclasses do not need to override this method, as they should override
-     * {@link #inverseTransform(double[], int, double[], int) 
inverseTransform(…)} instead.
-     *
-     * @return The inverse of this map projection.
-     */
-    @Override
-    public MathTransform2D inverse() {
-        return inverse;
-    }
-
-    /**
-     * Inverse of a normalized map projection.
-     *
-     * @author  Martin Desruisseaux (Geomatys)
-     * @since   0.6
-     * @version 0.6
-     * @module
-     */
-    private final class Inverse extends AbstractMathTransform2D.Inverse {
-        /**
-         * For cross-version compatibility.
-         */
-        private static final long serialVersionUID = -9138242780765956870L;
-
-        /**
-         * Default constructor.
-         */
-        public Inverse() {
-            NormalizedProjection.this.super();
-        }
-
-        /**
-         * Inverse transforms the specified {@code srcPts} and stores the 
result in {@code dstPts}.
-         * If the derivative has been requested, then this method will 
delegate the derivative
-         * calculation to the enclosing class and inverts the resulting matrix.
-         */
-        @Override
-        public Matrix transform(final double[] srcPts, final int srcOff,
-                                      double[] dstPts,       int dstOff,
-                                final boolean derivate) throws 
TransformException
-        {
-            if (!derivate) {
-                inverseTransform(srcPts, srcOff, dstPts, dstOff);
-                return null;
-            } else {
-                if (dstPts == null) {
-                    dstPts = new double[2];
-                    dstOff = 0;
-                }
-                inverseTransform(srcPts, srcOff, dstPts, dstOff);
-                return 
Matrices.inverse(NormalizedProjection.this.transform(dstPts, dstOff, null, 0, 
true));
-            }
-        }
-    }
-
-    /**
-     * Computes a hash code value for this {@code NormalizedProjection}.
-     *
-     * @return The hash code value.
-     */
-    @Override
-    protected int computeHashCode() {
-        long c = Double.doubleToLongBits(excentricity);
-        final double[] parameters = getInternalParameterValues();
-        if (parameters != null) {
-            for (int i=0; i<parameters.length; i++) {
-                c = c*31 + Double.doubleToLongBits(parameters[i]);
-            }
-        }
-        return super.computeHashCode() ^ Numerics.hashCode(c);
-    }
-
-    /**
-     * Compares the given object with this transform for equivalence. The 
default implementation checks if
-     * {@code object} is an instance of the same class than {@code this}, then 
compares the excentricity.
-     *
-     * <p>If this method returns {@code true}, then for any given identical 
source position, the two compared map
-     * projections shall compute the same target position. Many of the 
{@linkplain #getContextualParameters()
-     * contextual parameters} used for creating the map projections are 
irrelevant and do not need to be known.
-     * Those projection parameters will be compared only if the comparison 
mode is {@link ComparisonMode#STRICT}
-     * or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}.</p>
-     *
-     * <div class="note"><b>Example:</b>
-     * a {@linkplain Mercator Mercator} projection can be created in the 2SP 
case with a <cite>standard parallel</cite>
-     * value of 60°. The same projection can also be created in the 1SP case 
with a <cite>scale factor</cite> of 0.5.
-     * Nevertheless those two map projections applied on a sphere gives 
identical results. Considering them as
-     * equivalent allows the referencing module to transform coordinates 
between those two projections more efficiently.
-     * </div>
-     *
-     * @param object The object to compare with this map projection for 
equivalence.
-     * @param mode The strictness level of the comparison. Default to {@link 
ComparisonMode#STRICT}.
-     * @return {@code true} if the given object is equivalent to this map 
projection.
-     */
-    @Override
-    @SuppressWarnings("fallthrough")
-    public boolean equals(final Object object, final ComparisonMode mode) {
-        if (object == this) {
-            return true;
-        }
-        if (!super.equals(object, mode)) {
-            return false;
-        }
-        final NormalizedProjection that = (NormalizedProjection) object;
-        switch (mode) {
-            case STRICT:
-            case BY_CONTRACT: {
-                if (!Objects.equals(context, that.context)) {
-                    return false;
-                }
-                // Fall through for comparing the excentricity.
-            }
-            case IGNORE_METADATA: {
-                /*
-                 * There is no need to compare both 'excentricity' and 
'excentricitySquared' since the former
-                 * is computed from the later. We are better to compare 
'excentricitySquared' since it is the
-                 * original value from which the other value is derived.
-                 */
-                if (!Numerics.equals(excentricitySquared, 
that.excentricitySquared)) {
-                    return false;
-                }
-                break;
-            }
-            default: {
-                /*
-                 * We want to compare the excentricity with a tolerance 
threshold corresponding approximatively
-                 * to an error of 1 cm on Earth. The excentricity for an 
ellipsoid of semi-major axis a=1 is:
-                 *
-                 *     ℯ² = 1 - b²
-                 *
-                 * If we add a slight ε error to the semi-minor axis length 
(where ε will be our linear tolerance
-                 * threshold), we get:
-                 *
-                 *     (ℯ + ε′)²    =    1 - (b + ε)²    ≈    1 - (b² + 2⋅b⋅ε) 
   assuming ε ≪ b
-                 *
-                 * Replacing  1 - b²  by  ℯ²:
-                 *
-                 *     ℯ² + 2⋅ℯ⋅ε′  ≈   ℯ² - 2⋅b⋅ε
-                 *
-                 * After a few rearrangements:
-                 *
-                 *     ε′  ≈   ε⋅(ℯ - 1/ℯ)
-                 *
-                 * Note that  ε′  is negative for  ℯ < 1  so we actually need 
to compute  ε⋅(1/ℯ - ℯ)  instead.
-                 * The result is less than 2E-8 for the excentricity of the 
Earth.
-                 */
-                final double e = max(excentricity, that.excentricity);
-                if (!Numerics.epsilonEqual(excentricity, that.excentricity, 
ANGULAR_TOLERANCE * (1/e - e))) {
-                    assert (mode != ComparisonMode.DEBUG) : 
Numerics.messageForDifference(
-                            "excentricity", excentricity, that.excentricity);
-                    return false;
-                }
-                break;
-            }
-        }
-        final double[] parameters = getInternalParameterValues();
-        if (parameters != null) {
-            /*
-             * super.equals(…) guarantees that the two objects are of the same 
class.
-             * So in SIS implementation, this implies that the arrays have the 
same length.
-             */
-            final double[] others = that.getInternalParameterValues();
-            assert others.length == parameters.length;
-            for (int i=0; i<parameters.length; i++) {
-                if (!Numerics.epsilonEqual(parameters[i], others[i], mode)) {
-                    assert (mode != ComparisonMode.DEBUG) : 
Numerics.messageForDifference(
-                            getInternalParameterNames()[i], parameters[i], 
others[i]);
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
 
 
 
@@ -949,7 +231,21 @@ public abstract class NormalizedProjecti
      * @param  sinφ The sine of the φ latitude in radians.
      * @return Reciprocal of the radius of curvature of the ellipsoid 
perpendicular to the meridian at latitude φ.
      */
-    final double rν(final double sinφ) {
-        return sqrt(1 - excentricitySquared * (sinφ*sinφ));
+    final DoubleDouble rν(final double sinφ) {
+        /*
+         * Equivalent Java code:
+         *
+         *     return sqrt(1 - excentricitySquared * (sinφ*sinφ));
+         */
+        final DoubleDouble t = new DoubleDouble(sinφ, 0);
+        t.multiply(t);
+        t.multiply(excentricitySquared);
+        final double value = t.value;
+        final double error = t.error;
+        t.clear();
+        t.value = 1;
+        t.subtract(value, error);
+        t.sqrt();
+        return t;
     }
 }

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java?rev=1693439&r1=1693438&r2=1693439&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
 [UTF-8] Thu Jul 30 15:27:52 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
-import java.util.Map;
 import java.util.EnumMap;
 import org.opengis.util.FactoryException;
 import org.opengis.parameter.ParameterDescriptor;
@@ -123,13 +122,32 @@ public class LambertConicConformal exten
     final double n;
 
     /**
-     * Returns the (<var>role</var> → <var>parameter</var>) associations for a 
Lambert projection of the given variant.
+     * Creates a Lambert projection from the given parameters.
+     * The {@code method} argument can be the description of one of the 
following:
+     *
+     * <ul>
+     *   <li><cite>"Lambert Conic Conformal (1SP)"</cite>.</li>
+     *   <li><cite>"Lambert Conic Conformal (West Orientated)"</cite>.</li>
+     *   <li><cite>"Lambert Conic Conformal (2SP)"</cite>.</li>
+     *   <li><cite>"Lambert Conic Conformal (2SP Belgium)"</cite>.</li>
+     *   <li><cite>"Lambert Conic Conformal (2SP Michigan)"</cite>.</li>
+     * </ul>
      *
-     * @param  variant One of {@link #SP1}, {@link #SP2}, {@link #WEST}, 
{@link #BELGIUM} and {@link #MICHIGAN} constants.
-     * @return The roles map to give to super-class constructor.
+     * @param method     Description of the projection parameters.
+     * @param parameters The parameter values of the projection to create.
+     */
+    public LambertConicConformal(final OperationMethod method, final 
Parameters parameters) {
+        this(initializer(method, parameters));
+    }
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
     @SuppressWarnings("fallthrough")
-    private static Map<ParameterRole, ParameterDescriptor<Double>> roles(final 
byte variant) {
+    @Workaround(library="JDK", version="1.7")
+    private static Initializer initializer(final OperationMethod method, final 
Parameters parameters) {
+        final byte variant = getVariant(method);
         final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new 
EnumMap<>(ParameterRole.class);
         /*
          * "Scale factor" is not formally a "Lambert Conformal (2SP)" 
argument, but we accept it
@@ -166,26 +184,7 @@ public class LambertConicConformal exten
             default: throw new AssertionError(variant);
         }
         roles.put(ParameterRole.SCALE_FACTOR, scaleFactor);
-        return roles;
-    }
-
-    /**
-     * Creates a Lambert projection from the given parameters.
-     * The {@code method} argument can be the description of one of the 
following:
-     *
-     * <ul>
-     *   <li><cite>"Lambert Conic Conformal (1SP)"</cite>.</li>
-     *   <li><cite>"Lambert Conic Conformal (West Orientated)"</cite>.</li>
-     *   <li><cite>"Lambert Conic Conformal (2SP)"</cite>.</li>
-     *   <li><cite>"Lambert Conic Conformal (2SP Belgium)"</cite>.</li>
-     *   <li><cite>"Lambert Conic Conformal (2SP Michigan)"</cite>.</li>
-     * </ul>
-     *
-     * @param method     Description of the projection parameters.
-     * @param parameters The parameter values of the projection to create.
-     */
-    public LambertConicConformal(final OperationMethod method, final 
Parameters parameters) {
-        this(method, parameters, getVariant(method));
+        return new Initializer(method, parameters, roles, variant);
     }
 
     /**
@@ -193,17 +192,17 @@ public class LambertConicConformal exten
      * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
     @Workaround(library="JDK", version="1.7")
-    private LambertConicConformal(final OperationMethod method, final 
Parameters parameters, final byte type) {
-        super(method, parameters, roles(type));
-        double φ0 = getAndStore(parameters, ((type & 1) != 0) ?  // Odd 'type' 
are SP1, even 'type' are SP2.
+    private LambertConicConformal(final Initializer initializer) {
+        super(initializer);
+        double φ0 = initializer.getAndStore(((initializer.variant & 1) != 0) ? 
 // Odd 'type' are SP1, even 'type' are SP2.
                 LambertConformal1SP.LATITUDE_OF_ORIGIN : 
LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN);
         /*
          * Standard parallels (SP) are defined only for the 2SP case, but we 
look for them unconditionally
          * in case the user gave us non-standard parameters. For the 1SP case, 
or for the 2SP case left to
          * their default values, EPSG says that we shall use the latitude of 
origin as the SP.
          */
-        double φ1 = getAndStore(parameters, 
LambertConformal2SP.STANDARD_PARALLEL_1, φ0);
-        double φ2 = getAndStore(parameters, 
LambertConformal2SP.STANDARD_PARALLEL_2, φ1);
+        double φ1 = 
initializer.getAndStore(LambertConformal2SP.STANDARD_PARALLEL_1, φ0);
+        double φ2 = 
initializer.getAndStore(LambertConformal2SP.STANDARD_PARALLEL_2, φ1);
         if (abs(φ1 + φ2) < Formulas.ANGULAR_TOLERANCE) {
             /*
              * We can not allow that because if φ1 = -φ2, then n = 0 and the 
equations
@@ -248,33 +247,60 @@ public class LambertConicConformal exten
          * since rν(sinφ) = 1 and expOfNorthing(φ) = tan(π/4 + φ/2) when the 
excentricity is zero.
          * However we need special formulas for φ1 ≈ φ2 in the calculation of 
n, otherwise we got
          * a 0/0 indetermination.
+         *
+         * Opportunistically use double-double arithmetic below since this is 
what we will store in the
+         * (de)normalization matrices. The extra precision that we get is not 
necessarily significant,
+         * but we do that more in an attempt to reduce rounding errors in 
concatenations of a sequence
+         * of MathTransforms (through matrix multiplications) than for map 
projection precisions.
+         * Equivalent Java code for the following double-double arithmetic:
+         *
+         *     final double m1 = cos(φ1) / rν(sinφ1);
          */
         final double sinφ1 = sin(φ1);
-        final double m1    = cos(φ1) / rν(sinφ1);
-        final double t1    = expOfNorthing(φ1, excentricity*sinφ1);
+        final DoubleDouble m1 = initializer.rν(sinφ1);
+        m1.inverseDivide(cos(φ1), 0);
+        final double t1 = expOfNorthing(φ1, excentricity*sinφ1);
         /*
          * Computes n = (ln m₁ – ln m₂) / (ln t₁ – ln t₂), which we rewrite as 
ln(m₁/m₂) / ln(t₁/t₂)
          * since division is less at risk of precision lost than subtraction. 
Note that this equation
          * tends toward 0/0 if φ₁ ≈ φ₂, which force us to do a special check 
for the SP1 case.
+         *
+         * Equivalent Java code for the following double-double arithmetic:
+         *
+         *     final double sinφ2 = sin(φ2);
+         *     final double m2 = cos(φ2) / rν(sinφ2);
+         *     final double t2 = expOfNorthing(φ2, excentricity*sinφ2);
+         *     n = log(m1/m2) / log(t1/t2);    // Tend toward 0/0 if φ1 ≈ φ2.
          */
+        final DoubleDouble F = new DoubleDouble();
         if (abs(φ1 - φ2) >= ANGULAR_TOLERANCE) {  // Should be 'true' for 2SP 
case.
             final double sinφ2 = sin(φ2);
-            final double m2 = cos(φ2) / rν(sinφ2);
+            final DoubleDouble m2 = initializer.rν(sinφ2);
+            m2.inverseDivide(cos(φ2), 0);
             final double t2 = expOfNorthing(φ2, excentricity*sinφ2);
-            n = log(m1/m2) / log(t1/t2);    // Tend toward 0/0 if φ1 ≈ φ2.
+            m2.inverseDivide(m1);
+            F.value = log(m2.value);
+            F.divide(log(t1/t2), 0);
         } else {
-            n = -sinφ1;
+            F.value = -sinφ1;
+        }
+        n = F.value;
+        /*
+         * Scale factor for longitudes, stored now before we modify the F 
value.
+         */
+        final DoubleDouble sx = new DoubleDouble(F);
+        if (!isNorth) {
+            sx.negate();
         }
         /*
          * Computes F = m₁/(n⋅t₁ⁿ) from Geomatics Guidance Note number 7.
-         * Following constants will be stored in the denormalization matrix, 
to be applied after
-         * the non-linear formulas implemented by this LambertConicConformal 
class. Opportunistically
-         * use double-double arithmetic since we the matrix coefficients will 
be stored in this
-         * format anyway. This makes a change in the 2 or 3 last digits.
+         * Following constants will be stored in the denormalization matrix, 
to be applied
+         * after the non-linear formulas implemented by this 
LambertConicConformal class.
+         * Opportunistically use double-double arithmetic since the matrix 
coefficients will
+         * be stored in that format anyway. This makes a change in the 2 or 3 
last digits.
          */
-        final DoubleDouble F = new DoubleDouble(n, 0);
         F.multiply(pow(t1, n), 0);
-        F.inverseDivide(m1, 0);
+        F.inverseDivide(m1);
         if (!isNorth) {
             F.negate();
         }
@@ -287,7 +313,7 @@ public class LambertConicConformal exten
          * EPSG uses this term in the computation of  y = FN + rF – r⋅cos(θ).
          */
         final DoubleDouble rF = new DoubleDouble();    // Initialized to zero.
-        if (φ0 != copySign(PI/2, -n)) {    // For avoiding the rounding error 
documented in expOfNorthing(+π/2).
+        if (φ0 != copySign(PI/2, -n)) {    // For reducing the rounding error 
documented in expOfNorthing(+π/2).
             rF.value = pow(expOfNorthing(φ0, excentricity*sin(φ0)), n);
             rF.multiply(F);
         }
@@ -309,8 +335,7 @@ public class LambertConicConformal exten
          *   - Add false easting and false northing (done by the super-class 
constructor).
          */
         final MatrixSIS normalize = context.getMatrix(true);
-        normalize.convertAfter(0, new DoubleDouble(isNorth ? n : -n, 0),    // 
Multiplication factor for longitudes.
-                (type == BELGIUM) ? new DoubleDouble(-BELGE_A, 0) : null);  // 
Longitude translation for Belgium.
+        normalize.convertAfter(0, sx, (initializer.variant == BELGIUM) ? new 
DoubleDouble(-BELGE_A, 0) : null);
         if (isNorth) {
             normalize.convertAfter(1, new DoubleDouble(-1, 0), null);
         }

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java?rev=1693439&r1=1693438&r2=1693439&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
 [UTF-8] Thu Jul 30 15:27:52 2015
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.referencing.operation.projection;
 
-import java.util.Map;
 import java.util.EnumMap;
 import org.opengis.util.FactoryException;
 import org.opengis.parameter.ParameterDescriptor;
@@ -125,13 +124,33 @@ public class Mercator extends ConformalP
     private final byte variant;
 
     /**
-     * Returns the (<var>role</var> → <var>parameter</var>) associations for a 
Mercator projection of the given variant.
+     * Creates a Mercator projection from the given parameters.
+     * The {@code method} argument can be the description of one of the 
following:
+     *
+     * <ul>
+     *   <li><cite>"Mercator (variant A)"</cite>, also known as 
<cite>"Mercator (1SP)"</cite>.</li>
+     *   <li><cite>"Mercator (variant B)"</cite>, also known as 
<cite>"Mercator (2SP)"</cite>.</li>
+     *   <li><cite>"Mercator (variant C)"</cite>.</li>
+     *   <li><cite>"Mercator (Spherical)"</cite>.</li>
+     *   <li><cite>"Popular Visualisation Pseudo Mercator"</cite>.</li>
+     *   <li><cite>"Miller Cylindrical"</cite>.</li>
+     * </ul>
      *
-     * @param  variant One of {@link #REGIONAL}, {@link #SPHERICAL}, {@link 
#PSEUDO} or {@link #MILLER} constants.
-     * @return The roles map to give to super-class constructor.
+     * @param method     Description of the projection parameters.
+     * @param parameters The parameter values of the projection to create.
+     */
+    public Mercator(final OperationMethod method, final Parameters parameters) 
{
+        this(initializer(method, parameters));
+    }
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
     @SuppressWarnings("fallthrough")
-    private static Map<ParameterRole, ParameterDescriptor<Double>> roles(final 
byte variant) {
+    @Workaround(library="JDK", version="1.7")
+    private static Initializer initializer(final OperationMethod method, final 
Parameters parameters) {
+        final byte variant = getVariant(method);
         final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new 
EnumMap<>(ParameterRole.class);
         /*
          * "Longitude of origin" is a parameter of all Mercator projections, 
but is intentionally omitted from
@@ -171,27 +190,7 @@ public class Mercator extends ConformalP
                 break;
             }
         }
-        return roles;
-    }
-
-    /**
-     * Creates a Mercator projection from the given parameters.
-     * The {@code method} argument can be the description of one of the 
following:
-     *
-     * <ul>
-     *   <li><cite>"Mercator (variant A)"</cite>, also known as 
<cite>"Mercator (1SP)"</cite>.</li>
-     *   <li><cite>"Mercator (variant B)"</cite>, also known as 
<cite>"Mercator (2SP)"</cite>.</li>
-     *   <li><cite>"Mercator (variant C)"</cite>.</li>
-     *   <li><cite>"Mercator (Spherical)"</cite>.</li>
-     *   <li><cite>"Popular Visualisation Pseudo Mercator"</cite>.</li>
-     *   <li><cite>"Miller Cylindrical"</cite>.</li>
-     * </ul>
-     *
-     * @param method     Description of the projection parameters.
-     * @param parameters The parameter values of the projection to create.
-     */
-    public Mercator(final OperationMethod method, final Parameters parameters) 
{
-        this(method, parameters, getVariant(method));
+        return new Initializer(method, parameters, roles, variant);
     }
 
     /**
@@ -199,15 +198,15 @@ public class Mercator extends ConformalP
      * ("Relax constraint on placement of this()/super() call in 
constructors").
      */
     @Workaround(library="JDK", version="1.7")
-    private Mercator(final OperationMethod method, final Parameters 
parameters, final byte variant) {
-        super(method, parameters, roles(variant));
-        this.variant = variant;
+    private Mercator(final Initializer initializer) {
+        super(initializer);
+        this.variant = initializer.variant;
         /*
          * The "Longitude of natural origin" parameter is found in all 
Mercator projections and is mandatory.
          * Since this is usually the Greenwich meridian, the default value is 
0°. We keep the value in degrees
          * for now; it will be converted to radians later.
          */
-        final double λ0 = getAndStore(parameters, 
Mercator1SP.LONGITUDE_OF_ORIGIN);
+        final double λ0 = 
initializer.getAndStore(Mercator1SP.LONGITUDE_OF_ORIGIN);
         /*
          * The "Latitude of natural origin" is not formally a parameter of 
Mercator projection. But the parameter
          * is included for completeness in CRS labelling, with the restriction 
(specified in EPSG documentation)
@@ -220,7 +219,7 @@ public class Mercator extends ConformalP
          * "Latitude of origin" can not have a non-zero value, if it still 
have non-zero value we will process as
          * for "Latitude of false origin".
          */
-        final double φ0 = toRadians(getAndStore(parameters, (variant == 
REGIONAL)
+        final double φ0 = toRadians(initializer.getAndStore((variant == 
REGIONAL)
                 ? RegionalMercator.LATITUDE_OF_FALSE_ORIGIN : 
Mercator1SP.LATITUDE_OF_ORIGIN));
         /*
          * In theory, the "Latitude of 1st standard parallel" and the "Scale 
factor at natural origin" parameters
@@ -228,9 +227,9 @@ public class Mercator extends ConformalP
          * the later is for projections "1SP" (namely variant A and 
spherical). However we let users specify both
          * if they really want, since we sometime see such CRS definitions.
          */
-        final double φ1 = toRadians(getAndStore(parameters, 
Mercator2SP.STANDARD_PARALLEL));
+        final double φ1 = 
toRadians(initializer.getAndStore(Mercator2SP.STANDARD_PARALLEL));
         final DoubleDouble k0 = new DoubleDouble(cos(φ1), 0);
-        k0.divide(rν(sin(φ1)), 0);
+        k0.divide(initializer.rν(sin(φ1)));
         /*
          * In principle we should rotate the central meridian (λ0) in the 
normalization transform, as below:
          *

Modified: 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java?rev=1693439&r1=1693438&r2=1693439&view=diff
==============================================================================
--- 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
 [UTF-8] (original)
+++ 
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
 [UTF-8] Thu Jul 30 15:27:52 2015
@@ -41,16 +41,13 @@ import org.apache.sis.referencing.operat
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.referencing.operation.transform.AbstractMathTransform2D;
 import org.apache.sis.referencing.operation.transform.ContextualParameters;
-import org.apache.sis.internal.referencing.provider.MapProjection;
 import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.util.CollectionsExt;
-import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.internal.util.Utilities;
 import org.apache.sis.internal.util.Numerics;
 
 import static java.lang.Math.*;
-import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
 
 // Branch-dependent imports
 import java.util.Objects;
@@ -402,58 +399,19 @@ public abstract class NormalizedProjecti
     protected NormalizedProjection(final OperationMethod method, final 
Parameters parameters,
             final Map<ParameterRole, ? extends ParameterDescriptor<Double>> 
roles)
     {
-        ensureNonNull("method",     method);
-        ensureNonNull("parameters", parameters);
-        ensureNonNull("roles",      roles);
-        context = new ContextualParameters(method);
-        /*
-         * Note: we do not use Map.getOrDefault(K,V) below because the user 
could have explicitly associated
-         * a null value to keys (we are paranoiac...) and because it conflicts 
with the "? extends" part of
-         * in this constructor signature.
-         */
-        ParameterDescriptor<Double> semiMajor = 
roles.get(ParameterRole.SEMI_MAJOR);
-        ParameterDescriptor<Double> semiMinor = 
roles.get(ParameterRole.SEMI_MINOR);
-        if (semiMajor == null) semiMajor = MapProjection.SEMI_MAJOR;
-        if (semiMinor == null) semiMinor = MapProjection.SEMI_MINOR;
-
-              double a  = getAndStore(parameters, semiMajor);
-        final double b  = getAndStore(parameters, semiMinor);
-        final double λ0 = getAndStore(parameters, 
roles.get(ParameterRole.CENTRAL_MERIDIAN));
-        final double fe = getAndStore(parameters, 
roles.get(ParameterRole.FALSE_EASTING))
-                        - getAndStore(parameters, 
roles.get(ParameterRole.FALSE_WESTING));
-        final double fn = getAndStore(parameters, 
roles.get(ParameterRole.FALSE_NORTHING))
-                        - getAndStore(parameters, 
roles.get(ParameterRole.FALSE_SOUTHING));
-        final double rs = b / a;
-        excentricitySquared = 1 - (rs * rs);
-        excentricity = sqrt(excentricitySquared);
-        if (excentricitySquared != 0) {
-            final ParameterDescriptor<Double> radius = 
roles.get(ParameterRole.LATITUDE_OF_CONFORMAL_SPHERE_RADIUS);
-            if (radius != null) {
-                /*
-                 * EPSG said: R is the radius of the sphere and will normally 
be one of the CRS parameters.
-                 * If the figure of the earth used is an ellipsoid rather than 
a sphere then R should be calculated
-                 * as the radius of the conformal sphere at the projection 
origin at latitude φ₀ using the formula
-                 * for Rc given in section 1.2, table 3.
-                 *
-                 * Table 3 gives:
-                 * Radius of conformal sphere Rc = a √(1 – ℯ²) / (1 – ℯ²⋅sin²φ)
-                 *
-                 * Using √(1 – ℯ²) = b/a we rewrite as: Rc = b / (1 – ℯ²⋅sin²φ)
-                 */
-                final double sinφ = 
sin(toRadians(parameters.doubleValue(radius)));
-                a = b / (1 - excentricitySquared * (sinφ*sinφ));
-            }
-        }
-        context.normalizeGeographicInputs(λ0);
-        final DoubleDouble k = new DoubleDouble(a);
-        final ParameterDescriptor<Double> scaleFactor = 
roles.get(ParameterRole.SCALE_FACTOR);
-        if (scaleFactor != null) {
-            k.multiply(getAndStore(parameters, scaleFactor));
-        }
-        final MatrixSIS denormalize = context.getMatrix(false);
-        denormalize.convertAfter(0, k, new DoubleDouble(fe));
-        denormalize.convertAfter(1, k, new DoubleDouble(fn));
-        inverse = new Inverse();
+        this(new Initializer(method, parameters, roles, (byte) 0));
+    }
+
+    /**
+     * Creates a new normalized projection from the parameters computed by the 
given initializer.
+     *
+     * @param initializer The initializer for computing map projection 
internal parameters.
+     */
+    NormalizedProjection(final Initializer initializer) {
+        context             = initializer.context;
+        excentricitySquared = initializer.excentricitySquared.value;
+        excentricity        = sqrt(excentricitySquared);
+        inverse             = new Inverse();
     }
 
     /**
@@ -497,50 +455,6 @@ public abstract class NormalizedProjecti
     }
 
     /**
-     * Gets a parameter value identified by the given descriptor and stores it 
in the {@link #context}.
-     * A "contextual parameter" is a parameter that apply to the normalize → 
{@code this} → denormalize
-     * chain as a whole. It does not really apply to this {@code 
NormalizedProjection} instance when taken alone.
-     *
-     * <p>This method performs the following actions:</p>
-     * <ul>
-     *   <li>Convert the value to the units specified by the descriptor.</li>
-     *   <li>Ensure that the value is contained in the range specified by the 
descriptor.</li>
-     *   <li>Store the value only if different than the default value.</li>
-     * </ul>
-     *
-     * This method shall be invoked at construction time only.
-     */
-    final double getAndStore(final Parameters parameters, final 
ParameterDescriptor<Double> descriptor) {
-        if (descriptor == null) {
-            return 0;   // Default value for all parameters except scale 
factor.
-        }
-        final double value = parameters.doubleValue(descriptor);    // Apply a 
unit conversion if needed.
-        final Double defaultValue = descriptor.getDefaultValue();
-        if (defaultValue == null || !defaultValue.equals(value)) {
-            MapProjection.validate(descriptor, value);
-            context.getOrCreate(descriptor).setValue(value);
-        }
-        return value;
-    }
-
-    /**
-     * Same as {@link #getAndStore(Parameters, ParameterDescriptor)}, but 
returns the given default value
-     * if the parameter is not specified.  This method shall be used only for 
parameters having a default
-     * value more complex than what we can represent in {@link 
ParameterDescriptor#getDefaultValue()}.
-     */
-    final double getAndStore(final Parameters parameters, final 
ParameterDescriptor<Double> descriptor,
-            final double defaultValue)
-    {
-        final Double value = parameters.getValue(descriptor);   // Apply a 
unit conversion if needed.
-        if (value == null) {
-            return defaultValue;
-        }
-        MapProjection.validate(descriptor, value);
-        context.parameter(descriptor.getName().getCode()).setValue(value);
-        return value;
-    }
-
-    /**
      * Returns the sequence of <cite>normalization</cite> → {@code this} → 
<cite>denormalization</cite> transforms
      * as a whole. The transform returned by this method except 
(<var>longitude</var>, <var>latitude</var>)
      * coordinates in <em>degrees</em> and returns (<var>x</var>,<var>y</var>) 
coordinates in <em>metres</em>.
@@ -915,41 +829,4 @@ public abstract class NormalizedProjecti
         }
         return true;
     }
-
-
-
-
-    
//////////////////////////////////////////////////////////////////////////////////////////
-    ////////                                                                   
       ////////
-    ////////                       FORMULAS FROM EPSG or SNYDER                
       ////////
-    ////////                                                                   
       ////////
-    
//////////////////////////////////////////////////////////////////////////////////////////
-
-    /**
-     * Computes the reciprocal of the radius of curvature of the ellipsoid 
perpendicular to the meridian at latitude φ.
-     * That radius of curvature is:
-     *
-     * <blockquote>ν = 1 / √(1 - ℯ²⋅sin²φ)</blockquote>
-     *
-     * This method returns 1/ν.
-     *
-     * <div class="section">Relationship with Snyder</div>
-     * This is related to functions (14-15) from Snyder (used for computation 
of scale factors
-     * at the true scale latitude) as below:
-     *
-     * <blockquote>m = cosφ / rν</blockquote>
-     *
-     * Special cases:
-     * <ul>
-     *   <li>If φ is 0°, then <var>m</var> is 1.</li>
-     *   <li>If φ is ±90°, then <var>m</var> is 0 provided that we are not in 
the spherical case
-     *       (otherwise we get {@link Double#NaN}).</li>
-     * </ul>
-     *
-     * @param  sinφ The sine of the φ latitude in radians.
-     * @return Reciprocal of the radius of curvature of the ellipsoid 
perpendicular to the meridian at latitude φ.
-     */
-    final double rν(final double sinφ) {
-        return sqrt(1 - excentricitySquared * (sinφ*sinφ));
-    }
 }


Reply via email to