sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1801832 [3/4] - in /sis/branches/JDK7: ./ core/sis-feature/ core/sis-feature/src/main/java/org/apache/sis/feature/ core/sis-feature/src/main/java/org/apache/sis/feature/builder/ core/sis-feature/src/main/java/org/apache/sis/internal/featur...
Date Thu, 13 Jul 2017 12:10:36 GMT
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=1801832&r1=1801831&r2=1801832&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] Thu Jul 13 12:10:35 2017
@@ -31,11 +31,15 @@ import org.opengis.util.FactoryException
 import org.opengis.util.NoSuchIdentifierException;
 import org.opengis.parameter.ParameterNotFoundException;
 import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.operation.TransformException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.apache.sis.referencing.operation.matrix.Matrices;
+import org.apache.sis.referencing.operation.matrix.MatrixSIS;
 import org.apache.sis.internal.geotiff.Resources;
+import org.apache.sis.internal.storage.MetadataBuilder;
 import org.apache.sis.internal.storage.io.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;
 
@@ -66,6 +70,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.
      */
@@ -159,6 +170,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>
@@ -268,9 +285,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}
@@ -314,6 +332,38 @@ final class ImageFileDirectory {
     private String asciiGeoParameters;
 
     /**
+     * Raster model tie points. This vector contains ordinate values structured as (I,J,K, X,Y,Z) records.
+     * The (I,J,K) ordinate values specify the point at location (I,J) in raster space with pixel-value K,
+     * and (X,Y,Z) ordinate values specify the point in the Coordinate Reference System. In most cases the
+     * coordinate system is only two-dimensional, in which case both K and Z should be set to zero.
+     */
+    private Vector modelTiePoints;
+
+    /**
+     * The conversion from grid coordinates to CRS coordinates. It can be determined in different ways,
+     * from simpler to more complex:
+     *
+     * <ul>
+     *   <li>By a combination of a single {@link #modelTiePoints} with the 3 values given in
+     *       {@link Tags#ModelPixelScaleTag} as documented in the Javadoc of that tag.</li>
+     *   <li>By a {@link Tags#ModelTransformation} giving all coefficients of the 4×4 matrix}.
+     *       Note that the third row and the third column have all their value set to 0 if the
+     *       space model (or the coordinate reference system) should be two-dimensional.</li>
+     *   <li>By building a non-linear transformation from all {@link #modelTiePoints}.
+     *       Such transformation can not be stored in a matrix, so will leave this field {@code null}.</li>
+     * </ul>
+     *
+     * By convention, the translation column is set to NaN values if it needs to be computed from the tie point.
+     */
+    private MatrixSIS gridToCRS;
+
+    /**
+     * {@code true} if {@link #gridToCRS} has been specified by a complete matrix ({@link Tags#ModelTransformation}),
+     * or {@code false} if it has been specified by the scale factors only ({@link Tags#ModelPixelScaleTag}).
+     */
+    private boolean completeMatrixSpecified;
+
+    /**
      * Creates a new image file directory.
      *
      * @param reader  information about the input stream to read, the metadata and the character encoding.
@@ -332,6 +382,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;
     }
@@ -486,6 +543,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.
@@ -502,7 +574,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;
@@ -559,11 +631,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;
             }
             /*
@@ -571,9 +644,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;
             }
 
@@ -651,6 +724,58 @@ final class ImageFileDirectory {
                 // TODO
                 break;
             }
+            /*
+             * The "grid to CRS" conversion as a 4×4 matrix in row-major fashion. The third matrix row and
+             * the third matrix column may contain only zero values; this block does not reduce the number
+             * of dimensions from 3 to 2.
+             */
+            case Tags.ModelTransformation: {
+                final Vector m = type.readVector(input(), count);
+                final int size = m.size();
+                final int n;
+                switch (size) {
+                    case  6:                    // Assume 2D model with implicit [0 0 1] last row.
+                    case  9: n = 3; break;      // Assume 2D model with full 3×3 matrix.
+                    case 12:                    // Assume 3D model with implicit [0 0 0 1] last row.
+                    case 16: n = 4; break;      // 3D model with full 4×4 matrix, as required by GeoTIFF spec.
+                    default: return m;
+                }
+                completeMatrixSpecified = true;
+                gridToCRS = Matrices.createZero(n, n);
+                gridToCRS.setElement(n-1, n-1, 1);
+                for (int i=0; i<size; i++) {
+                    gridToCRS.setElement(i / n, i % n, m.doubleValue(i));
+                }
+                break;
+            }
+            /*
+             * The "grid to CRS" conversion with only the scale factor specified. This block sets the
+             * translation column to NaN, meaning that it will need to be computed from the tie point.
+             */
+            case Tags.ModelPixelScaleTag: {
+                final Vector m = type.readVector(input(), count);
+                final int size = m.size();
+                if (size < 2 || size > 3) {     // Length should be exactly 3, but we make this reader tolerant.
+                    return m;
+                }
+                completeMatrixSpecified = false;
+                gridToCRS = Matrices.createZero(size+1, size+1);
+                gridToCRS.setElement(size, size, 1);
+                for (int i=0; i<size; 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;
+            }
+            /*
+             * The mapping from pixel coordinates to CRS coordinates as a sequence of (I,J,K, X,Y,Z) records.
+             */
+            case Tags.ModelTiePoints: {
+                modelTiePoints = type.readVector(input(), count);
+                break;
+            }
 
             ////////////////////////////////////////////////////////////////////////////////////////////////
             ////                                                                                        ////
@@ -831,12 +956,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.
      *
@@ -904,7 +1056,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) {
@@ -921,8 +1073,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,
@@ -967,9 +1136,9 @@ final class ImageFileDirectory {
             }
         }
         /*
-         * 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.
+         * Report an error 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(
@@ -981,7 +1150,16 @@ 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
+         * the translation terms now.
+         */
+        if (gridToCRS != null && !completeMatrixSpecified) {
+            if (!GridGeometry.setTranslationTerms(gridToCRS, modelTiePoints)) {
+                throw missingTag(Tags.ModelTiePoints);
+            }
         }
     }
 
