sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1801297 - in /sis/branches/JDK8: core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/ storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/ storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ stor...
Date Sat, 08 Jul 2017 15:05:46 GMT
Author: desruisseaux
Date: Sat Jul  8 15:05:45 2017
New Revision: 1801297

URL: http://svn.apache.org/viewvc?rev=1801297&view=rev
Log:
Do not store anymore the GeoTIFF localization grid as Ground Control Point (GCP) since they
are not GCP.
More robust calculation of minimal and maximal values.

Modified:
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCP.java
    sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCPCollection.java
    sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
    sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
    sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
    sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometry.java
    sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
    sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCP.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCP.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCP.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCP.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -29,6 +29,8 @@ import org.apache.sis.xml.Namespaces;
 
 /**
  * Information on ground control point.
+ * Ground control points (GCP) are large marked targets on the ground,
+ * not to be confused with <cite>localization grid</cite> points embedded in
some file formats like GeoTIFF or NetCDF.
  * The following property is mandatory in a well-formed metadata according ISO 19115:
  *
  * <div class="preformat">{@code MI_GCP}
@@ -46,7 +48,10 @@ import org.apache.sis.xml.Namespaces;
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.3
- * @since   0.3
+ *
+ * @see DefaultGCPCollection
+ *
+ * @since 0.3
  * @module
  */
 @SuppressWarnings("CloneableClassWithoutClone")                 // ModifiableMetadata needs
