Merge branch 'geoapi-3.1'
diff --git a/application/sis-console/src/main/artifact/bin/sis b/application/sis-console/src/main/artifact/bin/sis
index ed98e33..691d163 100755
--- a/application/sis-console/src/main/artifact/bin/sis
+++ b/application/sis-console/src/main/artifact/bin/sis
@@ -16,7 +16,10 @@
 # limitations under the License.
 # ------------------------------------------------------------------------
 
-BASE_DIR="`dirname $0`/.."
+set -o errexit
+
+BASE_DIR="`readlink --canonicalize-existing $0`"
+BASE_DIR="`dirname $BASE_DIR`/.."
 SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"
 export SIS_DATA
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/Category.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/Category.java
index ce35f0d..dfb2632 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/Category.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/Category.java
@@ -69,7 +69,7 @@
  * <p>All {@code Category} objects are immutable and thread-safe.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -217,6 +217,8 @@
      *                  in the same {@link SampleDimension}.
      *                  The input is a real number in the {@code samples} range and the output shall be a unique value between
      *                  {@value MathFunctions#MIN_NAN_ORDINAL} and {@value MathFunctions#MAX_NAN_ORDINAL} inclusive.
+     * @throws IllegalSampleDimensionException if the {@code samples} range of values is empty
+     *         or the transfer function can not be used.
      */
     protected Category(final CharSequence name, NumberRange<?> samples, final MathTransform1D toUnits, final Unit<?> units,
              final DoubleToIntFunction toNaN)
@@ -237,7 +239,7 @@
          */
         if (!(minimum <= maximum)) {
             if (toUnits != null || !isNaN || doubleToRawLongBits(minimum) != doubleToRawLongBits(maximum)) {
-                throw new IllegalArgumentException(Resources.format(Resources.Keys.IllegalCategoryRange_2, name, samples));
+                throw new IllegalSampleDimensionException(Resources.format(Resources.Keys.IllegalCategoryRange_2, name, samples));
             }
         }
         if (isNaN) {
@@ -276,7 +278,7 @@
             range = samples;
             converse = new ConvertedCategory(this, toSamples, toUnits != null, units);
         } catch (TransformException e) {
-            throw new IllegalArgumentException(Resources.format(Resources.Keys.IllegalTransferFunction_1, name), e);
+            throw new IllegalSampleDimensionException(Resources.format(Resources.Keys.IllegalTransferFunction_1, name), e);
         }
     }
 
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
index 498bca6..f9cfe10 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/CategoryList.java
@@ -68,7 +68,7 @@
  * <p>Instances of {@link CategoryList} are immutable and thread-safe.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -162,7 +162,7 @@
      * @param  categories  the list of categories. This array is not cloned and is modified in-place.
      * @param  converse    if we are creating the list of categories after conversion from samples to real values,
      *                     the original list before conversion. Otherwise {@code null}.
