sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1773010 [7/8] - in /sis/branches/JDK7: ./ application/sis-console/src/main/artifact/ application/sis-openoffice/src/main/unopkg/ core/sis-build-helper/src/main/javadoc/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-featur...
Date Wed, 07 Dec 2016 04:14:47 GMT
Copied: sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java (from r1769011, sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java?p2=sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java&p1=sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java&r1=1769011&r2=1773010&rev=1773010&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java [UTF-8] Wed Dec  7 04:14:44 2016
@@ -22,8 +22,10 @@ import org.opengis.referencing.operation
 
 
 /**
- * All Geographic Keys needed for building {@link CoordinateReferenceSystem} instances
- * and {@link MathTransform} "grid to CRS" from TIFF tags values.
+ * GeoTIFF keys associated to values needed for building {@link CoordinateReferenceSystem} instances
+ * and {@link MathTransform} "grid to CRS". In this class, field names are close to GeoTIFF key names
+ * with the {@code "GeoKey"} suffix omitted. For that reason, many of those field names do not follow
+ * usual Java convention for constants.
  *
  * @author  Rémi Maréchal (Geomatys)
  * @since   0.8
@@ -37,340 +39,105 @@ final class GeoKeys {
     private GeoKeys() {
     }
 
-    ////////////////////////////////////////////////////////////////////////////
-    // KEYS, values are taken from :
-    // http://www.remotesensing.org/geotiff/spec/geotiff6.html#6
-    ////////////////////////////////////////////////////////////////////////////
-    static class Configuration {
+    // 6.2.1 GeoTIFF Configuration Keys
+    /** Section 6.3.1.1 Codes. */ public static final short ModelType            = 1024;
+    /** Section 6.3.1.2 Codes. */ public static final short RasterType           = 1025;
+    /** Documentation.         */ public static final short Citation             = 1026;
+
+    // 6.2.2 Geographic CS Parameter Keys
+    /** Section 6.3.2.1 Codes. */ public static final short GeographicType       = 2048;
+    /** Documentation.         */ public static final short GeogCitation         = 2049;
+    /** Section 6.3.2.2 Codes. */ public static final short GeodeticDatum        = 2050;
+    /** Section 6.3.2.4 codes. */ public static final short PrimeMeridian        = 2051;
+    /** Section 6.3.1.3 Codes. */ public static final short GeogLinearUnits      = 2052;
+    /** Relative to meters.    */ public static final short GeogLinearUnitSize   = 2053;
+    /** Section 6.3.1.4 Codes. */ public static final short AngularUnits         = 2054;
+    /** Relative to radians.   */ public static final short AngularUnitSize      = 2055;
+    /** Section 6.3.2.3 Codes. */ public static final short Ellipsoid            = 2056;
+    /** In GeogLinearUnits.    */ public static final short SemiMajorAxis        = 2057;
+    /** In GeogLinearUnits.    */ public static final short SemiMinorAxis        = 2058;
+    /** A ratio.               */ public static final short InvFlattening        = 2059;
+    /** Section 6.3.1.4 Codes. */ public static final short AzimuthUnits         = 2060;
+    /** In AngularUnit.        */ public static final short PrimeMeridianLong    = 2061;
+
+    // 6.2.3 Projected CS Parameter Keys
+    /** Section 6.3.3.1 codes. */ public static final short ProjectedCSType      = 3072;
+    /** Documentation.         */ public static final short PCSCitation          = 3073;
+    /** Section 6.3.3.2 codes. */ public static final short Projection           = 3074;
+    /** Section 6.3.3.3 codes. */ public static final short CoordTrans           = 3075;
+    /** Section 6.3.1.3 codes. */ public static final short LinearUnits          = 3076;
+    /** Relative to meters.    */ public static final short LinearUnitSize       = 3077;
+    /** In AngularUnit.        */ public static final short StdParallel1         = 3078;    // First projection parameter
+    /** In AngularUnit.        */ public static final short StdParallel2         = 3079;
+    /** In AngularUnit.        */ public static final short NatOriginLong        = 3080;
+    /** In AngularUnit.        */ public static final short NatOriginLat         = 3081;
+    /** In LinearUnits.        */ public static final short FalseEasting         = 3082;
+    /** In LinearUnits.        */ public static final short FalseNorthing        = 3083;
+    /** In AngularUnit.        */ public static final short FalseOriginLong      = 3084;
+    /** In AngularUnit.        */ public static final short FalseOriginLat       = 3085;
+    /** In LinearUnits.        */ public static final short FalseOriginEasting   = 3086;
+    /** In LinearUnits.        */ public static final short FalseOriginNorthing  = 3087;
+    /** In AngularUnit.        */ public static final short CenterLong           = 3088;
+    /** In AngularUnit.        */ public static final short CenterLat            = 3089;
+    /** In LinearUnits.        */ public static final short CenterEasting        = 3090;
+    /** In LinearUnits.        */ public static final short CenterNorthing       = 3091;
+    /** A ratio.               */ public static final short ScaleAtNatOrigin     = 3092;
+    /** A ratio.               */ public static final short ScaleAtCenter        = 3093;
+    /** In AzimuthUnit.        */ public static final short AzimuthAngle         = 3094;
+    /** In AngularUnit.        */ public static final short StraightVertPoleLong = 3095;    // Last projection parameter (for now)
+
+    // 6.2.4 Vertical CS Keys
+    /** Section 6.3.4.1 codes. */ public static final short VerticalCSType       = 4096;
+    /** Documentation.         */ public static final short VerticalCitation     = 4097;
+    /** Section 6.3.4.2 codes. */ public static final short VerticalDatum        = 4098;
+    /** Section 6.3.1.3 codes. */ public static final short VerticalUnits        = 4099;
 
-        //6.2.1 GeoTIFF Configuration Keys
-        static final int GTModelTypeGeoKey           = 1024; /* Section 6.3.1.1 Codes */
-        static final int GTRasterTypeGeoKey          = 1025; /* Section 6.3.1.2 Codes */
-        static final int GTCitationGeoKey            = 1026; /* documentation */
-
-        /*
-         * 6.3.1.1 Model Type Codes
-         *
-         * Ranges:
-         *   0              = undefined
-         *   [   1,  32766] = GeoTIFF Reserved Codes
-         *   32767          = user-defined
-         *   [32768, 65535] = Private User Implementations
-         *
-         * Notes:
-         *   1. ModelTypeGeographic and ModelTypeProjected
-         *   correspond to the FGDC metadata Geographic and
-         *   Planar-Projected coordinate system types.
-         */
-        //GeoTIFF defined CS Model Type Codes:
-        static final int ModelTypeProjected   = 1;   /* Projection Coordinate System         */
-        static final int ModelTypeGeographic  = 2;   /* Geographic latitude-longitude System */
-        static final int ModelTypeGeocentric  = 3;   /* Geocentric (X,Y,Z) Coordinate System */
-        ////////////////////////////////////////////////////////////////////////////
-        // Codes
-        ////////////////////////////////////////////////////////////////////////////
-
-        static final short GTUserDefinedGeoKey         = 32767;
-        static final String GTUserDefinedGeoKey_String = "32767";
-
-        /**
-         * Return tag Name from {@link GeoTiffConstants} class.
-         *
-         * @param tag
-         * @return tag Name from {@link GeoTiffConstants} class.
-         */
-        static String getName(final int tag) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == tag) {
-                            return field.getName();
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return Integer.toHexString(tag);
-        }
-
-        static boolean contain(int key) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == key) {
-                            return true;
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return false;
-        }
-    }
-
-    static class Operation_Method {
-        /*
-         * 6.3.3.3 Coordinate Transformation Codes
-         * Ranges:
-         * 0 = undefined
-         * [    1, 16383] = GeoTIFF Coordinate Transformation codes
-         * [16384, 32766] = Reserved by GeoTIFF
-         * 32767          = user-defined
-         * [32768, 65535] = Private User Implementations
-         */
-        static final int CT_TransverseMercator =             1;
-        static final int CT_TransvMercator_Modified_Alaska = 2;
-        static final int CT_ObliqueMercator =                3;
-        static final int CT_ObliqueMercator_Laborde =        4;
-        static final int CT_ObliqueMercator_Rosenmund =      5;
-        static final int CT_ObliqueMercator_Spherical =      6;
-        static final int CT_Mercator =                       7;
-        static final int CT_LambertConfConic_2SP =           8;
-        static final int CT_LambertConfConic_1SP =           9;
-        static final int CT_LambertAzimEqualArea =           10;
-        static final int CT_AlbersEqualArea =                11;
-        static final int CT_AzimuthalEquidistant =           12;
-        static final int CT_EquidistantConic =               13;
-        static final int CT_Stereographic =                  14;
-        static final int CT_PolarStereographic =             15;
-        static final int CT_ObliqueStereographic =           16;
-        static final int CT_Equirectangular =                17;
-        static final int CT_CassiniSoldner =                 18;
-        static final int CT_Gnomonic =                       19;
-        static final int CT_MillerCylindrical =              20;
-        static final int CT_Orthographic =                   21;
-        static final int CT_Polyconic =                      22;
-        static final int CT_Robinson =                       23;
-        static final int CT_Sinusoidal =                     24;
-        static final int CT_VanDerGrinten =                  25;
-        static final int CT_NewZealandMapGrid =              26;
-        static final int CT_TransvMercator_SouthOriented=    27;
-        //Aliases:
-        static final int CT_AlaskaConformal =                CT_TransvMercator_Modified_Alaska;
-        static final int CT_TransvEquidistCylindrical =      CT_CassiniSoldner;
-        static final int CT_ObliqueMercator_Hotine =         CT_ObliqueMercator;
-        static final int CT_SwissObliqueCylindrical =        CT_ObliqueMercator_Rosenmund;
-        static final int CT_GaussBoaga =                     CT_TransverseMercator;
-        static final int CT_GaussKruger =                    CT_TransverseMercator;
-        static final int CT_LambertConfConic =               CT_LambertConfConic_2SP ;
-        static final int CT_LambertConfConic_Helmert =       CT_LambertConfConic_1SP;
-        static final int CT_SouthOrientedGaussConformal =    CT_TransvMercator_SouthOriented;
-
-        /**
-         * Return tag Name from {@link GeoTiffConstants} class.
-         *
-         * @param tag
-         * @return tag Name from {@link GeoTiffConstants} class.
-         */
-        static String getName(final int tag) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == tag) {
-                            return field.getName();
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return Integer.toHexString(tag);
-        }
-
-        static boolean contain(int key) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == key) {
-                            return true;
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return false;
-        }
-    }
-
-    static class Units {
-        /*
-         * 6.3.1.3 Linear Units Codes
-         *
-         *  There are several different kinds of units that may be used in geographically related raster data: linear units, angular units, units of time (e.g. for radar-return), CCD-voltages, etc. For this reason there will be a single, unique range for each kind of unit, broken down into the following currently defined ranges:
-         *  Ranges:
-         *     0             = undefined
-         *     [   1,  2000] = Obsolete GeoTIFF codes
-         *     [2001,  8999] = Reserved by GeoTIFF
-         *     [9000,  9099] = EPSG Linear Units.
-         *     [9100,  9199] = EPSG Angular Units.
-         *     32767         = user-defined unit
-         *     [32768, 65535]= Private User Implementations
-         *  Linear Unit Values (See the ESPG/POSC tables for definition):
-         */
-        static final int Linear_Meter                       = 9001;
-        static final int Linear_Foot                        = 9002;
-        static final int Linear_Foot_US_Survey              = 9003;
-        static final int Linear_Foot_Modified_American      = 9004;
-        static final int Linear_Foot_Clarke                 = 9005;
-        static final int Linear_Foot_Indian                 = 9006;
-        static final int Linear_Link                        = 9007;
-        static final int Linear_Link_Benoit                 = 9008;
-        static final int Linear_Link_Sears                  = 9009;
-        static final int Linear_Chain_Benoit                = 9010;
-        static final int Linear_Chain_Sears                 = 9011;
-        static final int Linear_Yard_Sears                  = 9012;
-        static final int Linear_Yard_Indian                 = 9013;
-        static final int Linear_Fathom                      = 9014;
-        static final int Linear_Mile_International_Nautical = 9015;
-
-        /*
-         * 6.3.1.4 Angular Units Codes
-         * These codes shall be used for any key that requires specification of an angular unit of measurement.
-         */
-        static final int Angular_Radian         = 9101;
-        static final int Angular_Degree         = 9102;
-        static final int Angular_Arc_Minute     = 9103;
-        static final int Angular_Arc_Second     = 9104;
-        static final int Angular_Grad           = 9105;
-        static final int Angular_Gon            = 9106;
-        static final int Angular_DMS            = 9107;
-        static final int Angular_DMS_Hemisphere = 9108;
-
-        /**
-         * Return tag Name from {@link GeoTiffConstants} class.
-         *
-         * @param tag
-         * @return tag Name from {@link GeoTiffConstants} class.
-         */
-        static String getName(final int tag) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == tag) {
-                            return field.getName();
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return Integer.toHexString(tag);
-        }
+    /**
+     * Enumeration of return values for the {@link #unitOf(short)} method.
+     */
+    static final int RATIO = 0, LINEAR = 1, ANGULAR = 2, AZIMUTH = 3;
 