@@ -1002,10 +1180,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.
@@ -1043,10 +1224,16 @@ final class ImageFileDirectory {
          * 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).
          */
+        final boolean isGeorectified = (modelTiePoints == null) || (gridToCRS != null);
+        metadata.newGridRepresentation(isGeorectified ? MetadataBuilder.GridType.GEORECTIFIED
+                                                      : MetadataBuilder.GridType.GEOREFERENCEABLE);
+        metadata.setGeoreferencingAvailability(gridToCRS != null, false, false);
+        CoordinateReferenceSystem crs = null;
         if (geoKeyDirectory != null) {
             final CRSBuilder helper = new CRSBuilder(reader);
             try {
-                metadata.addReferenceSystem(helper.build(geoKeyDirectory, numericGeoParameters, asciiGeoParameters));
+                crs = helper.build(geoKeyDirectory, numericGeoParameters, asciiGeoParameters);
+                metadata.addReferenceSystem(crs);
                 helper.complete(metadata);
             } catch (NoSuchIdentifierException | ParameterNotFoundException e) {
                 short key = Resources.Keys.UnsupportedProjectionMethod_1;
@@ -1059,10 +1246,18 @@ final class ImageFileDirectory {
                     reader.owner.warning(null, e);
                 }
             }
-            geoKeyDirectory      = null;            // Not needed anymore, so let GC do its work.
-            numericGeoParameters = null;
-            asciiGeoParameters   = null;
         }
