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

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

commit 871d8fd9a33739ae10d8716c30ad1e56a390b174
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Tue Dec 13 18:05:28 2022 +0100

    Replace most of `org.apache.sis.storage.geotiff.Tags` by 
`javax.imageio.plugins.tiff`.
    The latter provides a more complete set of tags that we can use as labels 
in metadata.
---
 .../apache/sis/internal/geotiff/Compression.java   |  36 ++--
 .../org/apache/sis/internal/geotiff/Predictor.java |   8 +-
 .../apache/sis/internal/geotiff/package-info.java  |   2 +-
 .../org/apache/sis/storage/geotiff/DataCube.java   |   6 +-
 .../apache/sis/storage/geotiff/GeoKeysLoader.java  |  23 +--
 .../sis/storage/geotiff/ImageFileDirectory.java    | 222 +++++++++++----------
 .../sis/storage/geotiff/ImageMetadataBuilder.java  |  18 +-
 .../apache/sis/storage/geotiff/NativeMetadata.java |  26 +--
 .../java/org/apache/sis/storage/geotiff/Tags.java  | 192 ++++--------------
 .../apache/sis/storage/geotiff/package-info.java   |   2 +-
 .../org/apache/sis/storage/geotiff/TagsTest.java}  |  33 ++-
 .../apache/sis/test/suite/GeoTiffTestSuite.java    |   3 +-
 12 files changed, 255 insertions(+), 316 deletions(-)

diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Compression.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Compression.java
index e3d00695c9..384d6f9e51 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Compression.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Compression.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.internal.geotiff;
 
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
+
 
 /**
  * Possible values for {@link org.apache.sis.storage.geotiff.Tags#Compression}.
@@ -30,7 +32,7 @@ package org.apache.sis.internal.geotiff;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   0.8
  * @module
  */