-     * @throws IllegalArgumentException if two or more categories have overlapping sample value range.
+     * @throws IllegalSampleDimensionException if two or more categories have overlapping sample value range.
      */
     private CategoryList(final Category[] categories, CategoryList converse) {
         this.categories = categories;
@@ -229,7 +229,7 @@
             final Category previous = categories[i-1];
             final double   minimum  = minimums[i];
             if (Category.compare(minimum, previous.range.getMaxDouble(true)) <= 0) {
-                throw new IllegalArgumentException(Resources.format(Resources.Keys.CategoryRangeOverlap_4,
+                throw new IllegalSampleDimensionException(Resources.format(Resources.Keys.CategoryRangeOverlap_4,
                             previous.name, previous.getRangeLabel(),
                             category.name, category.getRangeLabel()));
             }
@@ -313,7 +313,7 @@
      * <p>This is defined as a static method for allowing the addition of a caching mechanism in the future if desired.</p>
      *
      * @param  categories  the list of categories. This array is not cloned and is modified in-place.
-     * @throws IllegalArgumentException if two or more categories have overlapping sample value range.
+     * @throws IllegalSampleDimensionException if two or more categories have overlapping sample value range.
      */
     static CategoryList create(final Category[] categories) {
         return new CategoryList(categories, null);
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/IllegalSampleDimensionException.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/IllegalSampleDimensionException.java
new file mode 100644
index 0000000..ec6003a
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/IllegalSampleDimensionException.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage;
+
+
+/**
+ * Thrown when {@link SampleDimension} can not be created.
+ * The most common cause is overlapping {@linkplain Category#getSampleRange() ranges of sample values}.
+ * This exception is caused by an illegal argument, but may happen at a later stage
+ * (for example when {@link SampleDimension.Builder#build()} is invoked).
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public class IllegalSampleDimensionException extends IllegalArgumentException {
+    /**
+     * Serial number for inter-operability with different versions.
+     */
+    private static final long serialVersionUID = 6564945210063482395L;
+
+    /**
+     * Creates an exception with no message.
+     */
+    public IllegalSampleDimensionException() {
+        super();
+    }
+
+    /**
+     * Creates an exception with the specified message.
+     *
+     * @param message  the detail message, saved for later retrieval by the {@link #getMessage()} method.
+     */
+    public IllegalSampleDimensionException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an exception with the specified message and cause.
+     *
+     * @param message  the detail message, saved for later retrieval by the {@link #getMessage()} method.
+     * @param cause    the cause, saved for later retrieval by the {@link #getCause()} method.
+     */
+    public IllegalSampleDimensionException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleDimension.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleDimension.java
index 1968bda..42d832f 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleDimension.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleDimension.java
@@ -68,7 +68,7 @@
  * not used in same time.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since 1.0
  * @module
  */
@@ -157,6 +157,7 @@
      * @param name        an identification for the sample dimension.
      * @param background  the background value, or {@code null} if none.
      * @param categories  the list of categories. May be empty if none.
+     * @throws IllegalSampleDimensionException if two or more categories have overlapping sample value range.
      */
     public SampleDimension(final GenericName name, final Number background, final Collection<? extends Category> categories) {
         ArgumentChecks.ensureNonNull("name", name);
@@ -973,6 +974,8 @@
          * Creates a new sample with the properties defined to this builder.
          *
          * @return the sample dimension.
+         * @throws IllegalSampleDimensionException if there is overlapping {@linkplain Category#getSampleRange()
+         *         ranges of sample values} or other problems that prevent the construction of sample dimensions.
          */
         public SampleDimension build() {
             GenericName name = dimensionName;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleRangeFormat.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleRangeFormat.java
index 1a7865e..e697aa0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleRangeFormat.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/SampleRangeFormat.java
@@ -35,7 +35,7 @@
  * Formats the range of a category. This is used for {@link SampleDimension#toString()} implementation.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -66,6 +66,11 @@
     private boolean hasQuantitative;
 
     /**
+     * Whether at least one category of any type has been found in at least one sample dimension.
+     */
+    private boolean hasCategory;
+
+    /**
      * The localize resources for table header. Words will be "Values", "Measures" and "Name".
      */
     private final Vocabulary words;
@@ -95,6 +100,7 @@
         numFractionDigits = new int[count];
         hasPackedValues   = false;
         hasQuantitative   = false;
+        hasCategory       = false;
         for (int i=0; i<count; i++) {
             int ndigits = 0;
             for (final Category category : dimensions[i].getCategories()) {
@@ -107,6 +113,7 @@
                 final boolean isPacked = (Double.doubleToRawLongBits(smin) != Double.doubleToRawLongBits(cmin))
                                        | (Double.doubleToRawLongBits(smax) != Double.doubleToRawLongBits(cmax));
                 hasPackedValues |= isPacked;
+                hasCategory = true;
                 /*
                  * If the sample values are already real values, pretend that they are packed in bytes.
                  * The intent is only to compute an arbitrary number of fraction digits.
@@ -209,37 +216,46 @@
         if (hasPackedValues) table.append(words.getString(Vocabulary.Keys.Values))  .nextColumn();
         if (hasQuantitative) table.append(words.getString(Vocabulary.Keys.Measures)).nextColumn();
         /* Unconditional  */ table.append(words.getString(Vocabulary.Keys.Name))    .nextLine();
-        table.nextLine('═');
-        table.append('#');                      // Dummy character to be replaced by band name later.
-        table.appendHorizontalSeparator();
-        for (final SampleDimension dim : dimensions) {
-            for (final Category category : dim.getCategories()) {
-                /*
-                 * "Sample values" column. Omitted if all values are already real values.
-                 */
-                if (hasPackedValues) {
-                    table.setCellAlignment(TableAppender.ALIGN_RIGHT);
-                    table.append(formatSample(category.getRangeLabel()));
-                    table.nextColumn();
-                }
-                table.setCellAlignment(TableAppender.ALIGN_LEFT);
-                /*
-                 * "Real values" column. Omitted if no category has a transfer function.
-                 */
-                if (hasQuantitative) {
-                    final Category converted = category.converted();
-                    final String text;
-                    if (converted.isConvertedQualitative()) {
-                        text = String.valueOf(converted.getRangeLabel());       // Example: NaN #0
-                    } else {
-                        text = formatMeasure(converted.getSampleRange());       // Example: [6.0 … 25.0)°C
-                    }
-                    table.append(text);
-                    table.nextColumn();
-                }
-                table.append(category.getName().toString(getLocale()));
+        if (!hasCategory) {
+            table.nextLine('═');
+            table.setCellAlignment(TableAppender.ALIGN_LEFT);
+            for (final SampleDimension dim : dimensions) {
+                table.append(getName(dim));
                 table.nextLine();
             }
+        } else {
+            for (final SampleDimension dim : dimensions) {
+                table.nextLine('═');
+                table.append('#');                      // Dummy character to be replaced by band name later.
+                table.appendHorizontalSeparator();
+                for (final Category category : dim.getCategories()) {
+                    /*
+                     * "Sample values" column. Omitted if all values are already real values.
+                     */
+                    if (hasPackedValues) {
+                        table.setCellAlignment(TableAppender.ALIGN_RIGHT);
+                        table.append(formatSample(category.getRangeLabel()));
+                        table.nextColumn();
+                    }
+                    table.setCellAlignment(TableAppender.ALIGN_LEFT);
+                    /*
+                     * "Real values" column. Omitted if no category has a transfer function.
+                     */
+                    if (hasQuantitative) {
+                        final Category converted = category.converted();
+                        final String text;
+                        if (converted.isConvertedQualitative()) {
+                            text = String.valueOf(converted.getRangeLabel());       // Example: NaN #0
+                        } else {
+                            text = formatMeasure(converted.getSampleRange());       // Example: [6.0 … 25.0)°C
+                        }
+                        table.append(text);
+                        table.nextColumn();
+                    }
+                    table.append(category.getName().toString(getLocale()));
+                    table.nextLine();
+                }
+            }
         }
         table.appendHorizontalSeparator();
         try {
@@ -253,44 +269,52 @@
          * a value on many cells. The following code changes some characters but do not change buffer
          * length.
          */
-        int lastDimensionEnd = 0;
-        final String lineSeparator = table.getLineSeparator();
-        final String toSearch = lineSeparator + '╞';
-        for (final SampleDimension dim : dimensions) {
-            int lineStart = buffer.indexOf(toSearch, lastDimensionEnd);
-            if (lineStart < 0) break;                                           // Should not happen.
-            lineStart += toSearch.length();
-            int i = replace(buffer, lineStart, '╪', '╧', '╡');
-            int limit = (i-2) - lineStart;                                      // Space available in a row.
-            i += lineSeparator.length() + 2;                                    // Beginning of next line.
-            /*
-             * At this point, 'i' is at the beginning of the row where to format the band name.
-             * The line above that row has been modified for removing vertical lines. Now fill
-             * the space in current row with band name and pad with white spaces.
-             */
-            final GenericName name = dim.getName();
-            String label;
-            if (name != null) {
-                label = name.toInternationalString().toString(getLocale());
-            } else {
-                label = words.getString(Vocabulary.Keys.Unnamed);
+        if (hasCategory) {
+            int lastDimensionEnd = 0;
+            final String lineSeparator = table.getLineSeparator();
+            final String toSearch = lineSeparator + '╞';
+            for (final SampleDimension dim : dimensions) {
+                int lineStart = buffer.indexOf(toSearch, lastDimensionEnd);
+                if (lineStart < 0) break;                                           // Should not happen.
+                lineStart += toSearch.length();
+                int i = replace(buffer, lineStart, '╪', '╧', '╡');
+                int limit = (i-2) - lineStart;                                      // Space available in a row.
+                i += lineSeparator.length() + 2;                                    // Beginning of next line.
+                /*
+                 * At this point, 'i' is at the beginning of the row where to format the band name.
+                 * The line above that row has been modified for removing vertical lines. Now fill
+                 * the space in current row with band name and pad with white spaces.
+                 */
+                String name = getName(dim);
+                if (name.length() > limit) {
+                    name = name.substring(0, limit);
+                }
+                limit += i;                                         // Now an absolute index instead than a length.
+                buffer.replace(i, i += name.length(), name);
+                while (i < limit) buffer.setCharAt(i++, ' ');
+                /*
+                 * At this point the sample dimension name has been written.
+                 * Update the next line and move to the next sample dimension.
+                 */
+                lastDimensionEnd = replace(buffer, i + lineSeparator.length() + 2, '┼', '┬', '┤');
             }
-            if (label.length() > limit) {
-                label = label.substring(0, limit);
-            }
-            limit += i;                                         // Now an absolute index instead than a length.
-            buffer.replace(i, i += label.length(), label);
-            while (i < limit) buffer.setCharAt(i++, ' ');
-            /*
-             * At this point the sample dimension name has been written.
-             * Update the next line and move to the next sample dimension.
-             */
-            lastDimensionEnd = replace(buffer, i + lineSeparator.length() + 2, '┼', '┬', '┤');
         }
         return buffer.toString();
     }
 
     /**
+     * Returns the localized name for the given dimension, or "unnamed" if none.
+     */
+    private String getName(final SampleDimension dim) {
+        final GenericName name = dim.getName();
+        if (name != null) {
+            return name.toInternationalString().toString(getLocale());
+        } else {
+            return words.getString(Vocabulary.Keys.Unnamed);
+        }
+    }
+
+    /**
      * Replaces characters in the given buffer until a sentinel value, which must exist.
      *
      * @param  buffer   the buffer where to perform the replacements.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/ToNaN.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/ToNaN.java
index ffee34f..e9f584e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/ToNaN.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/ToNaN.java
@@ -29,7 +29,7 @@
  * {@link Category} constructor.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -102,7 +102,7 @@
                 do if (add(--ordinal)) break search;
                 while (ordinal > MathFunctions.MIN_NAN_ORDINAL);
             }
-            throw new IllegalStateException(Resources.format(Resources.Keys.TooManyQualitatives));
+            throw new IllegalSampleDimensionException(Resources.format(Resources.Keys.TooManyQualitatives));
         }
         return ordinal;
     }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/package-info.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/package-info.java
index 6bdf0cc..2b67959 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/package-info.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/package-info.java
@@ -23,7 +23,7 @@
  * {@link org.apache.sis.coverage.grid}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/SampleRangeFormatTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/SampleRangeFormatTest.java
new file mode 100644
index 0000000..c88cd31
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/SampleRangeFormatTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.coverage;
+
+import java.util.Locale;
+import org.apache.sis.measure.Units;
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static org.apache.sis.test.Assert.*;
+
+
+/**
+ * Tests {@link SampleDimension#toString(Locale, SampleDimension...)}.
+ * Note that the formatting may change in any future version.
+ *
+ * @author  Martin Desruisseaux (IRD, Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final strictfp class SampleRangeFormatTest extends TestCase {
+    /**
+     * Creates a band for temperature data.
+     */
+    private static SampleDimension temperature(final SampleDimension.Builder builder) {
+        builder.clear();
+        return builder.setName("Temperature").addQualitative("No data", 0)
+               .addQuantitative("Value", 1, 256, 0.15, -5, Units.CELSIUS).build();
+    }
+
+    /**
+     * Creates a band for salinity data.
+     */
+    private static SampleDimension salinity(final SampleDimension.Builder builder) {
+        builder.clear();
+        return builder.setName("Salinity").addQualitative("No data", 0)
+               .addQuantitative("Value", 1, 256, 0.01, 20, Units.PSU).build();
+    }
+
+    /**
+     * Tests formatting of a single band with some categories.
+     */
+    @Test
+    public void testSingleBand() {
+        final SampleDimension.Builder builder = new SampleDimension.Builder();
+        final String text = SampleDimension.toString(Locale.US, temperature(builder));
+        assertMultilinesEquals(
+                "┌───────────┬─────────────────┬─────────┐\n" +
+                "│  Values   │    Measures     │  Name   │\n" +
+                "╞═══════════╧═════════════════╧═════════╡\n" +
+                "│ Temperature                           │\n" +
+                "├───────────┬─────────────────┬─────────┤\n" +
+                "│        0  │ NaN #0          │ No data │\n" +
+                "│ [1 … 256) │ [-4.8 … 33.4)°C │ Value   │\n" +
+                "└───────────┴─────────────────┴─────────┘\n", text);
+    }
+
+    /**
+     * Tests formatting of more than one band.
+     */
+    @Test
+    public void testMultiBands() {
+        final SampleDimension.Builder builder = new SampleDimension.Builder();
+        final String text = SampleDimension.toString(Locale.US, temperature(builder), salinity(builder));
+        assertMultilinesEquals(
+                "┌───────────┬───────────────────┬─────────┐\n" +
+                "│  Values   │     Measures      │  Name   │\n" +
+                "╞═══════════╧═══════════════════╧═════════╡\n" +
+                "│ Temperature                             │\n" +
+                "├───────────┬───────────────────┬─────────┤\n" +
+                "│        0  │ NaN #0            │ No data │\n" +
+                "│ [1 … 256) │ [-4.8 … 33.4)°C   │ Value   │\n" +
+                "╞═══════════╧═══════════════════╧═════════╡\n" +
+                "│ Salinity                                │\n" +
+                "├───────────┬───────────────────┬─────────┤\n" +
+                "│        0  │ NaN #0            │ No data │\n" +
+                "│ [1 … 256) │ [20.0 … 22.6) psu │ Value   │\n" +
+                "└───────────┴───────────────────┴─────────┘\n", text);
+    }
+
+    /**
+     * Tests formatting of sample dimensions having only qualitative categories.
+     */
+    @Test
+    public void testQualitativeOnly() {
+        final SampleDimension land = new SampleDimension.Builder().setName("Land use")
+                .addQualitative("Lake",   0)
+                .addQualitative("Forest", 1)
+                .addQualitative("Urban",  2).build();
+        final String text = SampleDimension.toString(Locale.US, land);
+        assertMultilinesEquals(
+                "┌────────┬────────┐\n" +
+                "│ Values │  Name  │\n" +
+                "╞════════╧════════╡\n" +
+                "│ Land use        │\n" +
+                "├────────┬────────┤\n" +
+                "│     0  │ Lake   │\n" +
+                "│     1  │ Forest │\n" +
+                "│     2  │ Urban  │\n" +
+                "└────────┴────────┘\n", text);
+    }
+
+    /**
+     * Tests formatting of sample dimensions having only names.
+     */
+    @Test
+    public void testNameOnly() {
+        final SampleDimension.Builder builder = new SampleDimension.Builder();
+        final SampleDimension red   = builder.setName("Red"  ).build(); builder.clear();
+        final SampleDimension green = builder.setName("Green").build(); builder.clear();
+        final SampleDimension blue  = builder.setName("Blue" ).build(); builder.clear();
+        final String text = SampleDimension.toString(Locale.US, red, green, blue);
+        assertMultilinesEquals(
+                "┌───────┐\n" +
+                "│ Name  │\n" +
+                "╞═══════╡\n" +
+                "│ Red   │\n" +
+                "│ Green │\n" +
+                "│ Blue  │\n" +
+                "└───────┘\n", text);
+    }
+}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 744eed8..72dd461 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -26,7 +26,7 @@
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.5
  * @module
  */
@@ -70,6 +70,7 @@
     org.apache.sis.coverage.CategoryTest.class,
     org.apache.sis.coverage.CategoryListTest.class,
     org.apache.sis.coverage.SampleDimensionTest.class,
+    org.apache.sis.coverage.SampleRangeFormatTest.class,
     org.apache.sis.internal.coverage.ScaledColorSpaceTest.class,
     org.apache.sis.internal.coverage.BufferedGridCoverageTest.class
 })
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractLambert.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractLambert.java
index 7c6a73c..4b73df5 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractLambert.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractLambert.java
@@ -43,12 +43,34 @@
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = Mercator1SP.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = Mercator1SP.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractMercator.java
index 5e85909..c792f94 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractMercator.java
@@ -45,12 +45,34 @@
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = Equirectangular.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = Equirectangular.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractStereographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractStereographic.java
index e2dd870..d15a4a9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractStereographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AbstractStereographic.java
@@ -44,12 +44,34 @@
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = Mercator1SP.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = Mercator1SP.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
index 33058d1..fcb6d03 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/AlbersEqualArea.java
@@ -48,12 +48,34 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of false origin</cite> (φf) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_center </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of false origin</cite> (λf) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> longitude_of_center </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_central_meridian </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_FALSE_ORIGIN;
 
@@ -61,6 +83,21 @@
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is the value given to the {@link #LATITUDE_OF_FALSE_ORIGIN}
      * parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 1st standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_1 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1;
 
@@ -68,18 +105,54 @@
      * The operation parameter descriptor for the <cite>Latitude of 2nd standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is the value given to the {@link #STANDARD_PARALLEL_1}
      * parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 2nd standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_2 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_2 </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel2 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_2 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2;
 
     /**
      * The operation parameter descriptor for the <cite>Easting at false origin</cite> (Ef) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Easting at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> EASTING_AT_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Northing at false origin</cite> (Nf) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Northing at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> NORTHING_AT_FALSE_ORIGIN;
 
@@ -97,8 +170,9 @@
          * GeoTIFF: NatOriginLat
          */
         LATITUDE_OF_FALSE_ORIGIN = createLatitude(
-                renameAlias(LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN, Citations.GEOTIFF, Mercator1SP.LATITUDE_OF_ORIGIN, builder)
-                .rename(Citations.OGC, "latitude_of_center"), true);
+                renameAlias(builder,   LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN,    // Copy from this parameter…
+                            Citations.GEOTIFF, Mercator1SP.LATITUDE_OF_ORIGIN)          // … except for this name.
+                    .rename(Citations.OGC, "latitude_of_center"), true);
         /*
          * EPSG:    Longitude of false origin
          * OGC:     longitude_of_center
@@ -107,8 +181,9 @@
          * GeoTIFF: NatOriginLong
          */
         LONGITUDE_OF_FALSE_ORIGIN = createLongitude(
-                renameAlias(LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN, Citations.GEOTIFF, Mercator1SP.LONGITUDE_OF_ORIGIN, builder)
-                .rename(Citations.OGC, "longitude_of_center"));
+                renameAlias(builder,   LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN,
+                            Citations.GEOTIFF, Mercator1SP.LONGITUDE_OF_ORIGIN)
+                    .rename(Citations.OGC, "longitude_of_center"));
         /*
          * EPSG:    Latitude of 1st standard parallel
          * OGC:     standard_parallel_1
@@ -137,7 +212,8 @@
          * GeoTIFF: FalseEasting
          */
         EASTING_AT_FALSE_ORIGIN = createShift(
-                renameAlias(LambertConformal2SP.EASTING_AT_FALSE_ORIGIN, Citations.GEOTIFF, LambertConformal1SP.FALSE_EASTING, builder));
+                renameAlias(builder, LambertConformal2SP.EASTING_AT_FALSE_ORIGIN,
+                            Citations.GEOTIFF, LambertConformal1SP.FALSE_EASTING));
         /*
          * EPSG:    Northing at false origin
          * OGC:     false_northing
@@ -146,7 +222,8 @@
          * GeoTIFF: FalseNorthing
          */
         NORTHING_AT_FALSE_ORIGIN = createShift(
-                renameAlias(LambertConformal2SP.NORTHING_AT_FALSE_ORIGIN, Citations.GEOTIFF, LambertConformal1SP.FALSE_NORTHING, builder));
+                renameAlias(builder, LambertConformal2SP.NORTHING_AT_FALSE_ORIGIN,
+                            Citations.GEOTIFF, LambertConformal1SP.FALSE_NORTHING));
 
         PARAMETERS = builder
                 .addIdentifier(             "9822")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
index 7510166..3ee4617 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ESRI.java
@@ -38,36 +38,84 @@
     /**
      * The operation parameter descriptor for the <cite>Longitude of origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> CENTRAL_MERIDIAN;
 
     /**
      * The operation parameter descriptor for the <cite>Latitude of origin</cite> (φ₀) parameter value.
      * Valid values range is (-90 … 90)° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_1 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> STANDARD_PARALLEL_1;
 
     /**
      * The operation parameter descriptor for the <cite>Latitude of 2nd standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_2 </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_2 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_2 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> STANDARD_PARALLEL_2;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> FALSE_NORTHING;
     static {
@@ -97,8 +145,8 @@
      * @return the given {@code builder}, for method call chaining.
      */
     static ParameterBuilder copyNames(final ParameterBuilder builder, final ParameterDescriptor<Double> template) {
-        return builder.addName(MapProjection.sameNameAs(Citations.ESRI,  template))
-                      .addName(MapProjection.sameNameAs(Citations.OGC,   template))
-                      .addName(MapProjection.sameNameAs(Citations.PROJ4, template));
+        return builder.addNameAndIdentifier(Citations.ESRI,  template)
+                      .addNameAndIdentifier(Citations.OGC,   template)
+                      .addNameAndIdentifier(Citations.PROJ4, template);
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
index 941d45c..b145716 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Equirectangular.java
@@ -91,6 +91,21 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> (φ₁) parameter value.
      * Valid values range is (-90 … 90)° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 1st standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_ts </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (-90.0 … 90.0)°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL;
 
@@ -100,24 +115,73 @@
      *
      * <p>In theory, this parameter should not be used and its value should be 0 in all cases.
      * This parameter is included for completeness in CRS labeling only, and is declared optional.</p>
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value restricted to 0</li>
+     *   <li>Optional</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
index 32a0734..453fa3c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
@@ -138,6 +138,16 @@
 
     /**
      * The operation parameter descriptor for the <cite>Geocentric translation file</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Geocentric translation file </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Default value: {@code gr3df97a.txt}</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Path> FILE;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
index 262ea25..501236a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffine.java
@@ -79,6 +79,13 @@
      * The operation parameter descriptor for the <cite>X-axis translation</cite>
      * ({@linkplain BursaWolfParameters#tX tX}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> X-axis translation </td></tr>
+     *   <tr><td> OGC:     </td><td> dx </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> TX;
 
@@ -86,6 +93,13 @@
      * The operation parameter descriptor for the <cite>Y-axis translation</cite>
      * ({@linkplain BursaWolfParameters#tY tY}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Y-axis translation </td></tr>
+     *   <tr><td> OGC:     </td><td> dy </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> TY;
 
@@ -93,6 +107,13 @@
      * The operation parameter descriptor for the <cite>Z-axis translation</cite>
      * ({@linkplain BursaWolfParameters#tZ tZ}) parameter value. Valid values range
      * from negative to positive infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Z-axis translation </td></tr>
+     *   <tr><td> OGC:     </td><td> dz </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> TZ;
 
@@ -100,6 +121,17 @@
      * The operation parameter descriptor for the <cite>X-axis rotation</cite>
      * ({@linkplain BursaWolfParameters#rX rX}) parameter value.
      * Units are {@linkplain Units#ARC_SECOND arc-seconds}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> X-axis rotation </td></tr>
+     *   <tr><td> OGC:     </td><td> ex </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-648000.0 … 648000.0]″</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> RX;
 
@@ -107,6 +139,17 @@
      * The operation parameter descriptor for the <cite>Y-axis rotation</cite>
      * ({@linkplain BursaWolfParameters#rY rY}) parameter value.
      * Units are {@linkplain Units#ARC_SECOND arc-seconds}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Y-axis rotation </td></tr>
+     *   <tr><td> OGC:     </td><td> ey </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-648000.0 … 648000.0]″</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> RY;
 
@@ -114,6 +157,17 @@
      * The operation parameter descriptor for the <cite>Z-axis rotation</cite>
      * ({@linkplain BursaWolfParameters#rZ rZ}) parameter value.
      * Units are {@linkplain Units#ARC_SECOND arc-seconds}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Z-axis rotation </td></tr>
+     *   <tr><td> OGC:     </td><td> ez </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-648000.0 … 648000.0]″</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> RZ;
 
@@ -122,6 +176,13 @@
      * ({@linkplain BursaWolfParameters#dS dS}) parameter value.
      * Valid values range from negative to positive infinity.
      * Units are {@linkplain Units#PPM parts per million}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale difference </td></tr>
+     *   <tr><td> OGC:     </td><td> ppm </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> DS;
     static {
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
index e024721..5c9d580 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeocentricAffineBetweenGeographic.java
@@ -64,30 +64,86 @@
      * of user's parameters in {@link #createMathTransform(MathTransformFactory, ParameterValueGroup)}.</p>
      *
      * @see GeographicToGeocentric#DIMENSION
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> dim </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [2…3]</li>
+     *   <li>No default value</li>
+     *   <li>Optional</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Integer> DIMENSION;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> src_semi_major </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> SRC_SEMI_MAJOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> src_semi_minor </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> SRC_SEMI_MINOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> tgt_semi_major </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> TGT_SEMI_MAJOR;
 
     /**
      * The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
      * Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> tgt_semi_minor </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> TGT_SEMI_MINOR;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Geographic2Dto3D.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Geographic2Dto3D.java
index 0a6c624..9083d76 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Geographic2Dto3D.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Geographic2Dto3D.java
@@ -56,6 +56,12 @@
 
     /**
      * The ellipsoidal height to set.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> height </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> HEIGHT;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicAndVerticalOffsets.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicAndVerticalOffsets.java
index 0becf83..9701333 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicAndVerticalOffsets.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicAndVerticalOffsets.java
@@ -53,6 +53,12 @@
      * The operation parameter descriptor for the <cite>"Geoid undulation"</cite> parameter value.
      *
      * @see #TZ
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Geoid undulation </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> TH;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java
index b6e27e0..ec3470c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicOffsets.java
@@ -48,16 +48,34 @@
 
     /**
      * The operation parameter descriptor for the <cite>"Longitude offset"</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude offset </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> TX;
 
     /**
      * The operation parameter descriptor for the <cite>"Latitude offset"</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude offset </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> TY;
 
     /**
      * The operation parameter descriptor for the <cite>"Vertical Offset"</cite> parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Vertical Offset </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> TZ;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
index d605e4c..97c283e 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/GeographicToGeocentric.java
@@ -70,6 +70,18 @@
      * </ul>
      *
      * @see GeocentricAffineBetweenGeographic#DIMENSION
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> dim </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [2…3]</li>
+     *   <li>Default value: 3</li>
+     *   <li>Optional</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Integer> DIMENSION;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal1SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal1SP.java
index 5a76fa9..9cb8a97 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal1SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal1SP.java
@@ -50,18 +50,55 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values range is [-90 … 90]°. There is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN = Mercator1SP.LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor at natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> scale_factor_at_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtNatOrigin </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR = Mercator1SP.SCALE_FACTOR;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
index 86d4f0e..5d895ed 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformal2SP.java
@@ -51,12 +51,34 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of false origin</cite> (φf) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of false origin</cite> (λf) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_central_meridian </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_FALSE_ORIGIN;
 
@@ -64,6 +86,21 @@
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is the value given to the {@link #LATITUDE_OF_FALSE_ORIGIN}
      * parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 1st standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_1 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1;
 
@@ -71,18 +108,54 @@
      * The operation parameter descriptor for the <cite>Latitude of 2nd standard parallel</cite> parameter value.
      * Valid values range is [-90 … 90]° and default value is the value given to the {@link #STANDARD_PARALLEL_2}
      * parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 2nd standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_2 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_2 </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel2 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_2 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2;
 
     /**
      * The operation parameter descriptor for the <cite>Easting at false origin</cite> (Ef) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Easting at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> EASTING_AT_FALSE_ORIGIN = RegionalMercator.EASTING_AT_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Northing at false origin</cite> (Nf) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Northing at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> NORTHING_AT_FALSE_ORIGIN = RegionalMercator.NORTHING_AT_FALSE_ORIGIN;
 
@@ -164,7 +237,7 @@
                 .addName(Citations.NETCDF,   "LambertConformal")
                 .addName(Citations.GEOTIFF,  "CT_LambertConfConic_2SP")
                 .addName(Citations.GEOTIFF,  "CT_LambertConfConic")
-                .addName(sameNameAs(Citations.PROJ4, LambertConformal1SP.PARAMETERS))
+                .addNameAndIdentifier(Citations.PROJ4, LambertConformal1SP.PARAMETERS)
                 .addIdentifier(Citations.GEOTIFF,  "8")
                 .addIdentifier(Citations.MAP_INFO, "3")
                 .addIdentifier(Citations.S57,      "6")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalMichigan.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalMichigan.java
index 6a01228..0716f30 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalMichigan.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalMichigan.java
@@ -46,6 +46,16 @@
     /**
      * The operation parameter descriptor for the <cite>Ellipsoid scaling factor</cite> (EPSG:1038)
      * parameter value. Valid values range is (0 … ∞) and there is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Ellipsoid scaling factor </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalWest.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalWest.java
index 49c264f..08636cc 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalWest.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertConformalWest.java
@@ -57,6 +57,17 @@
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * In the case of West Orientated projection, despite its EPSG name this parameter is actually
      * <cite>False westing</cite> (FW)
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     static final ParameterDescriptor<Double> FALSE_WESTING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertCylindricalEqualArea.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertCylindricalEqualArea.java
index 8cbad39..5719dd6 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertCylindricalEqualArea.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/LambertCylindricalEqualArea.java
@@ -47,24 +47,72 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> (φ₁) parameter value.
      * Valid values range is (-90 … 90)° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 1st standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_ts </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (-90.0 … 90.0)°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL = Equirectangular.STANDARD_PARALLEL;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN = Mercator1SP.LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = Equirectangular.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = Equirectangular.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
index 9386a88..585c0f2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjection.java
@@ -31,11 +31,11 @@
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.ParameterNotFoundException;
-import org.opengis.referencing.ReferenceIdentifier;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.opengis.referencing.operation.Projection;
+import org.opengis.referencing.ReferenceIdentifier;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.measure.MeasurementRange;
@@ -76,6 +76,21 @@
      * This parameter is mandatory and has no default value. The range of valid values is (0 … ∞).
      *
      * <p>Some names for this parameter are {@code "semi_major"}, {@code "SemiMajorAxis"} and {@code "a"}.</p>
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> semi_major </td></tr>
+     *   <tr><td> ESRI:    </td><td> Semi_Major </td></tr>
+     *   <tr><td> NetCDF:  </td><td> semi_major_axis </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> SemiMajorAxis </td></tr>
+     *   <tr><td> Proj4:   </td><td> a </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final DefaultParameterDescriptor<Double> SEMI_MAJOR;
 
@@ -84,12 +99,38 @@
      * This parameter is mandatory and has no default value. The range of valid values is (0 … ∞).
      *
      * <p>Some names for this parameter are {@code "semi_minor"}, {@code "SemiMinorAxis"} and {@code "b"}.</p>
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> semi_minor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Semi_Minor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> semi_minor_axis </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> SemiMinorAxis </td></tr>
+     *   <tr><td> Proj4:   </td><td> b </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) m</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final DefaultParameterDescriptor<Double> SEMI_MINOR;
 
     /**
      * The ellipsoid eccentricity, computed from the semi-major and semi-minor axis lengths.
      * This a SIS-specific parameter used mostly for debugging purpose.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> eccentricity </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [0.0 … 1.0]</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     @Debug
     public static final DefaultParameterDescriptor<Double> ECCENTRICITY;
@@ -268,8 +309,12 @@
     /**
      * Returns the name of the given authority declared in the given parameter descriptor.
      * This method is used only as a way to avoid creating many instances of the same name.
+     *
+     * @param  authority   the authority for which to get the name.
+     * @param  parameters  where to get name for the given authority.
+     * @throws NoSuchElementException if the given authority has not been found.
      */
-    static GenericName sameNameAs(final Citation authority, final GeneralParameterDescriptor parameters) {
+    private static GenericName sameNameAs(final Citation authority, final GeneralParameterDescriptor parameters) {
         for (final GenericName candidate : parameters.getAlias()) {
             if (candidate instanceof Identifier && ((Identifier) candidate).getAuthority() == authority) {
                 return candidate;
@@ -282,37 +327,80 @@
      * Copies name, aliases and identifiers of the given {@code template}, except the alias and identifiers of the
      * given authority which are replaced by the alias and identifiers of the same authority in {@code replacement}.
      *
+     * @param  builder      an initially clean builder where to add the names and identifiers.
      * @param  template     the parameter from which to copy names and identifiers.
      * @param  toRename     authority of the alias to rename.
      * @param  replacement  the parameter from which to get the new name for the alias to rename.
-     * @param  builder      an initially clean builder where to add the names and identifiers.
      * @return the given {@code builder}, for method call chaining.
      *
      * @since 0.8
      */
-    static ParameterBuilder renameAlias(final ParameterDescriptor<Double> template, final Citation toRename,
-            final ParameterDescriptor<Double> replacement, final ParameterBuilder builder)
+    static ParameterBuilder renameAlias(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+                                        final Citation toRename, final ParameterDescriptor<Double> replacement)
+    {
+        renameAliases(builder, template, new Citation[] {toRename}, new ParameterDescriptor<?>[] {replacement});
+        return builder;
+    }
+
+    /**
+     * Same as above {@link #renameAlias(ParameterBuilder, ParameterDescriptor, Citation, ParameterDescriptor)
+     * renameAlias(…)} but with two aliases to rename.
+     *
+     * @param  builder      an initially clean builder where to add the names and identifiers.
+     * @param  template     the parameter from which to copy names and identifiers.
+     * @param  s1           authority of the first alias to rename.
+     * @param  r1           the parameter from which to get the new name for the first alias to rename.
+     * @param  s2           authority of the second alias to rename.
+     * @param  r2           the parameter from which to get the new name for the second alias to rename.
+     * @return the given {@code builder}, for method call chaining.
+     *
+     * @since 1.1
+     */
+    static ParameterBuilder renameAlias(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+                                        final Citation s1, final ParameterDescriptor<Double> r1,
+                                        final Citation s2, final ParameterDescriptor<Double> r2)
+    {
+        renameAliases(builder, template, new Citation[] {s1, s2}, new ParameterDescriptor<?>[] {r1, r2});
+        return builder;
+    }
+
+    /**
+     * Implementation of {@code renameAlias(…)} methods.
+     */
+    private static void renameAliases(final ParameterBuilder builder, final ParameterDescriptor<Double> template,
+            final Citation[] toRename, final ParameterDescriptor<?>[] replacement)
     {
         builder.addName(template.getName());
-        GenericName newName = sameNameAs(toRename, replacement);
-        ReferenceIdentifier newCode = (ReferenceIdentifier) IdentifiedObjects.getIdentifier(replacement, toRename);
+        final GenericName[] newNames = new GenericName[toRename.length];
+        final Identifier[]  newCodes = new Identifier [toRename.length];
+        for (int i=0; i<toRename.length; i++) {
+            newNames[i] = sameNameAs(toRename[i], replacement[i]);
+            newCodes[i] = IdentifiedObjects.getIdentifier(replacement[i], toRename[i]);
+        }
         for (GenericName alias : template.getAlias()) {
-            if (((Identifier) alias).getAuthority() == toRename) {
-                if (newName == null) continue;
-                alias = newName;
-                newName = null;
+            final Citation authority = ((Identifier) alias).getAuthority();
+            for (int i=0; i<toRename.length; i++) {
+                if (authority == toRename[i]) {
+                    if (newNames[i] == null) continue;
+                    alias = newNames[i];
+                    newNames[i] = null;
+                    break;
+                }
             }
             builder.addName(alias);
         }
         for (ReferenceIdentifier id : template.getIdentifiers()) {
-            if (id.getAuthority() == toRename) {
-                if (newCode == null) continue;
-                id = newCode;
-                newCode = null;
+            final Citation authority = id.getAuthority();
+            for (int i=0; i<toRename.length; i++) {
+                if (authority == toRename[i]) {
+                    if (newCodes[i] == null) continue;
+                    id = (ReferenceIdentifier) newCodes[i];
+                    newCodes[i] = null;
+                    break;
+                }
             }
             builder.addIdentifier(id);
         }
-        return builder;
     }
 
     /**
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java
index 0c7f3a3..d84fd0e 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator1SP.java
@@ -48,18 +48,55 @@
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * In theory, this parameter should not be used and its value should be 0 in all cases.
      * This parameter is included in the EPSG dataset for completeness in CRS labeling only.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value restricted to 0</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor at natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> scale_factor_at_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtNatOrigin </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
index 99ff3c3..5529538 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mercator2SP.java
@@ -53,6 +53,21 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite> (φ₁) parameter value.
      * Valid values range is (-90 … 90)° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of 1st standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_ts </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (-90.0 … 90.0)°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL = Equirectangular.STANDARD_PARALLEL;
 
@@ -64,6 +79,18 @@
      * projection. Nevertheless we declare it is as an optional parameter because it is sometime used in Well
      * Known Text (WKT). However it shall be interpreted as a <cite>Scale factor at the standard parallel</cite>
      * rather than at the natural origin.</p>
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Optional</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> SCALE_FACTOR;
 
@@ -105,7 +132,7 @@
                 .addName(Citations.OGC,     "Mercator_2SP")
                 .addName(Citations.ESRI,    "Mercator")
                 .addName(Citations.NETCDF,  "Mercator")
-                .addName(sameNameAs(Citations.PROJ4, Mercator1SP.PARAMETERS))
+                .addNameAndIdentifier(Citations.PROJ4, Mercator1SP.PARAMETERS)
                 .addIdentifier(Citations.MAP_INFO, "26")    // MapInfo names this projection "Regional Mercator".
                 .addIdentifier(Citations.S57,       "8")
                 .createGroupForMapProjection(
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
index 64cd5f5..8ef6651 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MercatorSpherical.java
@@ -67,7 +67,7 @@
 
         PARAMETERS = addNameAndLegacy(addIdentifierAndLegacy(builder, IDENTIFIER, "9841"),
                 "Mercator (Spherical)", "Mercator (1SP) (Spherical)")   // "Mercator (Spherical)" starting from EPSG version 7.6
-                .addName(sameNameAs(Citations.PROJ4, Mercator1SP.PARAMETERS))
+                .addNameAndIdentifier(Citations.PROJ4, Mercator1SP.PARAMETERS)
                 .createGroupForMapProjection(
                         Mercator1SP.LATITUDE_OF_ORIGIN,
                         Mercator1SP.LONGITUDE_OF_ORIGIN,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mollweide.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mollweide.java
index 68861a1..ca2abb2 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mollweide.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Mollweide.java
@@ -48,18 +48,42 @@
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN = ESRI.CENTRAL_MERIDIAN;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = ESRI.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = ESRI.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
index d415281..7bb3083 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Molodensky.java
@@ -77,14 +77,34 @@
      * optional parameter value. This parameter is defined by the EPSG database and can be used
      * in replacement of {@link #TGT_SEMI_MAJOR}.
      * Units are {@linkplain Units#METRE metres}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Semi-major axis length difference </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> AXIS_LENGTH_DIFFERENCE;
 
     /**
-     * The operation parameter descriptor for the <cite>Flattening difference</cite> optional
-     * parameter value. This parameter is defined by the EPSG database and can be used in
-     * replacement of {@link #TGT_SEMI_MINOR}.
+     * The operation parameter descriptor for the <cite>Flattening difference</cite> optional parameter value.
+     * This parameter is defined by the EPSG database and can be used in replacement of {@link #TGT_SEMI_MINOR}.
      * Valid values range from -1 to +1, {@linkplain Units#UNITY dimensionless}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Flattening difference </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-1.0 … 1.0]</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> FLATTENING_DIFFERENCE;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
index ee02a22..67e9823 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NADCON.java
@@ -73,12 +73,32 @@
     /**
      * The operation parameter descriptor for the <cite>"Latitude difference file"</cite> parameter value.
      * The default value is {@code "conus.las"}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude difference file </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Default value: {@code conus.las}</li>
+     * </ul>
      */
     private static final ParameterDescriptor<Path> LATITUDE;
 
     /**
      * The operation parameter descriptor for the <cite>"Longitude difference file"</cite> parameter value.
      * The default value is {@code "conus.los"}.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude difference file </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Default value: {@code conus.los}</li>
+     * </ul>
      */
     private static final ParameterDescriptor<Path> LONGITUDE;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
index ba3c018..c65afec 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
@@ -75,6 +75,16 @@
     /**
      * The operation parameter descriptor for the <cite>"Latitude and longitude difference file"</cite> parameter value.
      * The file extension is typically {@code ".gsb"}. There is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude and longitude difference file </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     private static final ParameterDescriptor<Path> FILE;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
index 8f65451..323cddd 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercator.java
@@ -33,7 +33,7 @@
  *
  * @author  Rueben Schulz (UBC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see <a href="http://geotiff.maptools.org/proj_list/hotine_oblique_mercator.html">GeoTIFF parameters for Hotine Oblique Mercator</a>
  *
@@ -56,30 +56,94 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of projection centre</cite> (φc) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of projection centre </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_center </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Center </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (-90.0 … 90.0)°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_CENTRE;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of projection centre</cite> (λc) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of projection centre </td></tr>
+     *   <tr><td> OGC:     </td><td> longitude_of_center </td></tr>
+     *   <tr><td> ESRI:    </td><td> Longitude_Of_Center </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lonc </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_CENTRE;
 
     /**
      * The operation parameter descriptor for the <cite>Azimuth of initial line</cite> (α) parameter value.
      * Valid values ranges are [-360 … -270]°, [-90 … 90]° and [270 … 360]°. There is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Azimuth of initial line </td></tr>
+     *   <tr><td> OGC:     </td><td> azimuth </td></tr>
+     *   <tr><td> ESRI:    </td><td> Azimuth </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> AzimuthAngle </td></tr>
+     *   <tr><td> Proj4:   </td><td> alpha </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-360.0 … 360.0]°</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> AZIMUTH;
 
     /**
      * The operation parameter descriptor for the <cite>Angle from rectified to skew grid</cite> (γ) parameter value.
      * Valid values range is [-360 … 360]° and default value is the value given to the {@link #AZIMUTH} parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Angle from Rectified to Skew Grid </td></tr>
+     *   <tr><td> OGC:     </td><td> rectified_grid_angle </td></tr>
+     *   <tr><td> ESRI:    </td><td> XY_Plane_Rotation </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> RectifiedGridAngle </td></tr>
+     *   <tr><td> Proj4:   </td><td> gamma </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: [-360.0 … 360.0]°</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> RECTIFIED_GRID_ANGLE;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor on initial line</cite> (k) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor on initial line </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtCenter </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR;
 
@@ -90,18 +154,21 @@
     static {
         final ParameterBuilder builder = builder();
 
-        LATITUDE_OF_CENTRE = createLatitude(builder.addNamesAndIdentifiers(AlbersEqualArea.LATITUDE_OF_FALSE_ORIGIN)
-                .reidentify(Citations.EPSG, "8811")
-                .rename    (Citations.EPSG, "Latitude of projection centre")
-                .addName   (Citations.ESRI, "Latitude_Of_Center")
-                .rename    (Citations.NETCDF), false);                  // Remove the netCDF name.
+        LATITUDE_OF_CENTRE = createLatitude(builder
+                .addIdentifier("8811")
+                .addName("Latitude of projection centre")
+                .addNameAndIdentifier(Citations.OGC,     AlbersEqualArea.LATITUDE_OF_FALSE_ORIGIN)
+                .addName             (Citations.ESRI,   "Latitude_Of_Center")
+                .addNameAndIdentifier(Citations.GEOTIFF, Equirectangular.LATITUDE_OF_ORIGIN)
+                .addNameAndIdentifier(Citations.PROJ4,   Equirectangular.LATITUDE_OF_ORIGIN), false);
 
-        LONGITUDE_OF_CENTRE = createLongitude(builder.addNamesAndIdentifiers(AlbersEqualArea.LONGITUDE_OF_FALSE_ORIGIN)
-                .reidentify(Citations.EPSG,  "8812")
-                .rename    (Citations.EPSG,  "Longitude of projection centre")
-                .addName   (Citations.ESRI,  "Longitude_Of_Center")
-                .rename    (Citations.PROJ4, "lonc")
-                .rename    (Citations.NETCDF));                         // Remove the netCDF name.
+        LONGITUDE_OF_CENTRE = createLongitude(builder
+                .addIdentifier("8812")
+                .addName("Longitude of projection centre")
+                .addNameAndIdentifier(Citations.OGC,     AlbersEqualArea.LONGITUDE_OF_FALSE_ORIGIN)
+                .addName             (Citations.ESRI,    "Longitude_Of_Center")
+                .addNameAndIdentifier(Citations.GEOTIFF, Equirectangular.LONGITUDE_OF_ORIGIN)
+                .addName             (Citations.PROJ4,   "lonc"));
 
         AZIMUTH = builder
                 .addIdentifier("8813")
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercatorCenter.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercatorCenter.java
index ed517e7..2dbc218 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercatorCenter.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueMercatorCenter.java
@@ -56,12 +56,32 @@
     /**
      * The operation parameter descriptor for the <cite>Easting at projection centre</cite> (Ec) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Easting at projection centre </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> EASTING_AT_CENTRE;
 
     /**
      * The operation parameter descriptor for the <cite>Northing at projection centre</cite> (Nc) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Northing at projection centre </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> NORTHING_AT_CENTRE;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueStereographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueStereographic.java
index 7312eb3..c453bd6 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueStereographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ObliqueStereographic.java
@@ -47,18 +47,51 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = TransverseMercator.LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN = Mercator1SP.LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor at natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> scale_factor_at_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtNatOrigin </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR = Mercator1SP.SCALE_FACTOR;    // Same as PolarStereographicA.
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java
new file mode 100644
index 0000000..41fe49c
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Orthographic.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.referencing.provider;
+
+import javax.xml.bind.annotation.XmlTransient;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.referencing.operation.PlanarProjection;
+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.projection.NormalizedProjection;
+
+import static org.apache.sis.internal.referencing.provider.AbstractProvider.builder;
+
+
+/**
+ * The provider for <cite>"Orthographic"</cite> projection (EPSG:9840).
+ *
+ * @author  Rueben Schulz (UBC)
+ * @author  Martin Desruisseaux (Geomatys)
+ *
+ * @see <a href="http://geotiff.maptools.org/proj_list/orthographic.html">GeoTIFF parameters for Orthographic</a>
+ *
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+@XmlTransient
+public class Orthographic extends MapProjection {
+    /**
+     * For compatibility with different versions during deserialization.
+     */
+    private static final long serialVersionUID = -8669271590570783071L;
+
+    /**
+     * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
+     * Valid values can be -90° or 90° only. There is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Center </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     */
+    public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
+
+    /**
+     * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
+     * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Longitude_Of_Center </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> CenterLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
+     */
+    public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
+
+    /**
+     * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
+     * Valid values range is (0 … ∞) and default value is 1. This is not formally a parameter of this projection.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Optional</li>
+     * </ul>
+     */
+    public static final ParameterDescriptor<Double> SCALE_FACTOR = Mercator2SP.SCALE_FACTOR;
+
+    /**
+     * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
+     * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
+     */
+    public static final ParameterDescriptor<Double> FALSE_EASTING = LambertConformal1SP.FALSE_EASTING;
+
+    /**
+     * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
+     * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
+     */
+    public static final ParameterDescriptor<Double> FALSE_NORTHING = LambertConformal1SP.FALSE_NORTHING;
+
+    /**
+     * The group of all parameters expected by this coordinate operation.
+     */
+    static final ParameterDescriptorGroup PARAMETERS;
+    static {
+        final ParameterBuilder builder = builder();
+
+        LATITUDE_OF_ORIGIN = createLatitude(
+                renameAlias(builder,       LambertConformal1SP.LATITUDE_OF_ORIGIN,          // Copy from this parameter…
+                            Citations.ESRI,    ObliqueMercator.LATITUDE_OF_CENTRE,          // … except for this name.
+                            Citations.GEOTIFF, ObliqueMercator.LATITUDE_OF_CENTRE), true);
+
+        LONGITUDE_OF_ORIGIN = createLongitude(
+                renameAlias(builder,       LambertConformal1SP.LONGITUDE_OF_ORIGIN,
+                            Citations.ESRI,    ObliqueMercator.LONGITUDE_OF_CENTRE,
+                            Citations.GEOTIFF, ObliqueMercator.LONGITUDE_OF_CENTRE));
+
+        PARAMETERS = builder.addIdentifier("9840")
+                .addName(                    "Orthographic")
+                .addName(Citations.OGC,      "Orthographic")
+                .addName(Citations.ESRI,     "Orthographic")
+                .addName(Citations.NETCDF,   "Orthographic")
+                .addName(Citations.GEOTIFF,  "CT_Orthographic")
+                .addName(Citations.S57,      "Orthographic")
+                .addName(Citations.S57,      "ORT")
+                .addName(Citations.PROJ4,    "ortho")
+                .addIdentifier(Citations.GEOTIFF, "21")
+                .addIdentifier(Citations.S57,     "10")
+                .createGroupForMapProjection(
+                        LATITUDE_OF_ORIGIN,
+                        LONGITUDE_OF_ORIGIN,
+                        SCALE_FACTOR,
+                        FALSE_EASTING,
+                        FALSE_NORTHING);
+    }
+
+    /**
+     * Constructs a new provider.
+     */
+    public Orthographic() {
+        super(PARAMETERS);
+    }
+
+    /**
+     * Returns the operation type for this map projection.
+     */
+    @Override
+    public Class<PlanarProjection> getOperationType() {
+        return PlanarProjection.class;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the map projection created from the given parameter values.
+     */
+    @Override
+    protected final NormalizedProjection createProjection(final Parameters parameters) {
+        return new org.apache.sis.referencing.operation.projection.Orthographic(this, parameters);
+    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
index 12fa782..15f023c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicA.java
@@ -62,18 +62,55 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values can be -90° or 90° only. There is no default value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = LambertConformal1SP.LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StraightVertPoleLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor at natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> scale_factor_at_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtNatOrigin </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR = Mercator1SP.SCALE_FACTOR;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
index 8862f97..4d17593 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicB.java
@@ -49,12 +49,38 @@
     /**
      * The operation parameter descriptor for the <cite>Longitude of origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StraightVertPoleLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Latitude of standard parallel</cite> (φ₁) parameter value.
      * Valid values are -90° or 90°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of standard parallel </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> NetCDF:  </td><td> standard_parallel </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> StdParallel1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_ts </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL;
 
@@ -66,6 +92,18 @@
      * {@code PolarStereographicB} projection. Nevertheless we declare it is as an optional parameter
      * because it is sometime used in Well Known Text (WKT). However it shall be interpreted as a
      * <cite>Scale factor at the standard parallel</cite> rather than at the natural origin.</p>
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Optional</li>
+     * </ul>
      */
     static final ParameterDescriptor<Double> SCALE_FACTOR;
 
@@ -86,7 +124,8 @@
 
         SCALE_FACTOR = createScale(builder
                 .addNamesAndIdentifiers(Mercator2SP.SCALE_FACTOR)
-                .setRemarks(notFormalParameter("Polar Stereographic (variant A)")).setDeprecated(true));
+                .setRemarks(notFormalParameter("Polar Stereographic (variant A)"))
+                .setRequired(false).setDeprecated(true));
 
         PARAMETERS = builder
                 .addIdentifier(IDENTIFIER)
@@ -94,7 +133,7 @@
                 .addName(Citations.S57,  "Polar stereographic")
                 .addName(Citations.S57,  "PST")
                 .addIdentifier(Citations.S57, "11")
-                .addName(sameNameAs(Citations.PROJ4, PolarStereographicA.PARAMETERS))
+                .addNameAndIdentifier(Citations.PROJ4, PolarStereographicA.PARAMETERS)
                 .createGroupForMapProjection(
                         STANDARD_PARALLEL,
                         LONGITUDE_OF_ORIGIN,
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicC.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicC.java
index af0099b..acf115a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicC.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/PolarStereographicC.java
@@ -45,12 +45,34 @@
     /**
      * The operation parameter descriptor for the <cite>Easting at false origin</cite> (Ef) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Easting at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> EASTING_AT_FALSE_ORIGIN = RegionalMercator.EASTING_AT_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Northing at false origin</cite> (Nf) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Northing at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> NORTHING_AT_FALSE_ORIGIN = RegionalMercator.NORTHING_AT_FALSE_ORIGIN;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Polyconic.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Polyconic.java
index ac774db..069d956 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Polyconic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Polyconic.java
@@ -47,24 +47,68 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = TransverseMercator.LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_central_meridian </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN = TransverseMercator.LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = TransverseMercator.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> False northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = TransverseMercator.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RegionalMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RegionalMercator.java
index 42797d9..8f64cb9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RegionalMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/RegionalMercator.java
@@ -52,18 +52,55 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of false origin</cite> (φf) parameter value.
      * Valid values range is (-90 … 90)° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (-90.0 … 90.0)°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Easting at false origin</cite> (Ef) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Easting at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_easting </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginEasting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> EASTING_AT_FALSE_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Northing at false origin</cite> (Nf) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Northing at false origin </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> NetCDF:  </td><td> false_northing </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> FalseOriginNorthing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> NORTHING_AT_FALSE_ORIGIN;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
index e6a3946..a6c4093 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -51,11 +51,27 @@
     /**
      * The operation parameter descriptor for the <cite>central meridian</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN = ESRI.CENTRAL_MERIDIAN;
 
     /**
      * Latitude crossing the central meridian at the desired origin of rectangular coordinates.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN = ESRI.LATITUDE_OF_ORIGIN;;
 
@@ -63,6 +79,14 @@
      * The operation parameter descriptor for the <cite>Latitude of 1st standard parallel</cite>.
      * For conical satellite-tracking projection, this is the first parallel of conformality with true scale.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_1 </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_1 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_1 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_1 = ESRI.STANDARD_PARALLEL_1;
 
@@ -70,6 +94,14 @@
      * The operation parameter descriptor for the <cite>second parallel of conformality but without true scale</cite>
      * parameter value for conic projection. Valid values range is [-90 … 90]° and default value is the opposite value
      * given to the {@link #STANDARD_PARALLEL_1} parameter.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Standard_Parallel_2 </td></tr>
+     *   <tr><td> OGC:     </td><td> standard_parallel_2 </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_2 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> STANDARD_PARALLEL_2 = ESRI.STANDARD_PARALLEL_2;
 
@@ -77,12 +109,29 @@
      * The operation parameter descriptor for the angle of inclination between the plane of the Earth's
      * Equator and the plane of the satellite orbit. It is measured counterclockwise from the Equator to
      * the orbit plane at the ascending node. Examples: 99.092° for Landsat 1, 2 and 3.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> satellite_orbit_inclination </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SATELLITE_ORBIT_INCLINATION;
 
     /**
      * The operation parameter descriptor for the time required for revolution of the satellite.
      * Examples: 103.267 minutes for Landsat 1, 2 and 3; 98.884 minutes for Landsat 4 and 5.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> satellite_orbital_period </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) d</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> SATELLITE_ORBITAL_PERIOD;
 
@@ -91,6 +140,17 @@
      * precessed ascending node. The ascending node is the point on the satellite orbit at which
      * the satellite crosses the Earth's equatorial plane in a northerly direction.
      * For Landsat (Sun-synchronous satellite orbit), this is 1440 minutes.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> SIS:     </td><td> ascending_node_period </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞) d</li>
+     *   <li>No default value</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> ASCENDING_NODE_PERIOD;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
index 3335093..3c3dfff 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Sinusoidal.java
@@ -55,18 +55,42 @@
     /**
      * The operation parameter descriptor for the <cite>Longitude of projection center</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> CENTRAL_MERIDIAN = ESRI.CENTRAL_MERIDIAN;
 
     /**
      * The operation parameter descriptor for the <cite>False easting</cite> (FE) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Easting </td></tr>
+     *   <tr><td> OGC:     </td><td> false_easting </td></tr>
+     *   <tr><td> Proj4:   </td><td> x_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_EASTING = ESRI.FALSE_EASTING;
 
     /**
      * The operation parameter descriptor for the <cite>False northing</cite> (FN) parameter value.
      * Valid values range is unrestricted and default value is 0 metre.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> ESRI:    </td><td> False_Northing </td></tr>
+     *   <tr><td> OGC:     </td><td> false_northing </td></tr>
+     *   <tr><td> Proj4:   </td><td> y_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> FALSE_NORTHING = ESRI.FALSE_NORTHING;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
index 57a5d88..3671f3c 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/TransverseMercator.java
@@ -59,18 +59,51 @@
     /**
      * The operation parameter descriptor for the <cite>Latitude of natural origin</cite> (φ₀) parameter value.
      * Valid values range is [-90 … 90]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Latitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> latitude_of_origin </td></tr>
+     *   <tr><td> ESRI:    </td><td> Latitude_Of_Origin </td></tr>
+     *   <tr><td> NetCDF:  </td><td> latitude_of_projection_origin </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLat </td></tr>
+     *   <tr><td> Proj4:   </td><td> lat_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LATITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Longitude of natural origin</cite> (λ₀) parameter value.
      * Valid values range is [-180 … 180]° and default value is 0°.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Longitude of natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> central_meridian </td></tr>
+     *   <tr><td> ESRI:    </td><td> Central_Meridian </td></tr>
+     *   <tr><td> NetCDF:  </td><td> longitude_of_central_meridian </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> NatOriginLong </td></tr>
+     *   <tr><td> Proj4:   </td><td> lon_0 </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> LONGITUDE_OF_ORIGIN;
 
     /**
      * The operation parameter descriptor for the <cite>Scale factor at natural origin</cite> (k₀) parameter value.
      * Valid values range is (0 … ∞) and default value is 1.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Scale factor at natural origin </td></tr>
+     *   <tr><td> OGC:     </td><td> scale_factor </td></tr>
+     *   <tr><td> ESRI:    </td><td> Scale_Factor </td></tr>
+     *   <tr><td> NetCDF:  </td><td> scale_factor_at_central_meridian </td></tr>
+     *   <tr><td> GeoTIFF: </td><td> ScaleAtNatOrigin </td></tr>
+     *   <tr><td> Proj4:   </td><td> k </td></tr>
+     * </table>
      */
     public static final ParameterDescriptor<Double> SCALE_FACTOR;
 
@@ -83,8 +116,8 @@
         LATITUDE_OF_ORIGIN = createLatitude(builder
                 .addNamesAndIdentifiers(Mercator1SP.LATITUDE_OF_ORIGIN), true);
 
-        LONGITUDE_OF_ORIGIN = createLongitude(renameAlias(Mercator1SP.LONGITUDE_OF_ORIGIN,
-                Citations.NETCDF, LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN, builder));
+        LONGITUDE_OF_ORIGIN = createLongitude(renameAlias(builder, Mercator1SP.LONGITUDE_OF_ORIGIN,             // Copy from this parameter…
+                                         Citations.NETCDF, LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN));     // … except for this name.
 
         SCALE_FACTOR = createScale(builder
                 .addNamesAndIdentifiers(Mercator1SP.SCALE_FACTOR)
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ZonedTransverseMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ZonedTransverseMercator.java
index 1df8763..facccde 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ZonedTransverseMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/ZonedTransverseMercator.java
@@ -49,11 +49,32 @@
 
     /**
      * The operation parameter descriptor for the <cite>Initial longitude</cite> (λ₁) parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Initial longitude </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Default value: -180°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> INITIAL_LONGITUDE;
 
     /**
      * The operation parameter descriptor for the <cite>Zone width</cite> (W) parameter value.
+     *
+     * <!-- Generated by ParameterNameTableGenerator -->
+     * <table class="sis">
+     *   <caption>Parameter names</caption>
+     *   <tr><td> EPSG:    </td><td> Zone width </td></tr>
+     * </table>
+     * <b>Notes:</b>
+     * <ul>
+     *   <li>Value domain: (0.0 … ∞)°</li>
+     *   <li>Default value: 6°</li>
+     * </ul>
      */
     public static final ParameterDescriptor<Double> ZONE_WIDTH;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
index bd1522e..e4a4fa5 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java
@@ -171,7 +171,7 @@
  * </div>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  *
  * @param <B>  the builder subclass.
  *
@@ -273,7 +273,7 @@
         if (object != null) {
             properties.putAll(IdentifiedObjects.getProperties(object));
             final GenericName[] valueAlias = (GenericName[]) properties.remove(IdentifiedObject.ALIAS_KEY);
-            final ReferenceIdentifier[] valueIds = (ReferenceIdentifier[])  properties.remove(IdentifiedObject.IDENTIFIERS_KEY);
+            final ReferenceIdentifier[] valueIds = (ReferenceIdentifier[]) properties.remove(IdentifiedObject.IDENTIFIERS_KEY);
             if (valueAlias != null) aliases.addAll(Arrays.asList(valueAlias));
             if (valueIds != null) identifiers.addAll(Arrays.asList(valueIds));
         }
@@ -686,12 +686,12 @@
 
 
     /**
-     * Returns {@code true} if the given name or identifier is deprecated.
+     * Returns {@code true} if the given name or identifier is non-null and non-deprecated.
      *
      * @see #isDeprecated()
      */
-    private static boolean isDeprecated(final Object object) {
-        return (object instanceof Deprecable) && ((Deprecable) object).isDeprecated();
+    private static boolean isValid(final Object object) {
+        return (object != null) && !((object instanceof Deprecable) && ((Deprecable) object).isDeprecated());
     }
 
     /**
@@ -701,7 +701,7 @@
      * <p>This is a convenience method for using an existing object as a template, before to modify
      * some names by calls to {@link #rename(Citation, CharSequence[])}.</p>
      *
-     * @param  object  the object from which to copy the references to names and identifiers.
+     * @param  object  the object from which to copy the names and identifiers.
      * @return {@code this}, for method call chaining.
      *
      * @since 0.6
@@ -709,16 +709,46 @@
     public B addNamesAndIdentifiers(final IdentifiedObject object) {
         ensureNonNull("object", object);
         for (final ReferenceIdentifier id : object.getIdentifiers()) {
-            if (!isDeprecated(id)) {
+            if (isValid(id)) {
                 addIdentifier(id);
             }
         }
         ReferenceIdentifier id = object.getName();
-        if (!isDeprecated(id)) {
+        if (isValid(id)) {
             addName(id);
         }
         for (final GenericName alias : object.getAlias()) {
-            if (!isDeprecated(alias)) {
+            if (isValid(alias)) {
+                addName(alias);
+            }
+        }
+        return self();
+    }
+
+    /**
+     * Adds the non-deprecated names and identifiers from the given object for the specified authority.
+     * This is a convenience method for reusing name and identifier already declared for another object.
+     *
+     * @param  authority  the authority for which to copy the name and identifier.
+     * @param  object     the object from which to copy the name and identifier.
+     * @return {@code this}, for method call chaining.
+     *
+     * @since 1.1
+     */
+    public B addNameAndIdentifier(final Citation authority, final IdentifiedObject object) {
+        ensureNonNull("authority", authority);
+        ensureNonNull("object", object);
+        for (final ReferenceIdentifier id : object.getIdentifiers()) {
+            if (isValid(id) && authority.equals(id.getAuthority())) {
+                addIdentifier(id);
+            }
+        }
+        ReferenceIdentifier id = object.getName();
+        if (isValid(id) && authority.equals(id.getAuthority())) {
+            addName(id);
+        }
+        for (final GenericName alias : object.getAlias()) {
+            if (isValid(alias) && (alias instanceof Identifier) && authority.equals(((Identifier) alias).getAuthority())) {
                 addName(alias);
             }
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix1.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix1.java
index 8504238..ff93401 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix1.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix1.java
@@ -30,7 +30,7 @@
  * └     ┘</pre></blockquote>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.4
+ * @version 1.1
  *
  * @see Matrix2
  * @see Matrix3
@@ -39,8 +39,7 @@
  * @since 0.4
  * @module
  */
-@SuppressWarnings("CloneableClassWithoutClone")             // No field in this class needs clone.
-public final class Matrix1 extends MatrixSIS {
+public class Matrix1 extends MatrixSIS {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -260,6 +259,14 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Matrix1 clone() {
+        return (Matrix1) super.clone();
+    }
+
+    /**
      * Returns {@code true} if the specified object is of type {@code Matrix1} and
      * all of the data members are equal to the corresponding data members in this matrix.
      *
@@ -268,7 +275,7 @@
      */
     @Override
     public boolean equals(final Object object) {
-        if (object instanceof Matrix1) {
+        if (object != null && object.getClass() == getClass()) {
             final Matrix1 that = (Matrix1) object;
             return Numerics.equals(m00, that.m00);
         }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix2.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix2.java
index 04e0f51..c3f455a 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix2.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix2.java
@@ -31,7 +31,7 @@
  * └         ┘</pre></blockquote>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.4
+ * @version 1.1
  *
  * @see Matrix1
  * @see Matrix3
@@ -40,8 +40,7 @@
  * @since 0.4
  * @module
  */
-@SuppressWarnings("CloneableClassWithoutClone")             // No field in this class needs clone.
-public final class Matrix2 extends MatrixSIS {
+public class Matrix2 extends MatrixSIS {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -284,6 +283,14 @@
     }
 
     /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Matrix2 clone() {
+        return (Matrix2) super.clone();
+    }
+
+    /**
      * Returns {@code true} if the specified object is of type {@code Matrix2} and
      * all of the data members are equal to the corresponding data members in this matrix.
      *
@@ -292,7 +299,7 @@
      */
     @Override
     public boolean equals(final Object object) {
-        if (object instanceof Matrix2) {
+        if (object != null && object.getClass() == getClass()) {
             final Matrix2 that = (Matrix2) object;
             return Numerics.equals(this.m00, that.m00) &&
                    Numerics.equals(this.m01, that.m01) &&
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix3.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix3.java
index 6edfb8f..d246bc0 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix3.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix3.java
@@ -30,7 +30,7 @@
  * └             ┘</pre></blockquote>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.6
+ * @version 1.1
  *
  * @see Matrix1
  * @see Matrix2
@@ -39,8 +39,7 @@
  * @since 0.4
  * @module
  */
-@SuppressWarnings("CloneableClassWithoutClone")             // No field in this class needs clone.
-public final class Matrix3 extends MatrixSIS {
+public class Matrix3 extends MatrixSIS {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -298,4 +297,12 @@
         swap = m02; m02 = m20; m20 = swap;
         swap = m12; m12 = m21; m21 = swap;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Matrix3 clone() {
+        return (Matrix3) super.clone();
+    }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix4.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix4.java
index 8093b87..2f14f39 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix4.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrix4.java
@@ -31,7 +31,7 @@
  * └                 ┘</pre></blockquote>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.6
+ * @version 1.1
  *
  * @see Matrix1
  * @see Matrix2
@@ -40,8 +40,7 @@
  * @since 0.4
  * @module
  */
-@SuppressWarnings("CloneableClassWithoutClone")             // No field in this class needs clone.
-public final class Matrix4 extends MatrixSIS {
+public class Matrix4 extends MatrixSIS {
     /**
      * Serial number for inter-operability with different versions.
      */
@@ -336,4 +335,12 @@
         swap = m13; m13 = m31; m31 = swap;
         swap = m23; m23 = m32; m32 = swap;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Matrix4 clone() {
+        return (Matrix4) super.clone();
+    }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java
index 0005bb1..db87112 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/package-info.java
@@ -69,7 +69,7 @@
  * Like SIS, Vecmath is optimized for small matrices of interest for 2D and 3D graphics.</p>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.4
  * @module
  */
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
index e31c363..fe17827 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/AlbersEqualArea.java
@@ -39,8 +39,11 @@
 
 /**
  * <cite>Albers Equal Area</cite> projection (EPSG code 9822).
- * See the <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area
- * Conic projection on MathWorld</a> for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Albers_projection">Albers projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection on MathWorld</a></li>
+ * </ul>
  *
  * <p>The {@code "standard_parallel_2"} parameter is optional and will be given the same value as
  * {@code "standard_parallel_1"} if not set.</p>
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
index e76708f..804d082 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Initializer.java
@@ -56,7 +56,7 @@
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Rémi Maréchal (Geomatys)
- * @version 0.7
+ * @version 1.1
  * @since   0.6
  * @module
  */
@@ -326,6 +326,20 @@
     }
 
     /**
+     * Returns the radius of curvature of the ellipsoid perpendicular to the meridian at latitude φ.
+     * This is {@code 1/sqrt(rν2(sinφ))}.
+     *
+     * @param  sinφ  the sine of the φ latitude.
+     * @return radius of curvature of the ellipsoid perpendicular to the meridian at latitude φ.
+     */
+    final double radiusOfCurvature(final double sinφ) {
+        final DoubleDouble rν2 = rν2(sinφ);
+        rν2.sqrt();
+        rν2.inverseDivide(1);
+        return rν2.doubleValue();
+    }
+
+    /**
      * Returns the radius of the conformal sphere (assuming a semi-major axis length of 1) at a given latitude.
      * The radius of conformal sphere is computed from ρ, which is the radius of curvature in the meridian at
      * latitude φ, and ν which is the radius of curvature in the prime vertical, as below:
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Inverter.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Inverter.java
new file mode 100644
index 0000000..d928119
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Inverter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import org.apache.sis.internal.referencing.Resources;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+
+import static java.lang.Math.abs;
+
+
+/**
+ * A temporary Jacobian matrix where to write the derivative of a forward projection.
+ * This Jacobian matrix is used for calculation of inverse projection when no inverse
+ * formulas is available, or when the inverse formula is too approximate (for example
+ * because eccentricity is too high). This class processes as below:
+ *
+ * <ol>
+ *   <li>Given a first estimation of (λ,φ), compute the forward projection (x′,y′)
+ *       for that estimation together with the Jacobian matrix at that position.</li>
+ *   <li>Compute the errors compared to the specified (x,y) values.</li>
+ *   <li>Convert that (Δx,Δy) error into a (Δλ,Δφ) error using the inverse of the Jacobian matrix.</li>
+ *   <li>Correct (λ,φ) and continue iteratively until the error is small enough.</li>
+ * </ol>
+ *
+ * This algorithm described in EPSG guidance note for {@link Orthographic} projection
+ * but can be applied to any map projection, not only orthographic.
+ *
+ * <p>This algorithm is defined in a {@link Matrix2} subclass for allowing map projection
+ * implementations to use {@code if (derivative instanceof Inverter)} check for detecting
+ * when a {@code transform} method is invoked for the purpose of an inverse projection.</p>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @ee <a href="https://issues.apache.org/jira/browse/SIS-478">SIS-478</a>
+ *
+ * @since 1.1
+ * @module
+ */
+@SuppressWarnings({"CloneableClassWithoutClone", "serial"})
+final class Inverter extends Matrix2 {
+    /**
+     * Creates a new matrix initialized to identity.
+     */
+    Inverter() {
+    }
+
+    /**
+     * Computes the inverse of the given projection. Before this method is invoked,
+     * the {@code dstPts[dstOff]} and {@code dstPts[dstOff+1]} array elements must
+     * contain the initial λ and φ estimations respectively. After method returns,
+     * the same array elements contain the refined λ and φ values.
+     *
+     * <p>Note: restricted to {@link Orthographic} projection for now,
+     * but may be generalized to any projection in a future version.</p>
+     *
+     * @param  projection  the forward projection for which to compute an inverse projection.
+     * @param  x           the  easting value from {@code srcPts[srcOff]}.
+     * @param  y           the northing value from {@code srcPts[srcOff+1]}.
+     * @param  dstPts      the array where to refine the (λ,φ) values.
+     * @param  dstOff      index of the λ element in the {@code dstPts} array.
+     * @throws ProjectionException if the iterative algorithm does not converge.
+     */
+    final void inverseTransform(final Orthographic projection, final double x, final double y,
+            final double[] dstPts, final int dstOff) throws ProjectionException
+    {
+        double λ = dstPts[dstOff  ];
+        double φ = dstPts[dstOff+1];
+        for (int it=NormalizedProjection.MAXIMUM_ITERATIONS; --it >= 0;) {
+            final double cosφ = projection.transform(dstPts, dstOff, dstPts, dstOff, this);
+            final double ΔE   = x - dstPts[dstOff  ];
+            final double ΔN   = y - dstPts[dstOff+1];
+            final double D    = (m01 * m10) - (m00 * m11);    // Determinant.
+            final double Δλ   = (m01*ΔN - m11*ΔE) / D;
+            final double Δφ   = (m10*ΔE - m00*ΔN) / D;
+            dstPts[dstOff  ]  = λ += Δλ;
+            dstPts[dstOff+1]  = φ += Δφ;
+            /*
+             * Following condition uses ! for stopping iteration on NaN values.
+             * We do not use Math.max(…) because we want to check NaN separately
+             * for φ and λ.
+             */
+            if (!(abs(Δφ)      > NormalizedProjection.ANGULAR_TOLERANCE ||
+                  abs(Δλ*cosφ) > NormalizedProjection.ANGULAR_TOLERANCE))
+            {
+                return;
+            }
+        }
+        throw new ProjectionException(Resources.format(Resources.Keys.NoConvergence));
+    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
index 18e35a4..090f0da 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/LambertConicConformal.java
@@ -45,8 +45,11 @@
 
 /**
  * <cite>Lambert Conic Conformal</cite> projection (EPSG codes 9801, 9802, 9803, 9826, 1051).
- * See the <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert conformal
- * conic projection on MathWorld</a> for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Lambert_conformal_conic_projection">Lambert Conformal Conic projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic projection on MathWorld</a></li>
+ * </ul>
  *
  * <h2>Description</h2>
  * Areas and shapes are deformed as one moves away from standard parallels.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
index ee94c9e..2c260e1 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mercator.java
@@ -44,7 +44,11 @@
 
 /**
  * <cite>Mercator Cylindrical</cite> projection (EPSG codes 9804, 9805, 1026, 1024, 1044, <span class="deprecated">9841</span>).
- * See the <a href="http://mathworld.wolfram.com/MercatorProjection.html">Mercator projection on MathWorld</a> for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Mercator_projection">Mercator projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/MercatorProjection.html">Mercator projection on MathWorld</a></li>
+ * </ul>
  *
  * <h2>Description</h2>
  * The parallels and the meridians are straight lines and cross at right angles; this projection thus produces
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mollweide.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mollweide.java
index e48903b..fd5353f 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mollweide.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Mollweide.java
@@ -36,9 +36,11 @@
 
 /**
  * <cite>Mollweide</cite> projection.
- * See the <a href="http://mathworld.wolfram.com/MollweideProjection.html">Mollweide projection on MathWorld</a>
- * or the <a href="https://en.wikipedia.org/wiki/Mollweide_projection">Mollweide projection on Wikipedia</a>
- * for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Mollweide_projection">Mollweide projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/MollweideProjection.html">Mollweide projection on MathWorld</a></li>
+ * </ul>
  *
  * @todo This projection is not {@link org.apache.sis.math.FunctionProperty#SURJECTIVE surjective}.
  *       Consequently {@link org.apache.sis.referencing.CRS#suggestCommonTarget CRS.suggestCommonTarget(…)}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueMercator.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueMercator.java
index e4fb52a..9e5b2b9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueMercator.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueMercator.java
@@ -170,7 +170,7 @@
         final double rν2 = initializer.rν2(sinφ).doubleValue();                         // rν² = (1 - ℯ²⋅sin²φc)
         final double A   = Br / rν2;                                                    // a and kc handled later.
         final double D   = Br / (cosφ * sqrt(rν2));
-        final double sD1 = sqrt(Math.max(D*D - 1, 0));
+        final double sD1 = sqrt(max(D*D - 1, 0));
         final double F   = D + copySign(sD1, φc);
         H = F * pow(expΨ(φc, eccentricity*sinφ), -B);                                   // expΨ(…) = 1/t
         /*
@@ -222,13 +222,13 @@
             final double P  = (L - H1) / (L + H1);
             double Δλ = λ1 - λ2;
             if (abs(Δλ) > PI) {
-                λ2 += copySign(2*Math.PI, Δλ);                      // Adjustment recommended by Snyder.
+                λ2 += copySign(2*PI, Δλ);                           // Adjustment recommended by Snyder.
                 Δλ = λ1 - λ2;
             }
             λ0 = (λ1 + λ2)/2 - atan(J * tan(B*Δλ/2) / P) / B;
             Δλ = λ1 - λ0;
             if (abs(Δλ) > PI) {
-                λ0 += copySign(2*Math.PI, Δλ);                      // Adjustment recommended by Snyder.
+                λ0 += copySign(2*PI, Δλ);                           // Adjustment recommended by Snyder.
                 Δλ = λ1 - λ0;
             }
             γ0 = atan(2 * sin(B * Δλ) / (H/H1 - H1/H));             // Do not use atan2(…) here.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
index ddcf04c..e4828f8 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ObliqueStereographic.java
@@ -38,8 +38,11 @@
 
 /**
  * <cite>Oblique Stereographic</cite> projection (EPSG code 9809).
- * See the <a href="http://mathworld.wolfram.com/StereographicProjection.html">Stereographic projection
- * on MathWorld</a> for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Stereographic_projection">Stereographic projection or Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/StereographicProjection.html">Stereographic projection or MathWorld</a></li>
+ * </ul>
  *
  * <h2>Description</h2>
  * The directions starting from the central point are true, but the areas and the lengths become
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java
new file mode 100644
index 0000000..87b9705
--- /dev/null
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Orthographic.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import java.util.EnumMap;
+import org.apache.sis.internal.util.Numerics;
+import org.opengis.referencing.operation.Matrix;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.parameter.ParameterDescriptor;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.referencing.operation.matrix.Matrix2;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
+import org.apache.sis.referencing.operation.transform.ContextualParameters;
+import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.Workaround;
+
+import static java.lang.Math.*;
+import static org.apache.sis.internal.referencing.provider.Orthographic.*;
+
+
+/**
+ * <cite>Orthographic</cite> projection (EPSG:9840).
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Orthographic_projection_in_cartography">Orthographic projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/OrthographicProjection.html">Orthographic projection on MathWorld</a></li>
+ * </ul>
+ *
+ * <h2>Description</h2>
+ * This is a perspective azimuthal (planar) projection that is neither conformal nor equal-area.
+ * It resembles a globe viewed from a point of perspective at infinite distance.
+ * Only one hemisphere can be seen at a time.
+ * While not useful for accurate measurements, this projection is useful for pictorial views of the world.
+ *
+ * @author  Rueben Schulz (UBC)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public class Orthographic extends NormalizedProjection {
+    /**
+     * For compatibility with different versions during deserialization.
+     */
+    private static final long serialVersionUID = -6140156868989213344L;
+
+    /**
+     * Sine and cosine of latitude of origin.
+     */
+    private final double sinφ0, cosφ0;
+
+    /**
+     * Value of (1 – ℯ²)⋅cosφ₀.
+     */
+    private final double mℯ2_cosφ0;
+
+    /**
+     * Work around for RFE #4093999 in Sun's bug database
+     * ("Relax constraint on placement of this()/super() call in constructors").
+     */
+    @Workaround(library="JDK", version="1.8")
+    private static Initializer initializer(final OperationMethod method, final Parameters parameters) {
+        final EnumMap<ParameterRole, ParameterDescriptor<Double>> roles = new EnumMap<>(ParameterRole.class);
+        roles.put(ParameterRole.CENTRAL_MERIDIAN, LONGITUDE_OF_ORIGIN);
+        roles.put(ParameterRole.FALSE_EASTING,    FALSE_EASTING);
+        roles.put(ParameterRole.FALSE_NORTHING,   FALSE_NORTHING);
+        return new Initializer(method, parameters, roles, (byte) 0);
+    }
+
+    /**
+     * Creates an orthographic projection from the given parameters.
+     * The {@code method} argument can be the description of one of the following:
+     *
+     * <ul>
+     *   <li><cite>"Orthographic"</cite>.</li>
+     * </ul>
+     *
+     * @param method      description of the projection parameters.
+     * @param parameters  the parameter values of the projection to create.
+     */
+    public Orthographic(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").
+     */
+    @Workaround(library="JDK", version="1.8")
+    private Orthographic(final Initializer initializer) {
+        super(initializer);
+        final double φ0 = toRadians(initializer.getAndStore(LATITUDE_OF_ORIGIN));
+        sinφ0 = sin(φ0);
+        cosφ0 = cos(φ0);
+        mℯ2_cosφ0 = (1 - eccentricitySquared)*cosφ0;
+        /*
+         * For ellipsoidal formulas, equation given by EPSG guidance note contains:
+         *
+         *     N = FN + ν⋅[sinφ⋅cosφ₀ – cosφ⋅sinφ₀⋅cos(λ – λ₀)] + ℯ²⋅(ν₀⋅sinφ₀ – ν⋅sinφ)⋅cosφ₀
+         *
+         * In addition of false northing (FN), another constant term is ℯ²⋅(ν₀⋅sinφ₀)⋅cosφ₀
+         * which we factor out below. Note that we do not really need the "if" statement
+         * since the computed value below is zero in the spherical case.
+         */
+        if (eccentricity != 0) {
+            final double ν0 = initializer.radiusOfCurvature(sinφ0);
+            final MatrixSIS denormalize = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION);
+            denormalize.convertBefore(1, null, eccentricitySquared * ν0 * (sinφ0 * cosφ0));
+        }
+    }
+
+    /**
+     * Converts the specified (λ,φ) coordinate and stores the (<var>x</var>,<var>y</var>) result in {@code dstPts}.
+     * The units of measurement are implementation-specific (see subclass javadoc).
+     *
+     * @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 Matrix transform(final double[] srcPts, final int srcOff,
+                            final double[] dstPts, final int dstOff,
+                            final boolean derivate) throws ProjectionException
+    {
+        final Matrix2 derivative = derivate ? new Matrix2() : null;
+        transform(srcPts, srcOff, dstPts, dstOff, derivative);
+        return derivative;
+    }
+
+    /**
+     * Implementation of {@link #transform(double[], int, double[], int, boolean)}
+     * with possibility to recycle an existing matrix instance.
+     *
+     * <div class="note"><b>Implementation note:</b>
+     * in other map projections, we use a different class for ellipsoidal formulas.
+     * But the orthographic projection is a bit different; for this one it is more
+     * convenient to use {@code if} statements.</div>
+     *
+     * @param  derivative  where to store the Jacobian matrix, or {@code null} if none.
+     *         If this matrix is an {@link Inverter} instance, we take that as a flag
+     *         meaning to not set out-of-range results to NaN.
+     * @return {@code cos(φ)}, useful for error tolerance check.
+     */
+    final double transform(final double[] srcPts, final int srcOff,
+                           final double[] dstPts, final int dstOff,
+                           final Matrix2 derivative)
+    {
+        final double λ    = srcPts[srcOff  ];
+        final double φ    = srcPts[srcOff+1];
+        final double cosλ = cos(λ);
+        final double sinλ = sin(λ);
+        final double cosφ = cos(φ);
+        final double sinφ = sin(φ);
+        final double cosφ_cosλ = cosφ*cosλ;
+        final double sinφ0_sinφ = sinφ0*sinφ;                   // Note: φ₀ here is φ₁ in Snyder.
+        final double cosc = sinφ0_sinφ + cosφ0*cosφ_cosλ;       // Snyder (5-3) page 149
+        double x, y;
+        /*
+         * c is the distance from the center of orthographic projection.
+         * If that distance is greater than 90° (identied by cos(c) < 0)
+         * then the point is on the opposite hemisphere and should be clipped.
+         */
+        double rν;
+        if (cosc >= 0 || derivative instanceof Inverter) {
+            x  = cosφ * sinλ;                           // Snyder (20-3)
+            y  = mℯ2_cosφ0*sinφ - sinφ0*cosφ_cosλ;      // Snyder (20-4) × (1 – ℯ²)
+            rν = 1;
+            if (eccentricity != 0) {
+                /*
+                 * EPSG equations without the  ℯ²⋅(ν₀⋅sinφ₀ – ν⋅sinφ)⋅cosφ₀  additional term in y;
+                 * the  ℯ²⋅(ν₀⋅sinφ₀)⋅cosφ₀  part is applied by the denormalization matrix and the
+                 * remaining was applied by the (1 - ℯ²) multiplication factor above.
+                 */
+                final double ℯsinφ = eccentricity * sinφ;
+                rν = sqrt(1 - ℯsinφ * ℯsinφ);
+                x /= rν;
+                y /= rν;
+            }
+        } else {
+            x  = Double.NaN;
+            y  = Double.NaN;
+            rν = Double.NaN;
+        }
+        if (dstPts != null) {
+            dstPts[dstOff  ] = x;
+            dstPts[dstOff+1] = y;
+        }
+        if (derivative != null) {
+            final double ρ = (1 - eccentricitySquared) / (rν*rν*rν);
+            derivative.m00 =  cosφ_cosλ / rν;                           // ∂E/∂λ
+            derivative.m01 = -sinφ*sinλ * ρ;                            // ∂E/∂φ
+            derivative.m10 =  sinφ0 * x;                                // ∂N/∂λ
+            derivative.m11 = (cosφ0*cosφ + sinφ0_sinφ*cosλ) * ρ;        // ∂N/∂φ
+        }
+        return cosφ;
+    }
+
+    /**
+     * Converts the specified (<var>x</var>,<var>y</var>) coordinates
+     * and stores the result in {@code dstPts} (angles in radians).
+     */
+    @Override
+    protected void inverseTransform(final double[] srcPts, final int srcOff,
+                                    final double[] dstPts, final int dstOff)
+            throws ProjectionException
+    {
+        /*
+         * Note: Synder said that equations become undetermined if ρ=0.
+         * But setting the radius R to 1 allows simplifications to emerge.
+         * In particular sin(c) = ρ, so terms like sin(c)/ρ disappear.
+         */
+        final double x    = srcPts[srcOff  ];
+        final double y    = srcPts[srcOff+1];
+        final double ρ2   = x*x + y*y;                          // sin(c) = ρ/R, but in this method R=1.
+        final double cosc = sqrt(1 - ρ2);                       // NaN if ρ > 1.
+        dstPts[dstOff  ]  = atan2(x, cosc*cosφ0 - y*sinφ0);     // Synder (20-15) with ρ = sin(c)
+        dstPts[dstOff+1]  = asin(cosc*sinφ0 + y*cosφ0);         // Synder (20-14) where y⋅sin(c)/ρ = y/R with R=1.
+        if (eccentricity != 0) {
+            /*
+             * In the ellipsoidal case, there is no reverse formulas. Instead we take a first estimation of (λ,φ),
+             * compute the forward projection (E′,N′) for that estimation, compute the errors compared to specified
+             * (E,N) values, convert that (ΔE,ΔN) error into a (Δλ,Δφ) error using the inverse of Jacobian matrix,
+             * correct (λ,φ) and continue iteratively until the error is small enough. This algorithm described in
+             * EPSG guidance note could be applied to any map projection, not only Orthographic.
+             *
+             * See https://issues.apache.org/jira/browse/SIS-478
+             */
+            final Inverter j = new Inverter();                      // Jacobian matrix.
+            j.inverseTransform(this, x, y, dstPts, dstOff);
+            dstPts[dstOff] = IEEEremainder(dstPts[dstOff], PI);
+        }
+    }
+
+    /**
+     * Compares the given object with this transform for equivalence.
+     */
+    @Override
+    public boolean equals(final Object object, final ComparisonMode mode) {
+        if (super.equals(object, mode)) {
+            final Orthographic that = (Orthographic) object;
+            return Numerics.epsilonEqual(sinφ0, that.sinφ0, mode) &&
+                   Numerics.epsilonEqual(cosφ0, that.cosφ0, mode);
+        }
+        return false;
+    }
+}
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
index 545851f..a3895ee 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/Sinusoidal.java
@@ -34,8 +34,11 @@
 
 /**
  * <cite>Sinusoidal equal-area</cite> projection, also known as <cite>"Sanson-Flamsteed"</cite>.
- * See the <a href="https://en.wikipedia.org/wiki/Sinusoidal_projection">Sinusoidal projection on Wikipedia</a>
- * for an overview.
+ * See the following references for an overview:
+ * <ul>
+ *   <li><a href="https://en.wikipedia.org/wiki/Sinusoidal_projection">Sinusoidal projection on Wikipedia</a></li>
+ *   <li><a href="http://mathworld.wolfram.com/SinusoidalProjection.html">Sinusoidal projection on MathWorld</a></li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ZonedGridSystem.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ZonedGridSystem.java
index 91b8401..aff1726 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ZonedGridSystem.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/projection/ZonedGridSystem.java
@@ -20,6 +20,7 @@
 import java.io.Serializable;
 import org.opengis.util.FactoryException;
 import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.operation.OperationMethod;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.referencing.operation.Matrix;
@@ -38,7 +39,6 @@
 import static java.lang.Math.floor;
 import static org.apache.sis.internal.referencing.provider.TransverseMercator.*;
 import static org.apache.sis.internal.referencing.provider.ZonedTransverseMercator.*;
-import org.opengis.parameter.ParameterValueGroup;
 
 
 /**
diff --git a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
index 26726f5..5aa20e9 100644
--- a/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
+++ b/core/sis-referencing/src/main/resources/META-INF/services/org.opengis.referencing.operation.OperationMethod
@@ -54,6 +54,7 @@
 org.apache.sis.internal.referencing.provider.ObliqueMercatorCenter
 org.apache.sis.internal.referencing.provider.ObliqueMercatorTwoPoints
 org.apache.sis.internal.referencing.provider.ObliqueMercatorTwoPointsCenter
+org.apache.sis.internal.referencing.provider.Orthographic
 org.apache.sis.internal.referencing.provider.ZonedTransverseMercator
 org.apache.sis.internal.referencing.provider.Sinusoidal
 org.apache.sis.internal.referencing.provider.Polyconic
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
index 019020c..25e3b8b 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/ProvidersTest.java
@@ -104,6 +104,7 @@
             ObliqueMercatorCenter.class,
             ObliqueMercatorTwoPoints.class,
             ObliqueMercatorTwoPointsCenter.class,
+            Orthographic.class,
             ZonedTransverseMercator.class,
             SatelliteTracking.class,
             Sinusoidal.class,
@@ -154,7 +155,7 @@
         final Map<GeneralParameterDescriptor, GeneralParameterDescriptor> parameters = new HashMap<>();
         final Map<Object, Object> namesAndIdentifiers = new HashMap<>();
         for (final Class<?> c : methods()) {
-            final OperationMethod method = (OperationMethod) c.newInstance();
+            final OperationMethod method = (OperationMethod) c.getConstructor((Class[]) null).newInstance((Object[]) null);
             final ParameterDescriptorGroup group = method.getParameters();
             final String operationName = group.getName().getCode();
             for (final GeneralParameterDescriptor param : group.descriptors()) {
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java
new file mode 100644
index 0000000..42fc7c5
--- /dev/null
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/projection/OrthographicTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.referencing.operation.projection;
+
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.transform.CoordinateDomain;
+import org.apache.sis.internal.referencing.provider.MapProjection;
+import org.apache.sis.parameter.Parameters;
+import org.apache.sis.test.DependsOn;
+import org.junit.*;
+
+import static java.lang.StrictMath.*;
+
+
+/**
+ * Tests the {@link Orthographic} class.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @author  Rémi Maréchal (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+@DependsOn(NormalizedProjectionTest.class)
+public final strictfp class OrthographicTest extends MapProjectionTestCase {
+    /**
+     * Creates a new instance of {@link Orthographic} using spherical formulas.
+     *
+     * @param  φ0  latitude of projection centre.
+     */
+    private void createSpherical(final double φ0) {
+        final MapProjection provider = new org.apache.sis.internal.referencing.provider.Orthographic();
+        final Parameters parameters = parameters(provider, false);
+        parameters.parameter("latitude_of_origin").setValue(φ0);
+        transform = new Orthographic(provider, parameters);
+        final double delta = toRadians(100.0 / 60) / 1852;              // Approximatively 100 metres.
+        derivativeDeltas = new double[] {delta, delta};
+        validate();
+    }
+
+    /**
+     * Tests the equatorial projection on a sphere.
+     * This method uses points from Snyder table 22.
+     *
+     * @throws TransformException should never happen.
+     */
+    @Test
+    public void testEquatorial() throws TransformException {
+        createSpherical(0);
+        tolerance = 1E-4;                       // Accuracy of numbers provided in Snyder tables.
+        verifyTransform(
+            new double[] {                      // (λ,φ) coordinates in radians to project.
+                toRadians( 0), toRadians( 0),
+                toRadians(20), toRadians( 0),
+                toRadians( 0), toRadians(40),
+                toRadians(30), toRadians(20),
+                toRadians(20), toRadians(80)
+            },
+            new double[] {                      // Expected (x,y) results.
+                0.0,           0.0,
+                0.3420,        0.0,
+                0.0,           0.6428,
+                0.4698,        0.3420,
+                0.0594,        0.9848
+            });
+
+        tolerance = NORMALIZED_TOLERANCE;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_HALF_λ, 209067359);
+        verifyDerivative(toRadians(5), toRadians(3));
+    }
+
+    /**
+     * Tests the oblique projection on a sphere.
+     * This method uses points from Snyder table 23.
+     *
+     * @throws TransformException should never happen.
+     */
+    @Test
+    public void testOblique() throws TransformException {
+        createSpherical(40);
+        tolerance = 1E-4;                       // Accuracy of numbers provided in Snyder tables.
+        verifyTransform(
+            new double[] {                      // (λ,φ) coordinates in radians to project.
+                toRadians( 0), toRadians(40),
+                toRadians( 0), toRadians( 0),
+                toRadians(20), toRadians( 0),
+                toRadians( 0), toRadians(50),
+                toRadians(30), toRadians(20),
+                toRadians(20), toRadians(80)
+            },
+            new double[] {                      // Expected (x,y) results.
+                0.0,           0.0,
+                0.0,          -0.6428,
+                0.3420,       -0.6040,
+                0.0,           0.1736,
+                0.4698,       -0.2611,
+                0.0594,        0.6495
+            });
+
+        tolerance = NORMALIZED_TOLERANCE;
+        verifyDerivative(toRadians(5), toRadians(30));
+    }
+
+    /**
+     * Tests the polar projection on a sphere.
+     * There is no reference points for this method;
+     * we just test consistency between forward and inverse methods.
+     *
+     * @throws TransformException should never happen.
+     */
+    @Test
+    public void testPolarNorth() throws TransformException {
+        createSpherical(+90);
+        tolerance = NORMALIZED_TOLERANCE;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_NORTH, 753524735);
+        verifyDerivative(toRadians(5), toRadians(85));
+    }
+
+    /**
+     * Tests the polar projection on a sphere.
+     * There is no reference points for this method;
+     * we just test consistency between forward and inverse methods.
+     *
+     * @throws TransformException should never happen.
+     */
+    @Test
+    public void testPolarSouth() throws TransformException {
+        createSpherical(-90);
+        tolerance = NORMALIZED_TOLERANCE;
+        verifyInDomain(CoordinateDomain.GEOGRAPHIC_RADIANS_SOUTH, 753524735);
+        verifyDerivative(toRadians(5), toRadians(-85));
+    }
+}
diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
index d0a3236..c89627d 100644
--- a/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/test/suite/ReferencingTestSuite.java
@@ -191,6 +191,7 @@
     org.apache.sis.referencing.operation.projection.SinusoidalTest.class,
     org.apache.sis.referencing.operation.projection.PolyconicTest.class,
     org.apache.sis.referencing.operation.projection.MollweideTest.class,
+    org.apache.sis.referencing.operation.projection.OrthographicTest.class,
     org.apache.sis.referencing.operation.projection.SatelliteTrackingTest.class,
 
     // Coordinate operation and derived Coordinate Reference Systems (cyclic dependency).
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
index 90344b0..f886b3f 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/JDK9.java
@@ -36,7 +36,7 @@
  * This file will be deleted on the SIS JDK9 branch.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   1.0
+ * @since   1.1
  * @version 0.8
  * @module
  */
@@ -110,4 +110,21 @@
         if (b instanceof DoubleBuffer) return ((DoubleBuffer) b).duplicate();
         throw new IllegalArgumentException();
     }
+
+    /**
+     * Place holder for {@code Class.getPackageName()}.
+     *
+     * @param  c  the class for which to get the package name.
+     * @return the name of the package.
+     */
+    public static String getPackageName(Class<?> c) {
+        Class<?> outer;
+        while ((outer = c.getEnclosingClass()) != null) {
+            c = outer;
+        }
+        String name = c.getName();
+        final int separator = name.lastIndexOf('.');
+        name = (separator >= 1) ? name.substring(0, separator) : "";
+        return name;
+    }
 }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
index 2e9df6d..a396f43 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/jdk9/package-info.java
@@ -24,7 +24,7 @@
  * may change in incompatible ways in any future version without notice.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @since   0.8
+ * @since   1.1
  * @version 0.8
  * @module
  */
diff --git a/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java b/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
index eb3a2bc..0feac21 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/io/TableAppender.java
@@ -866,11 +866,13 @@
      *
      * @param  out    the stream or buffer where to repeat the character.
      * @param  car    character to write (usually ' ').
-     * @param  count  number of repetition.
+     * @param  count  number of repetition, negative means 0.
      */
     private static void repeat(final Appendable out, final char car, int count) throws IOException {
         if (out instanceof StringBuilder) {
-            StringBuilders.repeat((StringBuilder) out, car, count);
+            if (count > 0) {
+                StringBuilders.repeat((StringBuilder) out, car, count);
+            }
         } else while (--count >= 0) {
             out.append(car);
         }
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
index 6a9cd42..9539899 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
@@ -26,6 +26,7 @@
 import org.apache.sis.util.Static;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
+import org.apache.sis.internal.jdk9.JDK9;
 import org.apache.sis.internal.system.Modules;
 
 
@@ -173,15 +174,9 @@
      *
      * @since 1.0
      */
-    public static Logger getLogger(Class<?> source) {
+    public static Logger getLogger(final Class<?> source) {
         ArgumentChecks.ensureNonNull("source", source);
-        Class<?> outer;
-        while ((outer = source.getEnclosingClass()) != null) {
-            source = outer;
-        }
-        String name = source.getName();
-        final int separator = name.lastIndexOf('.');
-        name = (separator >= 1) ? name.substring(0, separator) : "";
+        String name = JDK9.getPackageName(source);
         if (name.startsWith(Modules.INTERNAL_CLASSNAME_PREFIX)) {
             // Remove the "internal" part from Apache SIS package names.
             name = Modules.CLASSNAME_PREFIX + name.substring(Modules.INTERNAL_CLASSNAME_PREFIX.length());
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java b/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java
new file mode 100644
index 0000000..07e465a
--- /dev/null
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/ProjectDirectories.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.test;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.apache.sis.internal.jdk9.JDK9;
+
+import static org.junit.Assert.*;
+
+
+/**
+ * Provides methods that depend on assumption on the project directory layout.
+ * We currently use Maven conventions, but this class provide a central place
+ * to revisit if we want to change convention in the future.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final class ProjectDirectories {
+    /**
+     * The directory where are stored the compiled Java classes for the package of the given class.
+     * The constructor used the given class as a sample member for getting this package directory.
+     */
+    public final Path classesPackageDirectory;
+
+    /**
+     * The root directory where are stored the compiled Java classes.
+     * The constructor used the given class as a sample member for getting this directory.
+     *
+     * <p>If we are running the tests from another environment than Maven (e.g. from NetBeans project),
+     * then this directory may contain all modules instead than only the module of the class given to
+     * the constructor.</p>
+     */
+    public final Path classesRootDirectory;
+
+    /**
+     * Name of the package of the class given at construction time.
+     */
+    private final String packageName;
+
+    /**
+     * Find classes and sources directories using the given class as a member.
+     * In the case of Maven multi-modules project, the directories will apply
+     * only to the module containing the given class.
+     *
+     * @param  c  a sample member of the classes for which to get the directories.
+     */
+    public ProjectDirectories(final Class<?> c) {
+        final URL resource = c.getResource(c.getSimpleName() + ".class");
+        assertNotNull("Class not found.", resource);
+        Path dir;
+        try {
+            dir = Paths.get(resource.toURI()).getParent();
+        } catch (URISyntaxException e) {
+            throw new AssertionError(e);
+        }
+        classesPackageDirectory = dir;
+        packageName = JDK9.getPackageName(c);
+        String pkg = packageName;
+        int s = pkg.length();
+        do {
+            pkg = pkg.substring(0, s);
+            s = pkg.lastIndexOf('.');
+            assertEquals ("Unexpected directory structure.", pkg.substring(s+1), dir.getFileName().toString());
+            assertNotNull("Unexpected directory structure.", dir = dir.getParent());
+        } while (s >= 0);
+        classesRootDirectory = dir;
+    }
+
+    /**
+     * Returns the root directory of source Java code.
+     *
+     * @param  module  module name, e.g. {@code "core/sis-referencing"}.
+     *                 Used only if the project is not a Maven project.
+     * @return root directory of source Java files.
+     */
+    public Path getSourcesRootDirectory(final String module) {
+        Path dir = getMavenModule();
+        if (dir == null) {
+            /*
+             * This block is executed only if the compiled class file was not found in the location
+             * that we would have expected for a Maven project. Maybe the class is in the NetBeans
+             * build directory instead.
+             */
+            dir = classesRootDirectory;
+            do {
+                if (dir == null) {
+                    throw new AssertionError("No more parent directory.");
+                }
+                dir = dir.getParent();
+            } while (!Files.exists(dir.resolve("pom.xml")));
+            dir = dir.resolve(module.replace('/', File.separatorChar));
+        }
+        /*
+         * At this point `dir` is the root of the Maven module
+         * which contains the class given to the constructor.
+         */
+        dir = dir.resolve("src").resolve("main").resolve("java");
+        if (!Files.isDirectory(dir)) {
+            throw new AssertionError("Not a directory: " + dir);
+        }
+        return dir;
+    }
+
+    /**
+     * Returns the directory of source code for the package of the class given at construction time.
+     *
+     * @param  module  module name, e.g. {@code "core/sis-referencing"}.
+     *                 Used only if the project is not a Maven project.
+     * @return package directory of source Java files.
+     */
+    public Path getSourcesPackageDirectory(final String module) {
+        return getSourcesRootDirectory(module).resolve(packageName.replace('.', File.separatorChar));
+    }
+
+    /**
+     * Returns whether the {@link #classesRootDirectory} is the sub-directory of a tree following Maven conventions.
+     * This method verifies that the parent directories are {@code "target/*classes"} and that the parent directory
+     * contains a {@code pom.xml} file.
+     *
+     * @return whether we are in a Maven module.
+     */
+    public boolean isMavenModule() {
+        return getMavenModule() != null;
+    }
+
+    /**
+     * Returns the path to Maven module, or {@code null} if none.
+     */
+    private Path getMavenModule() {
+        Path dir = classesRootDirectory;
+        if (dir.getFileName().toString().endsWith("classes")) {
+            dir = dir.getParent();
+            if (dir != null && dir.getFileName().toString().equals("target")) {
+                dir = dir.getParent();
+                if (dir != null && Files.isRegularFile(dir.resolve("pom.xml"))) {
+                    return dir;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java b/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
index b2ae8f1..e66bc0d 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/TestCase.java
@@ -68,6 +68,10 @@
      * The intent is to make easier to identify test cases that fail with the current version
      * of SIS (e.g. because of unsupported operations), but should pass in a future version.
      *
+     * <p>Note: sometime the work is actually pending future GeoAPI development. But we still
+     * use that flag for those cases because the {@code "geoapi"} branches of Apache SIS follow
+     * closely GeoAPI developments.</p>
+     *
      * @since 0.4
      */
     public static final boolean PENDING_FUTURE_SIS_VERSION = false;
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/TestConfiguration.java b/core/sis-utility/src/test/java/org/apache/sis/test/TestConfiguration.java
index f81ce13..e5782de 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/TestConfiguration.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/TestConfiguration.java
@@ -29,26 +29,27 @@
  */
 public final strictfp class TestConfiguration extends Static {
     /**
-     * The {@value} system property for enabling more extensive tests.
+     * The {@systemProperty org.apache.sis.test.extensive} system property for enabling more extensive tests.
      * If this {@linkplain System#getProperties() system property} is set to {@code true},
      * then Apache SIS will run some tests which were normally skipped because they are slow.
      */
     public static final String EXTENSIVE_TESTS_KEY = "org.apache.sis.test.extensive";
 
     /**
-     * The {@value} system property for enabling verbose outputs.
+     * The {@systemProperty org.apache.sis.test.verbose} system property for enabling verbose outputs.
      * If this {@linkplain System#getProperties() system property} is set to {@code true},
      * then the content sent to the {@link TestCase#out} field will be printed after each test.
      */
     public static final String VERBOSE_OUTPUT_KEY = "org.apache.sis.test.verbose";
 
     /**
-     * The {@value} system property for enabling display of test images or widgets.
+     * The {@systemProperty org.apache.sis.test.gui.show} system property
+     * for enabling display of test images or widgets.
      */
     public static final String SHOW_WIDGET_KEY = "org.apache.sis.test.gui.show";
 
     /**
-     * The {@value} system property for setting the output encoding.
+     * The {@systemProperty org.apache.sis.test.encoding} system property for setting the output encoding.
      * This property is used only if the {@link #VERBOSE_OUTPUT_KEY} property
      * is set to "{@code true}". If this property is not set, then the system
      * encoding will be used.
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java b/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
index d6c40eb..2e98b2a 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/TestSuite.java
@@ -25,8 +25,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.URL;
-import java.net.URISyntaxException;
 import org.apache.sis.internal.system.Shutdown;
 import org.apache.sis.internal.system.SystemListener;
 import org.apache.sis.util.logging.MonolineFormatter;
@@ -92,35 +90,12 @@
      */
     protected static void assertNoMissingTest(final Class<? extends TestSuite> suite) {
         if (skipCheckForMissingTests) return;
-        final ClassLoader loader = suite.getClassLoader();
-        final URL url = loader.getResource(suite.getName().replace('.', '/') + ".class");
-        assertNotNull("Test suite class not found.", url);
-        File root;
-        try {
-            root = new File(url.toURI());
-        } catch (URISyntaxException | IllegalArgumentException e) {
-            // If not a file, then it is probably an entry in a JAR file.
-            fail(e.toString());
-            return;
-        }
-        for (File c = new File(suite.getName().replace('.', File.separatorChar)); (c = c.getParentFile()) != null;) {
-            root = root.getParentFile();
-            assertNotNull("Unexpected directory structure.", root);
-            assertEquals("Unexpected directory structure.", c.getName(), root.getName());
-        }
         /*
-         * At this point, we found the root "org" package. Verifies if we are in the Maven target directory.
-         * In some IDE configuration, all the ".class" files are in the same directory, in which case the
-         * verification performed by this method become irrelevant.
+         * Verifies if we are in the Maven target directory. In some IDE configuration, all the ".class" files
+         * are in the same directory, in which case the verification performed by this method become irrelevant.
          */
-        File moduleDir = root;
-        for (int i=0; i<3; i++) {
-            moduleDir = moduleDir.getParentFile();
-            if (moduleDir == null) {
-                return;
-            }
-        }
-        if (!new File(moduleDir, "pom.xml").isFile()) {
+        final ProjectDirectories dir = new ProjectDirectories(suite);
+        if (!dir.isMavenModule()) {
             return;
         }
         /*
@@ -142,6 +117,8 @@
                 it.remove();
             }
         }
+        final ClassLoader loader = suite.getClassLoader();
+        final File root = dir.classesRootDirectory.resolve("org").toFile();
         removeExistingTests(loader, root, new StringBuilder(120).append(root.getName()), tests);
         if (!tests.isEmpty()) {
             fail("Classes not found. Are they defined in an other module? " + tests);
diff --git a/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java b/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
index 2899f39..48d530c 100644
--- a/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
+++ b/core/sis-utility/src/test/java/org/apache/sis/test/package-info.java
@@ -41,7 +41,7 @@
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */
diff --git a/ide-project/NetBeans/nbproject/project.properties b/ide-project/NetBeans/nbproject/project.properties
index 0b1421b..12b6aee 100644
--- a/ide-project/NetBeans/nbproject/project.properties
+++ b/ide-project/NetBeans/nbproject/project.properties
@@ -102,9 +102,9 @@
 istack.version       = 3.0.8
 activation.version   = 1.1
 jama.version         = 1.0.3
-geographlib.version  = 1.49
+geographlib.version  = 1.50
 guava.version        = 27.0.1-jre
-esri.api.version     = 2.2.2
+esri.api.version     = 2.2.3
 jts.version          = 1.16.1
 georss.version       = 0.9.8
 rome.version         = 0.9
@@ -122,7 +122,7 @@
 hamcrest.version     = 1.3
 derby.version        = 10.14.2.0
 hsqldb.version       = 2.5.0
-postgresql.version   = 42.2.6
+postgresql.version   = 42.2.8
 icons.version        = 3.0.1
 
 #
diff --git a/pom.xml b/pom.xml
index c28b1e3..5af5050 100644
--- a/pom.xml
+++ b/pom.xml
@@ -439,7 +439,7 @@
       <dependency>
         <groupId>com.esri.geometry</groupId>
         <artifactId>esri-geometry-api</artifactId>
-        <version>2.2.2</version>
+        <version>2.2.3</version>
         <optional>true</optional>
       </dependency>
       <dependency>
@@ -457,7 +457,7 @@
       <dependency>
         <groupId>net.sf.geographiclib</groupId>
         <artifactId>GeographicLib-Java</artifactId>
-        <version>1.49</version>
+        <version>1.50</version>
         <scope>test</scope>
       </dependency>
       <dependency>
@@ -481,7 +481,7 @@
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-compress</artifactId>
-        <version>1.18</version>
+        <version>1.19</version>
       </dependency>
 
       <!-- Databases -->
@@ -500,7 +500,7 @@
       <dependency>
         <groupId>org.postgresql</groupId>
         <artifactId>postgresql</artifactId>
-        <version>42.2.6</version>
+        <version>42.2.8</version>
         <scope>test</scope>
       </dependency>
 
diff --git a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
index b74c9c4..9d38bf8 100644
--- a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
+++ b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
@@ -110,7 +110,7 @@
  *
  * @author  Alexis Manin (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see <a href="http://global.jaxa.jp/projects/sat/gcom_c/">SHIKISAI (GCOM-C) on JAXA</a>
  * @see <a href="https://en.wikipedia.org/wiki/Global_Change_Observation_Mission">GCOM on Wikipedia</a>
diff --git a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java
index f353184..1381a2a 100644
--- a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java
+++ b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/package-info.java
@@ -21,7 +21,7 @@
  *
  * @author  Alexis Manin (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
index 6310d20..0cce4f1 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
@@ -63,7 +63,7 @@
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @see <a href="https://issues.apache.org/jira/browse/SIS-315">SIS-315</a>
  *
@@ -555,8 +555,7 @@
      * Otherwise if this method returns the range of real values, then that range shall be an instance
      * of {@link MeasurementRange} for allowing the caller to distinguish the two cases.
      *
-     * @param  data  the variable to get valid range of values for.
-     *               This is usually a variable containing raster data.
+     * @param  data  the variable to get valid range of values for (usually a variable containing raster data).
      * @return the range of valid values, or {@code null} if unknown.
      *
      * @see Variable#getRangeFallback()
@@ -590,23 +589,48 @@
                     data.decoder.illegalAttributeValue(attribute, values.stringValue(i), e);
                 }
             }
+            /*
+             * Stop the loop and return a range as soon as we have enough information.
+             * Note that we may loop over many attributes before to complete information.
+             */
             if (minimum != null && maximum != null) {
                 /*
                  * Heuristic rule defined in UCAR documentation (see EnhanceScaleMissing interface):
                  * if the type of the range is equal to the type of the scale, and the type of the
                  * data is not wider, then assume that the minimum and maximum are real values.
                  */
+                final Class<?> scaleType  = data.getAttributeType(CDM.SCALE_FACTOR);
+                final Class<?> offsetType = data.getAttributeType(CDM.ADD_OFFSET);
                 final int rangeType = Numbers.getEnumConstant(type);
-                if (rangeType >= data.getDataType().number &&
-                    rangeType >= Math.max(Numbers.getEnumConstant(data.getAttributeType(CDM.SCALE_FACTOR)),
-                                          Numbers.getEnumConstant(data.getAttributeType(CDM.ADD_OFFSET))))
+                if ((scaleType != null || offsetType != null)
+                        && rangeType >= data.getDataType().number
+                        && rangeType >= Math.max(Numbers.getEnumConstant(scaleType),
+                                                 Numbers.getEnumConstant(offsetType)))
                 {
                     @SuppressWarnings({"unchecked", "rawtypes"})
                     final NumberRange<?> range = new MeasurementRange(type, minimum, true, maximum, true, data.getUnit());
                     return range;
                 } else {
+                    /*
+                     * At this point, we determined that the range uses sample values (i.e. values before
+                     * conversion to the unit of measurement). Before to return that range, check if the
+                     * minimum or maximum value overlaps with a "no data" value. If yes, resolve the
+                     * overlapping by making a range bound exclusive instead than inclusive.
+                     */
+                    boolean isMinIncluded = true;
+                    boolean isMaxIncluded = true;
+                    final Set<Number> nodataValues = data.getNodataValues().keySet();
+                    if (!nodataValues.isEmpty()) {
+                        final double minValue = minimum.doubleValue();
+                        final double maxValue = maximum.doubleValue();
+                        for (final Number pad : nodataValues) {
+                            final double value = pad.doubleValue();
+                            isMinIncluded &= (minValue != value);
+                            isMaxIncluded &= (maxValue != value);
+                        }
+                    }
                     @SuppressWarnings({"unchecked", "rawtypes"})
-                    final NumberRange<?> range = new NumberRange(type, minimum, true, maximum, true);
+                    final NumberRange<?> range = new NumberRange(type, minimum, isMinIncluded, maximum, isMaxIncluded);
                     return range;
                 }
             }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
index 20a8eea..9786b78 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
@@ -40,10 +40,13 @@
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridDerivation;
 import org.apache.sis.coverage.grid.GridRoundingMode;
+import org.apache.sis.coverage.grid.DisjointExtentException;
+import org.apache.sis.coverage.IllegalSampleDimensionException;
 import org.apache.sis.storage.netcdf.AttributeNames;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.DataStoreReferencingException;
+import org.apache.sis.storage.NoSuchDataException;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.MeasurementRange;
@@ -63,7 +66,7 @@
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Johann Sorel (Geomatys)
  * @author  Alexis Manin (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  * @module
  */
@@ -375,14 +378,16 @@
             synchronized (lock) {
                 for (int i=0; i<ranges.length; i++) {
                     if (ranges[i] == null) {
-                        if (builder == null) builder = new SampleDimension.Builder();
+                        if (builder == null) {
+                            builder = new SampleDimension.Builder();
+                        }
                         ranges[i] = createSampleDimension(builder, getVariable(i), i);
                         builder.clear();
                     }
                 }
             }
-        } catch (TransformException e) {
-            throw new DataStoreReferencingException(e);
+        } catch (RuntimeException e) {
+            throw new DataStoreContentException(e);
         }
         return UnmodifiableArrayList.wrap(ranges);
     }
@@ -393,18 +398,15 @@
      * @param  builder  the builder to use for creating the sample dimension.
      * @param  band     the data for which to create a sample dimension.
      * @param  index    index in the variable dimension identified by {@link #bandDimension}.
-     * @throws TransformException if an error occurred while using the transfer function.
      */
-    private SampleDimension createSampleDimension(final SampleDimension.Builder builder, final Variable band, final int index)
-            throws TransformException
-    {
+    private SampleDimension createSampleDimension(final SampleDimension.Builder builder, final Variable band, final int index) {
         /*
          * Take the minimum and maximum values as determined by Apache SIS through the Convention class.  The UCAR library
          * is used only as a fallback. We give precedence to the range computed by Apache SIS instead than the range given
          * by UCAR because we need the range of packed values instead than the range of converted values.
          */
         NumberRange<?> range;
-        if (!createEnumeration(builder, band, index) && (range = band.getValidRange()) != null) {
+        if (!createEnumeration(builder, band, index) && (range = band.getValidRange()) != null) try {
             final MathTransform1D mt = band.getTransferFunction().getTransform();
             if (!mt.isIdentity() && range instanceof MeasurementRange<?>) {
                 /*
@@ -445,6 +447,14 @@
                 if (name == null) name = band.getName();
                 builder.addQuantitative(name, range, mt, band.getUnit());
             }
+        } catch (TransformException e) {
+            /*
+             * This exception may happen in the call to `inverse.transform`, when we tried to convert
+             * a range of measurement values (in the unit of measurement) to a range of sample values.
+             * If we failed to do that, we will not add quantitative category. But we still can add
+             * qualitative categories for "no data" sample values in the rest of this method.
+             */
+            warning(e);
         }
         /*
          * Adds the "missing value" or "fill value" as qualitative categories.  If a value has both roles, use "missing value"
@@ -491,7 +501,21 @@
         if (bandDimension >= 0) {
             name = Strings.toIndexed(name, index);
         }
-        return builder.setName(name).build();
+        builder.setName(name);
+        SampleDimension sd;
+        try {
+            sd = builder.build();
+        } catch (IllegalSampleDimensionException e) {
+            /*
+             * This error may happen if we have overlapping ranges of sample values.
+             * Abandon all categories. We do not keep the quantitative category because
+             * using it without taking in account the "no data" values may be dangerous.
+             */
+            builder.categories().clear();
+            sd = builder.build();
+            warning(e);
+        }
+        return sd;
     }
 
     /**
@@ -632,15 +656,15 @@
              */
             imageBuffer = RasterFactory.wrap(dataType.rasterDataType, sampleValues);
         } catch (IOException e) {
-            throw new DataStoreException(e);
-        } catch (TransformException e) {
-            throw new DataStoreReferencingException(e);
+            throw new DataStoreException(canNotReadFile(), e);
+        } catch (DisjointExtentException e) {
+            throw new NoSuchDataException(canNotReadFile(), e);
         } catch (RuntimeException e) {                          // Many exceptions thrown by RasterFactory.wrap(…).
             final Throwable cause = e.getCause();
             if (cause instanceof TransformException) {
-                throw new DataStoreReferencingException(cause);
+                throw new DataStoreReferencingException(canNotReadFile(), cause);
             } else {
-                throw new DataStoreContentException(e);
+                throw new DataStoreContentException(canNotReadFile(), e);
             }
         }
         if (imageBuffer == null) {
@@ -651,14 +675,19 @@
     }
 
     /**
+     * Returns the error message for a file that can not be read.
+     *
+     * @return default error message to use in exceptions.
+     */
+    private String canNotReadFile() {
+        return Errors.getResources(getLocale()).getString(Errors.Keys.CanNotRead_1, getFilename());
+    }
+
+    /**
      * Returns the name of the netCDF file. This is used for error messages.
      */
     private String getFilename() {
-        if (location != null) {
-            return location.getFileName().toString();
-        } else {
-            return Vocabulary.getResources(getLocale()).getString(Vocabulary.Keys.Unnamed);
-        }
+        return (location != null) ? location.getFileName().toString() : getSourceName();
     }
 
     /**
diff --git a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileDescriptor.java b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileDescriptor.java
index 9c6318f..c3ab8bb 100644
--- a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileDescriptor.java
+++ b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileDescriptor.java
@@ -159,13 +159,13 @@
     }
 
     /**
-     * Returns the M Min property. 
+     * Returns the M Min property.
      * @return M min.
      */
     public double getMmin() {
         return this.mmin;
     }
-    
+
     /**
      * Returns the M Max property.
      * @return M Max.
@@ -180,7 +180,7 @@
     @Override
     public String toString() {
         StringBuilder s = new StringBuilder();
-        String lineSeparator = System.getProperty("line.separator", "\n");
+        String lineSeparator = System.lineSeparator();
 
         s.append("FileCode: ").append(this.fileCode).append(lineSeparator);
         s.append("FileLength: ").append(this.fileLength).append(lineSeparator);
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
index d12951e..738f8cc 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalNameException.java
@@ -37,7 +37,7 @@
  * @since   0.8
  * @module
  */
-public class IllegalNameException extends DataStoreException {
+public class IllegalNameException extends NoSuchDataException {
     /**
      * For cross-version compatibility.
      */
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/NoSuchDataException.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/NoSuchDataException.java
new file mode 100644
index 0000000..8c786ff
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/NoSuchDataException.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage;
+
+import java.util.Locale;
+import org.apache.sis.internal.storage.Resources;
+
+
+/**
+ * Thrown when requested data are not found in the data store.
+ * It may be because no resource of the given name has been found,
+ * or because data have been requested in a region of interest that
+ * does not intersect the data region.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public class NoSuchDataException extends DataStoreException {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -5883260753916229790L;
+
+    /**
+     * Creates an exception with no cause and no details message.
+     */
+    public NoSuchDataException() {
+    }
+
+    /**
+     * Creates an exception with the specified details message.
+     *
+     * @param message  the detail message.
+     */
+    public NoSuchDataException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Creates an exception with the specified cause and no details message.
+     *
+     * @param cause  the cause for this exception.
+     */
+    public NoSuchDataException(final Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates an exception with the specified details message and cause.
+     *
+     * @param message  the detail message.
+     * @param cause    the cause for this exception.
+     */
+    public NoSuchDataException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Creates a new exception which will format a localized message in the given locale.
+     *
+     * @param locale      the locale for the message to be returned by {@link #getLocalizedMessage()}.
+     * @param key         one of {@link Resources.Keys} constants.
+     * @param parameters  parameters to use for formatting the messages.
+     */
+    NoSuchDataException(final Locale locale, final short key, final Object... parameters) {
+        super(locale, key, parameters);
+    }
+}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java
index 039c60a..b5bd5c7 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java
@@ -23,12 +23,12 @@
 import java.util.logging.Logger;
 import java.util.logging.LogRecord;
 import java.lang.reflect.Method;
-import org.apache.sis.util.Classes;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.internal.system.Modules;
 import org.apache.sis.internal.storage.StoreResource;
 import org.apache.sis.storage.DataStoreProvider;
@@ -276,7 +276,7 @@
                 }
             }
         }
-        return Classes.getShortClassName(source);
+        return Vocabulary.getResources(getLocale()).getString(Vocabulary.Keys.Unnamed);
     }
 
     /**