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 2015/09/09 16:43:51 UTC

svn commit: r1702031 - in /sis/trunk: ./ core/sis-metadata/src/main/java/org/apache/sis/io/wkt/ core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ core/sis-referencing...

Author: desruisseaux
Date: Wed Sep  9 14:43:51 2015
New Revision: 1702031

URL: http://svn.apache.org/r1702031
Log:
Merge from the JDK6 branch.

Modified:
    sis/trunk/   (props changed)
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Colors.java
    sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
    sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ColorsTest.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedCode.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedName.java
    sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
    sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
    sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java

Propchange: sis/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Wed Sep  9 14:43:51 2015
@@ -1,4 +1,4 @@
 /sis/branches/Android:1430670-1480699
-/sis/branches/JDK6:1394364-1701642
-/sis/branches/JDK7:1394913-1701639
-/sis/branches/JDK8:1584960-1701637
+/sis/branches/JDK6:1394364-1702026
+/sis/branches/JDK7:1394913-1702025
+/sis/branches/JDK8:1584960-1702023

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Colors.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Colors.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Colors.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Colors.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -65,7 +65,8 @@ public class Colors implements Cloneable
         map.put(ElementKind.CODE_LIST,  X364.FOREGROUND_CYAN);
         map.put(ElementKind.PARAMETER,  X364.FOREGROUND_GREEN);
         map.put(ElementKind.METHOD,     X364.FOREGROUND_GREEN);
-        map.put(ElementKind.DATUM,      X364.FOREGROUND_GREEN);
+        map.put(ElementKind.DATUM,      X364.FOREGROUND_BLUE);  // Because datum names in SIS are like identifiers.
+        map.put(ElementKind.IDENTIFIER, X364.FOREGROUND_BLUE);
         map.put(ElementKind.SCOPE,      X364.FOREGROUND_GRAY);
         map.put(ElementKind.EXTENT,     X364.FOREGROUND_GRAY);
         map.put(ElementKind.CITATION,   X364.FOREGROUND_GRAY);