+        try {
+            if (!isGeorectified) {
+                metadata.addGeolocation(new GridGeometry(filename(), crs, modelTiePoints));
+            }
+        } catch (TransformException e) {
+            reader.owner.warning(null, e);
+        }
+        geoKeyDirectory      = null;            // Not needed anymore, so let GC do its work.
+        numericGeoParameters = null;
+        asciiGeoParameters   = null;
+        modelTiePoints       = null;
     }
 
     /**
@@ -1106,6 +1301,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/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Tags.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -161,6 +161,35 @@ final class Tags {
     public static final short GeoAsciiParams = (short) 0x87B1;            // 34737
 
     /**
+     * The tie points as (I,J,K,X,Y,Z) records in an array of floating-point numbers.
+     * This tag is also known as {@code Georeference}.
+     */
+    public static final short ModelTiePoints = (short) 0x8482;            // 33922
+
+    /**
+     * A vector of 3 floating-point values defining the "grid to CRS" conversion without rotation.
+     * The conversion is defined as below, when (I,J,K,X,Y,Z) is the tie point singleton record:
+     *
+     * ┌                       ┐
+     * │   Sx   0    0    Tx   │       Tx = X - I/Sx
+     * │   0   -Sy   0    Ty   │       Ty = Y + J/Sy
+     * │   0    0    Sz   Tz   │       Tz = Z - K/Sz  (if not 0)
+     * │   0    0    0    1    │
+     * └                       ┘
+     *
+     * Only one of {@code ModelPixelScaleTag} and {@link #ModelTransformation} should be used.
+     */
+    public static final short ModelPixelScaleTag = (short) 0x830E;        // 33550
+
+    /**
+     * Specifies the "grid to CRS" conversion (the transformation matrix between the raster space and the model space).
+     * If specified, the tag shall have the 16 values of a 4×4 matrix in row-major fashion. The last matrix row (i.e.
+     * the last 4 values) should be [0 0 0 1]. The row before should be [0 0 0 0] if the conversion is two-dimensional.
+     * Only one of {@link #ModelPixelScaleTag} and {@code ModelTransformation} should be used.
+     */
+    public static final short ModelTransformation = (short) 0x85D8;       // 34264
+
+    /**
      * Do not allow instantiation of this class.
      */
     private Tags() {

Modified: sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/package-info.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -23,7 +23,7 @@
  *   <li><a href="http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf">TIFF specification</a>: baseline</li>
  *   <li><a href="http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf">TIFF Tecnical Notes</a>: add new compressions</li>
  *   <li><a href="http://www.awaresystems.be/imaging/tiff/tifftags.html">TIFF Tag Reference</a></li>
- *   <li><a href="http://www.remotesensing.org/geotiff/spec/contents.html">GeoTIFF specification</a></li>
+ *   <li><a href="http://download.osgeo.org/geotiff/spec/geotiff.rtf">GeoTIFF specification</a></li>
  * </ul>
  *
  * @author  Rémi Maréchal (Geomatys)

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -18,6 +18,7 @@ package org.apache.sis.internal.netcdf;
 
 import java.util.Date;
 import java.util.Objects;
+import java.util.Collection;
 import java.io.Closeable;
 import java.io.IOException;
 import javax.measure.Unit;
@@ -86,6 +87,13 @@ public abstract class Decoder implements
     public abstract String[] getSearchPath();
 
     /**
+     * Returns the names of all global attributes found in the file.
+     *
+     * @return names of all global attributes in the file.
+     */
+    public abstract Collection<String> getAttributeNames();
+
+    /**
      * Returns the value for the attribute of the given name, or {@code null} if none.
      * This method searches in the groups specified by the last call to {@link #setSearchPath(String[])}.
      * Null values and empty strings are ignored.

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -16,6 +16,10 @@
  */
 package org.apache.sis.internal.netcdf;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.Stream;
+import org.opengis.feature.Feature;
+
 
 /**
  * Returns the features encoded in the NetCDF files when they are encoded as discrete sampling.
@@ -34,4 +38,11 @@ public abstract class DiscreteSampling {
      */
     protected DiscreteSampling() {
     }
+
+    /**
+     * Returns the stream of features.
+     *
+     * @return the stream of features.
+     */
+    public abstract Stream<Feature> features();
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.netcdf;
 
+import java.util.Collection;
 import java.io.IOException;
 import java.awt.image.DataBuffer;
 import org.apache.sis.math.Vector;
@@ -154,6 +155,13 @@ public abstract class Variable extends N
     public abstract int[] getGridEnvelope();
 
     /**
+     * Returns the names of all attributes associated to this variable.
+     *
+     * @return names of all attributes associated to this variable.
+     */
+    public abstract Collection<String> getAttributeNames();
+
+    /**
      * Returns the sequence of values for the given attribute, or an empty array if none.
      * The elements will be of class {@link String} if {@code numeric} is {@code false},
      * or {@link Number} if {@code numeric} is {@code true}.

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -18,6 +18,8 @@ package org.apache.sis.internal.netcdf.i
 
 import java.util.Set;
 import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.AbstractMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashMap;
@@ -650,6 +652,8 @@ public final class ChannelDecoder extend
      *
      * @param  name  the name of the attribute to search, or {@code null}.
      * @return the attribute value, or {@code null} if none.
+     *
+     * @see #getAttributeNames()
      */
     private Object findAttribute(final String name) {
         Object value = attributeMap.get(name);
@@ -664,6 +668,16 @@ public final class ChannelDecoder extend
     }
 
     /**
+     * Returns the names of all global attributes found in the file.
+     *
+     * @return names of all global attributes in the file.
+     */
+    @Override
+    public Collection<String> getAttributeNames() {
+        return Collections.unmodifiableSet(attributeMap.keySet());
+    }
+
+    /**
      * Returns the value for the attribute of the given name, or {@code null} if none.
      *
      * @param  name  the name of the attribute to search, or {@code null}.

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/FeaturesInfo.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -16,16 +16,36 @@
  */
 package org.apache.sis.internal.netcdf.impl;
 
-import java.util.ArrayList;
+import java.util.Map;
 import java.util.List;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.io.IOException;
 import org.apache.sis.math.Vector;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.DiscreteSampling;
 import org.apache.sis.internal.netcdf.Resources;
+import org.apache.sis.internal.feature.Geometries;
+import org.apache.sis.internal.feature.MovingFeature;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.feature.DefaultAttributeType;
+import org.apache.sis.util.collection.BackingStoreException;
 import ucar.nc2.constants.CF;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.Spliterator;
+import org.apache.sis.internal.jdk8.Stream;
+import org.apache.sis.internal.jdk8.StreamSupport;
+import org.apache.sis.internal.jdk8.Consumer;
+import org.apache.sis.internal.jdk8.JDK8;
+import org.opengis.feature.Feature;
+import org.opengis.feature.FeatureType;
+import org.opengis.feature.PropertyType;
+import org.opengis.feature.AttributeType;
+
 
 /**
  * Implementations of the discrete sampling features decoder. This implementation shall be able to decode at least the
@@ -44,18 +64,85 @@ final class FeaturesInfo extends Discret
 
     /**
      * The moving feature identifiers ("mfIdRef").
+     * The amount of identifiers shall be the same than the length of the {@link #counts} vector.
      */
     private final VariableInfo identifiers;
 
     /**
+     * The variable that contains time.
+     */
+    private final VariableInfo time;
+
+    /**
+     * The variable that contains <var>x</var> and <var>y</var> ordinate values (typically longitudes and latitudes).
+     * All variables in this array shall have the same length, and that length shall be the same than {@link #time}.
+     */
+    private final VariableInfo[] coordinates;
+
+    /**
+     * Any custom properties.
+     */
+    private final VariableInfo[] properties;
+
+    /**
+     * The type of all features to be read by this {@code FeaturesInfo}.
+     */
+    private final FeatureType type;
+
+    private final Geometries<?> factory = Geometries.implementation(null);          // TODO: shall be given by the store.
+
+    /**
      * Creates a new discrete sampling parser for features identified by the given variable.
      *
      * @param  counts       the count of instances per feature.
      * @param  identifiers  the feature identifiers.
      */
-    private FeaturesInfo(final Vector counts, final VariableInfo identifiers) {
+    @SuppressWarnings("rawtypes")                               // Because of generic array creation.
+    private FeaturesInfo(final Vector counts, final VariableInfo identifiers, final VariableInfo time,
+            final Collection<VariableInfo> coordinates, final Collection<VariableInfo> properties)
+    {
         this.counts      = counts;
         this.identifiers = identifiers;
+        this.coordinates = coordinates.toArray(new VariableInfo[coordinates.size()]);
+        this.properties  = properties .toArray(new VariableInfo[properties .size()]);
+        this.time        = time;
+        /*
+         * Creates a description of the features to be read.
+         */
+        final Map<String,Object> info = new HashMap<>(4);
+        final PropertyType[] pt = new PropertyType[this.properties.length + 2];
+        AttributeType[] characteristics = null;
+        for (int i=0; i<pt.length; i++) {
+            final VariableInfo variable;
+            final Class<?> valueClass;
+            int minOccurs = 1;
+            int maxOccurs = 1;
+            switch (i) {
+                case 0: {
+                    variable        = identifiers;
+                    valueClass      = Integer.class;
+                    break;
+                }
+                case 1: {
+                    variable        = null;
+                    valueClass      = factory.polylineClass;
+                    characteristics = new AttributeType[] {MovingFeature.TIME};
+                    break;
+                }
+                default: {
+                    variable        = this.properties[i-2];
+                    valueClass      = Number.class;           // TODO: use more accurate value class.
+                    minOccurs       = 0;
+                    maxOccurs       = Integer.MAX_VALUE;
+                    break;
+                }
+            }
+            info.put(DefaultAttributeType.NAME_KEY, (variable != null) ? variable.getName() : "trajectory");
+            // TODO: add description.
+            pt[i] = new DefaultAttributeType<>(info, valueClass, minOccurs, maxOccurs, null, characteristics);
+        }
+        info.put(DefaultAttributeType.NAME_KEY, "Feature");     // TODO: find a better name.
+        type = new DefaultFeatureType(info, false, null, pt);
     }
 
     /**
@@ -148,12 +235,159 @@ search: for (final VariableInfo counts :
                         }
                     }
                     /*
-                     * At this point, all information have been verified as valid.
+                     * At this point, all information have been verified as valid. Now search all variables having
+                     * the expected sample dimension. Those variable contains the actual data. For example if the
+                     * sample dimension name is "points", then we may have:
+                     *
+                     *     double longitude(points);
+                     *         longitude:axis = "X";
+                     *         longitude:standard_name = "longitude";
+                     *         longitude:units = "degrees_east";
+                     *     double latitude(points);
+                     *         latitude:axis = "Y";
+                     *         latitude:standard_name = "latitude";
+                     *         latitude:units = "degrees_north";
+                     *     double time(points);
+                     *         time:axis = "T";
+                     *         time:standard_name = "time";
+                     *         time:units = "minutes since 2014-11-29 00:00:00";
+                     *     short myCustomProperty(points);
                      */
-                    features.add(new FeaturesInfo(counts.read().compress(0), identifiers));
+                    final Map<String,VariableInfo> coordinates = new LinkedHashMap<>();
+                    final List<VariableInfo> properties  = new ArrayList<>();
+                    for (final VariableInfo data : decoder.variables) {
+                        if (data.dimensions.length == 1 && data.dimensions[0] == sampleDimension) {
+                            final Object axisType = data.getAttributeValue(CF.AXIS);
+                            if (axisType == null) {
+                                properties.add(data);
+                            } else if (coordinates.put(axisType.toString(), data) != null) {
+                                continue search;    // Two axes of the same type: abort.
+                            }
+                        }
+                    }
+                    final VariableInfo time = coordinates.remove("T");
+                    if (time != null) {
+                        features.add(new FeaturesInfo(counts.read().compress(0), identifiers, time, coordinates.values(), properties));
+                    }
                 }
             }
         }
         return features.toArray(new FeaturesInfo[features.size()]);
     }
