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,