shallow clones.

Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCPCollection.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCPCollection.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCPCollection.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/spatial/DefaultGCPCollection.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -51,7 +51,10 @@ import org.apache.sis.xml.Namespaces;
  * @author  Cédric Briançon (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.3
- * @since   0.3
+ *
+ * @see DefaultGCP
+ *
+ * @since 0.3
  * @module
  */
 @SuppressWarnings("CloneableClassWithoutClone")                 // ModifiableMetadata needs
shallow clones.

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -146,6 +146,11 @@ public final class Resources extends Ind
         public static final short RandomizedProcessApplied = 15;
 
         /**
+         * The “{0}” GeoTIFF file does not specify the values format.
+         */
+        public static final short UndefinedDataFormat_1 = 25;
+
+        /**
          * A single value was expected for the “{0}” key but {1} values have been found.
          */
         public static final short UnexpectedListOfValues_2 = 16;

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources.properties
[ISO-8859-1] Sat Jul  8 15:05:45 2017
@@ -35,6 +35,7 @@ MissingGeoValue_1                 = No v
 MissingValue_2                    = Can not read TIFF image from \u201c{0}\u201d because
the \u201c{1}\u201d tag is missing.
 NotTheEpsgValue_5                 = The file defines \u201c{2}\u201d with value {3}{4}, but
that value should be {1}{4} according parent definition ({0}).
 RandomizedProcessApplied          = A randomized process such as error diffusion has been
applied to the image data.
+UndefinedDataFormat_1             = The \u201c{0}\u201d GeoTIFF file does not specify the
values format.
 UnexpectedListOfValues_2          = A single value was expected for the \u201c{0}\u201d key
but {1} values have been found.
 UnexpectedParameter_2             = The \u201c{1}\u201d parameter was not expected for the
\u201c{0}\u201d projection method.
 UnexpectedTileCount_3             = Found {2} tiles or strips in the \u201c{0}\u201d file
while {1} were expected.

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
[ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/Resources_fr.properties
[ISO-8859-1] Sat Jul  8 15:05:45 2017
@@ -40,6 +40,7 @@ MissingGeoValue_1                 = Aucu
 MissingValue_2                    = Ne peut pas lire l\u2019image TIFF provenant de \u00ab\u202f{0}\u202f\u00bb
car le tag \u00ab\u202f{1}\u202f\u00bb est manquant.
 NotTheEpsgValue_5                 = Le fichier d\u00e9finit \u00ab\u202f{2}\u202f\u00bb avec
la valeur {3}{4}, mais cette valeur devrait \u00eatre {1}{4} pour \u00eatre en accord avec
la d\u00e9finition du parent {0}.
 RandomizedProcessApplied          = Un processus randomis\u00e9 comme la diffusion d\u2019erreur
a \u00e9t\u00e9 appliqu\u00e9.
+UndefinedDataFormat_1             = Le fichier GeoTIFF \u00ab\u202f{0}\u202f\u00bb ne sp\u00e9cifie
pas le format de ses valeurs.
 UnexpectedListOfValues_2          = Une seule valeur \u00e9tait attendue pour la cl\u00e9
\u00ab\u202f{0}\u202f\u00bb, mais on en a trouv\u00e9es {1}.
 UnexpectedParameter_2             = Le param\u00e8tre \u00ab\u202f{1}\u202f\u00bb est inattendu
pour la m\u00e9thode de projection \u00ab\u202f{0}\u202f\u00bb.
 UnexpectedTileCount_3             = {2} tuiles ont \u00e9t\u00e9 trouv\u00e9es dans le fichier
\u00ab\u202f{0}\u202f\u00bb alors qu\u2019on en attendait {1}.

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometry.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometry.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometry.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -28,7 +28,6 @@ import org.apache.sis.referencing.operat
 import org.apache.sis.referencing.operation.transform.LinearTransform;
 import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
 import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
-import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.util.DoubleDouble;
 import org.apache.sis.math.Vector;
 
@@ -41,6 +40,32 @@ import org.apache.sis.math.Vector;
  * We implement {@code CoordinateOperation} for now for allowing access to the math transform
from
  * outside this package, but we will probably not keep this class hierarchy in a future version.
  *
+ * <div class="section">Pixel center versus pixel corner</div>
+ * The policy about whether the conversion map pixel corner or pixel center does not seem
totally clear.
+ * But the following seems to be the practice at least with GDAL:
+ *
+ * {@preformat text
+ *     ModelTiepointTag = (0.0, 0.0, 0.0, -180.0, 90.0, 0.0)
+ *     ModelPixelScaleTag = (0.002777777778, 0.002777777778, 0.0)
+ *     GeoKeyDirectoryTag:
+ *         GTModelTypeGeoKey    = 2    (ModelTypeGeographic)
+ *         GTRasterTypeGeoKey   = 1    (RasterPixelIsArea)
+ *         GeographicTypeGeoKey = 4326 (GCS_WGS_84)
+ * }
+ *
+ * and
+ *
+ * {@preformat text
+ *     ModelTiepointTag = (-0.5, -0.5, 0.0, -180.0, 90.0, 0.0)
+ *     ModelPixelScaleTag = (0.002777777778, 0.002777777778, 0.0)
+ *     GeoKeyDirectoryTag:
+ *         GTModelTypeGeoKey    = 2    (ModelTypeGeographic)
+ *         GTRasterTypeGeoKey   = 2    (RasterPixelIsPoint)
+ *         GeographicTypeGeoKey = 4326 (GCS_WGS_84)
+ * }
+ *
+ * are considered equivalent.
+ *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
  * @since   0.8
@@ -97,38 +122,51 @@ final class GridGeometry extends Abstrac
 
     /**
      * Computes translation terms in the given matrix from the (usually singleton) tie point.
+     * This method is invoked when the GeoTIFF file has a {@link Tags#ModelPixelScaleTag}
and
+     * {@link Tags#ModelTiePoints}. The later should have a single record.
      *
      * @param  gridToCRS       the matrix to update. That matrix shall contain the scale
factors before to invoke this method.
      * @param  modelTiePoints  the vector of model tie points. Only the first point will
be used.
      * @return {@code true} if the given vector is non-null and contains at least one complete
record.
      */
     static boolean setTranslationTerms(final MatrixSIS gridToCRS, final Vector modelTiePoints)
{
-        if (modelTiePoints == null || modelTiePoints.size() < RECORD_LENGTH) {
-            return false;
-        }
-        final DoubleDouble t = new DoubleDouble();
-        final int numDim = gridToCRS.getNumRow() - 1;
-        final int trCol  = gridToCRS.getNumCol() - 1;
-        for (int j=0; j<numDim; j++) {
-            t.value = -modelTiePoints.doubleValue(j);
-            t.error = DoubleDouble.errorForWellKnownValue(t.value);
-            t.divide(gridToCRS.getNumber(j, j));
-            t.add(modelTiePoints.doubleValue(j + RECORD_LENGTH/2));
-            gridToCRS.setNumber(j, trCol, t);
+        if (modelTiePoints != null) {
+            /*
+             * The GeoTIFF specification recommends that the first point is located at grid
indices (0,0).
+             * But as a safety, we will nevertheless search in the grid for the point closest
to origin.
+             * If the grid is affine, using the corner closest to (0,0) reduces rounding
errors compared
+             * to using another corner. If the grid is not affine, then ModelPixelScaleTag
should not have
+             * been defined for that file…
+             */
+            int nearest = 0;                                // Index of the record nearest
to origin.
+            double distance = Double.POSITIVE_INFINITY;     // Distance squared of the nearest
record.
+            final int size = modelTiePoints.size();
+            for (int i=0; i<size; i += RECORD_LENGTH) {
+                double t;
+                final double d = (t = modelTiePoints.doubleValue(i    )) * t
+                               + (t = modelTiePoints.doubleValue(i + 1)) * t
+                               + (t = modelTiePoints.doubleValue(i + 2)) * t;
+                if (d < distance) {
+                    distance = d;
+                    nearest = i;
+                    if (d == 0) break;                      // Optimization for the standard
case.
+                }
+            }
+            if (distance != Double.POSITIVE_INFINITY) {
+                final DoubleDouble t = new DoubleDouble();
+                final int numDim = gridToCRS.getNumRow() - 1;
+                final int trCol  = gridToCRS.getNumCol() - 1;
+                for (int j=0; j<numDim; j++) {
+                    t.value = -modelTiePoints.doubleValue(nearest + j);
+                    t.error = DoubleDouble.errorForWellKnownValue(t.value);
+                    t.divide(gridToCRS.getNumber(j, j));
+                    t.add(modelTiePoints.doubleValue(nearest + j + RECORD_LENGTH/2));
+                    gridToCRS.setNumber(j, trCol, t);
+                }
+                return true;
+            }
         }
-        return true;
-    }
-
-    /**
-     * Writes the check point or Ground Control Points (GCP) in the metadata.
-     *
-     * @param metadata        where to write the ground control points.
-     * @param modelTiePoints  the vector of model tie points.
-     */
-    static void addControlPoints(final CoordinateReferenceSystem crs, final MetadataBuilder
metadata, final Vector modelTiePoints)
-            throws FactoryException, TransformException
-    {
-        metadata.addControlPoints(crs, modelTiePoints, RECORD_LENGTH/2, RECORD_LENGTH);
+        return false;
     }
 
     /**

Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -67,6 +67,13 @@ final class ImageFileDirectory {
     private static final byte TILE = 1, STRIP = 2;
 
     /**
+     * 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}.
+     * Default value is {@link #UNSIGNED}.
+     */
+    private static final byte SIGNED = 1, UNSIGNED = 0, FLOAT = 3;
+
+    /**
      * The GeoTIFF reader which contain this {@code ImageFileDirectory}.
      * Used for fetching information like the input channel and where to report warnings.
      */
@@ -160,6 +167,12 @@ final class ImageFileDirectory {
     private boolean isPlanar;
 
     /**
+     * How to interpret each data sample in a pixel.
+     * Possible values are {@link #SIGNED}, {@link #UNSIGNED} or {@link #FLOAT}.
+     */
+    private byte sampleFormat;
+
+    /**
      * Whether the bit order should be reversed. This boolean value is determined from the
{@code FillOrder} TIFF tag.
      *
      * <ul>
@@ -269,9 +282,10 @@ final class ImageFileDirectory {
     private short cellWidth = -1, cellHeight = -1;
 
     /**
-     * The minimum or maximum sample value found in the image, or {@code NaN} if unspecified.
+     * The minimum or maximum sample value found in the image, with one value per band.
+     * May be a vector of length 1 if the same single value applies to all bands.
      */
-    private double minValue = Double.NaN, maxValue = Double.NaN;
+    private Vector minValues, maxValues;
 
     /**
      * The number of pixels per {@link #resolutionUnit} in the {@link #imageWidth} and the
{@link #imageHeight}
@@ -365,6 +379,13 @@ final class ImageFileDirectory {
     /**
      * Shortcut for a frequently requested information.
      */
+    private String filename() {
+        return input().filename;
+    }
+
+    /**
+     * Shortcut for a frequently requested information.
+     */
     private Charset encoding() {
         return reader.owner.encoding;
     }
@@ -519,6 +540,21 @@ final class ImageFileDirectory {
                 break;
             }
             /*
+             * How to interpret each data sample in a pixel. The size of data samples is
still
+             * specified by the BitsPerSample field.
+             */
+            case Tags.SampleFormat: {
+                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;
+                }
+                break;
+            }
+            /*
              * Number of bits per component. The array length should be the number of components
in a
              * pixel (e.g. 3 for RGB values). Typically, all components have the same number
of bits.
              * But the TIFF specification allows different values.
@@ -535,7 +571,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",
input().filename, values));
+                                Resources.Keys.ConstantValueRequired_3, "BitsPerSample",
filename(), values));
                     }
                 }
                 break;
@@ -592,11 +628,12 @@ final class ImageFileDirectory {
                 break;
             }
             /*
-             * The minimum component value used. Default is 0.
+             * 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: {
-                final double v = type.readDouble(input(), count);
-                if (Double.isNaN(minValue) || v < minValue) minValue = v;
+            case Tags.MinSampleValue:
+            case Tags.SMinSampleValue: {
+                minValues = extremum(minValues, type.readVector(input(), count), false);
                 break;
             }
             /*
@@ -604,9 +641,9 @@ final class ImageFileDirectory {
              * 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: {
-                final double v = type.readDouble(input(), count);
-                if (Double.isNaN(maxValue) || v > maxValue) maxValue = v;
+            case Tags.MaxSampleValue:
+            case Tags.SMaxSampleValue: {
+                maxValues = extremum(maxValues, type.readVector(input(), count), true);
                 break;
             }
 
@@ -722,7 +759,9 @@ final class ImageFileDirectory {
                 gridToCRS = Matrices.createZero(size+1, size+1);
                 gridToCRS.setElement(size, size, 1);
                 for (int i=0; i<size; i++) {
-                    gridToCRS.setElement(i, i, m.doubleValue(i));
+                    double e = m.doubleValue(i);
+                    if (i == 1) e = -e;                             // Make y scale factor
negative.
+                    gridToCRS.setElement(i, i, e);
                     gridToCRS.setElement(i, size, Double.NaN);
                 }
                 break;
@@ -914,12 +953,39 @@ final class ImageFileDirectory {
     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));
+                    Resources.Keys.InconsistentTileStrip_1, filename()));
         }
         tileTagFamily = family;
     }
 
     /**
+     * Computes the minimal or maximal values of the given vector. Those vectors do not need
to have the same length.
+     * One of those two vector will be modified in-place.
+     *
+     * @param  a    the first vector, or {@code null} if none.
+     * @param  b    the new vector to combine with the existing one. Can not be null.
+     * @param  max  {@code true} for computing the maximal values, or {@code false} for the
minimal value.
+     */
+    private static Vector extremum(Vector a, Vector b, final boolean max) {
+        if (a != null) {
+            int s = b.size();
+            int i = a.size();
+            if (i > s) {                            // If a vector is longer than b, swap
a and b.
+                i = s;
+                final Vector t = a; a = b; b = t;
+            }
+            while (--i >= 0) {                      // At this point, 'b' shall be the
longest vector.
+                final double va = a.doubleValue(i);
+                final double vb = b.doubleValue(i);
+                if (Double.isNaN(vb) || (max ? va > vb : va < vb)) {
+                    b.set(i, va);
+                }
+            }
+        }
+        return b;
+    }
+
+    /**
      * Multiplies the given value by the number of bytes in one pixel,
      * or return -1 if the result is not an integer.
      *
@@ -987,7 +1053,7 @@ final class ImageFileDirectory {
             }
             default: {
                 throw new DataStoreContentException(reader.resources().getString(
-                        Resources.Keys.InconsistentTileStrip_1, input().filename));
+                        Resources.Keys.InconsistentTileStrip_1, filename()));
             }
         }
         if (tileOffsets == null) {
@@ -1004,8 +1070,25 @@ final class ImageFileDirectory {
         if (colorMap != null) {
             ensureSameLength(Tags.ColorMap, Tags.BitsPerSample, colorMap.size(),  3 * (1
<< bitsPerSample));
         }
-        if (Double.isNaN(minValue)) minValue = 0;
-        if (Double.isNaN(maxValue)) maxValue = (1 << bitsPerSample) - 1;
+        if (sampleFormat != FLOAT) {
+            long minValue, maxValue;
+            if (sampleFormat == UNSIGNED) {
+                minValue =  0L;
+                maxValue = -1L;                 // All bits set to 1.
+            } else {
+                minValue = Long.MIN_VALUE;
+                maxValue = Long.MAX_VALUE;
+            }
+            final int shift = Long.SIZE - bitsPerSample;
+            if (shift >= 0 && shift < Long.SIZE) {
+                minValue >>>= shift;
+                maxValue >>>= shift;
+                if (minValue < maxValue) {      // Exclude the unsigned long case since
we can not represent it.
+                    minValues = extremum(minValues, Vector.createSequence(minValue, 0, samplesPerPixel),
false);
+                    maxValues = extremum(maxValues, Vector.createSequence(maxValue, 0, samplesPerPixel),
true);
+                }
+            }
+        }
         /*
          * 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,
@@ -1064,7 +1147,7 @@ final class ImageFileDirectory {
         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));
+                    filename(), expectedCount, actualCount));
         }
         /*
          * If a "grid to CRS" conversion has been specified with only the scale factor, we
need to compute
@@ -1094,10 +1177,13 @@ final class ImageFileDirectory {
         if (compression != null) {
             metadata.addCompression(compression.name().toLowerCase(locale));
         }
-        // TODO: set band name and repeat for each band.
-        metadata.setBitPerSample(bitsPerSample);
-        metadata.addMinimumSampleValue(minValue);
-        metadata.addMaximumSampleValue(maxValue);
+        for (int band = 0; band < samplesPerPixel;) {
+            metadata.newSampleDimension();
+            metadata.setBitPerSample(bitsPerSample);
+            if (minValues != null) metadata.addMinimumSampleValue(minValues.doubleValue(Math.min(band,
minValues.size()-1)));
+            if (maxValues != null) metadata.addMaximumSampleValue(maxValues.doubleValue(Math.min(band,
maxValues.size()-1)));
+            metadata.setBandIdentifier(++band);
+        }
         /*
          * 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.
@@ -1138,7 +1224,7 @@ final class ImageFileDirectory {
         final boolean isGeorectified = (modelTiePoints == null) || (gridToCRS != null);
         metadata.newGridRepresentation(isGeorectified ? MetadataBuilder.GridType.GEORECTIFIED
                                                       : MetadataBuilder.GridType.GEOREFERENCEABLE);
-        metadata.setGeoreferencingAvailability(gridToCRS != null, modelTiePoints != null,
completeMatrixSpecified);
+        metadata.setGeoreferencingAvailability(gridToCRS != null, false, false);
         CoordinateReferenceSystem crs = null;
         if (geoKeyDirectory != null) {
             final CRSBuilder helper = new CRSBuilder(reader);
@@ -1159,10 +1245,8 @@ final class ImageFileDirectory {
             }
         }
         try {
-            if (isGeorectified) {
-                GridGeometry.addControlPoints(crs, metadata, modelTiePoints);
-            } else {
-                metadata.addGeolocation(new GridGeometry(input().filename, crs, modelTiePoints));
+            if (!isGeorectified) {
+                metadata.addGeolocation(new GridGeometry(filename(), crs, modelTiePoints));
             }
         } catch (TransformException e) {
             reader.owner.warning(null, e);
@@ -1214,6 +1298,6 @@ final class ImageFileDirectory {
      */
     private DataStoreContentException missingTag(final short missing) {
         return new DataStoreContentException(reader.resources().getString(
-                Resources.Keys.MissingValue_2, input().filename, Tags.name(missing)));
+                Resources.Keys.MissingValue_2, filename(), Tags.name(missing)));
     }
 }

Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java?rev=1801297&r1=1801296&r2=1801297&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
[UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
[UTF-8] Sat Jul  8 15:05:45 2017
@@ -29,7 +29,6 @@ import javax.measure.Unit;
 import org.opengis.util.MemberName;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
-import org.opengis.util.FactoryException;
 import org.opengis.metadata.Identifier;
 import org.opengis.metadata.citation.Role;
 import org.opengis.metadata.citation.DateType;
@@ -51,16 +50,12 @@ import org.opengis.metadata.identificati
 import org.opengis.metadata.identification.KeywordType;
 import org.opengis.metadata.identification.TopicCategory;
 import org.opengis.metadata.distribution.Format;
+import org.opengis.metadata.quality.Element;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.referencing.crs.VerticalCRS;
-import org.opengis.referencing.crs.GeographicCRS;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
-import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
-import org.apache.sis.internal.referencing.ReferencingUtilities;
-import org.apache.sis.referencing.CRS;
 import org.apache.sis.geometry.AbstractEnvelope;
-import org.apache.sis.geometry.GeneralDirectPosition;
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.metadata.iso.DefaultIdentifier;
 import org.apache.sis.metadata.iso.DefaultMetadataScope;
@@ -108,8 +103,8 @@ import org.apache.sis.metadata.sql.Metad
 import org.apache.sis.metadata.sql.MetadataSource;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.iso.Names;
 import org.apache.sis.util.iso.Types;
-import org.apache.sis.math.Vector;
 
 import static java.util.Collections.singleton;
 import static org.apache.sis.internal.util.StandardDateFormat.MILLISECONDS_PER_DAY;
@@ -134,6 +129,11 @@ import org.opengis.metadata.citation.Res
  */
 public class MetadataBuilder {
     /**
+     * Band numbers, created when first needed.
+     */
+    private static final MemberName[] BAND_NUMBERS = new MemberName[16];
+
+    /**
      * Whether the next party to create should be an instance of {@link DefaultIndividual}
or {@link DefaultOrganisation}.
      *
      * @see #party()
@@ -1885,6 +1885,8 @@ parse:      for (int i = 0; i < length;)
 
     /**
      * Adds <cite>check points</cite> (if georectified) or <cite>ground
control points</cite> (if georeferenceable).
+     * Ground control points (GCP) are large marked targets on the ground. GCP should not
be used for storing the
+     * localization grid (e.g. "model tie points" in a GeoTIFF file).
      * Storage location is:
      *
      * <ul>
@@ -1892,26 +1894,12 @@ parse:      for (int i = 0; i < length;)
      *   <li>{@code metadata/spatialRepresentationInfo/geolocationInformation/gcp/geographicCoordinates}
if georeferenceable</li>
      * </ul>
      *
-     * @param  crs        the coordinate reference system of given ordinate values.
-     * @param  ordinates  the ordinate values of control points.
-     * @param  offset     index of the first ordinate value.
-     * @param  stride     increment between two coordinates in the given vector.
-     * @throws FactoryException if an error occurred while preparing the conversion to geographic
coordinates
-     * @throws TransformException if an error occurred while converting the given coordinates
to geographic coordinates.
-     *
-     * @todo Factor most of this code outside MetadataBuilder. May opportunistically define
a class that extend
-     *       AbstractDirectPosition and implement GCP for saving space.
+     * @param  geographicCoordinates  the geographic or map position of the control point,
in either two or three dimensions.
+     * @param  accuracyReport         the accuracy of a ground control point, or {@code null}
if none.
+     *                                Ignored if {@code geographicCoordinates} is null.
      */
-    public final void addControlPoints(final CoordinateReferenceSystem crs, final Vector
ordinates,
-            int offset, final int stride) throws FactoryException, TransformException
-    {
-        final int size;
-        if (ordinates != null && (size = ordinates.size()) != 0) {
-            final GeographicCRS geoCRS = ReferencingUtilities.toNormalizedGeographicCRS(crs);
-            if (geoCRS == null) {
-                return;
-            }
-            final MathTransform mt = CRS.findOperation(crs, geoCRS, null).getMathTransform();
+    public final void addControlPoints(final DirectPosition geographicCoordinates, final
Element accuracyReport) {
+        if (geographicCoordinates != null) {
             final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
             final Collection<GCP> points;
             if (gridRepresentation instanceof DefaultGeorectified) {
@@ -1921,21 +1909,12 @@ parse:      for (int i = 0; i < length;)
             } else {
                 return;
             }
-            final int dimension = crs.getCoordinateSystem().getDimension();
-            while (offset < size) {
-                DirectPosition pos = new GeneralDirectPosition(crs);
-                for (int i=0; i<dimension; i++) {
-                    pos.setOrdinate(i, ordinates.doubleValue(offset + i));
-                }
-                pos = mt.transform(pos, pos);
-                final double t = pos.getOrdinate(0);        // Swap ordinate for (latitude,
longitude) order.
-                pos.setOrdinate(0, pos.getOrdinate(1));
-                pos.setOrdinate(1, t);
-                final DefaultGCP gcp = new DefaultGCP();
-                gcp.setGeographicCoordinates(pos);
-                points.add(gcp);
-                offset += stride;
+            final DefaultGCP gcp = new DefaultGCP();
+            gcp.setGeographicCoordinates(geographicCoordinates);
+            if (accuracyReport != null) {
+                addIfNotPresent(gcp.getAccuracyReports(), accuracyReport);
             }
+            addIfNotPresent(points, gcp);
         }
     }
 
@@ -2038,6 +2017,34 @@ parse:      for (int i = 0; i < length;)
         }
     }
 
+    /**
+     * Sets the number that uniquely identifies instances of bands of wavelengths on which
a sensor operates.
+     * This is a convenience method for {@link #setBandIdentifier(MemberName)} when the band
is specified only
+     * by a number.
+     *
+     * @param  sequenceIdentifier  the band number, or 0 or negative if none.
+     */
+    public final void setBandIdentifier(final int sequenceIdentifier) {
+        if (sequenceIdentifier > 0) {
+            final boolean cached = (sequenceIdentifier <= BAND_NUMBERS.length);
+            MemberName name = null;
+            if (cached) synchronized (BAND_NUMBERS) {
+                name = BAND_NUMBERS[sequenceIdentifier - 1];
+            }
+            if (name == null) {
+                name = Names.createMemberName(null, null, String.valueOf(sequenceIdentifier),
Integer.class);
+                if (cached) synchronized (BAND_NUMBERS) {
+                    /*
+                     * No need to check if a value has been set concurrently because Names.createMemberName(…)
+                     * already checked if an equal instance exists in the current JVM.
+                     */
+                    BAND_NUMBERS[sequenceIdentifier - 1] = name;
+                }
+            }
+            setBandIdentifier(name);
+        }
+    }
+
     /**
      * Adds an identifier for the current band.
      * These identifiers can be use to provide names for the attribute from a standard set
of names.



Mime
View raw message