+
+    /**
+     * Returns the stream of features.
+     */
+    @Override
+    public Stream<Feature> features() {
+        return StreamSupport.stream(new Iter(), false);
+    }
+
+    /**
+     * Implementation of the iterator returned by {@link #features()}.
+     */
+    private final class Iter implements Spliterator<Feature> {
+        /**
+         * Index of the next feature to read.
+         */
+        private int index;
+
+        /**
+         * Position in the data vectors of the next feature to read.
+         * This is the sum of the length of data in all previous features.
+         */
+        private int position;
+
+        /**
+         * Creates a new iterator.
+         */
+        Iter() {
+        }
+
+        @Override
+        public void forEachRemaining(Consumer<? super Feature> action) {
+            while (tryAdvance(action));
+        }
+
+        /**
+         * Executes the given action only on the next feature, if any.
+         *
+         * @todo current reading process implies lot of seeks, which is inefficient.
+         */
+        @Override
+        public boolean tryAdvance(final Consumer<? super Feature> action) {
+            final int   length = counts.intValue(index);
+            final int[] lower  = {position};
+            final int[] upper  = {position + length};
+            final int[] step   = {1};
+            final Vector   id, t;
+            final Vector[] coords = new Vector[coordinates.length];
+            final Vector[] props  = new Vector[properties.length];
+            try {
+                id = identifiers.read();                    // Efficiency should be okay because of cached value.
+                t = time.read(lower, upper, step);
+                for (int i=0; i<coordinates.length; i++) {
+                    coords[i] = coordinates[i].read(lower, upper, step);
+                }
+                for (int i=0; i<properties.length; i++) {
+                    props[i] = properties[i].read(lower, upper, step);
+                }
+            } catch (IOException | DataStoreException e) {
+                throw new BackingStoreException(canNotReadFile(), e);
+            }
+            final Feature feature = type.newInstance();
+            feature.setPropertyValue(identifiers.getName(), id.intValue(index));
+            for (int i=0; i<properties.length; i++) {
+                feature.setPropertyValue(properties[i].getName(), props[i]);
+                // TODO: set time characteristic.
+            }
+            // TODO: temporary hack - to be replaced by support in Vector.
+            final int dimension = coordinates.length;
+            final double[] tmp = new double[length * dimension];
+            for (int i=0; i<tmp.length; i++) {
+                tmp[i] = coords[i % dimension].doubleValue(i / dimension);
+            }
+            feature.setPropertyValue("trajectory", factory.createPolyline(dimension, Vector.create(tmp, false)));
+            action.accept(feature);
+            position = JDK8.addExact(position, length);
+            return ++index < counts.size();
+        }
+
+        /**
+         * Current implementation can not split this iterator.
+         */
+        @Override
+        public Spliterator<Feature> trySplit() {
+            return null;
+        }
+
+        /**
+         * Returns the number of features.
+         */
+        @Override
+        public long estimateSize() {
+            return counts.size();
+        }
+
+        /**
+         * Returns the characteristics of the iteration over feature instances.
+         * The iteration is assumed {@link #ORDERED} in the declaration order in the NetCDF file.
+         * The iteration is {@link #NONNULL} (i.e. {@link #tryAdvance(Consumer)} is not allowed
+         * to return null value) and {@link #IMMUTABLE} (i.e. we do not support modification of
+         * the NetCDF file while an iteration is in progress).
+         *
+         * @return characteristics of iteration over the features in the NetCDF file.
+         */
+        @Override
+        public int characteristics() {
+            return ORDERED | NONNULL | IMMUTABLE | SIZED;
+        }
+    }
+
+    /**
+     * Returns the error message for a file that can not be read.
+     */
+    final String canNotReadFile() {
+        return null;    // TODO
+    }
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -17,6 +17,8 @@
 package org.apache.sis.internal.netcdf.impl;
 
 import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
 import java.io.IOException;
 import java.lang.reflect.Array;
 import ucar.nc2.constants.CF;