Modified: sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -761,6 +761,7 @@ public class Formatter implements Locali
             appendForSubtypes(object);
         }
         if (showIDs) {
+            @SuppressWarnings("null")
             Collection<ReferenceIdentifier> identifiers = object.getIdentifiers();
             if (identifiers != null) {  // Paranoiac check
                 if (filterID) {
@@ -1112,7 +1113,11 @@ public class Formatter implements Locali
      */
     public void append(final long number) {
         appendSeparator();
-        setColor(ElementKind.INTEGER);
+        /*
+         * The check for 'isComplement' is a hack for ImmutableIdentifier.formatTo(Formatter).
+         * We do not have a public API for controlling the integer colors (it may not be desirable).
+         */
+        setColor(isComplement ? ElementKind.IDENTIFIER : ElementKind.INTEGER);
         numberFormat.setMaximumFractionDigits(0);
         numberFormat.format(number, buffer, dummy);
         resetColor();

Modified: sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ColorsTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ColorsTest.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ColorsTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ColorsTest.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -39,7 +39,8 @@ public final strictfp class ColorsTest e
     public void testGetName() {
         final Colors colors = Colors.DEFAULT;
         assertEquals("cyan",  colors.getName(ElementKind.CODE_LIST));
-        assertEquals("green", colors.getName(ElementKind.DATUM));
+        assertEquals("green", colors.getName(ElementKind.METHOD));
+        assertEquals("blue",  colors.getName(ElementKind.DATUM));
         assertEquals("red",   colors.getName(ElementKind.ERROR));
     }
 
@@ -50,9 +51,9 @@ public final strictfp class ColorsTest e
     @DependsOnMethod("testGetName")
     public void testSetName() {
         final Colors colors = new Colors(Colors.DEFAULT);
-        assertEquals("green", colors.getName(ElementKind.DATUM));
-        colors.setName(ElementKind.DATUM, "blue");
-        assertEquals("blue", colors.getName(ElementKind.DATUM));
+        assertEquals("green", colors.getName(ElementKind.METHOD));
+        colors.setName(ElementKind.METHOD, "blue");
+        assertEquals("blue", colors.getName(ElementKind.METHOD));
     }
 
     /**
@@ -61,7 +62,7 @@ public final strictfp class ColorsTest e
     @Test
     public void testImmutability() {
         try {
-            Colors.DEFAULT.setName(ElementKind.DATUM, "blue");
+            Colors.DEFAULT.setName(ElementKind.METHOD, "blue");
             fail("Constant shall be immutable.");
         } catch (UnsupportedOperationException e) {
             // This is the expected exception.
@@ -77,9 +78,9 @@ public final strictfp class ColorsTest e
     public void testSerialization() {
         assertSame(Colors.DEFAULT, assertSerializedEquals(Colors.DEFAULT));
         final Colors colors = new Colors(Colors.DEFAULT);
-        colors.setName(ElementKind.DATUM, "blue");
+        colors.setName(ElementKind.METHOD, "blue");
         final Colors c = assertSerializedEquals(colors);
         assertNotSame(colors, c); // Expect a new instance.
-        assertEquals("blue", c.getName(ElementKind.DATUM));
+        assertEquals("blue", c.getName(ElementKind.METHOD));
     }
 }

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractProvider.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -35,6 +35,7 @@ import org.apache.sis.metadata.iso.citat
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.referencing.operation.DefaultOperationMethod;
 import org.apache.sis.referencing.operation.transform.MathTransformProvider;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Workaround;
 
@@ -114,6 +115,26 @@ abstract class AbstractProvider extends
     }
 
     /**
+     * Adds a name together with its previous (legacy) name.
+     * The legacy name will be added as a deprecated alias.
+     */
+    static ParameterBuilder addNameAndLegacy(final ParameterBuilder builder, final String name, final String legacy) {
+        return builder.addName(name).setDeprecated(true)
+                .setRemarks(Vocabulary.formatInternational(Vocabulary.Keys.SupersededBy_1, name))
+                .addName(legacy).setDeprecated(false).setRemarks(null);
+    }
+
+    /**
+     * Adds an identifier code together with its previous (legacy) code.
+     * The legacy code will be added as a deprecated identifier.
+     */
+    static ParameterBuilder addIdentifierAndLegacy(final ParameterBuilder builder, final String code, final String legacy) {
+        return builder.addIdentifier(code).setDeprecated(true)
+                .setRemarks(Vocabulary.formatInternational(Vocabulary.Keys.SupersededBy_1, code))
+                .addIdentifier(legacy).setDeprecated(false).setRemarks(null);
+    }
+
+    /**
      * Creates a descriptor for a 0 constant value in degrees.
      *
      * @see MapProjection#validate(ParameterDescriptor, double)

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -176,9 +176,7 @@ public final class Equirectangular exten
 
         // Do not declare the ESRI "Equidistant_Cylindrical" projection name below,
         // for avoiding confusion with EPSG "Equidistant Cylindrical" ellipsoidal projection.
-        PARAMETERS = builder
-            .addIdentifier(             "1029")
-            .addDeprecatedIdentifier(   "9823", "1029")  // Using deprecated parameter names
+        PARAMETERS = addIdentifierAndLegacy(builder, "1029", "9823")  // 9823 uses deprecated parameter names
             .addName(                   "Equidistant Cylindrical (Spherical)")
             .addName(                   "Plate Carrée")  // Not formally defined by EPSG, but cited in documentation.
             .addName(Citations.OGC,     "Equirectangular")

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -91,7 +91,7 @@ public final class Mercator2SP extends A
                 .rename(Citations.GEOTIFF, (String[]) null)  // "ScaleAtNatOrigin" is too specific.
                 .setRemarks(remarks).setDeprecated(true));
 
-        PARAMETERS = builder.setDeprecated(false)
+        PARAMETERS = builder
             .addIdentifier(             "9805")
             .addName(                   "Mercator (variant B)")     // Starting from EPSG version 7.6
             .addName(                   "Mercator (2SP)")           // Prior to EPSG version 7.6

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -62,11 +62,8 @@ public final class MercatorSpherical ext
                 .setRemarks(Mercator2SP.SCALE_FACTOR.getRemarks())
                 .setRequired(false));
 
-        PARAMETERS = builder
-            .addIdentifier(IDENTIFIER)
-            .addDeprecatedIdentifier("9841", IDENTIFIER)
-            .addName("Mercator (Spherical)")                                          // Starting from EPSG version 7.6
-            .addDeprecatedName("Mercator (1SP) (Spherical)", "Mercator (Spherical)")  // Prior to EPSG version 7.6
+        PARAMETERS = addNameAndLegacy(addIdentifierAndLegacy(builder, IDENTIFIER, "9841"),
+            "Mercator (Spherical)", "Mercator (1SP) (Spherical)")   // "Mercator (Spherical)" starting from EPSG version 7.6
             .createGroupForMapProjection(
                     Mercator1SP.LATITUDE_OF_ORIGIN,
                     Mercator1SP.LONGITUDE_OF_ORIGIN,

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -76,7 +76,7 @@ public final class PolarStereographicB e
         final ParameterBuilder builder = builder();
         LONGITUDE_OF_ORIGIN = createLongitude(
                 exceptEPSG(PolarStereographicA.LONGITUDE_OF_ORIGIN,
-                builder.addIdentifier("8833").addName("Longitude of origin").setDeprecated(false)));
+                builder.addIdentifier("8833").addName("Longitude of origin")));
 
         STANDARD_PARALLEL = createMandatoryLatitude(builder
                 .addIdentifier("8832").addName("Latitude of standard parallel")

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterBuilder.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -166,6 +166,18 @@ public class ParameterBuilder extends Bu
     }
 
     /**
+     * Creates a descriptor for values of the given type without domain restriction.
+     *
+     * @param  <T>          The compile-time type of the {@code valueClass} argument.
+     * @param  valueClass   The class that describe the type of the parameter values.
+     * @param  defaultValue The default value for the parameter, or {@code null} if none.
+     * @return The parameter descriptor for the given default value and unit.
+     */
+    public <T> ParameterDescriptor<T> create(final Class<T> valueClass, final T defaultValue) {
+        return create(valueClass, null, null, defaultValue);
+    }
+
+    /**
      * Creates a descriptor for floating point values without domain restriction.
      * All {@code double} values are considered valid.
      *
@@ -184,18 +196,6 @@ public class ParameterBuilder extends Bu
     }
 
     /**
-     * Creates a descriptor for values of the given type without domain restriction.
-     *
-     * @param  <T>          The compile-time type of the {@code valueClass} argument.
-     * @param  valueClass   The class that describe the type of the parameter values.
-     * @param  defaultValue The default value for the parameter, or {@code null} if none.
-     * @return The parameter descriptor for the given default value and unit.
-     */
-    public <T> ParameterDescriptor<T> create(final Class<T> valueClass, final T defaultValue) {
-        return create(valueClass, null, null, defaultValue);
-    }
-
-    /**
      * Creates a descriptor for floating point values greater than zero.
      * The zero value is not considered valid. There is no maximal value.
      *

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -26,13 +26,14 @@ import java.lang.reflect.ParameterizedTy
 import org.opengis.util.NameSpace;
 import org.opengis.util.GenericName;
 import org.opengis.util.NameFactory;
+import org.opengis.util.InternationalString;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.referencing.IdentifiedObject;
-import org.opengis.referencing.ReferenceIdentifier;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.Citations;
+import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.Deprecable;
 import org.apache.sis.util.resources.Errors;
 
@@ -40,6 +41,7 @@ import static org.apache.sis.util.Argume
 
 // Branch-dependent imports
 import org.apache.sis.internal.jdk8.JDK8;
+import org.opengis.referencing.ReferenceIdentifier;
 
 
 /**
@@ -315,18 +317,6 @@ public abstract class Builder<B extends
     }
 
     /**
-     * Creates an identifier from a string for the given authority.
-     */
-    private ReferenceIdentifier createIdentifier(final Citation authority, final String identifier) {
-        if (authority == getAuthority()) {
-            return new ImmutableIdentifier(authority, getCodeSpace(), identifier, getVersion(), null);
-        } else {
-            // Do not use the version information since it applies to the default authority rather than the given one.
-            return new ImmutableIdentifier(authority, Citations.getCodeSpace(authority), identifier);
-        }
-    }
-
-    /**
      * Converts the given name into an identifier. Note that {@link NamedIdentifier}
      * implements both {@link GenericName} and {@link Identifier} interfaces.
      */
@@ -455,24 +445,35 @@ public abstract class Builder<B extends
 
     /**
      * Adds an {@code IdentifiedObject} name given by a {@code String} or {@code InternationalString}.
-     * The given string will be combined with the authority, {@link #setCodeSpace(Citation, String) code space}
-     * and {@link #setVersion(String) version} information for creating the {@link Identifier} or {@link GenericName}
-     * object.
+     * The given string will be combined with the authority, {@linkplain #setCodeSpace(Citation, String)
+     * code space} and {@linkplain #setVersion(String) version} information for creating the
+     * {@link Identifier} or {@link GenericName} object.
      *
      * <div class="section">Name and aliases</div>
      * This method can be invoked many times. The first invocation sets the
      * {@linkplain AbstractIdentifiedObject#getName() primary name}, and
      * all subsequent invocations add an {@linkplain AbstractIdentifiedObject#getAlias() alias}.
      *
+     * <div class="section">Deprecated names</div>
+     * Some names may exist for historical reasons but have their use discouraged.
+     * If <code>{@linkplain #setDeprecated(boolean) setDeprecated}(true)</code> has been invoked, then this
+     * method creates a deprecated alias with the current {@linkplain #setRemarks(CharSequence) remarks}.
+     * The remark should suggest a replacement, for example with a sentence like
+     * <cite>"Superseded by {@literal <new-name>}"</cite>.
+     *
+     * <p>Note that deprecated names are always added as aliases, never as the primary name of an identified object.</p>
+     *
      * <p><b>Lifetime:</b>
      * the name and all aliases are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  name The {@code IdentifiedObject} name.
+     * @param  name The {@code IdentifiedObject} name as a {@link String} or {@link InternationalString} instance.
      * @return {@code this}, for method call chaining.
      */
     public B addName(final CharSequence name) {
         ensureNonNull("name", name);
-        if (JDK8.putIfAbsent(properties, IdentifiedObject.NAME_KEY, name.toString()) != null) {
+        if (isDeprecated()) {
+            aliases.add(new DeprecatedName(getAuthority(), getCodeSpace(), name, getVersion(), getRemarks()));
+        } else if (JDK8.putIfAbsent(properties, IdentifiedObject.NAME_KEY, name.toString()) != null) {
             // A primary name is already present. Add the given name as an alias instead.
             aliases.add(createName(name));
         }
@@ -509,10 +510,27 @@ public abstract class Builder<B extends
      */
     public B addName(final Citation authority, final CharSequence name) {
         ensureNonNull("name", name);
-        final NamedIdentifier identifier = new NamedIdentifier(authority, name);
-        if (JDK8.putIfAbsent(properties, IdentifiedObject.NAME_KEY, identifier) != null) {
+        final boolean isDeprecated = isDeprecated();
+        if (!isDeprecated && properties.get(IdentifiedObject.NAME_KEY) != null) {
             // A primary name is already present. Add the given name as an alias instead.
-            aliases.add(identifier);
+            aliases.add(createName(authority, name));
+        } else {
+            final String codeSpace;
+            final String version;
+            if (authority == getAuthority()) {
+                codeSpace  = getCodeSpace();
+                version    = getVersion();
+            } else {
+                // Do not use the version information since it applies to the default authority rather than the given one.
+                codeSpace = Citations.getCodeSpace(authority);
+                version   = null;
+            }
+            if (isDeprecated) {
+                aliases.add(new DeprecatedName(authority, codeSpace, name, version, getRemarks()));
+            } else {
+                properties.put(IdentifiedObject.NAME_KEY,
+                        new NamedIdentifier(authority, codeSpace, name, version, getDescription()));
+            }
         }
         return self();
     }
@@ -571,38 +589,17 @@ public abstract class Builder<B extends
     }
 
     /**
-     * Adds a deprecated name given by a {@code CharSequence}. Some objects have deprecated names for historical reasons.
-     * The deprecated name typically has a replacement, which can be given by the {@code supersededBy} argument.
-     * The later, if non-null, shall be a name specified by a previous call to an {@code addName(…)} method.
-     *
-     * <p>The given string will be combined with the authority, {@link #setCodeSpace(Citation, String) code space} and
-     * {@link #setVersion(String) version} information for creating the deprecated {@link NamedIdentifier} object.</p>
-     *
-     * <p><b>Lifetime:</b>
-     * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
-     *
-     * @param  name The {@code IdentifiedObject} deprecated name.
-     * @param  supersededBy The name to use instead of this one, or {@code null} if none.
-     * @return {@code this}, for method call chaining.
-     *
-     * @see #addDeprecatedIdentifier(String, String)
-     * @see #setDeprecated(boolean)
-     *
-     * @since 0.6
-     */
-    public B addDeprecatedName(final CharSequence name, final CharSequence supersededBy) {
-        ensureNonNull("name", name);
-        final DeprecatedName dn = new DeprecatedName(getAuthority(), getCodeSpace(), name, getVersion(), supersededBy);
-        if (JDK8.putIfAbsent(properties, IdentifiedObject.NAME_KEY, dn) != null) {
-            aliases.add(dn);
-        }
-        return self();
-    }
-
-    /**
      * Adds an {@code IdentifiedObject} identifier given by a {@code String}.
-     * The given string will be combined with the authority, {@link #setCodeSpace(Citation, String) code space}
-     * and {@link #setVersion(String) version} information for creating the {@link Identifier} object.
+     * The given string will be combined with the authority, {@linkplain #setCodeSpace(Citation, String) code space}
+     * {@linkplain #setVersion(String) version} and {@linkplain #setDescription(CharSequence) description} information
+     * for creating the {@link Identifier} object.
+     *
+     * <div class="section">Deprecated identifiers</div>
+     * Some identifiers may exist for historical reasons but have their use discouraged.
+     * If <code>{@linkplain #setDeprecated(boolean) setDeprecated}(true)</code> has been invoked, then this
+     * method creates a deprecated identifier with the current {@linkplain #setRemarks(CharSequence) remarks}.
+     * The remark should suggest a replacement, for example with a sentence like
+     * <cite>"Superseded by {@literal <new-code>}"</cite>.
      *
      * <p><b>Lifetime:</b>
      * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
@@ -612,7 +609,7 @@ public abstract class Builder<B extends
      */
     public B addIdentifier(final String identifier) {
         ensureNonNull("identifier", identifier);
-        identifiers.add(new ImmutableIdentifier(getAuthority(), getCodeSpace(), identifier, getVersion(), null));
+        addIdentifier(getAuthority(), getCodeSpace(), identifier, getVersion());
         return self();
     }
 
@@ -623,7 +620,7 @@ public abstract class Builder<B extends
      * <p><b>Lifetime:</b>
      * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  authority Bibliographic reference to the authority defining the codes, or {@code null} if none.
+     * @param  authority  Bibliographic reference to the authority defining the codes, or {@code null} if none.
      * @param  identifier The {@code IdentifiedObject} identifier as a code in the namespace of the given authority.
      * @return {@code this}, for method call chaining.
      *
@@ -631,15 +628,38 @@ public abstract class Builder<B extends
      */
     public B addIdentifier(final Citation authority, final String identifier) {
         ensureNonNull("identifier", identifier);
-        identifiers.add(createIdentifier(authority, identifier));
+        final String codeSpace;
+        final String version;
+        if (authority == getAuthority()) {
+            codeSpace  = getCodeSpace();
+            version    = getVersion();
+        } else {
+            // Do not use the version information since it applies to the default authority rather than the given one.
+            codeSpace = Citations.getCodeSpace(authority);
+            version   = null;
+        }
+        addIdentifier(authority, codeSpace, identifier, version);
         return self();
     }
 
     /**
+     * Implementation of {@link #addIdentifier(String)} and {@link #addIdentifier(Citation, String)}.
+     */
+    private void addIdentifier(final Citation authority, final String codeSpace, final String identifier, final String version) {
+        final ReferenceIdentifier id;
+        if (isDeprecated()) {
+            id = new DeprecatedCode(authority, codeSpace, identifier, version, getRemarks());
+        } else {
+            id = new ImmutableIdentifier(authority, codeSpace, identifier, version, getDescription());
+        }
+        identifiers.add(id);
+    }
+
+    /**
      * Adds an {@code IdentifiedObject} identifier fully specified by the given identifier.
-     * This method ignores the authority, {@link #setCodeSpace(Citation, String) code space} or
-     * {@link #setVersion(String) version} specified to this builder (if any), since the given
-     * identifier already contains those information.
+     * This method ignores the authority, {@linkplain #setCodeSpace(Citation, String) code space},
+     * {@linkplain #setVersion(String) version} and {@linkplain #setDescription(CharSequence) description}
+     * specified to this builder (if any), since the given identifier already contains those information.
      *
      * <p><b>Lifetime:</b>
      * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
@@ -653,35 +673,11 @@ public abstract class Builder<B extends
         return self();
     }
 
-    /**
-     * Adds a deprecated identifier given by a {@code String}. Some objects have deprecated identifiers for
-     * historical reasons. The deprecated identifier typically has a replacement, which can be given by the
-     * {@code supersededBy} argument. The later, if non-null, shall be an identifier specified by a previous
-     * call to an {@code addIdentifier(…)} method.
-     *
-     * <p>The given string will be combined with the authority, {@link #setCodeSpace(Citation, String) code space}
-     * and {@link #setVersion(String) version} information for creating the deprecated {@link Identifier} object.</p>
-     *
-     * <p><b>Lifetime:</b>
-     * all identifiers are cleared after a {@code createXXX(…)} method has been invoked.</p>
-     *
-     * @param  identifier   The {@code IdentifiedObject} deprecated identifier.
-     * @param  supersededBy The identifier to use instead of this one, or {@code null} if none.
-     * @return {@code this}, for method call chaining.
-     *
-     * @see #addDeprecatedName(CharSequence, CharSequence)
-     * @see #setDeprecated(boolean)
-     *
-     * @since 0.6
-     */
-    public B addDeprecatedIdentifier(final String identifier, final String supersededBy) {
-        ensureNonNull("identifier", identifier);
-        identifiers.add(new DeprecatedCode(getAuthority(), getCodeSpace(), identifier, getVersion(), supersededBy));
-        return self();
-    }
 
     /**
      * Returns {@code true} if the given name or identifier is deprecated.
+     *
+     * @see #isDeprecated()
      */
     private static boolean isDeprecated(final Object object) {
         return (object instanceof Deprecable) && ((Deprecable) object).isDeprecated();
@@ -805,22 +801,57 @@ public abstract class Builder<B extends
     }
 
     /**
-     * Sets the parameter description as a {@code String} or {@code InternationalString} instance.
+     * Returns the parameter description specified by the last call to {@link #setDescription(CharSequence)},
+     * or {@code null} if none.
+     */
+    private InternationalString getDescription() {
+        return (InternationalString) properties.get("description");
+    }
+
+    /**
+     * Sets an {@code Identifier} or {@code IdentifiedObject} description.
+     * Descriptions can be used in various contexts:
+     *
+     * <ul>
+     *   <li>Before calls to {@link #addIdentifier(String)} or {@link #addIdentifier(Citation, String)}
+     *       for specifying a natural language description of the meaning of the code value.
+     *
+     *       <div class="note"><b>Example:</b>
+     *       {@code setDescription("World Geodetic System 1984").addIdentifier("4326")}</div></li>
+     *
+     *   <li>Before calls to a {@code createXXX(…)} method for providing a narrative explanation
+     *       of the role of the object. Not all {@code IdentifiedObject} supports description.</li>
+     * </ul>
+     *
      * Calls to this method overwrite any previous value.
      *
      * <p><b>Lifetime:</b>
      * previous descriptions are discarded by calls to {@code setDescription(…)}.
      * Descriptions are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  description The description, or {@code null} if none.
+     * @param  description The description as a {@link String} or {@link InternationalString} instance, or {@code null} if none.
      * @return {@code this}, for method call chaining.
+     *
+     * @see ImmutableIdentifier#getDescription()
      */
     public B setDescription(final CharSequence description) {
-        properties.put("description", description);
+        /*
+         * Convert to InternationalString now in order to share the same instance if
+         * the same description is used both for an Identifier and an IdentifiedObject.
+         */
+        properties.put("description", Types.toInternationalString(description));
         return self();
     }
 
     /**
+     * Returns the remarks specified by the last call to {@link #setRemarks(CharSequence)},
+     * or {@code null} if none.
+     */
+    private InternationalString getRemarks() {
+        return (InternationalString) properties.get(IdentifiedObject.REMARKS_KEY);
+    }
+
+    /**
      * Sets remarks as a {@code String} or {@code InternationalString} instance.
      * Calls to this method overwrite any previous value.
      *
@@ -828,30 +859,40 @@ public abstract class Builder<B extends
      * previous remarks are discarded by calls to {@code setRemarks(…)}.
      * Remarks are cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
-     * @param  remarks The remarks, or {@code null} if none.
+     * @param  remarks The remarks as a {@link String} or {@link InternationalString} instance, or {@code null} if none.
      * @return {@code this}, for method call chaining.
      */
     public B setRemarks(final CharSequence remarks) {
-        properties.put(IdentifiedObject.REMARKS_KEY, remarks);
+        /*
+         * Convert to InternationalString now in order to share the same instance if
+         * the same remarks is used both for an Identifier and an IdentifiedObject.
+         */
+        properties.put(IdentifiedObject.REMARKS_KEY, Types.toInternationalString(remarks));
         return self();
     }
 
     /**
-     * Sets whether the next {@code IdentifiedObject}s to create shall be considered deprecated. Deprecated objects
-     * exist in some {@linkplain org.opengis.referencing.AuthorityFactory authority factories} like the EPSG database.
+     * Returns {@code true} if the deprecated flag is set to {@code true}.
+     */
+    private boolean isDeprecated() {
+        return Boolean.TRUE.equals(properties.get(AbstractIdentifiedObject.DEPRECATED_KEY));
+    }
+
+    /**
+     * Sets whether the next {@code Identifier} or {@code IdentifiedObject}s to create shall be considered deprecated.
+     * Deprecated objects exist in some {@linkplain org.opengis.referencing.AuthorityFactory authority factories} like
+     * the EPSG database.
      *
      * <p>Note that this method does not apply to name and identifiers, which have their own
      * {@code addDeprecatedFoo(…)} methods.</p>
      *
      * <p><b>Lifetime:</b>
-     * this property is kept unchanged until this {@code setDeprecated(…)} method is invoked again.</p>
+     * Deprecation status is cleared after a {@code createXXX(…)} method has been invoked.</p>
      *
      * @param  deprecated {@code true} if the next names, identifiers and identified objects should be
      *         considered deprecated, or {@code false} otherwise.
      * @return {@code this}, for method call chaining.
      *
-     * @see #addDeprecatedName(CharSequence, CharSequence)
-     * @see #addDeprecatedIdentifier(String, String)
      * @see AbstractIdentifiedObject#isDeprecated()
      *
      * @since 0.6
@@ -879,8 +920,9 @@ public abstract class Builder<B extends
      * }
      *
      * If {@code cleanup} is {@code true}, then this method clears the identification information
-     * (name, aliases, identifiers and remarks) for preparing the builder to the construction of
-     * an other object. The authority, codespace and version properties are not cleared by this method.
+     * (name, aliases, identifiers, description, remarks and deprecation status) for preparing the
+     * builder to the construction of an other object.
+     * The authority, codespace and version properties are not cleared by this method.
      *
      * @param cleanup {@code false} when this method is invoked before object creation, and
      *                {@code true} when this method is invoked after object creation.
@@ -893,12 +935,14 @@ public abstract class Builder<B extends
         if (cleanup) {
             properties .put(IdentifiedObject.NAME_KEY, null);
             properties .remove(IdentifiedObject.REMARKS_KEY);
+            properties .remove("description");
+            properties .remove(AbstractIdentifiedObject.DEPRECATED_KEY);
             aliases    .clear();
             identifiers.clear();
             valueAlias = null;
             valueIds   = null;
         } else {
-            valueAlias = aliases    .toArray(new GenericName[aliases.size()]);
+            valueAlias = aliases    .toArray(new GenericName[aliases    .size()]);
             valueIds   = identifiers.toArray(new ReferenceIdentifier[identifiers.size()]);
         }
         properties.put(IdentifiedObject.ALIAS_KEY,       valueAlias);

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedCode.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedCode.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedCode.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedCode.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -20,7 +20,6 @@ import org.opengis.util.InternationalStr
 import org.opengis.metadata.citation.Citation;
 import org.apache.sis.metadata.iso.ImmutableIdentifier;
 import org.apache.sis.util.Deprecable;
-import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
@@ -46,14 +45,11 @@ final class DeprecatedCode extends Immut
 
     /**
      * Creates a deprecated identifier.
-     *
-     * @param supersededBy The code that replace this one.
      */
     DeprecatedCode(final Citation authority, final String codeSpace,
-            final String code, final String version, final CharSequence supersededBy)
+            final String code, final String version, final InternationalString remarks)
     {
-        super(authority, codeSpace, code, version,
-                Vocabulary.formatInternational(Vocabulary.Keys.SupersededBy_1, supersededBy));
+        super(authority, codeSpace, code, version, remarks);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedName.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedName.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedName.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/DeprecatedName.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -18,7 +18,6 @@ package org.apache.sis.referencing;
 
 import org.opengis.util.InternationalString;
 import org.opengis.metadata.citation.Citation;
-import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
@@ -49,10 +48,10 @@ final class DeprecatedName extends Named
      * @param supersededBy The name that replace this one.
      */
     DeprecatedName(final Citation authority, final String codeSpace, final CharSequence code, final String version,
-            final CharSequence supersededBy)
+            final InternationalString remarks)
     {
         super(authority, codeSpace, code, version, null);
-        remarks = Vocabulary.formatInternational(Vocabulary.Keys.SupersededBy_1, supersededBy);
+        this.remarks = remarks;
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/TransverseMercator.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -17,9 +17,13 @@
 package org.apache.sis.referencing.operation.projection;
 
 import java.util.EnumMap;
+import org.opengis.util.FactoryException;
 import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.OperationMethod;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
 import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.internal.referencing.provider.TransverseMercatorSouth;
 import org.apache.sis.internal.util.DoubleDouble;
@@ -51,6 +55,7 @@ import static org.apache.sis.math.MathFu
  * all zones and a false northing of 10000000 metres is used for zones in the southern hemisphere.
  *
  * @author  Martin Desruisseaux (Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
  * @since   0.6
  * @version 0.6
  * @module
@@ -200,6 +205,42 @@ public class TransverseMercator extends
     }
 
     /**
+     * Creates a new projection initialized to the same parameters than the given one.
+     */
+    TransverseMercator(final TransverseMercator other) {
+        super(other);
+        h1  = other. h1;
+        h2  = other. h2;
+        h3  = other. h3;
+        h4  = other. h4;
+        ih1 = other.ih1;
+        ih2 = other.ih2;
+        ih3 = other.ih3;
+        ih4 = other.ih4;
+    }
+
+    /**
+     * 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>.
+     *
+     * <p>The non-linear part of the returned transform will be {@code this} transform, except if the ellipsoid
+     * is spherical. In the later case, {@code this} transform will be replaced by a simplified implementation.</p>
+     *
+     * @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.
+     */
+    @Override
+    public MathTransform createMapProjection(final MathTransformFactory factory) throws FactoryException {
+        TransverseMercator kernel = this;
+        if (excentricity == 0) {
+            kernel = new Spherical(this);
+        }
+        return context.completeTransform(factory, kernel);
+    }
+
+    /**
      * Converts the specified (λ,φ) coordinate (units in radians) and stores the result in {@code dstPts}.
      * In addition, opportunistically computes the projection derivative if {@code derivate} is {@code true}.
      *
@@ -212,44 +253,139 @@ public class TransverseMercator extends
                             final double[] dstPts, final int dstOff,
                             final boolean derivate) throws ProjectionException
     {
-        final double λ  = srcPts[srcOff];
-        final double φ  = srcPts[srcOff + 1];
-        final double Q  = asinh(tan(φ)) - atanh(sin(φ)*excentricity)*excentricity;
-        final double β  = atan(sinh(Q));
-
-        // TODO: sin(atan(x)) = x / sqrt(1+x²)
-        //       cos(atan(x)) = 1 / sqrt(1+x²)
-        final double η0 = atanh(cos(β) * sin(λ));
-        final double ξ0 = asin(sin(β) * cosh(η0));
+        final double λ     = srcPts[srcOff];
+        final double φ     = srcPts[srcOff + 1];
 
-        // TODO: use trigonometric identities.
-        // See ConformalProjection for example.
-        final double ξ = h4 * sin(8*ξ0) * cosh(8*η0)
-                       + h3 * sin(6*ξ0) * cosh(6*η0)
-                       + h2 * sin(4*ξ0) * cosh(4*η0)
-                       + h1 * sin(2*ξ0) * cosh(2*η0)
+        final double ℯsinφ = excentricity * sin(φ);
+        final double Q     = asinh(tan(φ)) - atanh(ℯsinφ) * excentricity;
+        final double sinλ  = sin(λ);
+        final double coshQ = cosh(Q);
+        final double η0    = atanh(sinλ / coshQ);
+
+        /*
+         * Original formula: η0 = atanh(sin(λ) * cos(β)) where
+         * cos(β) = cos(atan(sinh(Q)))
+         *        = 1 / sqrt(1 + sinh²(Q))
+         *        = 1 / (sqrt(cosh²(Q) - sinh²(Q) + sinh²(Q)))
+         *        = 1 / sqrt(cosh²(Q))
+         *        = 1 / cosh(Q)
+         *
+         * So η0 = atanh(sin(λ) / cosh(Q))
+         */
+        final double coshη0 = cosh(η0);
+        final double ξ0     = asin(tanh(Q) * coshη0);
+
+        //-- ξ0
+        final double sin_8ξ0  = sin(8*ξ0);
+        final double sin_6ξ0  = sin(6*ξ0);
+        final double sin_4ξ0  = sin(4*ξ0);
+        final double sin_2ξ0  = sin(2*ξ0);
+        final double cos_8ξ0  = cos(8*ξ0);
+        final double cos_6ξ0  = cos(6*ξ0);
+        final double cos_4ξ0  = cos(4*ξ0);
+        final double cos_2ξ0  = cos(2*ξ0);
+
+        //-- η0
+        final double cosh_8η0 = cosh(8*η0);
+        final double cosh_6η0 = cosh(6*η0);
+        final double cosh_4η0 = cosh(4*η0);
+        final double cosh_2η0 = cosh(2*η0);
+        final double sinh_8η0 = sinh(8*η0);
+        final double sinh_6η0 = sinh(6*η0);
+        final double sinh_4η0 = sinh(4*η0);
+        final double sinh_2η0 = sinh(2*η0);
+
+        /*
+         * Assuming that (λ, φ) ↦ Proj((λ, φ))
+         * where Proj is defined by: Proj((λ, φ)) : (η(λ, φ), ξ(λ, φ)).
+         *
+         * => (λ, φ) ↦ (η(λ, φ), ξ(λ, φ)).
+         */
+        //-- ξ(λ, φ)
+        final double ξ = h4 * sin_8ξ0 * cosh_8η0
+                       + h3 * sin_6ξ0 * cosh_6η0
+                       + h2 * sin_4ξ0 * cosh_4η0
+                       + h1 * sin_2ξ0 * cosh_2η0
                        + ξ0;
 
-        final double η = h4 * cos(8*ξ0) * sinh(8*η0)
-                       + h3 * cos(6*ξ0) * sinh(6*η0)
-                       + h2 * cos(4*ξ0) * sinh(4*η0)
-                       + h1 * cos(2*ξ0) * sinh(2*η0)
+        //-- η(λ, φ)
+        final double η = h4 * cos_8ξ0 * sinh_8η0
+                       + h3 * cos_6ξ0 * sinh_6η0
+                       + h2 * cos_4ξ0 * sinh_4η0
+                       + h1 * cos_2ξ0 * sinh_2η0
                        + η0;
 
         if (dstPts != null) {
             dstPts[dstOff    ] = η;
             dstPts[dstOff + 1] = ξ;
         }
+
         if (!derivate) {
             return null;
         }
 
-        // TODO: compute projection derivative.
-        return null;
+        final double cosλ          = cos(λ);                                     //-- λ
+        final double cosφ          = cos(φ);                                     //-- φ
+        final double cosh2Q        = coshQ * coshQ;                              //-- Q
+        final double sinhQ         = sinh(Q);
+        final double tanhQ         = tanh(Q);
+        final double cosh2Q_sin2λ  = cosh2Q - sinλ * sinλ;                       //-- Qλ
+        final double sinhη0        = sinh(η0);                                   //-- η0
+        final double sqrt1_thQchη0 = sqrt(1 - tanhQ * tanhQ * coshη0 * coshη0);  //-- Qη0
+
+        //-- dQ_dλ = 0;
+        final double dQ_dφ  = 1 / cosφ - excentricitySquared * cosφ / (1 - ℯsinφ * ℯsinφ);
+
+        final double dη0_dλ =   cosλ * coshQ         / cosh2Q_sin2λ;
+        final double dη0_dφ = - dQ_dφ * sinλ * sinhQ / cosh2Q_sin2λ;
+
+        final double dξ0_dλ = sinhQ * sinhη0 * cosλ / (cosh2Q_sin2λ * sqrt1_thQchη0);
+        final double dξ0_dφ = (dQ_dφ * coshη0 / cosh2Q + dη0_dφ * sinhη0 * tanhQ) / sqrt1_thQchη0;
+
+        /*
+         * Assuming that Jac(Proj((λ, φ))) is the Jacobian matrix of Proj((λ, φ)) function.
+         *
+         * So derivative Proj((λ, φ)) is defined by:
+         *                    ┌                              ┐
+         *                    │ dη(λ, φ) / dλ, dη(λ, φ) / dφ │
+         * Jac              = │                              │
+         *    (Proj(λ, φ))    │ dξ(λ, φ) / dλ, dξ(λ, φ) / dφ │
+         *                    └                              ┘
+         */
+        //-- dξ(λ, φ) / dλ
+        final double dξ_dλ = dξ0_dλ
+                           + 2 * (h1 * (dξ0_dλ * cos_2ξ0 * cosh_2η0 + dη0_dλ * sinh_2η0 * sin_2ξ0)
+                           + 3 *  h3 * (dξ0_dλ * cos_6ξ0 * cosh_6η0 + dη0_dλ * sinh_6η0 * sin_6ξ0)
+                           + 2 * (h2 * (dξ0_dλ * cos_4ξ0 * cosh_4η0 + dη0_dλ * sinh_4η0 * sin_4ξ0)
+                           + 2 *  h4 * (dξ0_dλ * cos_8ξ0 * cosh_8η0 + dη0_dλ * sinh_8η0 * sin_8ξ0)));
+
+        //-- dξ(λ, φ) / dφ
+        final double dξ_dφ = dξ0_dφ
+                           + 2 * (h1 * (dξ0_dφ * cos_2ξ0 * cosh_2η0 + dη0_dφ * sinh_2η0 * sin_2ξ0)
+                           + 3 *  h3 * (dξ0_dφ * cos_6ξ0 * cosh_6η0 + dη0_dφ * sinh_6η0 * sin_6ξ0)
+                           + 2 * (h2 * (dξ0_dφ * cos_4ξ0 * cosh_4η0 + dη0_dφ * sinh_4η0 * sin_4ξ0)
+                           + 2 *  h4 * (dξ0_dφ * cos_8ξ0 * cosh_8η0 + dη0_dφ * sinh_8η0 * sin_8ξ0)));
+
+        //-- dη(λ, φ) / dλ
+        final double dη_dλ = dη0_dλ
+                           + 2 * (h1 * (dη0_dλ * cosh_2η0 * cos_2ξ0 - dξ0_dλ * sin_2ξ0 * sinh_2η0)
+                           + 3 *  h3 * (dη0_dλ * cosh_6η0 * cos_6ξ0 - dξ0_dλ * sin_6ξ0 * sinh_6η0)
+                           + 2 * (h2 * (dη0_dλ * cosh_4η0 * cos_4ξ0 - dξ0_dλ * sin_4ξ0 * sinh_4η0)
+                           + 2 *  h4 * (dη0_dλ * cosh_8η0 * cos_8ξ0 - dξ0_dλ * sin_8ξ0 * sinh_8η0)));
+
+        //-- dη(λ, φ) / dφ
+        final double dη_dφ = dη0_dφ
+                           + 2 * (h1 * (dη0_dφ * cosh_2η0 * cos_2ξ0 - dξ0_dφ * sin_2ξ0 * sinh_2η0)
+                           + 3 *  h3 * (dη0_dφ * cosh_6η0 * cos_6ξ0 - dξ0_dφ * sin_6ξ0 * sinh_6η0)
+                           + 2 * (h2 * (dη0_dφ * cosh_4η0 * cos_4ξ0 - dξ0_dφ * sin_4ξ0 * sinh_4η0)
+                           + 2 *  h4 * (dη0_dφ * cosh_8η0 * cos_8ξ0 - dξ0_dφ * sin_8ξ0 * sinh_8η0)));
+
+        return new Matrix2(dη_dλ, dη_dφ,
+                           dξ_dλ, dξ_dφ);
     }
 
     /**
-     * Transforms the specified (η,ξ) coordinates and stores the result in {@code dstPts} (angles in radians).
+     * Transforms the specified (η, ξ) coordinates and stores the result in {@code dstPts} (angles in radians).
      *
      * @throws ProjectionException if the point can not be converted.
      */
@@ -291,4 +427,83 @@ public class TransverseMercator extends
         }
         throw new ProjectionException(Errors.Keys.NoConvergence);
     }
+
+
+    /**
+     * Provides the transform equations for the spherical case of the Transverse Mercator projection.
+     *
+     * @author  André Gosselin (MPO)
+     * @author  Martin Desruisseaux (IRD, Geomatys)
+     * @author  Rueben Schulz (UBC)
+     * @since   0.6
+     * @version 0.6
+     * @module
+     */
+    private static final class Spherical extends TransverseMercator {
+        /**
+         * For cross-version compatibility.
+         */
+        private static final long serialVersionUID = 8903592710452235162L;
+
+        /**
+         * Constructs a new map projection from the parameters of the given projection.
+         *
+         * @param other The other projection (usually ellipsoidal) from which to copy the parameters.
+         */
+        protected Spherical(final TransverseMercator other) {
+            super(other);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Matrix transform(final double[] srcPts, final int srcOff,
+                                final double[] dstPts, final int dstOff,
+                                final boolean derivate) throws ProjectionException
+        {
+            final double λ    = srcPts[srcOff];
+            final double φ    = srcPts[srcOff + 1];
+            final double sinλ = sin(λ);
+            final double cosλ = cos(λ);
+            final double sinφ = sin(φ);
+            final double cosφ = cos(φ);
+            final double tanφ = sinφ / cosφ;
+            final double B    = cosφ * sinλ;
+            /*
+             * Using Snyder's equation for calculating y, instead of the one used in Proj4.
+             * Potential problems when y and x = 90 degrees, but behaves ok in tests.
+             */
+            if (dstPts != null) {
+                dstPts[dstOff  ] = atanh(B);            // Snyder 8-1;
+                dstPts[dstOff+1] = atan2(tanφ, cosλ);   // Snyder 8-3;
+            }
+            if (!derivate) {
+                return null;
+            }
+            final double Bm  = B*B - 1;
+            final double sct = cosλ*cosλ + tanφ*tanφ;
+            return new Matrix2(-(cosφ * cosλ) / Bm,     // ∂x/∂λ
+                                (sinφ * sinλ) / Bm,     // ∂x/∂φ
+                                (tanφ * sinλ) / sct,    // ∂y/∂λ
+                         cosλ / (cosφ * cosφ * sct));   // ∂y/∂φ
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        protected void inverseTransform(final double[] srcPts, final int srcOff,
+                                        final double[] dstPts, final int dstOff)
+                throws ProjectionException
+        {
+            final double x = srcPts[srcOff  ];
+            final double y = srcPts[srcOff+1];
+            final double sinhx = sinh(x);
+            final double cosy  = cos(y);
+            // 'copySign' corrects for the fact that we made everything positive using sqrt(…)
+            dstPts[dstOff  ] = atan2(sinhx, cosy);
+            dstPts[dstOff+1] = copySign(asin(sqrt((1 - cosy*cosy) / (1 + sinhx*sinhx))), y);
+        }
+    }
 }

Modified: sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod [UTF-8] Wed Sep  9 14:43:51 2015
@@ -14,6 +14,8 @@ org.apache.sis.internal.referencing.prov
 org.apache.sis.internal.referencing.provider.LambertConformalWest
 org.apache.sis.internal.referencing.provider.LambertConformalBelgium
 org.apache.sis.internal.referencing.provider.LambertConformalMichigan
+org.apache.sis.internal.referencing.provider.TransverseMercator
+org.apache.sis.internal.referencing.provider.TransverseMercatorSouth
 org.apache.sis.internal.referencing.provider.PolarStereographicA
 org.apache.sis.internal.referencing.provider.PolarStereographicB
 org.apache.sis.internal.referencing.provider.PolarStereographicC

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/CoordinateOperationMethodsHTML.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -349,7 +349,7 @@ public final class CoordinateOperationMe
                     remarks = "Optional ";
                 } else {
                     final Comparable<?> min = param.getMinimumValue();
-                    if (min != null && min.equals(param.getMaximumValue())) {
+                    if ((min instanceof Number) && ((Number) min).doubleValue() == ((Number) param.getMaximumValue()).doubleValue()) {
                         remarks = "Unmodifiable ";
                     } else {
                         remarks = "See note ";

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/LambertConicConformalTest.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -71,7 +71,7 @@ public final strictfp class LambertConic
         a = a.multiply(new BigDecimal("57.29577951308232087679815481410517"));  // Conversion from radians to degrees.
         a = a.multiply(new BigDecimal(60 * 60));                                // Conversion from degrees to seconds.
         a = a.add     (new BigDecimal("29.2985"));                              // The standard value.
-        assertTrue(Math.abs(a.doubleValue()) < 1E-31);
+        assertTrue(abs(a.doubleValue()) < 1E-31);
     }
 
     /**

Modified: sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java
URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java?rev=1702031&r1=1702030&r2=1702031&view=diff
==============================================================================
--- sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] (original)
+++ sis/trunk/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/TransverseMercatorTest.java [UTF-8] Wed Sep  9 14:43:51 2015
@@ -18,8 +18,11 @@ package org.apache.sis.referencing.opera
 
 import org.opengis.util.FactoryException;
 import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.internal.referencing.Formulas;
 import org.apache.sis.internal.referencing.provider.TransverseMercatorSouth;
 import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.transform.CoordinateDomain;
+import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.DependsOn;
 import org.junit.Test;
 
@@ -61,7 +64,6 @@ public final strictfp class TransverseMe
      * @see org.opengis.test.referencing.ParameterizedTransformTest#testTransverseMercator()
      */
     @Test
-    @org.junit.Ignore("Missing implementation of the projection derivative.")
     public void testTransverseMercator() throws FactoryException, TransformException {
         new org.apache.sis.internal.referencing.provider.TransverseMercator();  // Test creation only, as GeoAPI 3.0 did not yet had the test method.
     }
@@ -76,18 +78,39 @@ public final strictfp class TransverseMe
      * @see org.opengis.test.referencing.ParameterizedTransformTest#testTransverseMercatorSouthOrientated()
      */
     @Test
-    @org.junit.Ignore("Missing implementation of the projection derivative.")
+    @DependsOnMethod("testTransverseMercator")
     public void testTransverseMercatorSouthOrientated() throws FactoryException, TransformException {
         new TransverseMercatorSouth();  // Test creation only, as GeoAPI 3.0 did not yet had the test method.
     }
 
     /**
+     * Verifies the consistency of elliptical formulas with the spherical formulas.
+     * This test compares the results of elliptical formulas with the spherical ones
+     * for some random points.
+     *
+     * @throws FactoryException if an error occurred while creating the map projection.
+     * @throws TransformException if an error occurred while projecting a coordinate.
+     */
+    @Test
+    @DependsOnMethod("testTransverseMercator")
+    public void compareEllipticalWithSpherical() throws FactoryException, TransformException {
+        createCompleteProjection(new org.apache.sis.internal.referencing.provider.TransverseMercator(), false,
+                  0.5,    // Central meridian
+                  2.5,    // Latitude of origin
+                  0,      // Standard parallel (none)
+                  0.997,  // Scale factor
+                200,      // False easting
+                100);     // False northing
+        tolerance = Formulas.LINEAR_TOLERANCE;
+        compareEllipticalWithSpherical(CoordinateDomain.RANGE_10, 0);
+    }
+
+    /**
      * Creates a projection and derivates a few points.
      *
      * @throws TransformException Should never happen.
      */
     @Test
-    @org.junit.Ignore("Missing implementation of the projection derivative.")
     public void testSphericalDerivative() throws TransformException {
         createNormalizedProjection(false, 0);
         tolerance = 1E-9;
@@ -105,7 +128,6 @@ public final strictfp class TransverseMe
      * @throws TransformException Should never happen.
      */
     @Test
-    @org.junit.Ignore("Missing implementation of the projection derivative.")
     public void testEllipsoidalDerivative() throws TransformException {
         createNormalizedProjection(true, 0);
         tolerance = 1E-9;