@@ -43,7 +45,7 @@ public enum Compression {
      *   <li>Name in WCS response: "None"</li>
      * </ul>
      */
-    NONE(1),
+    NONE(COMPRESSION_NONE),
 
     /**
      * CCITT Group 3, 1-Dimensional Modified Huffman run length encoding.
@@ -52,7 +54,7 @@ public enum Compression {
      *   <li>Name in WCS response: "CCITTRLE"</li>
      * </ul>
      */
-    CCITTRLE(2),
+    CCITTRLE(COMPRESSION_CCITT_RLE),
 
     /**
      * PackBits compression, a simple byte-oriented run length scheme.
@@ -61,7 +63,7 @@ public enum Compression {
      *   <li>Name in WCS response: "PackBits"</li>
      * </ul>
      */
-    PACKBITS(32773),
+    PACKBITS(COMPRESSION_PACKBITS),
 
     // ---- End of baseline GeoTIFF. Remaining are extensions cited by OGC 
standard ----
 
@@ -72,7 +74,7 @@ public enum Compression {
      *   <li>Name in WCS response: "LZW"</li>
      * </ul>
      */
-    LZW(5),
+    LZW(COMPRESSION_LZW),
 
     /**
      * Deflate compression, like ZIP format. This is sometimes named {@code 
"ADOBE_DEFLATE"},
@@ -83,7 +85,7 @@ public enum Compression {
      *   <li>Other name:           "ADOBE_DEFLATE"</li>
      * </ul>
      */
-    DEFLATE(8),
+    DEFLATE(COMPRESSION_ZLIB),
 
     /**
      * JPEG compression.
@@ -93,12 +95,12 @@ public enum Compression {
      *   <li>Name of old JPEG:     "OJPEG" (code 6)</li>
      * </ul>
      */
-    JPEG(7),
+    JPEG(COMPRESSION_JPEG),
 
     // ---- Remaining are extension to both baseline and OGC standard ----
 
-    /** Unsupported. */ CCITTFAX3(3),
-    /** Unsupported. */ CCITTFAX4(4),
+    /** Unsupported. */ CCITTFAX3(COMPRESSION_CCITT_T_4),
+    /** Unsupported. */ CCITTFAX4(COMPRESSION_CCITT_T_6),
     /** Unsupported. */ NEXT(32766),
     /** Unsupported. */ CCITTRLEW(32771),
     /** Unsupported. */ THUNDERSCAN(32809),
@@ -139,14 +141,14 @@ public enum Compression {
      */
     public static Compression valueOf(final int code) {
         switch (code) {
-            case 1:     return NONE;
-            case 2:     return CCITTRLE;
-            case 5:     return LZW;
-            case 6:     // "old-style" JPEG, later overriden in Technical 
Notes 2.
-            case 7:     return JPEG;
-            case 8:
-            case 32946: return DEFLATE;
-            case 32773: return PACKBITS;
+            case 32946:                 // Fall through
+            case COMPRESSION_ZLIB:      return DEFLATE;
+            case COMPRESSION_OLD_JPEG:  // "old-style" JPEG, later overriden 
in Technical Notes 2.
+            case COMPRESSION_JPEG:      return JPEG;
+            case COMPRESSION_CCITT_RLE: return CCITTRLE;
+            case COMPRESSION_LZW:       return LZW;
+            case COMPRESSION_PACKBITS:  return PACKBITS;
+            case COMPRESSION_NONE:      return NONE;
             default: {
                 // Fallback for uncommon formats.
                 for (final Compression c : values()) {
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Predictor.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Predictor.java
index 7066cef3f9..43d8b9ebeb 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Predictor.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Predictor.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.internal.geotiff;
 
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
+
 
 /**
  * Possible values for {@link org.apache.sis.storage.geotiff.Tags#Predictor}.
@@ -23,7 +25,7 @@ package org.apache.sis.internal.geotiff;
  * before an encoding scheme is applied.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  * @module
  */
@@ -56,8 +58,8 @@ public enum Predictor {
      */
     public static Predictor valueOf(final int code) {
         switch (code) {
-            case 1:  return NONE;
-            case 2:  return HORIZONTAL;
+            case PREDICTOR_NONE: return NONE;
+            case PREDICTOR_HORIZONTAL_DIFFERENCING: return HORIZONTAL;
             case 3:  return FLOAT;
             default: return UNKNOWN;
         }
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
index 3c0a7251cc..ac3c05f342 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
@@ -24,7 +24,7 @@
  * may change in incompatible ways in any future version without notice.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   0.8
  * @module
  */
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
index c7cbc41f52..6db58e1d26 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java
@@ -36,6 +36,8 @@ import org.apache.sis.internal.storage.ResourceOnFileSystem;
 import org.apache.sis.internal.storage.StoreResource;
 import org.apache.sis.math.Vector;
 
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.TAG_COMPRESSION;
+
 
 /**
  * One or many GeoTIFF images packaged as a single resource.
@@ -47,7 +49,7 @@ import org.apache.sis.math.Vector;
  * as it may cause an infinite loop in {@code listeners.getLocale()} call.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   1.1
  * @module
  */
@@ -214,7 +216,7 @@ abstract class DataCube extends TiledGridResource 
implements ResourceOnFileSyste
                 final Compression compression = getCompression();
                 if (compression == null) {
                     throw new 
DataStoreContentException(reader.resources().getString(
-                            Resources.Keys.MissingValue_2, 
Tags.name(Tags.Compression)));
+                            Resources.Keys.MissingValue_2, Tags.name((short) 
TAG_COMPRESSION)));
                 }
                 /*
                  * The `DataSubset` parent class is the most efficient but has 
many limitations
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeysLoader.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeysLoader.java
index abc728eba7..59f7bb95e8 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeysLoader.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeysLoader.java
@@ -21,15 +21,17 @@ import org.apache.sis.math.Vector;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.internal.geotiff.Resources;
 
+import static javax.imageio.plugins.tiff.GeoTIFFTagSet.*;
+
 
 /**
  * Loads GeoTIFF keys in a hash map, but without performing any interpretation.
  * A {@code GeoKeysLoader} receives as inputs the values of the following TIFF 
tags:
  *
  * <ul>
- *   <li>{@link Tags#GeoKeyDirectory} — array of unsigned {@code short} values 
grouped into blocks of 4.</li>
- *   <li>{@link Tags#GeoDoubleParams} — array of {@double} values referenced 
by {@code GeoKeyDirectory} elements.</li>
- *   <li>{@link Tags#GeoAsciiParams}  — array of characters referenced by 
{@code GeoKeyDirectory} elements.</li>
+ *   <li>{@code GeoKeyDirectory} — array of unsigned {@code short} values 
grouped into blocks of 4.</li>
+ *   <li>{@code GeoDoubleParams} — array of {@double} values referenced by 
{@code GeoKeyDirectory} elements.</li>
+ *   <li>{@code GeoAsciiParams}  — array of characters referenced by {@code 
GeoKeyDirectory} elements.</li>
  * </ul>
  *
  * For example, consider the following values for the above-cited tags:
@@ -65,7 +67,7 @@ import org.apache.sis.internal.geotiff.Resources;
  *
  * @author  Rémi Maréchal (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   1.2
  * @module
  */
@@ -83,16 +85,12 @@ class GeoKeysLoader {
     /**
      * References the {@link GeoKeys} needed for building the Coordinate 
Reference System.
      * Cannot be null when invoking {@link #load(Map)}.
-     *
-     * @see Tags#GeoKeyDirectory
      */
     public Vector keyDirectory;
 
     /**
      * The numeric values referenced by the {@link #keyDirectory}.
      * Can be {@code null} if none.
-     *
-     * @see Tags#GeoDoubleParams
      */
     public Vector numericParameters;
 
@@ -100,7 +98,6 @@ class GeoKeysLoader {
      * The characters referenced by the {@link #keyDirectory}.
      * Can be {@code null} if none.
      *
-     * @see Tags#GeoAsciiParams
      * @see #setAsciiParameters(String[])
      */
     public String asciiParameters;
@@ -125,7 +122,7 @@ class GeoKeysLoader {
     }
 
     /**
-     * Sets the value of {@link #asciiParameters} from {@link 
Tags#GeoAsciiParams} value.
+     * Sets the value of {@link #asciiParameters} from {@code GeoAsciiParams} 
value.
      */
     final void setAsciiParameters(final String[] values) {
         switch (values.length) {
@@ -208,7 +205,7 @@ class GeoKeysLoader {
                  * the specification does not allocate a separated vector for 
them. We use the
                  * `int` type if needed for allowing storage of unsigned short 
values.
                  */
-                case Tags.GeoKeyDirectory & 0xFFFF: {
+                case TAG_GEO_KEY_DIRECTORY: {
                     if (valueOffset + count > keyDirectory.size()) {
                         missingValue(key);
                         continue;
@@ -231,7 +228,7 @@ class GeoKeysLoader {
                  * Values of type `double` are read from a separated vector, 
`numericParameters`.
                  * Result is stored in a Double wrapper or in an array of type 
'double[]'.
                  */
-                case Tags.GeoDoubleParams & 0xFFFF: {
+                case TAG_GEO_DOUBLE_PARAMS: {
                     if (valueOffset + count > numberOfDoubles) {
                         missingValue(key);
                         continue;
@@ -254,7 +251,7 @@ class GeoKeysLoader {
                  * ASCII encoding use the pipe ('|') character as a 
replacement for the NUL character
                  * used in C/C++ programming languages. We need to omit those 
trailing characters.
                  */
-                case Tags.GeoAsciiParams & 0xFFFF: {
+                case TAG_GEO_ASCII_PARAMS: {
                     int upper = valueOffset + count;
                     if (upper > numberOfChars) {
                         missingValue(key);
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index 2a8f14615e..9b77520cf4 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -59,6 +59,9 @@ import org.apache.sis.math.Vector;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.image.DataType;
 
+import static javax.imageio.plugins.tiff.GeoTIFFTagSet.*;
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
+
 
 /**
  * An Image File Directory (FID) in a TIFF image.
@@ -72,7 +75,7 @@ import org.apache.sis.image.DataType;
  * @author  Johann Sorel (Geomatys)
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  *
  * @see <a href="http://www.awaresystems.be/imaging/tiff/tifftags.html";>TIFF 
Tag Reference</a>
  *
@@ -89,7 +92,7 @@ final class ImageFileDirectory extends DataCube {
 
     /**
      * Possible value for {@link #sampleFormat} specifying how to interpret 
each data sample in a pixel.
-     * Those values are not necessarily the same than the ones documented in 
{@link Tags#SampleFormat}.
+     * Those values are not necessarily the same than the ones documented in 
{@code TAG_SAMPLE_FORMAT}.
      * Default value is {@link #UNSIGNED}.
      */
     private static final byte SIGNED = 1, UNSIGNED = 0, FLOAT = 3;
@@ -537,33 +540,33 @@ final class ImageFileDirectory extends DataCube {
              * 1 = Chunky format. The component values for each pixel are 
stored contiguously (for example RGBRGBRGB).
              * 2 = Planar format. For example, one plane of Red components, 
one plane of Green and one plane if Blue.
              */
-            case Tags.PlanarConfiguration: {
+            case TAG_PLANAR_CONFIGURATION: {
                 final int value = type.readInt(input(), count);
                 switch (value) {
-                    case 1:  isPlanar = false; break;
-                    case 2:  isPlanar = true;  break;
-                    default: return value;                  // Cause a warning 
to be reported by the caller.
+                    case PLANAR_CONFIGURATION_CHUNKY: isPlanar = false; break;
+                    case PLANAR_CONFIGURATION_PLANAR: isPlanar = true;  break;
+                    default: return value;      // Cause a warning to be 
reported by the caller.
                 }
                 break;
             }
             /*
              * The number of columns in the image, i.e., the number of pixels 
per row.
              */
-            case Tags.ImageWidth: {
+            case TAG_IMAGE_WIDTH: {
                 imageWidth = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
              * The number of rows of pixels in the image.
              */
-            case Tags.ImageLength: {
+            case TAG_IMAGE_LENGTH: {
                 imageHeight = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
              * The tile width in pixels. This is the number of columns in each 
tile.
              */
-            case Tags.TileWidth: {
+            case TAG_TILE_WIDTH: {
                 setTileTagFamily(TILE);
                 tileWidth = type.readInt(input(), count);
                 break;
@@ -571,7 +574,7 @@ final class ImageFileDirectory extends DataCube {
             /*
              * The tile length (height) in pixels. This is the number of rows 
in each tile.
              */
-            case Tags.TileLength: {
+            case TAG_TILE_LENGTH: {
                 setTileTagFamily(TILE);
                 tileHeight = type.readInt(input(), count);
                 break;
@@ -580,7 +583,7 @@ final class ImageFileDirectory extends DataCube {
              * The number of rows per strip. This is considered by SIS as a 
special kind of tiles.
              * From this point of view, TileLength = RowPerStrip and TileWidth 
= ImageWidth.
              */
-            case Tags.RowsPerStrip: {
+            case TAG_ROWS_PER_STRIP: {
                 setTileTagFamily(STRIP);
                 tileHeight = type.readInt(input(), count);
                 break;
@@ -588,7 +591,7 @@ final class ImageFileDirectory extends DataCube {
             /*
              * The tile length (height) in pixels. This is the number of rows 
in each tile.
              */
-            case Tags.TileOffsets: {
+            case TAG_TILE_OFFSETS: {
                 setTileTagFamily(TILE);
                 tileOffsets = type.readVector(input(), count);
                 break;
@@ -597,7 +600,7 @@ final class ImageFileDirectory extends DataCube {
              * For each strip, the byte offset of that strip relative to the 
beginning of the TIFF file.
              * In Apache SIS implementation, strips are considered as a 
special kind of tiles.
              */
-            case Tags.StripOffsets: {
+            case TAG_STRIP_OFFSETS: {
                 setTileTagFamily(STRIP);
                 tileOffsets = type.readVector(input(), count);
                 break;
@@ -605,7 +608,7 @@ final class ImageFileDirectory extends DataCube {
             /*
              * The tile width in pixels. This is the number of columns in each 
tile.
              */
-            case Tags.TileByteCounts: {
+            case TAG_TILE_BYTE_COUNTS: {
                 setTileTagFamily(TILE);
                 tileByteCounts = type.readVector(input(), count);
                 break;
@@ -614,7 +617,7 @@ final class ImageFileDirectory extends DataCube {
              * For each strip, the number of bytes in the strip after 
compression.
              * In Apache SIS implementation, strips are considered as a 
special kind of tiles.
              */
-            case Tags.StripByteCounts: {
+            case TAG_STRIP_BYTE_COUNTS: {
                 setTileTagFamily(STRIP);
                 tileByteCounts = type.readVector(input(), count);
                 break;
@@ -622,12 +625,12 @@ final class ImageFileDirectory extends DataCube {
             /*
              * Legacy tags for JPEG formats, to be also interpreted as a tile.
              */
-            case Tags.JPEGInterchangeFormat: {
+            case TAG_JPEG_INTERCHANGE_FORMAT: {
                 setTileTagFamily(JPEG);
                 tileOffsets = type.readVector(input(), count);
                 break;
             }
-            case Tags.JPEGInterchangeFormatLength: {
+            case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH: {
                 setTileTagFamily(JPEG);
                 tileByteCounts = type.readVector(input(), count);
                 break;
@@ -643,7 +646,7 @@ final class ImageFileDirectory extends DataCube {
             /*
              * Compression scheme used on the image data.
              */
-            case Tags.Compression: {
+            case TAG_COMPRESSION: {
                 final int value = type.readInt(input(), count);
                 compression = Compression.valueOf(value);
                 if (compression == Compression.UNKNOWN) {
@@ -655,7 +658,7 @@ final class ImageFileDirectory extends DataCube {
              * Mathematical operator that is applied to the image data before 
an encoding scheme is applied.
              * 1=none, 2=horizontal differencing. More values may be added in 
the future.
              */
-            case Tags.Predictor: {
+            case TAG_PREDICTOR: {
                 final int value = type.readInt(input(), count);
                 predictor = Predictor.valueOf(value);
                 if (predictor == Predictor.UNKNOWN) {
@@ -667,12 +670,12 @@ final class ImageFileDirectory extends DataCube {
              * The logical order of bits within a byte. If this value is 2, 
then
              * bits order shall be reversed in every bytes before 
decompression.
              */
-            case Tags.FillOrder: {
+            case TAG_FILL_ORDER: {
                 final int value = type.readInt(input(), count);
                 switch (value) {
-                    case 1: isBitOrderReversed = false; break;
-                    case 2: isBitOrderReversed = true;  break;
-                    default: return value;                  // Cause a warning 
to be reported by the caller.
+                    case FILL_ORDER_LEFT_TO_RIGHT: isBitOrderReversed = false; 
break;
+                    case FILL_ORDER_RIGHT_TO_LEFT: isBitOrderReversed = true;  
break;
+                    default: return value;      // Cause a warning to be 
reported by the caller.
                 }
                 break;
             }
@@ -680,14 +683,17 @@ final class ImageFileDirectory extends DataCube {
              * How to interpret each data sample in a pixel. The size of data 
samples is still
              * specified by the BitsPerSample field.
              */
-            case Tags.SampleFormat: {
+            case TAG_SAMPLE_FORMAT: {
                 final int value = type.readInt(input(), count);
                 switch (value) {
-                    default: return value;                          // Warning 
to be reported by the caller.
-                    case 1: sampleFormat = UNSIGNED; break;         // 
Unsigned integer data (default).
-                    case 2: sampleFormat = SIGNED;   break;         // Two’s 
complement signed integer data.
-                    case 3: sampleFormat = FLOAT;    break;         // IEEE 
floating point data.
-                    case 4: warning(Level.WARNING, 
Resources.Keys.UndefinedDataFormat_1, filename()); break;
+                    default: return value;      // Warning to be reported by 
the caller.
+                    case SAMPLE_FORMAT_UNSIGNED_INTEGER: sampleFormat = 
UNSIGNED; break;
+                    case SAMPLE_FORMAT_SIGNED_INTEGER:   sampleFormat = 
SIGNED;   break;
+                    case SAMPLE_FORMAT_FLOATING_POINT:   sampleFormat = FLOAT; 
   break;
+                    case SAMPLE_FORMAT_UNDEFINED: {
+                        warning(Level.WARNING, 
Resources.Keys.UndefinedDataFormat_1, filename());
+                        break;
+                    }
                 }
                 break;
             }
@@ -696,7 +702,7 @@ final class ImageFileDirectory extends DataCube {
              * pixel (e.g. 3 for RGB values). Typically, all components have 
the same number of bits.
              * But the TIFF specification allows different values.
              */
-            case Tags.BitsPerSample: {
+            case TAG_BITS_PER_SAMPLE: {
                 final Vector values = type.readVector(input(), count);
                 /*
                  * The current implementation requires that all 
`bitsPerSample` elements have the same value.
@@ -717,7 +723,7 @@ final class ImageFileDirectory extends DataCube {
              * The number of components per pixel. Usually 1 for bilevel, 
grayscale, and palette-color images,
              * and 3 for RGB images. Default value is 1.
              */
-            case Tags.SamplesPerPixel: {
+            case TAG_SAMPLES_PER_PIXEL: {
                 samplesPerPixel = type.readShort(input(), count);
                 break;
             }
@@ -727,7 +733,7 @@ final class ImageFileDirectory extends DataCube {
              * image normally has SamplesPerPixel=3. If SamplesPerPixel is 
greater than 3, then the ExtraSamples field
              * describes the meaning of the extra samples. It may be an alpha 
channel, but not necessarily.
              */
-            case Tags.ExtraSamples: {
+            case TAG_EXTRA_SAMPLES: {
                 extraSamples = type.readVector(input(), count);
                 break;
             }
@@ -747,7 +753,7 @@ final class ImageFileDirectory extends DataCube {
              * 3 = Palette color. The value of the component is used as an 
index into the RGB values of the ColorMap.
              * 4 = Transparency Mask. Defines an irregularly shaped region of 
another image in the same TIFF file.
              */
-            case Tags.PhotometricInterpretation: {
+            case TAG_PHOTOMETRIC_INTERPRETATION: {
                 final short value = type.readShort(input(), count);
                 if (value < 0 || value > Byte.MAX_VALUE) return value;
                 photometricInterpretation = (byte) value;
@@ -760,7 +766,7 @@ final class ImageFileDirectory extends DataCube {
              * The number of values for each color is (1 << BitsPerSample) 
where 0 represents the minimum intensity
              * (black is 0,0,0) and 65535 represents the maximum intensity.
              */
-            case Tags.ColorMap: {
+            case TAG_COLOR_MAP: {
                 colorMap = type.readVector(input(), count);
                 break;
             }
@@ -768,8 +774,8 @@ final class ImageFileDirectory extends DataCube {
              * The minimum component value used. MinSampleValue is a single 
value that apply to all bands
              * while SMinSampleValue lists separated values for each band. 
Default is 0.
              */
-            case Tags.MinSampleValue:
-            case Tags.SMinSampleValue: {
+            case TAG_MIN_SAMPLE_VALUE:
+            case TAG_S_MIN_SAMPLE_VALUE: {
                 minValues = extremum(minValues, type.readVector(input(), 
count), false);
                 isMinSpecified = true;
                 break;
@@ -779,8 +785,8 @@ final class ImageFileDirectory extends DataCube {
              * This field is for statistical purposes and should not to be 
used to affect the
              * visual appearance of an image, unless a map styling is applied.
              */
-            case Tags.MaxSampleValue:
-            case Tags.SMaxSampleValue: {
+            case TAG_MAX_SAMPLE_VALUE:
+            case TAG_S_MAX_SAMPLE_VALUE: {
                 maxValues = extremum(maxValues, type.readVector(input(), 
count), true);
                 isMaxSpecified = true;
                 break;
@@ -801,7 +807,7 @@ final class ImageFileDirectory extends DataCube {
              * Bit 2 is 1 if the image defines a transparency mask for another 
image in this TIFF file (see PhotometricInterpretation).
              * Bit 4 indicates MRC imaging model as described in ITU-T 
recommendation T.44 [T.44] (See ImageLayer tag) - RFC 2301.
              */
-            case Tags.NewSubfileType: {
+            case TAG_NEW_SUBFILE_TYPE: {
                 subfileType = type.readInt(input(), count);
                 break;
             }
@@ -811,13 +817,13 @@ final class ImageFileDirectory extends DataCube {
              * 2 = reduced-resolution image data
              * 3 = a single page of a multi-page image (see PageNumber).
              */
-            case Tags.SubfileType: {
+            case TAG_SUBFILE_TYPE: {
                 final int value = type.readInt(input(), count);
                 switch (value) {
-                    default: return value;                          // Warning 
to be reported by the caller.
-                    case 1:  subfileType &= ~1; break;
-                    case 2:  subfileType |=  1; break;
-                    case 3:  subfileType |=  2; break;
+                    default: return value;                // Warning to be 
reported by the caller.
+                    case SUBFILE_TYPE_FULL_RESOLUTION:    subfileType &= 
~NEW_SUBFILE_TYPE_REDUCED_RESOLUTION; break;
+                    case SUBFILE_TYPE_REDUCED_RESOLUTION: subfileType |=  
NEW_SUBFILE_TYPE_REDUCED_RESOLUTION; break;
+                    case SUBFILE_TYPE_SINGLE_PAGE:        subfileType |=  
NEW_SUBFILE_TYPE_SINGLE_PAGE;        break;
                 }
                 break;
             }
@@ -833,14 +839,14 @@ final class ImageFileDirectory extends DataCube {
              * An array of unsigned SHORT values, which are primarily grouped 
into blocks of 4.
              * The first 4 values are special, and contain GeoKey directory 
header information.
              */
-            case Tags.GeoKeyDirectory: {
+            case (short) TAG_GEO_KEY_DIRECTORY: {
                 referencing().keyDirectory = type.readVector(input(), count);
                 break;
             }
             /*
              * Stores all of the `double` valued GeoKeys, referenced by the 
GeoKeyDirectory.
              */
-            case Tags.GeoDoubleParams: {
+            case (short) TAG_GEO_DOUBLE_PARAMS: {
                 referencing().numericParameters = type.readVector(input(), 
count);
                 break;
             }
@@ -849,7 +855,7 @@ final class ImageFileDirectory extends DataCube {
              * which will be splitted by CRSBuilder, but we allow an arbitrary 
amount as a paranoiac check.
              * Note that TIFF files use 0 as the end delimiter in strings 
(C/C++ convention).
              */
-            case Tags.GeoAsciiParams: {
+            case (short) TAG_GEO_ASCII_PARAMS: {
                 referencing().setAsciiParameters(type.readString(input(), 
count, encoding()));
                 break;
             }
@@ -857,16 +863,19 @@ final class ImageFileDirectory extends DataCube {
              * The orientation of the image with respect to the rows and 
columns.
              * This is an integer numeroted from 1 to 7 inclusive (see TIFF 
specification for meaning).
              */
-            case Tags.Orientation: {
+            case TAG_ORIENTATION: {
                 // TODO
                 break;
             }
             /*
-             * The "grid to CRS" conversion as a 4×4 matrix in row-major 
fashion. The third matrix row and
-             * the third matrix column may contain only zero values; this 
block does not reduce the number
-             * of dimensions from 3 to 2.
+             * Specifies the "grid to CRS" conversion between the raster space 
and the model space.
+             * If specified, the tag shall have the 16 values of a 4×4 matrix 
in row-major fashion.
+             * The last matrix row (i.e. the last 4 values) should be [0 0 0 
1].
+             * The row before should be [0 0 0 0] if the conversion is 
two-dimensional.
+             * This block does not reduce the number of dimensions from 3 to 2.
+             * Only one of `ModelPixelScaleTag` and `ModelTransformation` 
should be used.
              */
-            case Tags.ModelTransformation: {
+            case (short) TAG_MODEL_TRANSFORMATION: {
                 final Vector m = type.readVector(input(), count);
                 final int n;
                 switch (m.size()) {
@@ -880,10 +889,20 @@ final class ImageFileDirectory extends DataCube {
                 break;
             }
             /*
-             * The "grid to CRS" conversion with only the scale factor 
specified. This block sets the
-             * translation column to NaN, meaning that it will need to be 
computed from the tie point.
+             * A vector of 3 floating-point values defining the "grid to CRS" 
conversion without rotation.
+             * The conversion is defined as below, when (I,J,K,X,Y,Z) is the 
tie point singleton record:
+             *
+             * ┌                       ┐
+             * │   Sx   0    0    Tx   │       Tx = X - I/Sx
+             * │   0   -Sy   0    Ty   │       Ty = Y + J/Sy
+             * │   0    0    Sz   Tz   │       Tz = Z - K/Sz  (if not 0)
+             * │   0    0    0    1    │
+             * └                       ┘
+             *
+             * This block sets the translation column to NaN, meaning that it 
will need to be computed from
+             * the tie point. Only one of `ModelPixelScaleTag` and 
`ModelTransformation` should be used.
              */
-            case Tags.ModelPixelScaleTag: {
+            case (short) TAG_MODEL_PIXEL_SCALE: {
                 final Vector m = type.readVector(input(), count);
                 final int size = m.size();
                 if (size < 2 || size > 3) {     // Length should be exactly 3, 
but we make this reader tolerant.
@@ -894,8 +913,9 @@ final class ImageFileDirectory extends DataCube {
             }
             /*
              * The mapping from pixel coordinates to CRS coordinates as a 
sequence of (I,J,K, X,Y,Z) records.
+             * This tag is also known as `Georeference`.
              */
-            case Tags.ModelTiePoints: {
+            case (short) TAG_MODEL_TIE_POINT: {
                 referencing().modelTiePoints = type.readVector(input(), count);
                 break;
             }
@@ -913,7 +933,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/series/name
              */
-            case Tags.DocumentName: {
+            case TAG_DOCUMENT_NAME: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addSeries(value);
                 }
@@ -924,7 +944,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/series/page
              */
-            case Tags.PageName: {
+            case TAG_PAGE_NAME: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addPage(value);
                 }
@@ -937,7 +957,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/series/page
              */
-            case Tags.PageNumber: {
+            case TAG_PAGE_NUMBER: {
                 final Vector v = type.readVector(input(), count);
                 int p = 0, n = 0;
                 switch (v.size()) {
@@ -954,7 +974,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/title
              */
-            case Tags.ImageDescription: {
+            case TAG_IMAGE_DESCRIPTION: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addTitle(Strings.singleLine(" ", value));
                 }
@@ -966,7 +986,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/party/name
              */
-            case Tags.Artist: {
+            case TAG_ARTIST: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addAuthor(value);
                 }
@@ -978,7 +998,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/resourceConstraint
              */
-            case Tags.Copyright: {
+            case (short) TAG_COPYRIGHT: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.parseLegalNotice(value);
                 }
@@ -989,7 +1009,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: metadata/identificationInfo/citation/date
              */
-            case Tags.DateTime: {
+            case TAG_DATE_TIME: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     
metadata.addCitationDate(reader.getDateFormat().parse(value),
                             DateType.CREATION, MetadataBuilder.Scope.RESOURCE);
@@ -1001,7 +1021,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: 
metadata/resourceLineage/processStep/processingInformation/procedureDescription
              */
-            case Tags.HostComputer: {
+            case TAG_HOST_COMPUTER: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addHostComputer(value);
                 }
@@ -1012,7 +1032,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: 
metadata/resourceLineage/processStep/processingInformation/softwareReference/title
              */
-            case Tags.Software: {
+            case TAG_SOFTWARE: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addSoftwareReference(value);
                 }
@@ -1022,7 +1042,7 @@ final class ImageFileDirectory extends DataCube {
              * Manufacturer of the scanner, video digitizer, or other type of 
equipment used to generate the image.
              * Synthetic images should not include this field.
              */
-            case Tags.Make: {
+            case TAG_MAKE: {
                 // TODO: is 
Instrument.citation.citedResponsibleParty.party.name an appropriate place?
                 // what would be the citation title? A copy of Tags.Model?
                 break;
@@ -1033,7 +1053,7 @@ final class ImageFileDirectory extends DataCube {
              *
              * Destination: 
metadata/acquisitionInformation/platform/instrument/identifier
              */
-            case Tags.Model: {
+            case TAG_MODEL: {
                 for (final String value : type.readString(input(), count, 
encoding())) {
                     metadata.addInstrument(null, value);
                 }
@@ -1042,8 +1062,8 @@ final class ImageFileDirectory extends DataCube {
             /*
              * The number of pixels per ResolutionUnit in the ImageWidth or 
ImageHeight direction.
              */
-            case Tags.XResolution:
-            case Tags.YResolution: {
+            case TAG_X_RESOLUTION:
+            case TAG_Y_RESOLUTION: {
                 metadata.setResolution(type.readDouble(input(), count));
                 break;
             }
@@ -1054,7 +1074,7 @@ final class ImageFileDirectory extends DataCube {
              *   2 = Inch (default).
              *   3 = Centimeter.
              */
-            case Tags.ResolutionUnit: {
+            case TAG_RESOLUTION_UNIT: {
                 return metadata.setResolutionUnit(type.readInt(input(), 
count));
                 // Non-null return value cause a warning to be reported by the 
caller.
             }
@@ -1066,7 +1086,7 @@ final class ImageFileDirectory extends DataCube {
              *   2 = An ordered dither or halftone technique has been applied 
to the image data.
              *   3 = A randomized process such as error diffusion has been 
applied to the image data.
              */
-            case Tags.Threshholding: {
+            case TAG_THRESHHOLDING: {
                 return metadata.setThreshholding(type.readShort(input(), 
count));
                 // Non-null return value cause a warning to be reported by the 
caller.
             }
@@ -1074,9 +1094,9 @@ final class ImageFileDirectory extends DataCube {
              * The width and height of the dithering or halftoning matrix used 
to create
              * a dithered or halftoned bilevel file. Meaningful only if 
Threshholding = 2.
              */
-            case Tags.CellWidth:
-            case Tags.CellLength: {
-                metadata.setCellSize(type.readShort(input(), count), tag == 
Tags.CellWidth);
+            case TAG_CELL_WIDTH:
+            case TAG_CELL_LENGTH: {
+                metadata.setCellSize(type.readShort(input(), count), tag == 
TAG_CELL_WIDTH);
                 break;
             }
 
@@ -1090,14 +1110,14 @@ final class ImageFileDirectory extends DataCube {
              * For each string of contiguous unused bytes in a TIFF file, the 
number of bytes and the byte offset
              * in the string. Those tags are deprecated and do not need to be 
supported.
              */
-            case Tags.FreeByteCounts:
-            case Tags.FreeOffsets:
+            case TAG_FREE_BYTE_COUNTS:
+            case TAG_FREE_OFFSETS:
             /*
              * For grayscale data, the optical density of each possible pixel 
value, plus the precision of that
              * information. This is ignored by most TIFF readers.
              */
-            case Tags.GrayResponseCurve:
-            case Tags.GrayResponseUnit: {
+            case TAG_GRAY_RESPONSE_CURVE:
+            case TAG_GRAY_RESPONSE_UNIT: {
                 warning(Level.FINE, Resources.Keys.IgnoredTag_1, 
Tags.name(tag));
                 break;
             }
@@ -1214,21 +1234,21 @@ final class ImageFileDirectory extends DataCube {
      */
     final boolean validateMandatoryTags() throws DataStoreContentException {
         if (isValidated) return false;
-        if (imageWidth  < 0) throw missingTag(Tags.ImageWidth);
-        if (imageHeight < 0) throw missingTag(Tags.ImageLength);
+        if (imageWidth  < 0) throw missingTag((short) TAG_IMAGE_WIDTH);
+        if (imageHeight < 0) throw missingTag((short) TAG_IMAGE_LENGTH);
         final short offsetsTag, byteCountsTag;
         switch (tileTagFamily) {
             case JPEG:                      // Handled as strips.
             case STRIP: {
                 if (tileWidth  < 0) tileWidth  = Math.toIntExact(imageWidth);
                 if (tileHeight < 0) tileHeight = Math.toIntExact(imageHeight);
-                offsetsTag    = Tags.StripOffsets;
-                byteCountsTag = Tags.StripByteCounts;
+                offsetsTag    = TAG_STRIP_OFFSETS;
+                byteCountsTag = TAG_STRIP_BYTE_COUNTS;
                 break;
             }
             case TILE:  {
-                offsetsTag    = Tags.TileOffsets;
-                byteCountsTag = Tags.TileByteCounts;
+                offsetsTag    = TAG_TILE_OFFSETS;
+                byteCountsTag = TAG_TILE_BYTE_COUNTS;
                 break;
             }
             default: {
@@ -1241,14 +1261,14 @@ final class ImageFileDirectory extends DataCube {
         }
         if (samplesPerPixel == 0) {
             samplesPerPixel = 1;
-            missingTag(Tags.SamplesPerPixel, 1, false, false);
+            missingTag((short) TAG_SAMPLES_PER_PIXEL, 1, false, false);
         }
         if (bitsPerSample == 0) {
             bitsPerSample = 1;
-            missingTag(Tags.BitsPerSample, 1, false, false);
+            missingTag((short) TAG_BITS_PER_SAMPLE, 1, false, false);
         }
         if (colorMap != null) {
-            ensureSameLength(Tags.ColorMap, Tags.BitsPerSample, 
colorMap.size(),  3 * (1 << bitsPerSample));
+            ensureSameLength((short) TAG_COLOR_MAP, (short) 
TAG_BITS_PER_SAMPLE, colorMap.size(),  3 * (1 << bitsPerSample));
         }
         if (sampleFormat != FLOAT) {
             long minValue, maxValue;
@@ -1286,12 +1306,12 @@ final class ImageFileDirectory extends DataCube {
             }
             case 0b0001: {          // Compute missing tile width.
                 tileWidth = computeTileSize(tileHeight);
-                missingTag(Tags.TileWidth, tileWidth, true, true);
+                missingTag((short) TAG_TILE_WIDTH, tileWidth, true, true);
                 break;
             }
             case 0b0010: {          // Compute missing tile height.
                 tileHeight = computeTileSize(tileWidth);
-                missingTag(Tags.TileLength, tileHeight, true, true);
+                missingTag((short) TAG_TILE_LENGTH, tileHeight, true, true);
                 break;
             }
             case 0b0100: {          // Compute missing tile byte count in 
uncompressed case.
@@ -1308,8 +1328,8 @@ final class ImageFileDirectory extends DataCube {
             default: {
                 final short tag;
                 switch (Integer.lowestOneBit(missing)) {
-                    case 0b0001: tag = Tags.TileWidth;  break;
-                    case 0b0010: tag = Tags.TileLength; break;
+                    case 0b0001: tag = TAG_TILE_WIDTH;  break;
+                    case 0b0010: tag = TAG_TILE_LENGTH; break;
                     default:     tag = byteCountsTag;   break;
                 }
                 throw missingTag(tag);
@@ -1334,7 +1354,7 @@ final class ImageFileDirectory extends DataCube {
          * the translation terms now.
          */
         if (referencing != null && !referencing.validateMandatoryTags()) {
-            throw missingTag(Tags.ModelTiePoints);
+            throw missingTag((short) TAG_MODEL_TIE_POINT);
         }
         isValidated = true;
         return true;
@@ -1405,7 +1425,7 @@ final class ImageFileDirectory extends DataCube {
      * of another image in this TIFF file.
      */
     final boolean isReducedResolution() {
-        return (subfileType & 1) != 0;
+        return (subfileType & NEW_SUBFILE_TYPE_REDUCED_RESOLUTION) != 0;
     }
 
     /**
@@ -1612,17 +1632,17 @@ final class ImageFileDirectory extends DataCube {
             short missing = 0;              // Non-zero if there is a warning 
about missing information.
             switch (photometricInterpretation) {
                 default: {                  // For any unrecognized code, 
fallback on grayscale with 0 as black.
-                    unsupportedTagValue(Tags.PhotometricInterpretation, 
photometricInterpretation);
+                    unsupportedTagValue((short) 
TAG_PHOTOMETRIC_INTERPRETATION, photometricInterpretation);
                     break;
                 }
                 case -1: {
-                    missing = Tags.PhotometricInterpretation;
+                    missing = TAG_PHOTOMETRIC_INTERPRETATION;
                     break;
                 }
-                case  0:                   // WhiteIsZero: 0 is imaged as 
white.
-                case  1: {                 // BlackIsZero: 0 is imaged as 
black.
+                case  PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO:
+                case  PHOTOMETRIC_INTERPRETATION_BLACK_IS_ZERO: {
                     final Color[] colors = {Color.BLACK, Color.WHITE};
-                    if (photometricInterpretation == 0) {
+                    if (photometricInterpretation == 
PHOTOMETRIC_INTERPRETATION_WHITE_IS_ZERO) {
                         ArraysExt.swap(colors, 0, 1);
                     }
                     double min = 0;
@@ -1636,7 +1656,7 @@ final class ImageFileDirectory extends DataCube {
                     colorModel = ColorModelFactory.createColorScale(dataType, 
samplesPerPixel, visibleBand, min, max, colors);
                     break;
                 }
-                case 2: {                   // RGB: (0,0,0) is black and 
(255,255,255) is white.
+                case PHOTOMETRIC_INTERPRETATION_RGB: {
                     final int numBands = sm.getNumBands();
                     if (numBands < 3 || numBands > 4) {
                         throw new 
DataStoreContentException(Errors.format(Errors.Keys.UnexpectedValueInElement_2, 
"numBands", numBands));
@@ -1646,9 +1666,9 @@ final class ImageFileDirectory extends DataCube {
                     colorModel = ColorModelFactory.createRGB(bitsPerSample, 
packed, hasAlpha);
                     break;
                 }
-                case  3: {                  // PaletteColor
+                case PHOTOMETRIC_INTERPRETATION_PALETTE_COLOR: {
                     if (colorMap == null) {
-                        missing = Tags.ColorMap;
+                        missing = TAG_COLOR_MAP;
                         break;
                     }
                     int gi = colorMap.size() / 3;
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java
index 157a2d8405..9c2addcfd5 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java
@@ -27,6 +27,8 @@ import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.measure.Units;
 
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
+
 
 /**
  * A temporary object for building the metadata for a single GeoTIFF image.
@@ -39,7 +41,7 @@ import org.apache.sis.measure.Units;
  * discard them (which save a little bit of space) when no longer needed.</div>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   1.2
  * @module
  */
@@ -98,10 +100,16 @@ final class ImageMetadataBuilder extends MetadataBuilder {
     @SuppressWarnings("fallthrough")
     Integer setThreshholding(final int value) {
         switch (value) {
-            default: return value;                              // Cause a 
warning to be reported by the caller.
-            case 2:  if ((cellWidth & cellHeight) >= 0) break;  // Exit if at 
least one value is positive, else fallthrough.
-            case 1:  // Fall through
-            case 3:  cellWidth = cellHeight = (short) -value; break;
+            default: return value;                      // Cause a warning to 
be reported by the caller.
+            case THRESHHOLDING_ORDERED_DITHER: {
+                if ((cellWidth & cellHeight) >= 0) break;
+                // Exit if at least one value is positive, else fallthrough.
+            }
+            case THRESHHOLDING_RANDOMIZED_DITHER:       // Fall through
+            case THRESHHOLDING_NONE:  {
+                cellWidth = cellHeight = (short) -value;
+                break;
+            }
         }
         return null;
     }
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/NativeMetadata.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/NativeMetadata.java
index 3a4e3ef376..ab71e8017c 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/NativeMetadata.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/NativeMetadata.java
@@ -34,6 +34,8 @@ import org.apache.sis.internal.geotiff.Compression;
 import org.apache.sis.internal.geotiff.Predictor;
 
 import static java.lang.Math.addExact;
+import static javax.imageio.plugins.tiff.GeoTIFFTagSet.*;
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
 
 
 /**
@@ -48,7 +50,7 @@ import static java.lang.Math.addExact;
  * <p>This class is thread-safe if the user does not try to write in the 
tree.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   1.2
  * @module
  */
@@ -82,8 +84,6 @@ final class NativeMetadata extends GeoKeysLoader {
 
     /**
      * The node for GeoKeys, or {@code null} if none.
-     *
-     * @see Tags#GeoKeyDirectory
      */
     private TreeTable.Node geoNode;
 
@@ -141,10 +141,10 @@ final class NativeMetadata extends GeoKeysLoader {
                      * This switch is only about tags to skip; special 
handlings of some tags are done later.
                      */
                     switch (tag) {
-                        case Tags.TileOffsets:
-                        case Tags.StripOffsets:
-                        case Tags.TileByteCounts:
-                        case Tags.StripByteCounts: visible = false; break;
+                        case TAG_TILE_OFFSETS:
+                        case TAG_STRIP_OFFSETS:
+                        case TAG_TILE_BYTE_COUNTS:
+                        case TAG_STRIP_BYTE_COUNTS: visible = false; break;
                         default: visible = (size != 0); break;
                     }
                     if (visible) {
@@ -159,18 +159,18 @@ final class NativeMetadata extends GeoKeysLoader {
                         Object value = null;
                         XMLMetadata children = null;
                         switch (tag) {
-                            case Tags.GeoKeyDirectory: {
+                            case (short) TAG_GEO_KEY_DIRECTORY: {
                                 writeGeoKeys();             // Flush previous 
keys if any (should never happen).
                                 keyDirectory = type.readVector(input, count);
                                 value = "GeoTIFF";
                                 break;
                             }
-                            case Tags.GeoDoubleParams: {
+                            case (short) TAG_GEO_DOUBLE_PARAMS: {
                                 numericParameters = type.readVector(input, 
count);
                                 visible = false;
                                 break;
                             }
-                            case Tags.GeoAsciiParams: {
+                            case (short) TAG_GEO_ASCII_PARAMS: {
                                 setAsciiParameters(type.readString(input, 
count, reader.store.encoding));
                                 visible = false;
                                 break;
@@ -199,8 +199,8 @@ final class NativeMetadata extends GeoKeysLoader {
                                  * an enumeration.
                                  */
                                 switch (tag) {
-                                    case Tags.Compression: value = 
toString(value, Compression::valueOf, Compression.UNKNOWN); break;
-                                    case Tags.Predictor:   value = 
toString(value,   Predictor::valueOf,   Predictor.UNKNOWN); break;
+                                    case TAG_COMPRESSION: value = 
toString(value, Compression::valueOf, Compression.UNKNOWN); break;
+                                    case TAG_PREDICTOR:   value = 
toString(value,   Predictor::valueOf,   Predictor.UNKNOWN); break;
                                 }
                             }
                         }
@@ -215,7 +215,7 @@ final class NativeMetadata extends GeoKeysLoader {
                                 node.setValue(VALUE, value);
                             }
                             node.setValue(CODE, Short.toUnsignedInt(tag));
-                            if (tag == Tags.GeoKeyDirectory) {
+                            if (tag == (short) TAG_GEO_KEY_DIRECTORY) {
                                 geoNode = node;
                             }
                         }
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java
index c0a47affc5..e3d2d2fe67 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java
@@ -17,121 +17,48 @@
 package org.apache.sis.storage.geotiff;
 
 import java.lang.reflect.Field;
+import java.util.function.Supplier;
+import javax.imageio.plugins.tiff.TIFFTag;
+import javax.imageio.plugins.tiff.TIFFTagSet;
 
 
 /**
  * Numerical values of GeoTIFF tags, as <strong>unsigned</strong> short 
integers.
- * In this class, field names are identical to TIFF tag names.
- * For that reason, many of those field names do not follow usual Java 
convention for constants.
+ * This class provides only the tags that are not already provided by {@link 
TIFFTagSet}.
+ *
+ * <p>In this class, field names are identical to TIFF tag names.
+ * For that reason, some field names do not follow usual Java convention for 
constants.</p>
  *
  * <p>A useful (but unofficial) reference is the
  * <a href="http://www.awaresystems.be/imaging/tiff/tifftags.html";>TIFF Tag 
Reference</a> page.</p>
  *
  * @author  Johann Sorel (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   0.8
  * @module
- *
- * @todo Use {@code javax.imageio.plugins.tiff.BaselineTIFFTagSet} with JDK9.
  */
 final class Tags {
+    /**
+     * XML packet containing metadata such as descriptions, titles, keywords, 
author and copyright information.
+     *
+     * @see <a href="https://www.adobe.com/products/xmp.html";>Adobe XMP 
technote 9-14-02</a>
+     * @see <a href="https://www.iso.org/standard/75163.html";>ISO 16684-1:2019 
Graphic technology — Extensible metadata platform (XMP)</a>
+     */
+    public static final short XML_Packet = 0x02BC;
 
-    //////////////////////////////////////////////////////////
-    //                  BASELINE TIFF TAGS                  //
-    //////////////////////////////////////////////////////////
-
-    public static final short NewSubfileType              = 0x00FE;
-    public static final short SubfileType                 = 0x00FF;
-    public static final short ImageWidth                  = 0x0100;
-    public static final short ImageLength                 = 0x0101;
-    public static final short BitsPerSample               = 0x0102;
-    public static final short Compression                 = 0x0103;
-    public static final short PhotometricInterpretation   = 0x0106;
-    public static final short Threshholding               = 0x0107;
-    public static final short CellWidth                   = 0x0108;
-    public static final short CellLength                  = 0x0109;
-    public static final short FillOrder                   = 0x010A;
-    public static final short DocumentName                = 0x010D;
-    public static final short ImageDescription            = 0x010E;
-    public static final short Make                        = 0x010F;
-    public static final short Model                       = 0x0110;
-    public static final short StripOffsets                = 0x0111;
-    public static final short Orientation                 = 0x0112;
-    public static final short SamplesPerPixel             = 0x0115;
-    public static final short RowsPerStrip                = 0x0116;
-    public static final short StripByteCounts             = 0x0117;
-    public static final short MinSampleValue              = 0x0118;
-    public static final short MaxSampleValue              = 0x0119;
-    public static final short XResolution                 = 0x011A;
-    public static final short YResolution                 = 0x011B;
-    public static final short PlanarConfiguration         = 0x011C;
-    public static final short PageName                    = 0x011D;
-    public static final short XPosition                   = 0x011E;
-    public static final short YPosition                   = 0x011F;
-    public static final short FreeOffsets                 = 0x0120;
-    public static final short FreeByteCounts              = 0x0121;
-    public static final short GrayResponseUnit            = 0x0122;
-    public static final short GrayResponseCurve           = 0x0123;
-    public static final short T4Options                   = 0x0124;
-    public static final short T6Options                   = 0x0125;
-    public static final short ResolutionUnit              = 0x0128;
-    public static final short PageNumber                  = 0x0129;
-    public static final short TransferFunction            = 0x012D;
-    public static final short Software                    = 0x0131;
-    public static final short DateTime                    = 0x0132;
-    public static final short DateTimeOriginal    = (short) 0x9003;
-    public static final short DateTimeDigitized   = (short) 0x9004;
-    public static final short Artist                      = 0x013B;
-    public static final short HostComputer                = 0x013C;
-    public static final short Predictor                   = 0x013D;
-    public static final short WhitePoint                  = 0x013E;
-    public static final short PrimaryChromaticities       = 0x013F;
-    public static final short ColorMap                    = 0x0140;
-    public static final short HalftoneHints               = 0x0141;
-    public static final short TileWidth                   = 0x0142;
-    public static final short TileLength                  = 0x0143;
-    public static final short TileOffsets                 = 0x0144;
-    public static final short TileByteCounts              = 0x0145;
-    public static final short InkSet                      = 0x014C;
-    public static final short InkNames                    = 0x014D;
-    public static final short NumberOfInks                = 0x014E;
-    public static final short DotRange                    = 0x0150;
-    public static final short TargetPrinter               = 0x0151;
-    public static final short ExtraSamples                = 0x0152;
-    public static final short SampleFormat                = 0x0153;
-    public static final short SMinSampleValue             = 0x0154;
-    public static final short SMaxSampleValue             = 0x0155;
-    public static final short TransferRange               = 0x0156;
-    public static final short JPEGProc                    = 0x0200;
-    public static final short JPEGInterchangeFormat       = 0x0201;
-    public static final short JPEGInterchangeFormatLength = 0x0202;
-    public static final short JPEGRestartInterval         = 0x0203;
-    public static final short JPEGLosslessPredictors      = 0x0205;
-    public static final short JPEGPointTransforms         = 0x0206;
-    public static final short JPEGQTables                 = 0x0207;
-    public static final short JPEGDCTables                = 0x0208;
-    public static final short JPEGACTables                = 0x0209;
-    public static final short YCbCrCoefficients           = 0x0211;
-    public static final short YCbCrSubSampling            = 0x0212;
-    public static final short YCbCrPositioning            = 0x0213;
-    public static final short ReferenceBlackWhite         = 0x0214;
-    public static final short Copyright           = (short) 0x8298;
-
-
-    /////////////////////////////////////////////////////////
-    //              OGC DGIWG EXTENSION TAGS               //
-    /////////////////////////////////////////////////////////
+    /**
+     * Collection of Photoshop "Image Resource Blocks".
+     *
+     * @see <a 
href="https://www.awaresystems.be/imaging/tiff/tifftags/docs/photoshopthumbnail.html";>PhotoShop
 private TIFF Tag</a>
+     */
+    public static final short PhotoshopImageResources = (short) 0x8649;
 
     /**
      * Embedded XML-encoded instance documents prepared using 19139-based 
schema.
+     * This is an OGC DGIWG extension tag.
      */
     public static final short GEO_METADATA = (short) 0xC6DD;
 
-
-    /////////////////////////////////////////////////////////
-    //                 GDAL EXTENSION TAGS                 //
-    /////////////////////////////////////////////////////////
-
     /**
      * Holds an XML list of name=value 'metadata' values about the image as a 
whole, and about specific samples.
      *
@@ -146,60 +73,20 @@ final class Tags {
      */
     public static final short GDAL_NODATA = (short) 0xA481;               // 
42113
 
-
-    /////////////////////////////////////////////////////////
-    //                 GEOTIFF EXTENSION TAGS              //
-    /////////////////////////////////////////////////////////
-
-    /**
-     * References all "GeoKeys" needed for building the Coordinate Reference 
System.
-     * GeoTIFF keys are stored in a kind of directory inside the TIFF 
directory, with
-     * the keys enumerated in the {@link CRSBuilder} class.
-     *
-     * @see GeoKeys
-     */
-    public static final short GeoKeyDirectory = (short) 0x87AF;           // 
34735
-
-    /**
-     * References all {@code double} values referenced by the {@link GeoKeys}.
-     * The keys are stored in the entry referenced by {@link #GeoKeyDirectory}.
-     */
-    public static final short GeoDoubleParams = (short) 0x87B0;           // 
34736
-
     /**
-     * References all {@link String} values referenced by the {@link GeoKeys}.
-     * The keys are stored in the entry referenced by {@link #GeoKeyDirectory}.
+     * Supplier of TIFF tag sets, in preference order.
+     * The sets that are most likely to be used (for the kind of data handled 
by SIS) should be first.
      */
-    public static final short GeoAsciiParams = (short) 0x87B1;            // 
34737
-
-    /**
-     * The tie points as (I,J,K,X,Y,Z) records in an array of floating-point 
numbers.
-     * This tag is also known as {@code Georeference}.
-     */
-    public static final short ModelTiePoints = (short) 0x8482;            // 
33922
-
-    /**
-     * A vector of 3 floating-point values defining the "grid to CRS" 
conversion without rotation.
-     * The conversion is defined as below, when (I,J,K,X,Y,Z) is the tie point 
singleton record:
-     *
-     * ┌                       ┐
-     * │   Sx   0    0    Tx   │       Tx = X - I/Sx
-     * │   0   -Sy   0    Ty   │       Ty = Y + J/Sy
-     * │   0    0    Sz   Tz   │       Tz = Z - K/Sz  (if not 0)
-     * │   0    0    0    1    │
-     * └                       ┘
-     *
-     * Only one of {@code ModelPixelScaleTag} and {@link #ModelTransformation} 
should be used.
-     */
-    public static final short ModelPixelScaleTag = (short) 0x830E;        // 
33550
-
-    /**
-     * Specifies the "grid to CRS" conversion (the transformation matrix 
between the raster space and the model space).
-     * If specified, the tag shall have the 16 values of a 4×4 matrix in 
row-major fashion. The last matrix row (i.e.
-     * the last 4 values) should be [0 0 0 1]. The row before should be [0 0 0 
0] if the conversion is two-dimensional.
-     * Only one of {@link #ModelPixelScaleTag} and {@code ModelTransformation} 
should be used.
-     */
-    public static final short ModelTransformation = (short) 0x85D8;       // 
34264
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static Supplier<TIFFTagSet>[] TAG_SETS = new Supplier[] {
+        javax.imageio.plugins.tiff.BaselineTIFFTagSet::getInstance,
+        javax.imageio.plugins.tiff.GeoTIFFTagSet::getInstance,
+        javax.imageio.plugins.tiff.ExifGPSTagSet::getInstance,
+        javax.imageio.plugins.tiff.ExifParentTIFFTagSet::getInstance,
+        javax.imageio.plugins.tiff.ExifTIFFTagSet::getInstance,
+        javax.imageio.plugins.tiff.ExifInteroperabilityTagSet::getInstance,
+        javax.imageio.plugins.tiff.FaxTIFFTagSet::getInstance
+    };
 
     /**
      * Do not allow instantiation of this class.
@@ -208,10 +95,15 @@ final class Tags {
     }
 
     /**
-     * Returns the name of the given tag. Implementation of this method is 
inefficient,
-     * but it should rarely be invoked (mostly for formatting error messages).
+     * Returns the name of the given tag.
+     * This method should be rarely invoked (mostly for formatting error 
messages).
      */
     static String name(final short tag) {
+        final int ti = Short.toUnsignedInt(tag);
+        for (final Supplier<TIFFTagSet> s : TAG_SETS) {
+            final TIFFTag t = s.get().getTag(ti);
+            if (t != null) return t.getName();
+        }
         try {
             for (final Field field : Tags.class.getFields()) {
                 if (field.getType() == Short.TYPE) {
@@ -223,6 +115,6 @@ final class Tags {
         } catch (IllegalAccessException e) {
             throw new AssertionError(e);        // Should never happen because 
we asked only for public fields.
         }
-        return Integer.toHexString(Short.toUnsignedInt(tag));
+        return "Tag #" + Integer.toHexString(ti);
     }
 }
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java
index 129ebd3913..70bf297c01 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java
@@ -32,7 +32,7 @@
  * @author  Thi Phuong Hao Nguyen (VNSC)
  * @author  Minh Chinh Vu (VNSC)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.4
  * @since   0.8
  * @module
  */
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/TagsTest.java
similarity index 52%
copy from 
storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
copy to 
storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/TagsTest.java
index 3c0a7251cc..1cf1d50e34 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/package-info.java
+++ 
b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/TagsTest.java
@@ -14,18 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.sis.storage.geotiff;
+
+import org.apache.sis.test.TestCase;
+import org.junit.Test;
+
+import static javax.imageio.plugins.tiff.GeoTIFFTagSet.*;
+import static javax.imageio.plugins.tiff.BaselineTIFFTagSet.*;
+import static org.junit.Assert.*;
+
 
 /**
- * Utility classes for the implementation of GeoTIFF reader and writer.
- *
- * <STRONG>Do not use!</STRONG>
- *
- * This package is for internal use by SIS only. Classes in this package
- * may change in incompatible ways in any future version without notice.
+ * Tests {@link Tags}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
- * @since   0.8
+ * @version 1.4
+ * @since   1.4
  * @module
  */
-package org.apache.sis.internal.geotiff;
+public final strictfp class TagsTest extends TestCase {
+    /**
+     * Tests {@link Tags#name(short)}.
+     */
+    @Test
+    public void testName() {
+        assertEquals("Artist",             Tags.name((short) TAG_ARTIST));
+        assertEquals("Copyright",          Tags.name((short) TAG_COPYRIGHT));
+        assertEquals("GeoKeyDirectoryTag", Tags.name((short) 
TAG_GEO_KEY_DIRECTORY));
+        assertEquals("GEO_METADATA",       Tags.name(Tags.GEO_METADATA));
+    }
+}
diff --git 
a/storage/sis-geotiff/src/test/java/org/apache/sis/test/suite/GeoTiffTestSuite.java
 
b/storage/sis-geotiff/src/test/java/org/apache/sis/test/suite/GeoTiffTestSuite.java
index 7540ccba7e..f08cb590ba 100644
--- 
a/storage/sis-geotiff/src/test/java/org/apache/sis/test/suite/GeoTiffTestSuite.java
+++ 
b/storage/sis-geotiff/src/test/java/org/apache/sis/test/suite/GeoTiffTestSuite.java
@@ -25,7 +25,7 @@ import org.junit.BeforeClass;
  * All tests from the {@code sis-geotiff} module, in rough dependency order.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
  * @since   0.8
  * @module
  */
@@ -34,6 +34,7 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.storage.inflater.InflaterTest.class,
     org.apache.sis.internal.storage.inflater.CCITTRLETest.class,
     org.apache.sis.storage.geotiff.TypeTest.class,
+    org.apache.sis.storage.geotiff.TagsTest.class,
     org.apache.sis.storage.geotiff.GeoKeysTest.class,
     org.apache.sis.storage.geotiff.CRSBuilderTest.class,
     org.apache.sis.storage.geotiff.XMLMetadataTest.class,

Reply via email to