@@ -271,6 +273,16 @@ final class VariableInfo extends Variabl
     }
 
     /**
+     * Returns the names of all attributes associated to this variable.
+     *
+     * @return names of all attributes associated to this variable.
+     */
+    @Override
+    public Collection<String> getAttributeNames() {
+        return Collections.unmodifiableSet(attributes.keySet());
+    }
+
+    /**
      * Returns the value of the given attribute, or {@code null} if none.
      * This method should be invoked only for hard-coded names that mix lower-case and upper-case letters.
      *

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -20,6 +20,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.EnumSet;
 import java.util.Formatter;
+import java.util.Collection;
 import java.io.IOException;
 import ucar.nc2.Group;
 import ucar.nc2.Attribute;
@@ -170,6 +171,16 @@ public final class DecoderWrapper extend
     }
 
     /**
+     * Returns the names of all global attributes found in the file.
+     *
+     * @return names of all global attributes in the file.
+     */
+    @Override
+    public Collection<String> getAttributeNames() {
+        return VariableWrapper.toNames(file.getGlobalAttributes());
+    }
+
+    /**
      * Returns the NetCDF attribute of the given name in the given group, or {@code null} if none.
      * This method is invoked for every global and group attributes to be read by this class (but
      * not {@linkplain ucar.nc2.VariableSimpleIF variable} attributes), thus providing a single point

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/FeaturesWrapper.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/FeaturesWrapper.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/FeaturesWrapper.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/FeaturesWrapper.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -19,6 +19,10 @@ package org.apache.sis.internal.netcdf.u
 import org.apache.sis.internal.netcdf.DiscreteSampling;
 import ucar.nc2.ft.FeatureCollection;
 
+// Branch-dependent imports
+import org.apache.sis.internal.jdk8.Stream;
+import org.opengis.feature.Feature;
+
 
 /**
  * A wrapper around the UCAR {@code ucar.nc2.ft} package.
@@ -41,5 +45,12 @@ final class FeaturesWrapper extends Disc
         this.features = features;
     }
 
-    // TODO
+
+    /**
+     * Returns the stream of features.
+     */
+    @Override
+    public Stream<Feature> features() {
+        throw new UnsupportedOperationException();      // TODO
+    }
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -18,6 +18,7 @@ package org.apache.sis.internal.netcdf.u
 
 import java.util.List;
 import java.io.IOException;