-        static boolean contain(int key) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == key) {
-                            return true;
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return false;
+    /**
+     * Returns the unit of measurement for the given map projection parameter.
+     *
+     * @param  key  GeoTIFF key for which to get the unit of associated map projection parameter value.
+     * @return one of {@link #RATIO}, {@link #LINEAR}, {@link #ANGULAR}, {@link #AZIMUTH} codes,
+     *         or -1 if the given key is not for a map projection parameter.
+     */
+    static int unitOf(final short key) {
+        if (key < StdParallel1 || key > StraightVertPoleLong) {
+            return -1;
+        }
+        switch (key) {
+            case FalseEasting:
+            case FalseNorthing:
+            case FalseOriginEasting:
+            case FalseOriginNorthing:
+            case CenterEasting:
+            case CenterNorthing:    return LINEAR;
+            case ScaleAtNatOrigin:
+            case ScaleAtCenter:     return RATIO;
+            case AzimuthAngle:      return AZIMUTH;
+            default:                return ANGULAR;
         }
     }
 
-
-    static class CRS {
-
-        //6.2.2 Geographic CS Parameter Keys
-        static final int GeographicTypeGeoKey        = 2048; /* Section 6.3.2.1 Codes */
-        static final int GeogCitationGeoKey          = 2049; /* documentation */
-        static final int GeogGeodeticDatumGeoKey     = 2050; /* Section 6.3.2.2 Codes */
-        static final int GeogPrimeMeridianGeoKey     = 2051; /* Section 6.3.2.4 codes */
-        static final int GeogLinearUnitsGeoKey       = 2052; /* Section 6.3.1.3 Codes */
-        static final int GeogLinearUnitSizeGeoKey    = 2053; /* meters */
-        static final int GeogAngularUnitsGeoKey      = 2054; /* Section 6.3.1.4 Codes */
-        static final int GeogAngularUnitSizeGeoKey   = 2055; /* radians */
-        static final int GeogEllipsoidGeoKey         = 2056; /* Section 6.3.2.3 Codes */
-        static final int GeogSemiMajorAxisGeoKey     = 2057; /* GeogLinearUnits */
-        static final int GeogSemiMinorAxisGeoKey     = 2058; /* GeogLinearUnits */
-        static final int GeogInvFlatteningGeoKey     = 2059; /* ratio */
-        static final int GeogAzimuthUnitsGeoKey      = 2060; /* Section 6.3.1.4 Codes */
-        static final int GeogPrimeMeridianLongGeoKey = 2061; /* GeogAngularUnit */
-
-        //6.2.3 Projected CS Parameter Keys
-        static final int ProjectedCSTypeGeoKey          = 3072;  /* Section 6.3.3.1 codes */
-        static final int PCSCitationGeoKey              = 3073;  /* documentation */
-        static final int ProjectionGeoKey               = 3074;  /* Section 6.3.3.2 codes */
-        static final int ProjCoordTransGeoKey           = 3075;  /* Section 6.3.3.3 codes */
-        static final int ProjLinearUnitsGeoKey          = 3076;  /* Section 6.3.1.3 codes */
-        static final int ProjLinearUnitSizeGeoKey       = 3077;  /* meters */
-        static final int ProjStdParallel1GeoKey         = 3078;  /* GeogAngularUnit */
-        static final int ProjStdParallel2GeoKey         = 3079;  /* GeogAngularUnit */
-        static final int ProjNatOriginLongGeoKey        = 3080;  /* GeogAngularUnit */
-        static final int ProjNatOriginLatGeoKey         = 3081;  /* GeogAngularUnit */
-        static final int ProjFalseEastingGeoKey         = 3082;  /* ProjLinearUnits */
-        static final int ProjFalseNorthingGeoKey        = 3083;  /* ProjLinearUnits */
-        static final int ProjFalseOriginLongGeoKey      = 3084;  /* GeogAngularUnit */
-        static final int ProjFalseOriginLatGeoKey       = 3085;  /* GeogAngularUnit */
-        static final int ProjFalseOriginEastingGeoKey   = 3086;  /* ProjLinearUnits */
-        static final int ProjFalseOriginNorthingGeoKey  = 3087;  /* ProjLinearUnits */
-        static final int ProjCenterLongGeoKey           = 3088;  /* GeogAngularUnit */
-        static final int ProjCenterLatGeoKey            = 3089;  /* GeogAngularUnit */
-        static final int ProjCenterEastingGeoKey        = 3090;  /* ProjLinearUnits */
-        static final int ProjCenterNorthingGeoKey       = 3091;  /* ProjLinearUnits */
-        static final int ProjScaleAtNatOriginGeoKey     = 3092;  /* ratio */
-        static final int ProjScaleAtCenterGeoKey        = 3093;  /* ratio */
-        static final int ProjAzimuthAngleGeoKey         = 3094;  /* GeogAzimuthUnit */
-        static final int ProjStraightVertPoleLongGeoKey = 3095;  /* GeogAngularUnit */
-        //Aliases:
-        static final int ProjStdParallelGeoKey       = ProjStdParallel1GeoKey;
-        static final int ProjOriginLongGeoKey        = ProjNatOriginLongGeoKey;
-        static final int ProjOriginLatGeoKey         = ProjNatOriginLatGeoKey;
-        static final int ProjScaleAtOriginGeoKey     = ProjScaleAtNatOriginGeoKey;
-
-        //6.2.4 Vertical CS Keys
-        static final int VerticalCSTypeGeoKey    = 4096;   /* Section 6.3.4.1 codes */
-        static final int VerticalCitationGeoKey  = 4097;   /* documentation */
-        static final int VerticalDatumGeoKey     = 4098;   /* Section 6.3.4.2 codes */
-        static final int VerticalUnitsGeoKey     = 4099;   /* Section 6.3.1.3 codes */
-
-        /**
-         * Return tag Name from {@link GeoTiffConstants} class.
-         *
-         * @param tag
-         * @return tag Name from {@link GeoTiffConstants} class.
-         */
-        static String getName(final int tag) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == tag) {
-                            return field.getName();
-                        }
-                    }
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
-            }
-            return Integer.toHexString(tag);
-        }
-
-        static boolean contain(int key) {
-            try {
-                for (final Field field : CRS.class.getDeclaredFields()) {
-                    if (field.getType() == Integer.TYPE) {
-                        if (field.getInt(null) == key) {
-                            return true;
-                        }
+    /**
+     * Returns the name of the given key. Implementation of this method is inefficient,
+     * but it should rarely be invoked (mostly for formatting error messages).
+     */
+    static String name(final short key) {
+        try {
+            for (final Field field : GeoKeys.class.getFields()) {
+                if (field.getType() == Short.TYPE) {
+                    if (field.getShort(null) == key) {
+                        return field.getName();
                     }
                 }
-            } catch (ReflectiveOperationException ex) {
-                throw new AssertionError(ex); // Should never happen.
             }
-            return false;
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(e);        // Should never happen because we asked only for public fields.
         }
+        return Integer.toHexString(key & 0xFFFF);
     }
-
-    static class GridToCrs {
-
-    }
-
 }

Modified: sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTIFF.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTIFF.java?rev=1773010&r1=1773009&r2=1773010&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTIFF.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTIFF.java [UTF-8] Wed Dec  7 04:14:44 2016
@@ -40,12 +40,18 @@ import org.apache.sis.internal.geotiff.R
  */
 abstract class GeoTIFF implements Closeable {
     /**
-     * The timezone specified at construction time, or {@code null} for the default.
+     * The timezone for the date and time parsing, or {@code null} for the default.
      * This is not yet configurable, but may become in a future version.
      */
     private static final TimeZone TIMEZONE = null;
 
     /**
+     * The locale to use for parsers or formatter. This is <strong>not</strong> the locale
+     * for warnings or other messages emitted to the users.
+     */
+    static final Locale LOCALE = Locale.US;
+
+    /**
      * The store which created this reader or writer.
      */
     final GeoTiffStore owner;
@@ -81,7 +87,7 @@ abstract class GeoTIFF implements Closea
      */
     final DateFormat getDateFormat() {
         if (dateFormat == null) {
-            dateFormat = new SimpleDateFormat("yyy:MM:dd HH:mm:ss", Locale.US);
+            dateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", LOCALE);
             if (TIMEZONE != null) {
                 dateFormat.setTimeZone(TIMEZONE);
             }

Modified: sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java?rev=1773010&r1=1773009&r2=1773010&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java [UTF-8] Wed Dec  7 04:14:44 2016
@@ -19,13 +19,19 @@ package org.apache.sis.storage.geotiff;
 import java.util.Locale;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.util.logging.LogRecord;
+import java.nio.charset.StandardCharsets;
+import org.opengis.util.FactoryException;
 import org.opengis.metadata.Metadata;
+import org.opengis.metadata.maintenance.ScopeCode;
 import org.apache.sis.setup.OptionKey;
 import org.apache.sis.storage.DataStore;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.internal.storage.ChannelDataInput;
+import org.apache.sis.internal.storage.MetadataBuilder;
+import org.apache.sis.metadata.sql.MetadataStoreException;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.Classes;
@@ -64,12 +70,13 @@ public class GeoTiffStore extends DataSt
      * This constructor invokes {@link StorageConnector#closeAllExcept(Object)},
      * keeping open only the needed resource.
      *
-     * @param  storage information about the storage (URL, stream, <i>etc</i>).
+     * @param  storage  information about the storage (URL, stream, <i>etc</i>).
      * @throws DataStoreException if an error occurred while opening the GeoTIFF file.
      */
     public GeoTiffStore(final StorageConnector storage) throws DataStoreException {
         ArgumentChecks.ensureNonNull("storage", storage);
-        encoding = storage.getOption(OptionKey.ENCODING);
+        final Charset encoding = storage.getOption(OptionKey.ENCODING);
+        this.encoding = (encoding != null) ? encoding : StandardCharsets.US_ASCII;
         final ChannelDataInput input = storage.getStorageAs(ChannelDataInput.class);
         if (input == null) {
             throw new DataStoreException(Errors.format(Errors.Keys.IllegalInputTypeForReader_2,
@@ -93,18 +100,28 @@ public class GeoTiffStore extends DataSt
      */
     @Override
     public synchronized Metadata getMetadata() throws DataStoreException {
-        if (metadata == null) try {
-            int n = 0;
-            ImageFileDirectory dir;
+        if (metadata == null) {
+            final MetadataBuilder builder = reader.metadata;
+            try {
+                builder.setFormat("GeoTIFF");
+            } catch (MetadataStoreException e) {
+                warning(null, e);
+            }
+            builder.add(encoding);
+            builder.add(ScopeCode.COVERAGE);
             final Locale locale = getLocale();
-            while ((dir = reader.getImageFileDirectory(n++)) != null) {
-                dir.completeMetadata(reader.metadata, locale);
+            int n = 0;
+            try {
+                ImageFileDirectory dir;
+                while ((dir = reader.getImageFileDirectory(n++)) != null) {
+                    dir.completeMetadata(builder, locale);
+                }
+                metadata = builder.build(true);
+            } catch (IOException e) {
+                throw new DataStoreException(reader.errors().getString(Errors.Keys.CanNotRead_1, reader.input.filename), e);
+            } catch (FactoryException | ArithmeticException e) {
+                throw new DataStoreContentException(reader.canNotDecode(), e);
             }
-            metadata = reader.metadata.build(true);
-        } catch (IOException e) {
-            throw new DataStoreException(reader.errors().getString(Errors.Keys.CanNotRead_1, reader.input.filename), e);
-        } catch (ArithmeticException e) {
-            throw new DataStoreContentException(reader.canNotDecode(), e);
         }
         return metadata;
     }
@@ -135,4 +152,23 @@ public class GeoTiffStore extends DataSt
     final void warning(final String message, final Exception exception) {
         listeners.warning(message, exception);
     }
+
+    /**
+     * Reports a warning contained in the given {@link LogRecord}.
+     * Note that the given record will not necessarily be sent to the logging framework;
+     * if the user as registered at least one listener, then the record will be sent to the listeners instead.
+     *
+     * <p>This method sets the {@linkplain LogRecord#setSourceClassName(String) source class name} and
+     * {@linkplain LogRecord#setSourceMethodName(String) source method name} to hard-coded values.
+     * Those values assume that the warnings occurred indirectly from a call to {@link #getMetadata()}
+     * in this class. We do not report private classes or methods as the source of warnings.</p>
+     *
+     * @param  record  the warning to report.
+     */
+    final void warning(final LogRecord record) {
+        // Logger name will be set by listeners.warning(record).
+        record.setSourceClassName(GeoTiffStore.class.getName());
+        record.setSourceMethodName("getMetadata");
+        listeners.warning(record);
+    }
 }

Modified: sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java?rev=1773010&r1=1773009&r2=1773010&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java [UTF-8] Wed Dec  7 04:14:44 2016
@@ -16,15 +16,26 @@
  */
 package org.apache.sis.storage.geotiff;
 
-import java.util.Locale;
 import java.io.IOException;
 import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.NoSuchElementException;
+import java.nio.charset.Charset;
+import javax.measure.Unit;
+import javax.measure.quantity.Length;
 import org.opengis.metadata.citation.DateType;
+import org.opengis.util.FactoryException;
+import org.apache.sis.internal.jdk8.JDK8;
 import org.apache.sis.internal.geotiff.Resources;
+import org.apache.sis.internal.storage.ChannelDataInput;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.math.Vector;
+import org.apache.sis.measure.Units;
 
 
 /**
@@ -42,6 +53,18 @@ import org.apache.sis.math.Vector;
  */
 final class ImageFileDirectory {
     /**
+     * Possible value for the {@link #tileTagFamily} field. That field tells whether image tiling
+     * was specified using the {@code Tile*} family of TIFF tags or the {@code Strip*} family.
+     */
+    private static final byte TILE = 1, STRIP = 2;
+
+    /**
+     * The GeoTIFF reader which contain this {@code ImageFileDirectory}.
+     * Used for fetching information like the input channel and where to report warnings.
+     */
+    private final Reader reader;
+
+    /**
      * {@code true} if this {@code ImageFileDirectory} has not yet read all deferred entries.
      * When this flag is {@code true}, the {@code ImageFileDirectory} is not yet ready for use.
      */
@@ -58,28 +81,68 @@ final class ImageFileDirectory {
 
     /**
      * The size of each tile, or -1 if the information has not be found.
-     * Tiles should be small enough for fitting in memory.
+     * Tiles shall be small enough for fitting in memory, typically in a {@link java.awt.image.Raster} object.
+     * The TIFF specification requires that tile width and height must be a multiple of 16, but the SIS reader
+     * implementation works for any size. Tiles need not be square.
+     *
+     * <p>Assuming integer arithmetic, the number of tiles in an image can be computed as below
+     * (these computed values are not TIFF fields):</p>
+     *
+     * {@preformat math
+     *   tilesAcross   = (imageWidth  + tileWidth  - 1) / tileWidth
+     *   tilesDown     = (imageHeight + tileHeight - 1) / tileHeight
+     *   tilesPerImage = tilesAcross * tilesDown
+     * }
+     *
+     * Note that {@link #imageWidth} can be less than {@code tileWidth} and/or {@link #imageHeight} can be less
+     * than {@code tileHeight}. Such case means that the tiles are too large or that the tiled image is too small,
+     * neither of which is recommended.
      *
      * <p><b>Note:</b>
      * the {@link #tileHeight} attribute is named {@code TileLength} in TIFF specification.</p>
+     *
+     * <div class="section">Strips considered as tiles</div>
+     * The TIFF specification also defines a {@code RowsPerStrip} tag, which is equivalent to the
+     * height of tiles having the same width than the image. While the TIFF specification handles
+     * "tiles" and "strips" separately, Apache SIS handles strips as a special kind of tiles where
+     * only {@code tileHeight} is specified and {@code tileWidth} defaults to {@link #imageWidth}.
      */
     private int tileWidth = -1, tileHeight = -1;
 
     /**
-     * The number of components per pixel.
-     * The {@code samplesPerPixel} value is usually 1 for bilevel, grayscale and palette-color images,
-     * and 3 for RGB images. If this value is higher, then the {@code ExtraSamples} TIFF tag should
-     * give an indication of the meaning of the additional channels.
+     * For each tile, the byte offset of that tile, as compressed and stored on disk.
+     * The offset is specified with respect to the beginning of the TIFF file.
+     * Each tile has a location independent of the locations of other tiles
+     *
+     * <p>Offsets are ordered left-to-right and top-to-bottom. if {@link #isPlanar} is {@code true}
+     * (i.e. components are stored in separate “component planes”), then the offsets for the first
+     * component plane are stored first, followed by all the offsets for the second component plane,
+     * and so on.</p>
+     *
+     * <div class="section">Strips considered as tiles</div>
+     * The TIFF specification also defines a {@code StripOffsets} tag, which contains the byte offset
+     * of each strip. In Apache SIS implementation, strips are considered as a special kind of tiles
+     * having a width equals to {@link #imageWidth}.
      */
-    private short samplesPerPixel = 1;
+    private Vector tileOffsets;
 
     /**
-     * Number of bits per component.
-     * The TIFF specification allows a different number of bits per component for each component corresponding to a pixel.
-     * For example, RGB color data could use a different number of bits per component for each of the three color planes.
-     * However, current Apache SIS implementation requires that all components have the same {@code BitsPerSample} value.
+     * For each tile, the number of (compressed) bytes in that tile.
+     * See {@link #tileOffsets} for a description of how the byte counts are ordered.
+     *
+     * <div class="section">Strips considered as tiles</div>
+     * The TIFF specification also defines a {@code RowsPerStrip} tag, which is the number
+     * of bytes in the strip after compression. In Apache SIS implementation, strips are
+     * considered as a special kind of tiles having a width equals to {@link #imageWidth}.
      */
-    private short bitsPerSample = 1;
+    private Vector tileByteCounts;
+
+    /**
+     * Whether the tiling was specified using the {@code Tile*} family of TIFF tags or the {@code Strip*}
+     * family of tags. Value can be {@link #TILE}, {@link #STRIP} or 0 if unspecified. This field is used
+     * for error detection since Each TIFF file shall use exactly one family of tags.
+     */
+    private byte tileTagFamily;
 
     /**
      * If {@code true}, the components are stored in separate “component planes”.
@@ -89,43 +152,192 @@ final class ImageFileDirectory {
     private boolean isPlanar;
 
     /**
-     * The compression method, or {@code null} if unknown. If the compression method is unknown
-     * or unsupported we can not read the image, but we still can read the metadata.
+     * Whether the bit order should be reversed. This boolean value is determined from the {@code FillOrder} TIFF tag.
+     *
+     * <ul>
+     *   <li>Value 1 (mapped to {@code false}) means that pixels with lower column values are stored in the
+     *       higher-order bits of the byte. This is the default value.</li>
+     *   <li>Value 2 (mapped to {@code true}) means that pixels with lower column values are stored in the
+     *       lower-order bits of the byte. In practice, this order is very uncommon and is not recommended.</li>
+     * </ul>
+     *
+     * Value 1 is mapped to {@code false} and 2 is mapped to {@code true}.
      */
-    private Compression compression;
+    private boolean reverseBitsOrder;
 
     /**
-     * The number of rows per strip.
-     * TIFF image data can be organized into strips for faster random access and efficient I/O buffering.
-     * The {@code rowsPerStrip} and {@link #imageHeight} fields together tell us the number of strips in the entire image.
-     * The equation is:
+     * Number of bits per component.
+     * The TIFF specification allows a different number of bits per component for each component corresponding to a pixel.
+     * For example, RGB color data could use a different number of bits per component for each of the three color planes.
+     * However, current Apache SIS implementation requires that all components have the same {@code BitsPerSample} value.
+     */
+    private short bitsPerSample;
+
+    /**
+     * The number of components per pixel.
+     * The {@code samplesPerPixel} value is usually 1 for bilevel, grayscale and palette-color images,
+     * and 3 for RGB images. If this value is higher, then the {@code ExtraSamples} TIFF tag should
+     * give an indication of the meaning of the additional channels.
+     */
+    private short samplesPerPixel;
+
+    /**
+     * Specifies that each pixel has {@code extraSamples.size()} extra components whose interpretation is defined
+     * by one of the values listed below. When this field is used, the {@link #samplesPerPixel} field has a value
+     * greater than what the {@link #photometricInterpretation} field suggests. For example, full-color RGB data
+     * normally has {@link #samplesPerPixel} = 3. If {@code samplesPerPixel} is greater than 3, then this
+     * {@code extraSamples} field describes the meaning of the extra samples. If {@code samplesPerPixel} is,
+     * say, 5 then this {@code extraSamples} field will contain 2 values, one for each extra sample.
      *
-     * {@preformat math
-     *     StripsPerImage = floor ((ImageLength + RowsPerStrip - 1) / RowsPerStrip)
-     * }
+     * <p>Extra components that are present must be stored as the last components in each pixel.
+     * For example, if {@code samplesPerPixel} is 4 and there is 1 extra component, then it is
+     * located in the last component location in each pixel.</p>
+     *
+     * <p>ExtraSamples is typically used to include non-color information, such as opacity, in an image.
+     * The possible values for each item are:</p>
+     *
+     * <ul>
+     *   <li>0 = Unspecified data.</li>
+     *   <li>1 = Associated alpha data (with pre-multiplied color).</li>
+     *   <li>2 = Unassociated alpha data.</li>
+     * </ul>
+     *
+     * Associated alpha is generally interpreted as true transparency information. Indeed, the original color
+     * values are lost in the case of complete transparency, and rounded in the case of partial transparency.
+     * Also, associated alpha is only logically possible as the single extra channel.
+     * Unassociated alpha channels, on the other hand, can be used to encode a number of independent masks.
+     * The original color data is preserved without rounding. Any number of unassociated alpha channels can
+     * accompany an image.
+     *
+     * <p>If an extra sample is used to encode information that has little or nothing to do with alpha,
+     * then {@code extraSample} = 0 ({@code EXTRASAMPLE_UNSPECIFIED}) is recommended.</p>
+     */
+    private Vector extraSamples;
+
+    /**
+     * The color space of the image data, or -1 if unspecified.
+     *
+     * <table>
+     *   <caption>Color space codes</caption>
+     *   <tr><th>Value</th> <th>Label</th>        <th>Description</th></tr>
+     *   <tr><td>0</td> <td>WhiteIsZero</td>      <td>For bilevel and grayscale images. 0 is imaged as white.</td></tr>
+     *   <tr><td>1</td> <td>BlackIsZero</td>      <td>For bilevel and grayscale images. 0 is imaged as black.</td></tr>
+     *   <tr><td>2</td> <td>RGB</td>              <td>RGB value of (0,0,0) represents black, and (255,255,255) represents white.</td></tr>
+     *   <tr><td>3</td> <td>PaletteColor</td>     <td>The value of the component is used as an index into the RGB values of the {@link #colorMap}.</td></tr>
+     *   <tr><td>4</td> <td>TransparencyMask</td> <td>Defines an irregularly shaped region of another image in the same TIFF file.</td></tr>
+     * </table>
+     */
+    private byte photometricInterpretation = -1;
+
+    /**
+     * A color map for palette color images ({@link #photometricInterpretation} = 3).
+     * This vector defines a Red-Green-Blue color map (often called a lookup table) for palette-color images.
+     * In a palette-color image, a pixel value is used to index into an RGB lookup table. For example, a
+     * palette-color pixel having a value of 0 would be displayed according to the 0th Red, Green, Blue triplet.
      *
-     * {@code StripsPerImage} is not a field. It is merely a value that a TIFF reader will want to compute
-     * because it specifies the number of {@code StripOffsets} and {@code StripByteCounts} for the image.
+     * <p>In a TIFF ColorMap, all the Red values come first, followed by all Green values, then all Blue values.
+     * The number of values for each color is 1 {@literal <<} {@link #bitsPerSample}. Therefore, the {@code ColorMap}
+     * vector for an 8-bit palette-color image would have 3 * 256 values. 0 represents the minimum intensity and 65535
+     * represents the maximum intensity. Black is represented by 0,0,0 and white by 65535, 65535, 65535.</p>
+     *
+     * <p>{@code ColorMap} must be included in all palette-color images.
+     * In Specification Supplement 1, support was added for color maps containing other then RGB values.
+     * This scheme includes the {@code Indexed} tag, with value 1, and a {@link #photometricInterpretation}
+     * different from {@code PaletteColor}.</p>
+     */
+    private Vector colorMap;
+
+    /**
+     * The size of the dithering or halftoning matrix used to create a dithered or halftoned bilevel file.
+     * This field should be present only if {@code Threshholding} tag is 2 (an ordered dither or halftone
+     * technique has been applied to the image data). Special values:
      *
-     * <p>This field should be interpreted as an unsigned value.
-     * The default is 2^32 - 1, which is effectively infinity (i.e. the entire image is one strip).</p>
+     * <ul>
+     *   <li>-1 means that {@code Threshholding} is 1 or unspecified.</li>
+     *   <li>-2 means that {@code Threshholding} is 2 but the matrix size has not yet been specified.</li>
+     *   <li>-3 means that {@code Threshholding} is 3 (randomized process such as error diffusion).</li>
+     * </ul>
+     */
+    private short cellWidth = -1, cellHeight = -1;
+
+    /**
+     * The minimum or maximum sample value found in the image, or {@code NaN} if unspecified.
+     */
+    private double minValue = Double.NaN, maxValue = Double.NaN;
+
+    /**
+     * The number of pixels per {@link #resolutionUnit} in the {@link #imageWidth} and the {@link #imageHeight}
+     * directions, or {@link Double#NaN} is unspecified. Since ISO 19115 does not have separated resolution fields
+     * for image width and height, Apache SIS stores only the maximal value.
+     */
+    private double resolution = Double.NaN;
+
+    /**
+     * The unit of measurement for the {@linkplain #resolution} value, or {@code null} if none.
+     * A null value is used for images that may have a non-square aspect ratio, but no meaningful
+     * absolute dimensions. Default value for TIFF files is inch.
+     */
+    private Unit<Length> resolutionUnit = Units.INCH;
+
+    /**
+     * The compression method, or {@code null} if unknown. If the compression method is unknown
+     * or unsupported we can not read the image, but we still can read the metadata.
+     */
+    private Compression compression;
+
+    /**
+     * References the {@link GeoKeys} needed for building the Coordinate Reference System.
+     * This is a GeoTIFF extension to the TIFF specification.
+     * Content will be parsed by {@link CRSBuilder}.
+     */
+    private Vector geoKeyDirectory;
+
+    /**
+     * The numeric values referenced by the {@link #geoKeyDirectory}.
+     * This is a GeoTIFF extension to the TIFF specification.
+     * Content will be parsed by {@link CRSBuilder}.
+     */
+    private Vector numericGeoParameters;
+
+    /**
+     * The characters referenced by the {@link #geoKeyDirectory}.
+     * This is a GeoTIFF extension to the TIFF specification.
+     * Content will be parsed by {@link CRSBuilder}.
      */
-    private int rowsPerStrip = 0xFFFFFFFF;
+    private String asciiGeoParameters;
 
     /**
      * Creates a new image file directory.
+     *
+     * @param reader  information about the input stream to read, the metadata and the character encoding.
+     */
+    ImageFileDirectory(final Reader reader) {
+        this.reader = reader;
+    }
+
+    /**
+     * Shortcut for a frequently requested information.
      */
-    ImageFileDirectory() {
+    private ChannelDataInput input() {
+        return reader.input;
     }
 
     /**
-     * Adds the value read from the current position in the given stream
-     * for the entry identified by the given GeoTIFF tag.
+     * Shortcut for a frequently requested information.
+     */
+    private Charset encoding() {
+        return reader.owner.encoding;
+    }
+
+    /**
+     * Adds the value read from the current position in the given stream for the entry identified
+     * by the given GeoTIFF tag. This method may store the value either in a field of this class,
+     * or directly in the {@link MetadataBuilder}. However in the later case, this method should
+     * not write anything under the {@code "metadata/contentInfo"} node.
      *
-     * @param  reader   the input stream to read, the metadata and the character encoding.
-     * @param  tag      the GeoTIFF tag to decode.
-     * @param  type     the GeoTIFF type of the value to read.
-     * @param  count    the number of values to read.
+     * @param  tag    the GeoTIFF tag to decode.
+     * @param  type   the GeoTIFF type of the value to read.
+     * @param  count  the number of values to read.
      * @return {@code null} on success, or the unrecognized value otherwise.
      * @throws IOException if an error occurred while reading the stream.
      * @throws ParseException if the value need to be parsed as date and the parsing failed.
@@ -135,7 +347,7 @@ final class ImageFileDirectory {
      * @throws UnsupportedOperationException if the given type is {@link Type#UNDEFINED}.
      * @throws DataStoreException if a logical error is found or an unsupported TIFF feature is used.
      */
-    Object addEntry(final Reader reader, final int tag, final Type type, final long count)
+    Object addEntry(final short tag, final Type type, final long count)
             throws IOException, ParseException, DataStoreException
     {
         switch (tag) {
@@ -153,66 +365,117 @@ final class ImageFileDirectory {
              * 2 = Planar format. For example one plane of Red components, one plane of Green and one plane if Blue.
              */
             case Tags.PlanarConfiguration: {
-                final long value = type.readLong(reader.input, count);
-                if (value < 1 || value > 2) {
-                    return value;
+                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.
                 }
-                isPlanar = (value == 2);
                 break;
             }
             /*
              * The number of columns in the image, i.e., the number of pixels per row.
              */
             case Tags.ImageWidth: {
-                imageWidth = type.readUnsignedLong(reader.input, count);
+                imageWidth = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
              * The number of rows of pixels in the image.
              */
             case Tags.ImageLength: {
-                imageHeight = type.readUnsignedLong(reader.input, count);
+                imageHeight = type.readUnsignedLong(input(), count);
                 break;
             }
             /*
-             * The number of rows per strip. RowsPerStrip and ImageLength together tell us the number of strips
-             * in the entire image: StripsPerImage = floor((ImageLength + RowsPerStrip - 1) / RowsPerStrip).
+             * The tile width in pixels. This is the number of columns in each tile.
+             */
+            case Tags.TileWidth: {
+                setTileTagFamily(TILE);
+                tileWidth = type.readInt(input(), count);
+                break;
+            }
+            /*
+             * The tile length (height) in pixels. This is the number of rows in each tile.
+             */
+            case Tags.TileLength: {
+                setTileTagFamily(TILE);
+                tileHeight = type.readInt(input(), count);
+                break;
+            }
+            /*
+             * 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: {
-                // TODO
+                setTileTagFamily(STRIP);
+                tileHeight = type.readInt(input(), count);
                 break;
             }
             /*
-             * For each strip, the number of bytes in the strip after compression.
+             * The tile length (height) in pixels. This is the number of rows in each tile.
              */
-            case Tags.StripByteCounts: {
-                // TODO
+            case Tags.TileOffsets: {
+                setTileTagFamily(TILE);
+                tileOffsets = type.readVector(input(), count);
                 break;
             }
             /*
              * 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: {
-                // TODO
+                setTileTagFamily(STRIP);
+                tileOffsets = type.readVector(input(), count);
                 break;
             }
             /*
+             * The tile width in pixels. This is the number of columns in each tile.
+             */
+            case Tags.TileByteCounts: {
+                setTileTagFamily(TILE);
+                tileByteCounts = type.readVector(input(), count);
+                break;
+            }
+            /*
+             * 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: {
+                setTileTagFamily(STRIP);
+                tileByteCounts = type.readVector(input(), count);
+                break;
+            }
+
+            ////////////////////////////////////////////////////////////////////////////////////////////////
+            ////                                                                                        ////
+            ////    Information that defines how the sample values are organized (their layout).        ////
+            ////    In Java2D, following information are needed for building the SampleModel.           ////
+            ////                                                                                        ////
+            ////////////////////////////////////////////////////////////////////////////////////////////////
+
+            /*
              * Compression scheme used on the image data.
              */
             case Tags.Compression: {
-                final long value = type.readLong(reader.input, count);
+                final long value = type.readLong(input(), count);
                 compression = Compression.valueOf(value);
                 if (compression == null) {
-                    return value;
+                    return value;                           // Cause a warning to be reported by the caller.
                 }
                 break;
             }
             /*
-             * The logical order of bits within a byte. If this value is 2, then bits order shall be reversed in every
-             * bytes before decompression.
+             * 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: {
-                // TODO
+                final int value = type.readInt(input(), count);
+                switch (value) {
+                    case 1: reverseBitsOrder = false; break;
+                    case 2: reverseBitsOrder = true;  break;
+                    default: return value;                  // Cause a warning to be reported by the caller.
+                }
                 break;
             }
             /*
@@ -221,7 +484,7 @@ final class ImageFileDirectory {
              * But the TIFF specification allows different values.
              */
             case Tags.BitsPerSample: {
-                final Vector values = type.readVector(reader.input, count);
+                final Vector values = type.readVector(input(), count);
                 /*
                  * The current implementation requires that all 'bitsPerSample' elements have the same value.
                  * This restriction may be revisited in future Apache SIS versions.
@@ -232,7 +495,7 @@ final class ImageFileDirectory {
                 for (int i = 1; i < length; i++) {
                     if (values.shortValue(i) != bitsPerSample) {
                         throw new DataStoreContentException(reader.resources().getString(
-                                Resources.Keys.ConstantValueRequired_3, "BitsPerSample", reader.input.filename, values));
+                                Resources.Keys.ConstantValueRequired_3, "BitsPerSample", input().filename, values));
                     }
                 }
                 break;
@@ -242,7 +505,7 @@ final class ImageFileDirectory {
              * and 3 for RGB images. Default value is 1.
              */
             case Tags.SamplesPerPixel: {
-                samplesPerPixel = type.readShort(reader.input, count);
+                samplesPerPixel = type.readShort(input(), count);
                 break;
             }
             /*
@@ -252,7 +515,7 @@ final class ImageFileDirectory {
              * describes the meaning of the extra samples. It may be an alpha channel, but not necessarily.
              */
             case Tags.ExtraSamples: {
-                // TODO
+                extraSamples = type.readVector(input(), count);
                 break;
             }
 
@@ -267,31 +530,33 @@ final class ImageFileDirectory {
              * The color space of the image data.
              * 0 = WhiteIsZero. For bilevel and grayscale images: 0 is imaged as white.
              * 1 = BlackIsZero. For bilevel and grayscale images: 0 is imaged as black.
-             * 2 = RGB. RGB value of (0,0,0) represents black, and (255,255,255) represents white.
-             * 3 = Palette color. The value of the component is used as an index into the RGB valuesthe ColorMap.
+             * 2 = RGB. RGB value of (0,0,0) represents black, and (65535,65535,65535) represents white.
+             * 3 = Palette color. The value of the component is used as an index into the RGB values of the ColorMap.
              * 4 = Transparency Mask the defines an irregularly shaped region of another image in the same TIFF file.
              */
             case Tags.PhotometricInterpretation: {
-                // TODO
+                final short value = type.readShort(input(), count);
+                if (value < 0 || value > Byte.MAX_VALUE) return value;
+                photometricInterpretation = (byte) value;
                 break;
             }
             /*
-             * The Red-Green-Blue lookup table map for palette-color images (IndexColorModel in Java2D).
-             * All the Red values come first, followed by the Green values, then the Blue values.
-             * The number of values for each color is (1 << BitsPerSample) and the type of each value is USHORT.
-             * 0 represents the minimum intensity (black is 0,0,0) and 65535 represents the maximum intensity.
-             * If the Indexed tag has value 1 and PhotometricInterpretation is different from PaletteColor,
-             * then the color space may be different than RGB.
+             * The lookup table for palette-color images. This is represented by IndexColorModel in Java2D.
+             * Color space is RGB if PhotometricInterpretation is "PaletteColor", or another color space otherwise.
+             * In the RGB case, all the Red values come first, followed by all Green values, then all Blue values.
+             * 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: {
-                // TODO
+                colorMap = type.readVector(input(), count);
                 break;
             }
             /*
              * The minimum component value used. Default is 0.
              */
             case Tags.MinSampleValue: {
-                reader.metadata.addMinimumSampleValue(type.readDouble(reader.input, count));
+                final double v = type.readDouble(input(), count);
+                if (Double.isNaN(minValue) || v < minValue) minValue = v;
                 break;
             }
             /*
@@ -300,7 +565,8 @@ final class ImageFileDirectory {
              * visual appearance of an image, unless a map styling is applied.
              */
             case Tags.MaxSampleValue: {
-                reader.metadata.addMaximumSampleValue(type.readDouble(reader.input, count));
+                final double v = type.readDouble(input(), count);
+                if (Double.isNaN(maxValue) || v > maxValue) maxValue = v;
                 break;
             }
 
@@ -341,6 +607,36 @@ final class ImageFileDirectory {
             ////////////////////////////////////////////////////////////////////////////////////////////////
 
             /*
+             * References the "GeoKeys" needed for building the Coordinate Reference System.
+             * 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: {
+                geoKeyDirectory = type.readVector(input(), count);
+                break;
+            }
+            /*
+             * Stores all of the 'double' valued GeoKeys, referenced by the GeoKeyDirectory.
+             */
+            case Tags.GeoDoubleParams: {
+                numericGeoParameters = type.readVector(input(), count);
+                break;
+            }
+            /*
+             * Stores all the characters referenced by the GeoKeyDirectory. Should contains exactly one string
+             * 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: {
+                final String[] values = type.readString(input(), count, encoding());
+                switch (values.length) {
+                    case 0:  break;
+                    case 1:  asciiGeoParameters = values[0]; break;
+                    default: asciiGeoParameters = JDK8.join("\u0000", values).concat("\u0000"); break;
+                }
+                break;
+            }
+            /*
              * 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).
              */
@@ -349,11 +645,11 @@ final class ImageFileDirectory {
                 break;
             }
 
-
             ////////////////////////////////////////////////////////////////////////////////////////////////
             ////                                                                                        ////
             ////    Metadata for discovery purposes, conditions of use, etc.                            ////
             ////    Those metadata are not "critical" information for reading the image.                ////
+            ////    Should not write anything under 'metadata/contentInfo' node.                        ////
             ////                                                                                        ////
             ////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -362,7 +658,7 @@ final class ImageFileDirectory {
              * For example, a user may wish to attach a comment such as "1988 company picnic" to an image.
              */
             case Tags.ImageDescription: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.addTitle(value);
                 }
                 break;
@@ -372,7 +668,7 @@ final class ImageFileDirectory {
              * Copyright information, but Apache SIS does not support this legacy practice.
              */
             case Tags.Artist: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.addAuthor(value);
                 }
                 break;
@@ -382,7 +678,7 @@ final class ImageFileDirectory {
              * Example: “Copyright, John Smith, 1992. All rights reserved.”
              */
             case Tags.Copyright: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.parseLegalNotice(value);
                 }
                 break;
@@ -391,7 +687,7 @@ final class ImageFileDirectory {
              * Date and time of image creation. The format is: "YYYY:MM:DD HH:MM:SS" with 24-hour clock.
              */
             case Tags.DateTime: {
-                for (final String value : type.readString(reader.input, count, reader.owner.encoding)) {
+                for (final String value : type.readString(input(), count, encoding())) {
                     reader.metadata.add(reader.getDateFormat().parse(value), DateType.CREATION);
                 }
                 break;
@@ -400,14 +696,18 @@ final class ImageFileDirectory {
              * The computer and/or operating system in use at the time of image creation.
              */
             case Tags.HostComputer: {
-                // TODO
+                for (final String value : type.readString(input(), count, encoding())) {
+                    reader.metadata.addHostComputer(value);
+                }
                 break;
             }
             /*
              * Name and version number of the software package(s) used to create the image.
              */
             case Tags.Software: {
-                // TODO
+                for (final String value : type.readString(input(), count, encoding())) {
+                    reader.metadata.addSoftwareReference(value);
+                }
                 break;
             }
             /*
@@ -415,7 +715,8 @@ final class ImageFileDirectory {
              * Synthetic images should not include this field.
              */
             case Tags.Make: {
-                // TODO
+                // TODO: is Instrument.citation.citedResponsibleParty.party.name an appropriate place?
+                // what would be the citation title? A copy of Tags.Model?
                 break;
             }
             /*
@@ -423,55 +724,68 @@ final class ImageFileDirectory {
              * generate the image.
              */
             case Tags.Model: {
-                // TODO
-                break;
-            }
-            /*
-             * The number of pixels per ResolutionUnit in the ImageWidth direction.
-             */
-            case Tags.XResolution: {
-                // TODO
+                for (final String value : type.readString(input(), count, encoding())) {
+                    reader.metadata.addInstrument(value);
+                }
                 break;
             }
             /*
-             * The number of pixels per ResolutionUnit in the ImageLength direction.
+             * The number of pixels per ResolutionUnit in the ImageWidth or ImageHeight direction.
              */
+            case Tags.XResolution:
             case Tags.YResolution: {
-                // TODO
+                final double r = type.readDouble(input(), count);
+                if (Double.isNaN(resolution) || r > resolution) {
+                    resolution = r;
+                }
                 break;
             }
             /*
              * The unit of measurement for XResolution and YResolution.
-             * 1 = None, 2 = Inch, 3 = Centimeter.
+             *
+             *   1 = None. Used for images that may have a non-square aspect ratio.
+             *   2 = Inch (default).
+             *   3 = Centimeter.
              */
             case Tags.ResolutionUnit: {
-                // TODO
+                final short unit = type.readShort(input(), count);
+                switch (unit) {
+                    case 1:  resolutionUnit = null;             break;
+                    case 2:  resolutionUnit = Units.INCH;       break;
+                    case 3:  resolutionUnit = Units.CENTIMETRE; break;
+                    default: return unit;                   // Cause a warning to be reported by the caller.
+                }
                 break;
             }
             /*
-             * The technique used to convert from gray to black and white pixels (if applicable):
-             * 1 = No dithering or halftoning has been applied to the image data.
-             * 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.
+             * For black and white TIFF files that represent shades of gray, the technique used to convert
+             * from gray to black and white pixels. The default value is 1 (nothing done on the image).
+             *
+             *   1 = No dithering or halftoning has been applied to the image data.
+             *   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: {
-                // TODO
+                final short value = type.readShort(input(), count);
+                switch (value) {
+                    case 1:  break;
+                    case 2:  if (cellWidth >= 0 || cellHeight >= 0) return null; else break;
+                    case 3:  break;
+                    default: return value;                  // Cause a warning to be reported by the caller.
+                }
+                cellWidth = cellHeight = (short) -value;
                 break;
             }
             /*
-             * The width of the dithering or halftoning matrix used to create a dithered or halftoned
-             * bilevel file. Meaningful only if Threshholding = 2.
+             * 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: {
-                // TODO
+                cellWidth = type.readShort(input(), count);
                 break;
             }
-            /*
-             * The height of the dithering or halftoning matrix used to create a dithered or halftoned
-             * bilevel file. Meaningful only if Threshholding = 2.
-             */
             case Tags.CellLength: {
-                // TODO
+                cellHeight = type.readShort(input(), count);
                 break;
             }
 
@@ -493,7 +807,7 @@ final class ImageFileDirectory {
              */
             case Tags.GrayResponseCurve:
             case Tags.GrayResponseUnit: {
-                // TODO: log a warning saying that this tag is ignored.
+                warning(Level.FINE, Resources.Keys.IgnoredTag_1, Tags.name(tag));
                 break;
             }
         }
@@ -501,12 +815,276 @@ final class ImageFileDirectory {
     }
 
     /**
+     * Sets the {@link #tileTagFamily} field to the given value if it does not conflict with previous value.
+     *
+     * @param  family  either {@link #TILE} or {@link #STRIP}.
+     * @throws DataStoreContentException if {@link #tileTagFamily} is already set to another value.
+     */
+    private void setTileTagFamily(final byte family) throws DataStoreContentException {
+        if (tileTagFamily != family && tileTagFamily != 0) {
+            throw new DataStoreContentException(reader.resources().getString(
+                    Resources.Keys.InconsistentTileStrip_1, input().filename));
+        }
+        tileTagFamily = family;
+    }
+
+    /**
+     * Multiplies the given value by the number of bytes in one pixel,
+     * or return -1 if the result is not an integer.
+     *
+     * @throws ArithmeticException if the result overflows.
+     */
+    private long pixelToByteCount(long value) {
+        value = JDK8.multiplyExact(value, samplesPerPixel * (int) bitsPerSample);
+        return (value % Byte.SIZE == 0) ? value / Byte.SIZE : -1;
+    }
+
+    /**
+     * Computes the tile width or height from the other size,
+     * or returns a negative number if the size can not be computed.
+     *
+     * @param  knownSize the tile width or height.
+     * @return the tile width if the known size was height, or the tile height if the known size was width,
+     *         or a negative number if the width or height can not be computed.
+     * @throws ArithmeticException if the result overflows.
+     */
+    private int computeTileSize(final int knownSize) {
+        final int n = tileByteCounts.size();
+        if (n != 0) {
+            final long count = tileByteCounts.longValue(0);
+            int i = 0;
+            do if (++i == n) {
+                // At this point, we verified that all vector values are equal.
+                final long length = pixelToByteCount(knownSize);
+                if (count % length != 0) break;
+                return JDK8.toIntExact(count / length);
+            } while (tileByteCounts.longValue(i) == n);
+        }
+        return -1;
+    }
+
+    /**
+     * Verifies that the mandatory tags are present and consistent with each others.
+     * If a mandatory tag is absent, then there is a choice:
+     *
+     * <ul>
+     *   <li>If the tag can be inferred from other tag values, performs that computation and logs a warning.</li>
+     *   <li>Otherwise throws an exception.</li>
+     * </ul>
+     *
+     * @throws DataStoreContentException if a mandatory tag is missing and can not be inferred.
+     */
+    final void validateMandatoryTags() throws DataStoreContentException {
+        if (imageWidth  < 0) throw missingTag(Tags.ImageWidth);
+        if (imageHeight < 0) throw missingTag(Tags.ImageLength);
+        final short offsetsTag, byteCountsTag;
+        switch (tileTagFamily) {
+            case STRIP: {
+                if (tileWidth  < 0) tileWidth  = JDK8.toIntExact(imageWidth);
+                if (tileHeight < 0) tileHeight = JDK8.toIntExact(imageHeight);
+                offsetsTag    = Tags.StripOffsets;
+                byteCountsTag = Tags.StripByteCounts;
+                break;
+            }
+            case TILE:  {
+                offsetsTag    = Tags.TileOffsets;
+                byteCountsTag = Tags.TileByteCounts;
+                break;
+            }
+            default: {
+                throw new DataStoreContentException(reader.resources().getString(
+                        Resources.Keys.InconsistentTileStrip_1, input().filename));
+            }
+        }
+        if (tileOffsets == null) {
+            throw missingTag(offsetsTag);
+        }
+        if (samplesPerPixel == 0) {
+            samplesPerPixel = 1;
+            missingTag(Tags.SamplesPerPixel, 1, false);
+        }
+        if (bitsPerSample == 0) {
+            bitsPerSample = 1;
+            missingTag(Tags.BitsPerSample, 1, false);
+        }
+        if (colorMap != null) {
+            ensureSameLength(Tags.ColorMap, Tags.BitsPerSample, colorMap.size(),  3 * (1 << bitsPerSample));
+        }
+        /*
+         * All of tile width, height and length information should be provided. But if only one of them is missing,
+         * we can compute it provided that the file does not use any compression method. If there is a compression,
+         * then we set a bit for preventing the 'switch' block to perform a calculation but we let the code performs
+         * the other checks in order to get an exception to be thrown with a good message.
+         */
+        int missing = !isPlanar && compression.equals(Compression.NONE) ? 0 : 0b1000;
+        if (tileWidth      < 0)     missing |= 0b0001;
+        if (tileHeight     < 0)     missing |= 0b0010;
+        if (tileByteCounts == null) missing |= 0b0100;
+        switch (missing) {
+            case 0:
+            case 0b1000: {          // Every thing is ok.
+                break;
+            }
+            case 0b0001: {          // Compute missing tile width.
+                tileWidth = computeTileSize(tileHeight);
+                missingTag(Tags.TileWidth, tileWidth, true);
+                break;
+            }
+            case 0b0010: {          // Compute missing tile height.
+                tileHeight = computeTileSize(tileWidth);
+                missingTag(Tags.TileLength, tileHeight, true);
+                break;
+            }
+            case 0b0100: {          // Compute missing tile byte count.
+                final long tileByteCount = pixelToByteCount(JDK8.multiplyExact(tileWidth, tileHeight));
+                final long[] tileByteCountArray = new long[tileOffsets.size()];
+                Arrays.fill(tileByteCountArray, tileByteCount);
+                tileByteCounts = Vector.create(tileByteCountArray, true);
+                missingTag(byteCountsTag, tileByteCount, true);
+                break;
+            }
+            default: {
+                final short tag;
+                switch (Integer.lowestOneBit(missing)) {
+                    case 0b0001: tag = Tags.TileWidth;  break;
+                    case 0b0010: tag = Tags.TileLength; break;
+                    default:     tag = byteCountsTag;   break;
+                }
+                throw missingTag(tag);
+            }
+        }
+        /*
+         * Log a warning if the tile offset and tile byte count vectors do not have the same length. Then
+         * ensure that the number of tiles is equal to the expected number.  The formula below is the one
+         * documented in the TIFF specification and reproduced in tileWidth & tileHeight fields javadoc.
+         */
+        ensureSameLength(offsetsTag, byteCountsTag, tileOffsets.size(), tileByteCounts.size());
+        long expectedCount = JDK8.multiplyExact(
+                JDK8.addExact(imageWidth,  tileWidth  - 1) / tileWidth,
+                JDK8.addExact(imageHeight, tileHeight - 1) / tileHeight);
+        if (isPlanar) {
+            expectedCount = JDK8.multiplyExact(expectedCount, samplesPerPixel);
+        }
+        final int actualCount = Math.min(tileOffsets.size(), tileByteCounts.size());
+        if (actualCount != expectedCount) {
+            throw new DataStoreContentException(reader.resources().getString(Resources.Keys.UnexpectedTileCount_3,
+                    input().filename, expectedCount, actualCount));
+        }
+    }
+
+    /**
      * Completes the metadata with the information stored in the field of this IFD.
      * This method is invoked only if the user requested the ISO 19115 metadata.
+     * This method creates a new {@code "metadata/contentInfo"} node for this image.
+     * Information not under the {@code "metadata/contentInfo"} node will be merged
+     * with the current content of the given {@code MetadataBuilder}.
+     *
+     * @param metadata  where to write metadata information. Caller should have already invoked
+     *        {@link MetadataBuilder#setFormat(String)} before {@code completeMetadata(…)} calls.
      */
-    final void completeMetadata(final MetadataBuilder metadata, final Locale locale) {
+    final void completeMetadata(final MetadataBuilder metadata, final Locale locale)
+            throws DataStoreContentException, FactoryException
+    {
+        metadata.newCoverage(false);
         if (compression != null) {
             metadata.addCompression(compression.name().toLowerCase(locale));
         }
+        metadata.addMinimumSampleValue(minValue);
+        metadata.addMaximumSampleValue(maxValue);
+        /*
+         * Add the resolution into the metadata. Our current ISO 19115 implementation restricts
+         * the resolution unit to metres, but it may be relaxed in a future SIS version.
+         */
+        if (!Double.isNaN(resolution) && resolutionUnit != null) {
+            metadata.addResolution(resolutionUnit.getConverterTo(Units.METRE).convert(resolution));
+        }
+        /*
+         * Cell size is relevant only if the Threshholding TIFF tag value is 2. By convention in
+         * this implementation class, other Threshholding values are stored as negative cell sizes:
+         *
+         *   -1 means that Threshholding is 1 or unspecified.
+         *   -2 means that Threshholding is 2 but the matrix size has not yet been specified.
+         *   -3 means that Threshholding is 3 (randomized process such as error diffusion).
+         */
+        switch (Math.min(cellWidth, cellHeight)) {
+            case -1: {
+                // Nothing to report.
+                break;
+            }
+            case -3: {
+                metadata.addProcessDescription(Resources.formatInternational(Resources.Keys.RandomizedProcessApplied));
+                break;
+            }
+            default: {
+                metadata.addProcessDescription(Resources.formatInternational(
+                            Resources.Keys.DitheringOrHalftoningApplied_2,
+                            (cellWidth  >= 0) ? cellWidth  : '?',
+                            (cellHeight >= 0) ? cellHeight : '?'));
+                break;
+            }
+        }
+        /*
+         * Add Coordinate Reference System built from GeoTIFF tags.  Note that the CRS may not exist,
+         * in which case the CRS builder returns null. This is safe since all MetadataBuilder methods
+         * ignore null values (a design choice because this pattern come very often).
+         */
+        if (geoKeyDirectory != null) {
+            final CRSBuilder helper = new CRSBuilder(reader);
+            try {
+                metadata.add(helper.build(geoKeyDirectory, numericGeoParameters, asciiGeoParameters));
+                helper.complete(metadata);
+            } catch (ClassCastException e) {
+                reader.owner.warning(null, e);
+            } catch (NumberFormatException | NoSuchElementException e) {
+                // Ignore - a warning with a better message has already been emitted.
+            }
+            geoKeyDirectory      = null;            // Not needed anymore, so let GC do its work.
+            numericGeoParameters = null;
+            asciiGeoParameters   = null;
+        }
+    }
+
+    /**
+     * Reports a warning with a message created from the given resource keys and parameters.
+     *
+     * @param level       the logging level for the message to log.
+     * @param key         the {@code Resources} key of the message to format.
+     * @param parameters  the parameters to put in the message.
+     */
+    private void warning(final Level level, final short key, final Object... parameters) {
+        final LogRecord r = reader.resources().getLogRecord(level, key, parameters);
+        reader.owner.warning(r);
+    }
+
+    /**
+     * Verifies that the given tags have the same length and reports a warning if they do not.
+     */
+    private void ensureSameLength(final short tag1, final short tag2, final int length1, final int length2) {
+        if (length1 != length2) {
+            warning(Level.WARNING, Resources.Keys.MismatchedLength_4, Tags.name(tag1), Tags.name(tag2), length1, length2);
+        }
+    }
+
+    /**
+     * Reports a warning for a missing TIFF tag for which a default value can be computed.
+     *
+     * @param  missing   the numerical value of the missing tag.
+     * @param  value     the default value or the computed value.
+     * @param  computed  whether the default value has been computed.
+     */
+    private void missingTag(final short missing, final long value, final boolean computed) {
+        warning(computed ? Level.WARNING : Level.FINE,
+                computed ? Resources.Keys.ComputedValueForAttribute_2 : Resources.Keys.DefaultValueForAttribute_2,
+                Tags.name(missing), value);
+    }
+
+    /**
+     * Builds an exception for a missing TIFF tag for which no default value can be computed.
+     *
+     * @param  missing  the numerical value of the missing tag.
+     */
+    private DataStoreContentException missingTag(final short missing) {
+        return new DataStoreContentException(reader.resources().getString(
+                Resources.Keys.MissingValue_2, input().filename, Tags.name(missing)));
     }
 }



Mime
View raw message