+import java.util.Collection;
 import ucar.ma2.Array;
 import ucar.ma2.Section;
 import ucar.ma2.InvalidRangeException;
@@ -27,6 +28,7 @@ import ucar.nc2.VariableIF;
 import org.apache.sis.math.Vector;
 import org.apache.sis.internal.netcdf.DataType;
 import org.apache.sis.internal.netcdf.Variable;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 
@@ -130,6 +132,16 @@ final class VariableWrapper extends Vari
     }
 
     /**
+     * Returns the names of all attributes associated to this variable.
+     *
+     * @return names of all attributes associated to this variable.
+     */
+    @Override
+    public Collection<String> getAttributeNames() {
+        return toNames(variable.getAttributes());
+    }
+
+    /**
      * Returns the sequence of values for the given attribute, or an empty array if none.
      * The elements will be of class {@link String} if {@code numeric} is {@code false},
      * or {@link Number} if {@code numeric} is {@code true}.
@@ -164,6 +176,17 @@ final class VariableWrapper extends Vari
     }
 
     /**
+     * Returns the names of all attributes in the given list.
+     */
+    static List<String> toNames(final List<Attribute> attributes) {
+        final String[] names = new String[attributes.size()];
+        for (int i=0; i<names.length; i++) {
+            names[i] = attributes.get(i).getShortName();
+        }
+        return UnmodifiableArrayList.wrap(names);
+    }
+
+    /**
      * Reads all the data for this variable and returns them as an array of a Java primitive type.
      * Multi-dimensional variables are flattened as a one-dimensional array (wrapped in a vector).
      * This method may cache the returned vector, at UCAR library choice.

Modified: sis/branches/JDK7/storage/sis-shapefile/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-shapefile/pom.xml?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-shapefile/pom.xml (original)
+++ sis/branches/JDK7/storage/sis-shapefile/pom.xml Thu Jul 13 12:10:35 2017
@@ -124,7 +124,7 @@ Read and write files in the Shapefile fo
     <dependency>
       <groupId>com.esri.geometry</groupId>
       <artifactId>esri-geometry-api</artifactId>
-      <scope>compile</scope>
+      <optional>false</optional>
     </dependency>
   </dependencies>
 

Modified: sis/branches/JDK7/storage/sis-storage/pom.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/pom.xml?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/pom.xml (original)
+++ sis/branches/JDK7/storage/sis-storage/pom.xml Thu Jul 13 12:10:35 2017
@@ -135,6 +135,11 @@ Provides the interfaces and base classes
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>com.esri.geometry</groupId>
+      <artifactId>esri-geometry-api</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -34,10 +34,12 @@ import org.opengis.metadata.citation.Rol
 import org.opengis.metadata.citation.DateType;
 import org.opengis.metadata.citation.Citation;
 import org.opengis.metadata.citation.CitationDate;
+import org.opengis.metadata.spatial.GCP;
 import org.opengis.metadata.spatial.Dimension;
 import org.opengis.metadata.spatial.DimensionNameType;
 import org.opengis.metadata.spatial.CellGeometry;
 import org.opengis.metadata.spatial.PixelOrientation;
+import org.opengis.metadata.spatial.GeolocationInformation;
 import org.opengis.metadata.spatial.SpatialRepresentationType;
 import org.opengis.metadata.constraint.Restriction;
 import org.opengis.metadata.content.TransferFunctionType;
@@ -48,6 +50,8 @@ 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.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.TransformException;
@@ -64,6 +68,8 @@ import org.apache.sis.metadata.iso.spati
 import org.apache.sis.metadata.iso.spatial.DefaultDimension;
 import org.apache.sis.metadata.iso.spatial.DefaultGeorectified;
 import org.apache.sis.metadata.iso.spatial.DefaultGeoreferenceable;
+import org.apache.sis.metadata.iso.spatial.DefaultGCPCollection;
+import org.apache.sis.metadata.iso.spatial.DefaultGCP;
 import org.apache.sis.metadata.iso.content.DefaultAttributeGroup;
 import org.apache.sis.metadata.iso.content.DefaultSampleDimension;
 import org.apache.sis.metadata.iso.content.DefaultCoverageDescription;
@@ -97,6 +103,7 @@ 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 static java.util.Collections.singleton;
@@ -123,6 +130,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()
@@ -436,6 +448,23 @@ public class MetadataBuilder {
     }
 
     /**
+     * Collection of ground control points.
+     */
+    private DefaultGCPCollection groundControlPoints;
+
+    /**
+     * Creates the collection of ground control points if it does not already exists, then returns it.
+     *
+     * @return the ground control points (never {@code null}).
+     */
+    private DefaultGCPCollection groundControlPoints() {
+        if (groundControlPoints == null) {
+            groundControlPoints = new DefaultGCPCollection();
+        }
+        return groundControlPoints;
+    }
+
+    /**
      * Information about the distributor of and options for obtaining the resource.
      */
     private DefaultDistribution distribution;
@@ -750,6 +779,10 @@ public class MetadataBuilder {
             if (n != 0) {
                 gridRepresentation.setNumberOfDimensions(shared(n));
             }
+            if (groundControlPoints != null && gridRepresentation instanceof DefaultGeoreferenceable) {
+                addIfNotPresent(((DefaultGeoreferenceable) gridRepresentation).getGeolocationInformation(), groundControlPoints);
+                groundControlPoints = null;
+            }
             addIfNotPresent(metadata.getSpatialRepresentationInfo(), gridRepresentation);
             gridRepresentation = null;
         }
@@ -1796,7 +1829,98 @@ parse:      for (int i = 0; i < length;)
      */
     public final void setPointInPixel(final PixelOrientation value) {
         if (value != null) {
-            ((DefaultGeorectified) gridRepresentation()).setPointInPixel(value);
+            final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
+            if (gridRepresentation instanceof DefaultGeorectified) {
+                ((DefaultGeorectified) gridRepresentation).setPointInPixel(value);
+            }
+        }
+    }
+
+    /**
+     * Sets whether parameters for transformation, control/check point(s) or orientation parameters are available.
+     * Storage location are:
+     *
+     * <ul>
+     *   <li>If georeferenceable:<ul>
+     *     <li>{@code metadata/spatialRepresentationInfo/transformationParameterAvailability}</li>
+     *     <li>{@code metadata/spatialRepresentationInfo/controlPointAvailability}</li>
+     *     <li>{@code metadata/spatialRepresentationInfo/orientationParameterAvailability}</li>
+     *   </ul></li>
+     *   <li>If georeferenced:<ul>
+     *     <li>{@code metadata/spatialRepresentationInfo/transformationParameterAvailability}</li>
+     *     <li>{@code metadata/spatialRepresentationInfo/checkPointAvailability}</li>
+     *   </ul></li>
+     * </ul>
+     *
+     * @param  transformationParameterAvailability  indication of whether or not parameters for transformation exists.
+     * @param  controlPointAvailability             indication of whether or not control or check point(s) exists.
+     * @param  orientationParameterAvailability     indication of whether or not orientation parameters are available.
+     */
+    public final void setGeoreferencingAvailability(final boolean transformationParameterAvailability,
+                                                    final boolean controlPointAvailability,
+                                                    final boolean orientationParameterAvailability)
+    {
+        final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
+        gridRepresentation.setTransformationParameterAvailable(transformationParameterAvailability);
+        if (gridRepresentation instanceof DefaultGeorectified) {
+            ((DefaultGeorectified) gridRepresentation).setCheckPointAvailable(controlPointAvailability);
+        } else if (gridRepresentation instanceof DefaultGeoreferenceable) {
+            ((DefaultGeoreferenceable) gridRepresentation).setControlPointAvailable(controlPointAvailability);
+            ((DefaultGeoreferenceable) gridRepresentation).setOrientationParameterAvailable(orientationParameterAvailability);
+        }
+    }
+
+    /**
+     * Adds information about the geolocation of an image.
+     * Storage location is:
+     *
+     * <ul>
+     *   <li>{@code metadata/spatialRepresentationInfo/geolocationInformation}</li>
+     * </ul>
+     *
+     * @param  info  the geolocation information to add, or {@code null} if none.
+     */
+    public final void addGeolocation(final GeolocationInformation info) {
+        if (info != null) {
+            final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
+            if (gridRepresentation instanceof DefaultGeoreferenceable) {
+                addIfNotPresent(((DefaultGeoreferenceable) gridRepresentation).getGeolocationInformation(), info);
+            }
+        }
+    }
+
+    /**
+     * 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>
+     *   <li>{@code metadata/spatialRepresentationInfo/checkPoint/geographicCoordinates} if georectified</li>
+     *   <li>{@code metadata/spatialRepresentationInfo/geolocationInformation/gcp/geographicCoordinates} if georeferenceable</li>
+     * </ul>
+     *
+     * @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 DirectPosition geographicCoordinates, final Element accuracyReport) {
+        if (geographicCoordinates != null) {
+            final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
+            final Collection<GCP> points;
+            if (gridRepresentation instanceof DefaultGeorectified) {
+                points = ((DefaultGeorectified) gridRepresentation).getCheckPoints();
+            } else if (gridRepresentation instanceof DefaultGeoreferenceable) {
+                points = groundControlPoints().getGCPs();
+            } else {
+                return;
+            }
+            final DefaultGCP gcp = new DefaultGCP();
+            gcp.setGeographicCoordinates(geographicCoordinates);
+            if (accuracyReport != null) {
+                addIfNotPresent(gcp.getAccuracyReports(), accuracyReport);
+            }
+            addIfNotPresent(points, gcp);
         }
     }
 
@@ -1813,7 +1937,10 @@ parse:      for (int i = 0; i < length;)
     public final void setGridToCRS(final CharSequence value) {
         final InternationalString i18n = trim(value);
         if (i18n != null) {
-            ((DefaultGeorectified) gridRepresentation()).setTransformationDimensionDescription(i18n);
+            final DefaultGridSpatialRepresentation gridRepresentation = gridRepresentation();
+            if (gridRepresentation instanceof DefaultGeorectified) {
+                ((DefaultGeorectified) gridRepresentation).setTransformationDimensionDescription(i18n);
+            }
         }
     }
 
@@ -1896,6 +2023,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.

Modified: sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/GeometryParser.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/GeometryParser.java?rev=1801832&r1=1801831&r2=1801832&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/GeometryParser.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/GeometryParser.java [UTF-8] Thu Jul 13 12:10:35 2017
@@ -21,8 +21,10 @@ import org.apache.sis.util.CharSequences
 
 
 /**
- * The converter to use for converting a text into a geometry. In current implementation,
- * geometries are line strings represented by an array of ordinate values.
+ * The converter to use for converting a text into a geometry.
+ * This converter performs only the first step, the conversion to a {@code double[]} array.
+ * The second step (the conversion to a geometry object) is performed after we collected all arrays.
+ * The resulting geometry class depends on the library available at runtime.
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 0.8
@@ -58,7 +60,7 @@ final class GeometryParser extends Surje
     }
 
     /**
-     * Converts an element from the CSV file to our current pseudo-geometry type.
+     * Converts an element from the CSV file to the array type.
      */
     @Override
     public double[] apply(final String text) {



Mime
View raw message