sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1481889 - in /sis/branches/JDK7: core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/
Date Mon, 13 May 2013 14:44:15 GMT
Author: desruisseaux
Date: Mon May 13 14:44:15 2013
New Revision: 1481889

URL: http://svn.apache.org/r1481889
Log:
Ported more NetCDF-ISO mapping code.

Added:
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java   (with props)
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java   (with props)
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java   (with props)
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java   (with props)
Modified:
    sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Decoder.java
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/DecoderUCAR.java
    sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java

Modified: sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java?rev=1481889&r1=1481888&r2=1481889&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] (original)
+++ sis/branches/JDK7/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/MetadataUtilities.java [UTF-8] Mon May 13 14:44:15 2013
@@ -40,6 +40,16 @@ import java.util.Objects;
  */
 public final class MetadataUtilities extends Static {
     /**
+     * The metadata standard name for ISO 19115-2.
+     */
+    public static final String STANDARD_NAME_2 = "ISO 19115-2 Geographic Information - Metadata Part 2 Extensions for imagery and gridded data";
+
+    /**
+     * The metadata standard version number for ISO 19115-2.
+     */
+    public static final String STANDARD_VERSION_2 = "ISO 19115-2:2009(E)";
+
+    /**
      * Do not allow instantiation of this class.
      */
     private MetadataUtilities() {

Added: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java?rev=1481889&view=auto
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java (added)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java [UTF-8] Mon May 13 14:44:15 2013
@@ -0,0 +1,274 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.netcdf;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+import ucar.nc2.Dimension;
+import ucar.nc2.dataset.CoordinateAxis;
+import ucar.nc2.dataset.CoordinateAxis1D;
+import ucar.nc2.dataset.CoordinateAxis2D;
+import ucar.nc2.dataset.CoordinateSystem;
+
+import org.apache.sis.util.resources.Errors;
+
+import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
+import static org.apache.sis.util.collection.Containers.hashMapCapacity;
+
+
+/**
+ * Information about NetCDF coordinate system.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.14)
+ * @version 0.3
+ * @module
+ */
+final class CRSBuilderUCAR extends WarningProducer {
+    /**
+     * The NetCDF coordinate system to wrap.
+     */
+    private final CoordinateSystem netcdfCS;
+
+    /**
+     * The coordinate axes in natural (reverse of NetCDF) order, or {@code null} for inferring
+     * it from the {@link #netcdfCS}.
+     */
+    private List<CoordinateAxis> axes;
+
+    /**
+     * Domain of the variable for which we are wrapping a coordinate system, in natural
+     * order (reverse of NetCDF order). This are often, but not necessarily, the NetCDF
+     * {@linkplain CoordinateSystem#getDomain() coordinate system domain} except for the
+     * dimension ordering. If {@code null}, will be inferred from the {@link #netcdfCS}.
+     */
+    private List<Dimension> domain;
+
+    /**
+     * Creates a new CRS builder.
+     *
+     * @param parent Where to send the warnings, or {@code null} if none.
+     * @param cs The NetCDF coordinate system, or {@code null} if none.
+     */
+    CRSBuilderUCAR(final WarningProducer parent, final CoordinateSystem cs) {
+        super(parent);
+        netcdfCS = cs;
+    }
+
+    /**
+     * Ensures that the given value is defined.
+     */
+    private static void ensureDefined(final String name, final Object value) throws IllegalStateException {
+        if (value == null) {
+            throw new IllegalStateException(Errors.format(Errors.Keys.MissingValueForProperty_1, name));
+        }
+    }
+
+    /**
+     * Returns the NetCDF coordinate axes in natural (reverse of NetCDF) order. The returned list is
+     * usually the NetCDF {@linkplain CoordinateSystem#getCoordinateAxes() coordinate axes} list in
+     * the reverse order.
+     *
+     * <p>By default, the returned list is modifiable. Any changes in the content of this list will
+     * be reflected in the wrappers to be {@linkplain #build() build}. This is useful if the caller
+     * wants to modify the axis order, as in the example below:</p>
+     *
+     * {@preformat java
+     *     builder.setCoordinateSystem(...);
+     *     Collection.sort(builder.getCoordinateAxes(), new CoordinateAxis.AxisComparator());
+     * }
+     *
+     * @return The NetCDF coordinate axis in natural order (reverse of NetCDF order),
+     *         or {@code null} if unknown.
+     *
+     * @see ucar.nc2.dataset.CoordinateAxis.AxisComparator
+     */
+    private List<CoordinateAxis> getCoordinateAxes() {
+        if (axes == null && netcdfCS != null) {
+            Collections.reverse(axes = new ArrayList<>(netcdfCS.getCoordinateAxes()));
+        }
+        return axes;
+    }
+
+    /**
+     * Returns the domain of the variable for which we are wrapping a coordinate system. This is often,
+     * but not necessarily, the NetCDF {@linkplain CoordinateSystem#getDomain() coordinate system domain}
+     * except for the dimension ordering.
+     *
+     * <p>By default, the returned list is modifiable. Any changes in the content of this list will be
+     * reflected in the wrappers to be {@linkplain #build() build}. This is useful if the caller
+     * wants to reduce the domain rank.</p>
+     *
+     * @return The domain in natural order (reverse of NetCDF order), or {@code null} if unknown.
+     */
+    private List<Dimension> getDomain() {
+        if (domain == null && netcdfCS != null) {
+            Collections.reverse(domain = new ArrayList<>(netcdfCS.getDomain()));
+        }
+        return domain;
+    }
+
+    /**
+     * Returns the dimensions of all axes, together with an axis associated to each dimension.
+     * If more than one axis use the same dimension, then the first axis has precedence.
+     *
+     * <p>The domain of all axes (or the {@linkplain CoordinateSystem#getDomain() coordinate system
+     * domain}) is often the same than the {@linkplain #getDomain() domain of the variable}, but
+     * not necessarily. In particular, the relationship is not straightforward when the coordinate
+     * system contains instances of {@link CoordinateAxis2D}.</p>
+     *
+     * @return All axis dimensions associated to their originating axis.
+     * @throws IllegalStateException If the {@linkplain #getCoordinateAxes() coordinate axes}
+     *         are not defined.
+     *
+     * @see CoordinateAxis#getDimensions()
+     */
+    public Map<Dimension,CoordinateAxis> getAxesDomain() throws IllegalStateException {
+        final List<CoordinateAxis> axes = getCoordinateAxes();
+        ensureDefined("axes", axes);
+        final Map<Dimension,CoordinateAxis> map = new LinkedHashMap<>(hashMapCapacity(axes.size()));
+        /*
+         * Stores all dimensions in the map, together with an arbitrary axis. If there is no
+         * conflict, we are done. If there is conflicts, then the first one-dimensional axis
+         * (if any) will have precedence over all other axes for that dimension. If the conflict
+         * involves only axes having 2 or more dimensions, then we will defer their handling
+         * to a later stage.
+         */
+        Map<Dimension, Set<CoordinateAxis>> conflicts = null;
+        for (final CoordinateAxis axis : axes) {
+            final int rank = axis.getRank();
+            for (int i=rank; --i>=0;) {
+                final Dimension dimension = axis.getDimension(i);
+                final CoordinateAxis previous = map.put(dimension, axis);
+                if (previous != null) {
+                    final int pr = previous.getRank();
+                    if (pr != 1 && rank != 1) {
+                        /*
+                         * Found a conflict (two axes using the same dimension) that can not be
+                         * resolved before the loop completion. Remember this conflict in order
+                         * to process it later.
+                         */
+                        if (conflicts == null) {
+                            conflicts = new HashMap<>(4);
+                        }
+                        Set<CoordinateAxis> deferred = conflicts.get(dimension);
+                        if (deferred == null) {
+                            deferred = new LinkedHashSet<>(4);
+                            conflicts.put(dimension, deferred);
+                        }
+                        deferred.add(previous);
+                        deferred.add(axis);
+                    } else {
+                        /*
+                         * The conflict can be resolved by giving precedence to a one-dimensional
+                         * axis and discart the other.
+                         */
+                        if (pr == 1) {
+                            map.put(dimension, previous);
+                        }
+                        if (conflicts != null) {
+                            conflicts.remove(dimension);
+                        }
+                    }
+                }
+            }
+        }
+        /*
+         * At this point the map is fully build, but some values may be inaccurate if conflicts
+         * exist. In such cases, we will first checks if there is any axis that can be assigned
+         * to only one dimension, because all other dimensions are not available anymore.
+         */
+redo:   while (!isNullOrEmpty(conflicts)) {
+            for (final Map.Entry<Dimension,Set<CoordinateAxis>> entry : conflicts.entrySet()) {
+                final Dimension dimension = entry.getKey();
+otherAxis:      for (final CoordinateAxis axis : entry.getValue()) {
+                    for (int i=axis.getRank(); --i>=0;) {
+                        final Dimension candidate = axis.getDimension(i);
+                        if (candidate != dimension && conflicts.containsKey(candidate)) {
+                            // Axis can be assigned to 2 or more dimensions. Search an other one.
+                            continue otherAxis;
+                        }
+                    }
+                    /*
+                     * If we reach this point, then this axis can be associated only
+                     * to the current dimension; no other dimension are available.
+                     */
+                    conflicts.remove(dimension);
+                    map.put(dimension, axis);
+                    continue redo; // Maybe some axes prior to this one can now be processed.
+                }
+            }
+            /*
+             * If we reach this point, we have not been able to process any axis.
+             * Pickup what seems the "main" dimension according an arbitrary rule.
+             */
+            for (final Set<CoordinateAxis> as : conflicts.values()) {
+                for (final CoordinateAxis axis : as) {
+                    final Dimension dimension = axis.getDimension(findMainDimensionOf(axis));
+                    if (conflicts.remove(dimension) != null) {
+                        map.put(dimension, axis);
+                        for (final Set<CoordinateAxis> toClean : conflicts.values()) {
+                            toClean.remove(axis);
+                        }
+                        continue redo; // Maybe some other axes can now be processed.
+                    }
+                }
+            }
+            /*
+             * If we reach this point, there is no "main" dimension available. Such case should
+             * never happen for two-dimensional axes, but could happen for axes having three or
+             * more dimensions. Such axes do not exist in the NetCDF API at the time of writting,
+             * but if they appear in a future version there is where we should complete the code.
+             */
+            throw new UnsupportedOperationException();
+        }
+        return map;
+    }
+
+    /**
+     * Returns the index of what seems to be the "main" dimension of the given coordinate axis.
+     * For {@link CoordinateAxis1D}, the returned index is trivially 0 in all cases.
+     * For {@link CoordinateAxis2D}, the default implementation returns the index (0 or 1)
+     * of the dimension for which the largest increment is found.
+     *
+     * <p>This method affects the sort performed by {@link #sortAxesAccordingDomain()}.</p>
+     *
+     * @param  axis The axis for which to get the index of the "main" dimension.
+     * @return Index of the axis dimension having the largest increment.
+     */
+    private static int findMainDimensionOf(final CoordinateAxis axis) {
+        int main = 0;
+        if (axis instanceof CoordinateAxis2D) {
+            final CoordinateAxis2D a2 = (CoordinateAxis2D) axis;
+            final int    si = a2.getShape(0);
+            final int    sj = a2.getShape(1);
+            final double di = Math.abs(a2.getCoordValue(0, sj >>> 1) - a2.getCoordValue(si-1, sj >>> 1)) / si;
+            final double dj = Math.abs(a2.getCoordValue(si >>> 1, 0) - a2.getCoordValue(si >>> 1, sj-1)) / sj;
+            if (dj > di) {
+                main = 1;
+            }
+        }
+        return main;
+    }
+}

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/CRSBuilderUCAR.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Decoder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Decoder.java?rev=1481889&r1=1481888&r2=1481889&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Decoder.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Decoder.java [UTF-8] Mon May 13 14:44:15 2013
@@ -17,13 +17,11 @@
 package org.apache.sis.storage.netcdf;
 
 import java.util.Date;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
+import java.util.List;
 import java.io.IOException;
 import javax.measure.unit.Unit;
 import org.apache.sis.measure.Units;
-import org.apache.sis.util.Exceptions;
-import org.apache.sis.util.logging.Logging;
+import org.apache.sis.metadata.iso.DefaultMetadata;
 
 
 /**
@@ -43,23 +41,14 @@ import org.apache.sis.util.logging.Loggi
  * @version 0.3
  * @module
  */
-abstract class Decoder {
+abstract class Decoder extends WarningProducer {
     /**
      * Creates a new decoder.
-     */
-    Decoder() {
-    }
-
-    /**
-     * Reports a warning. The current implementation just logs the warning. However if we want
-     * to implement a listener mechanism in a future version, this could be done here.
      *
-     * @param method    The method in which the warning occurred.
-     * @param exception The exception to log.
+     * @param parent Where to send the warnings, or {@code null} if none.
      */
-    static void warning(final String method, final Exception exception) {
-        final LogRecord record = new LogRecord(Level.WARNING, Exceptions.formatChainedMessages(null, null, exception));
-        Logging.log(Decoder.class, method, record);
+    Decoder(final WarningProducer parent) {
+        super(parent);
     }
 
     /**
@@ -136,8 +125,9 @@ abstract class Decoder {
      *
      * @param  values The values to convert. May contains {@code null} elements.
      * @return The converted values. May contains {@code null} elements.
+     * @throws IOException If an I/O operation was necessary but failed.
      */
-    public abstract Date[] numberToDate(final String symbol, final Number... values);
+    public abstract Date[] numberToDate(final String symbol, final Number... values) throws IOException;
 
     /**
      * Returns the value of the {@code "_Id"} global attribute. The UCAR library defines a
@@ -160,4 +150,20 @@ abstract class Decoder {
     public String getTitle() throws IOException {
         return stringValue("_Title");
     }
+
+    /**
+     * Returns all variables found in the NetCDF file.
+     *
+     * @return All variables, or an empty list if none.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    public abstract List<Variable> getVariables() throws IOException;
+
+    /**
+     * Adds the spatial information inferred from the the NetCDF {@code CoordinateSystem} objects.
+     *
+     * @param  metadata Where to add the spatial information.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    public abstract void addSpatialRepresentationInfo(DefaultMetadata metadata) throws IOException;
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/DecoderUCAR.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/DecoderUCAR.java?rev=1481889&r1=1481888&r2=1481889&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/DecoderUCAR.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/DecoderUCAR.java [UTF-8] Mon May 13 14:44:15 2013
@@ -17,14 +17,32 @@
 package org.apache.sis.storage.netcdf;
 
 import java.util.Date;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.Map;
+import java.io.IOException;
 import ucar.nc2.Group;
 import ucar.nc2.Attribute;
+import ucar.nc2.VariableIF;
 import ucar.nc2.NetcdfFile;
+import ucar.nc2.dataset.NetcdfDataset;
+import ucar.nc2.dataset.CoordinateAxis;
+import ucar.nc2.dataset.CoordinateSystem;
+import ucar.nc2.constants.AxisType;
 import ucar.nc2.units.DateUnit;
 import ucar.nc2.time.Calendar;
 import ucar.nc2.time.CalendarDate;
 import ucar.nc2.time.CalendarDateFormatter;
+
+import org.opengis.metadata.spatial.CellGeometry;
+import org.opengis.metadata.spatial.GridSpatialRepresentation;
 import org.apache.sis.util.ArraysExt;
+import org.apache.sis.metadata.iso.DefaultMetadata;
+import org.apache.sis.metadata.iso.spatial.DefaultDimension;
+import org.apache.sis.metadata.iso.spatial.DefaultGridSpatialRepresentation;
+
+import static org.apache.sis.storage.netcdf.AttributeNames.*;
 
 
 /**
@@ -60,9 +78,11 @@ final class DecoderUCAR extends Decoder 
      * {@link NetcdfFile} instance, the {@link NetcdfDataset} subclass is necessary in order to
      * get coordinate system information.
      *
+     * @param parent Where to send the warnings, or {@code null} if none.
      * @param file The NetCDF file from which to parse metadata.
      */
-    DecoderUCAR(final NetcdfFile file) {
+    DecoderUCAR(final WarningProducer parent, final NetcdfFile file) {
+        super(parent);
         this.file = file;
     }
 
@@ -251,4 +271,88 @@ final class DecoderUCAR extends Decoder 
     public String getTitle() {
         return file.getTitle();
     }
+
+    /**
+     * Returns all variables found in the NetCDF file.
+     *
+     * @return All variables, or an empty list if none.
+     */
+    @Override
+    public List<Variable> getVariables() {
+        final List<? extends VariableIF> all = file.getVariables();
+        final List<Variable> variables = new ArrayList<>(all.size());
+        for (final VariableIF variable : all) {
+            variables.add(new VariableUCAR(variable, all));
+        }
+        return variables;
+    }
+
+    /**
+     * Adds the spatial information inferred from the the NetCDF {@link CoordinateSystem} objects.
+     */
+    @Override
+    public void addSpatialRepresentationInfo(final DefaultMetadata metadata) throws IOException {
+        if (file instanceof NetcdfDataset) {
+            final NetcdfDataset ds = (NetcdfDataset) file;
+            final EnumSet<NetcdfDataset.Enhance> mode = EnumSet.copyOf(ds.getEnhanceMode());
+            if (mode.add(NetcdfDataset.Enhance.CoordSystems)) {
+                ds.enhance(mode);
+            }
+            for (final CoordinateSystem cs : ds.getCoordinateSystems()) {
+                if (cs.getRankDomain() >= Variable.MIN_DIMENSION && cs.getRankRange() >= Variable.MIN_DIMENSION) {
+                    metadata.getSpatialRepresentationInfo().add(createSpatialRepresentationInfo(cs));
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a {@code <gmd:spatialRepresentationInfo>} element from the given NetCDF coordinate system.
+     *
+     * @param  cs The NetCDF coordinate system.
+     * @return The grid spatial representation info.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    @SuppressWarnings("fallthrough")
+    private GridSpatialRepresentation createSpatialRepresentationInfo(final CoordinateSystem cs) throws IOException {
+        final DefaultGridSpatialRepresentation grid = new DefaultGridSpatialRepresentation();
+        grid.setNumberOfDimensions(cs.getRankDomain());
+        final CRSBuilderUCAR builder = new CRSBuilderUCAR(this, cs);
+        for (final Map.Entry<ucar.nc2.Dimension, CoordinateAxis> entry : builder.getAxesDomain().entrySet()) {
+            final CoordinateAxis axis = entry.getValue();
+            final int i = axis.getDimensions().indexOf(entry.getKey());
+            Dimension rsat = null;
+            Double resolution = null;
+            final AxisType at = axis.getAxisType();
+            if (at != null) {
+                boolean valid = false;
+                switch (at) {
+                    case Lon:      valid = true; // fallthrough
+                    case GeoX:     rsat  = LONGITUDE; break;
+                    case Lat:      valid = true; // fallthrough
+                    case GeoY:     rsat  = LATITUDE; break;
+                    case Height:   valid = true; // fallthrough
+                    case GeoZ:
+                    case Pressure: rsat  = VERTICAL; break;
+                    case Time:     valid = true; // fallthrough
+                    case RunTime:  rsat  = TIME; break;
+                }
+                if (valid) {
+                    final Number res = numericValue(rsat.RESOLUTION);
+                    if (res != null) {
+                        resolution = (res instanceof Double) ? (Double) res : res.doubleValue();
+                    }
+                }
+            }
+            final DefaultDimension dimension = new DefaultDimension();
+            if (rsat != null) {
+                dimension.setDimensionName(rsat.TYPE);
+                dimension.setResolution(resolution);
+            }
+            dimension.setDimensionSize(axis.getShape(i));
+            grid.getAxisDimensionProperties().add(dimension);
+        }
+        grid.setCellGeometry(CellGeometry.AREA);
+        return grid;
+    }
 }

Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java?rev=1481889&r1=1481888&r2=1481889&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java [UTF-8] Mon May 13 14:44:15 2013
@@ -21,13 +21,10 @@ import java.net.URISyntaxException;
 import java.util.Date;
 import java.util.List;
 import java.util.Set;
-import java.util.EnumSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.Collection;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
 import java.io.IOException;
 import javax.measure.unit.Unit;
 import javax.measure.unit.SI;
@@ -48,16 +45,14 @@ import org.opengis.metadata.maintenance.
 import org.opengis.metadata.constraint.Restriction;
 import org.opengis.referencing.crs.VerticalCRS;
 
-import org.apache.sis.util.Exceptions;
-import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.iso.DefaultNameSpace;
 import org.apache.sis.util.iso.SimpleInternationalString;
+import org.apache.sis.internal.util.DefaultFactories;
+import org.apache.sis.internal.metadata.MetadataUtilities;
 import org.apache.sis.metadata.iso.DefaultMetadata;
 import org.apache.sis.metadata.iso.DefaultIdentifier;
 import org.apache.sis.metadata.iso.extent.*;
-import org.apache.sis.metadata.iso.spatial.*;
 import org.apache.sis.metadata.iso.content.*;
 import org.apache.sis.metadata.iso.citation.*;
 import org.apache.sis.metadata.iso.distribution.*;
@@ -103,7 +98,7 @@ import static org.apache.sis.storage.net
  * @version 0.3
  * @module
  */
-final class MetadataReader {
+final class MetadataReader extends WarningProducer {
     /**
      * Names of groups where to search for metadata, in precedence order.
      * The {@code null} value stands for global attributes.
@@ -113,10 +108,11 @@ final class MetadataReader {
     private static final String[] SEARCH_PATH = {"NCISOMetadata", "CFMetadata", null, "THREDDSMetadata"};
 
     /**
-     * The character to use as a keyword separator. This separator is used for parsing the
+     * The string to use as a keyword separator. This separator is used for parsing the
      * {@value org.apache.sis.metadata.netcdf.AttributeNames#KEYWORDS} attribute value.
+     * This is a regular expression.
      */
-    private static final char KEYWORD_SEPARATOR = ',';
+    private static final String KEYWORD_SEPARATOR = ",";
 
     /**
      * The vertical coordinate reference system to be given to the object created by {@link #createExtent()}.
@@ -166,28 +162,18 @@ final class MetadataReader {
     /**
      * Creates a new <cite>NetCDF to ISO</cite> mapper for the given source.
      *
+     * @param  parent Where to send the warnings, or {@code null} if none.
      * @param  decoder The source of NetCDF attributes.
      * @throws IOException If an I/O operation was necessary but failed.
      */
-    MetadataReader(final Decoder decoder) throws IOException {
+    MetadataReader(final WarningProducer parent, final Decoder decoder) throws IOException {
+        super(parent);
         this.decoder = decoder;
         decoder.setSearchPath(SEARCH_PATH);
         searchPath = decoder.getSearchPath();
     }
 
     /**
-     * Reports a warning. The current implementation just logs the warning. However if we want
-     * to implement a listener mechanism in a future version, this could be done here.
-     *
-     * @param method    The method in which the warning occurred.
-     * @param exception The exception to log.
-     */
-    private static void warning(final String method, final Exception exception) {
-        final LogRecord record = new LogRecord(Level.WARNING, Exceptions.formatChainedMessages(null, null, exception));
-        Logging.log(MetadataReader.class, method, record);
-    }
-
-    /**
      * Returns the given string as an {@code InternationalString} if non-null, or {@code null} otherwise.
      */
     private static InternationalString toInternationalString(final String value) {
@@ -495,13 +481,13 @@ final class MetadataReader {
                 if (credits  != null) addIfAbsent(identification.getCredits(), credits);
                 if (license  != null) addIfAbsent(identification.getResourceConstraints(), constraints = new DefaultLegalConstraints(license));
                 if (access   != null) {
-                    for (final CharSequence token : CharSequences.split(access, ',')) {
-                        final String t = token.toString();
-                        if (!t.isEmpty()) {
+                    for (String keyword : access.split(KEYWORD_SEPARATOR)) {
+                        keyword = keyword.trim();
+                        if (!keyword.isEmpty()) {
                             if (constraints == null) {
                                 identification.getResourceConstraints().add(constraints = new DefaultLegalConstraints());
                             }
-                            addIfAbsent(constraints.getAccessConstraints(), Types.forCodeName(Restriction.class, t, true));
+                            addIfAbsent(constraints.getAccessConstraints(), Types.forCodeName(Restriction.class, keyword, true));
                         }
                     }
                 }
@@ -563,8 +549,8 @@ final class MetadataReader {
         DefaultKeywords keywords = null;
         if (list != null) {
             final Set<InternationalString> words = new LinkedHashSet<>();
-            for (CharSequence token : CharSequences.split(list, KEYWORD_SEPARATOR)) {
-                final String keyword = token.toString().trim();
+            for (String keyword : list.split(KEYWORD_SEPARATOR)) {
+                keyword = keyword.trim();
                 if (!keyword.isEmpty()) {
                     words.add(new SimpleInternationalString(keyword));
                 }
@@ -673,7 +659,7 @@ final class MetadataReader {
      * Returns the converter from the given source unit (which may be {@code null}) to the
      * given target unit, or {@code null} if none or incompatible.
      */
-    private static UnitConverter getConverterTo(final Unit<?> source, final Unit<?> target) {
+    private UnitConverter getConverterTo(final Unit<?> source, final Unit<?> target) {
         if (source != null) try {
             return source.getConverterToAny(target);
         } catch (ConversionException e) {
@@ -696,4 +682,205 @@ final class MetadataReader {
         }
         return n;
     }
+
+    /**
+     * Creates a {@code <gmd:contentInfo>} elements from all applicable NetCDF attributes.
+     *
+     * @return The content information.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    private Collection<DefaultCoverageDescription> createContentInfo() throws IOException {
+        final Map<List<String>, DefaultCoverageDescription> contents = new HashMap<>(4);
+        final String processingLevel = decoder.stringValue(PROCESSING_LEVEL);
+        final List<? extends Variable> variables = decoder.getVariables();
+        for (final Variable variable : variables) {
+            if (!variable.isCoverage(2)) {
+                continue;
+            }
+            /*
+             * Instantiate a CoverageDescription for each distinct set of NetCDF dimensions
+             * (e.g. longitude,latitude,time). This separation is based on the fact that a
+             * coverage has only one domain for every range of values.
+             */
+            final List<String> dimensions = variable.getDimensions();
+            DefaultCoverageDescription content = contents.get(dimensions);
+            if (content == null) {
+                /*
+                 * If there is some NetCDF attributes that can be stored only in the ImageDescription
+                 * subclass, instantiate that subclass. Otherwise instantiate the more generic class.
+                 */
+                if (processingLevel != null) {
+                    content = new DefaultImageDescription();
+                    ((DefaultImageDescription) content).setProcessingLevelCode(new DefaultIdentifier(processingLevel));
+                } else {
+                    content = new DefaultCoverageDescription();
+                }
+                contents.put(dimensions, content);
+            }
+            content.getDimensions().add(createSampleDimension(variable));
+            final Object[] names    = variable.getAttributeValues(FLAG_NAMES,    false);
+            final Object[] meanings = variable.getAttributeValues(FLAG_MEANINGS, false);
+            final Object[] masks    = variable.getAttributeValues(FLAG_MASKS,    true);
+            final Object[] values   = variable.getAttributeValues(FLAG_VALUES,   true);
+            final int length = Math.max(masks.length, Math.max(values.length, Math.max(names.length, meanings.length)));
+            for (int i=0; i<length; i++) {
+                final RangeElementDescription element = createRangeElementDescription(variable,
+                        i < names   .length ? (String) names   [i] : null,
+                        i < meanings.length ? (String) meanings[i] : null,
+                        i < masks   .length ? (Number) masks   [i] : null,
+                        i < values  .length ? (Number) values  [i] : null);
+                if (element != null) {
+                    content.getRangeElementDescriptions().add(element);
+                }
+            }
+        }
+        return contents.values();
+    }
+
+    /**
+     * Creates a {@code <gmd:dimension>} element from the given variable.
+     *
+     * @param  variable The NetCDF variable.
+     * @return The sample dimension information.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    private Band createSampleDimension(final Variable variable) throws IOException {
+        final DefaultBand band = new DefaultBand();
+        String name = variable.getName();
+        if (name != null && !(name = name.trim()).isEmpty()) {
+            if (nameFactory == null) {
+                nameFactory = DefaultFactories.forClass(NameFactory.class);
+            }
+            // TODO: should be band.setName(...) with ISO 19115:2011.
+            // Sequence identifiers are supposed to be numbers only.
+            band.setSequenceIdentifier(nameFactory.createMemberName(null, name,
+                    nameFactory.createTypeName(null, variable.getDataTypeName())));
+        }
+        String description = variable.getDescription();
+        if (description != null && !(description = description.trim()).isEmpty() && !description.equals(name)) {
+            band.setDescriptor(toInternationalString(description));
+        }
+//TODO: Can't store the units, because the Band interface restricts it to length.
+//      We need the SampleDimension interface proposed in ISO 19115 revision draft.
+//      band.setUnits(Units.valueOf(variable.getUnitsString()));
+        return band;
+    }
+
+    /**
+     * Creates a {@code <gmd:rangeElementDescription>} elements from the given information.
+     *
+     * <p><b>Note:</b> ISO 19115 range elements are approximatively equivalent to
+     * {@link org.apache.sis.coverage.Category} in the {@code sis-coverage} module.</p>
+     *
+     * @param  variable The NetCDF variable.
+     * @param  name     One of the elements in the {@link AttributeNames#FLAG_NAMES} attribute, or {@code null}.
+     * @param  meaning  One of the elements in the {@link AttributeNames#FLAG_MEANINGS} attribute or {@code null}.
+     * @param  mask     One of the elements in the {@link AttributeNames#FLAG_MASKS} attribute or {@code null}.
+     * @param  value    One of the elements in the {@link AttributeNames#FLAG_VALUES} attribute or {@code null}.
+     * @return The sample dimension information or {@code null} if none.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    private RangeElementDescription createRangeElementDescription(final Variable variable,
+            final String name, final String meaning, final Number mask, final Number value) throws IOException
+    {
+        if (name != null && meaning != null) {
+            final DefaultRangeElementDescription element = new DefaultRangeElementDescription();
+            element.setName(toInternationalString(name));
+            element.setDefinition(toInternationalString(meaning));
+            // TODO: create a record from values (and possibly from the masks).
+            //       if (pixel & mask == value) then we have that range element.
+            return element;
+        }
+        return null;
+    }
+
+    /**
+     * Creates an ISO {@code Metadata} object from the information found in the NetCDF file.
+     *
+     * @return The ISO metadata object.
+     * @throws IOException If an I/O operation was necessary but failed.
+     */
+    public Metadata read() throws IOException {
+        final DefaultMetadata metadata = new DefaultMetadata();
+        metadata.setMetadataStandardName(MetadataUtilities.STANDARD_NAME_2);
+        metadata.setMetadataStandardVersion(MetadataUtilities.STANDARD_VERSION_2);
+        final Identifier identifier = getFileIdentifier();
+        if (identifier != null) {
+            String code = identifier.getCode();
+            final Citation authority = identifier.getAuthority();
+            if (authority != null) {
+                final InternationalString title = authority.getTitle();
+                if (title != null) {
+                    code = title.toString() + DefaultNameSpace.DEFAULT_SEPARATOR + code;
+                }
+            }
+            metadata.setFileIdentifier(code);
+        }
+        metadata.setDateStamp(decoder.dateValue(METADATA_CREATION));
+        metadata.getHierarchyLevels().add(ScopeCode.DATASET);
+        final String wms = decoder.stringValue("wms_service");
+        final String wcs = decoder.stringValue("wcs_service");
+        if (wms != null || wcs != null) {
+            metadata.getHierarchyLevels().add(ScopeCode.SERVICE);
+        }
+        /*
+         * Add the ResponsibleParty which is declared in global attributes, or in
+         * the THREDDS attributes if no information was found in global attributes.
+         */
+        for (final String path : searchPath) {
+            decoder.setSearchPath(path);
+            final ResponsibleParty party = createResponsibleParty(CREATOR, true);
+            if (party != null && party != pointOfContact) {
+                addIfAbsent(metadata.getContacts(), party);
+                if (pointOfContact == null) {
+                    pointOfContact = party;
+                }
+            }
+        }
+        /*
+         * Add the publisher AFTER the creator, because this method may
+         * reuse the 'creator' field (if non-null and if applicable).
+         */
+        Set<InternationalString> publisher = null;
+        DefaultDistribution distribution   = null;
+        for (final String path : searchPath) {
+            decoder.setSearchPath(path);
+            final ResponsibleParty party = createResponsibleParty(PUBLISHER, false);
+            if (party != null) {
+                if (distribution == null) {
+                    distribution = new DefaultDistribution();
+                    metadata.setDistributionInfo(distribution);
+                }
+                final DefaultDistributor distributor = new DefaultDistributor(party);
+                // TODO: There is some transfert option, etc. that we could set there.
+                // See UnidataDD2MI.xsl for options for OPeNDAP, THREDDS, etc.
+                addIfAbsent(distribution.getDistributors(), distributor);
+                publisher = addIfNonNull(publisher, toInternationalString(party.getIndividualName()));
+            }
+            // Also add history.
+            final String history = decoder.stringValue(HISTORY);
+            if (history != null) {
+                final DefaultDataQuality quality = new DefaultDataQuality();
+                final DefaultLineage lineage = new DefaultLineage();
+                lineage.setStatement(new SimpleInternationalString(history));
+                quality.setLineage(lineage);
+                addIfAbsent(metadata.getDataQualityInfo(), quality);
+            }
+        }
+        /*
+         * Add the identification info AFTER the responsible parties (both creator and publisher),
+         * because this method will reuse the 'creator' and 'publisher' information (if non-null).
+         */
+        final DataIdentification identification = createIdentificationInfo(identifier, publisher);
+        if (identification != null) {
+            metadata.getIdentificationInfo().add(identification);
+        }
+        metadata.setContentInfo(createContentInfo());
+        /*
+         * Add the dimension information, if any. This metadata node
+         * is built from the NetCDF CoordinateSystem objects.
+         */
+        decoder.addSpatialRepresentationInfo(metadata);
+        return metadata;
+    }
 }

Added: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java?rev=1481889&view=auto
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java (added)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java [UTF-8] Mon May 13 14:44:15 2013
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.netcdf;
+
+import java.util.List;
+
+
+/**
+ * A NetCDF variable created by {@link Decoder}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+abstract class Variable {
+    /**
+     * Minimal number of dimension for accepting a variable as a coverage variable.
+     */
+    public static final int MIN_DIMENSION = 2;
+
+    /**
+     * Creates a new variable.
+     */
+    Variable() {
+    }
+
+    /**
+     * Returns the name of this variable, or {@code null} if none.
+     *
+     * @return The name of this variable, or {@code null}.
+     */
+    public abstract String getName();
+
+    /**
+     * Returns the description of this variable, or {@code null} if none.
+     *
+     * @return The description of this variable, or {@code null}.
+     */
+    public abstract String getDescription();
+
+    /**
+     * Returns the name of the variable data type as the name of the primitive type
+     * followed by the span of each dimension (in unit of grid cells) between brackets.
+     *
+     * @return The name of the variable data type.
+     */
+    public abstract String getDataTypeName();
+
+    /**
+     * Returns {@code true} if the given variable can be used for generating an image.
+     * This method checks for the following conditions:
+     *
+     * <ul>
+     *   <li>Images require at least {@value #MIN_DIMENSION} dimensions of size equals or greater
+     *       than {@code minLength}. They may have more dimensions, in which case a slice will be
+     *       taken later.</li>
+     *   <li>Exclude axes. Axes are often already excluded by the above condition
+     *       because axis are usually 1-dimensional, but some axes are 2-dimensional
+     *       (e.g. a localization grid).</li>
+     *   <li>Excludes characters, strings and structures, which can not be easily
+     *       mapped to an image type. In addition, 2-dimensional character arrays
+     *       are often used for annotations and we don't want to confuse them
+     *       with images.</li>
+     * </ul>
+     *
+     * @param  minSpan Minimal span (in unit of grid cells) along the dimensions.
+     * @return {@code true} if the variable can be considered a coverage.
+     */
+    public abstract boolean isCoverage(int minSpan);
+
+    /**
+     * Returns the names of the dimensions of this variable.
+     *
+     * @return The dimension names.
+     */
+    public abstract List<String> getDimensions();
+
+    /**
+     * 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}.
+     *
+     * @param attributeName The name of the attribute for which to get the values.
+     * @param numeric {@code true} if the values are expected to be numeric, or {@code false} for strings.
+     */
+    public abstract Object[] getAttributeValues(String attributeName, boolean numeric);
+}

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/Variable.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java?rev=1481889&view=auto
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java (added)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java [UTF-8] Mon May 13 14:44:15 2013
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.netcdf;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.awt.image.DataBuffer;
+import ucar.ma2.DataType;
+import ucar.nc2.Attribute;
+import ucar.nc2.Dimension;
+import ucar.nc2.VariableIF;
+import org.apache.sis.util.CharSequences;
+
+
+/**
+ * A {@link Variable} backed by the UCAR NetCDF library.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+final class VariableUCAR extends Variable {
+    /**
+     * The NetCDF variable.
+     */
+    private final VariableIF variable;
+
+    /**
+     * The list of all variables, used in order to determine
+     * if this variable seems to be a coverage.
+     */
+    private final List<? extends VariableIF> all;
+
+    /**
+     * Creates a new variable wrapping the given NetCDF interface.
+     */
+    VariableUCAR(final VariableIF variable, List<? extends VariableIF> all) {
+        this.variable = variable;
+        this.all = all;
+    }
+
+    /**
+     * Returns the name of this variable, or {@code null} if none.
+     */
+    @Override
+    public String getName() {
+        return variable.getShortName();
+    }
+
+    /**
+     * Returns the description of this variable, or {@code null} if none.
+     */
+    @Override
+    public String getDescription() {
+        return variable.getDescription();
+    }
+
+    /**
+     * Returns the name of the variable data type as the name of the primitive type
+     * followed by the span of each dimension (in unit of grid cells) between brackets.
+     */
+    @Override
+    public String getDataTypeName() {
+        final StringBuilder buffer = new StringBuilder(variable.getDataType().getPrimitiveClassType().getSimpleName());
+        final int[] shape = variable.getShape();
+        for (int i=shape.length; --i>=0;) {
+            buffer.append('[').append(shape[i]).append(']');
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Returns the {@link DataBuffer} constant which most closely represents
+     * the "raw" internal data of the variable.
+     *
+     * {@note There is no converse of this method because the unsigned values type
+     *        need to be handled in a special way (through a "_Unigned" attribute).}
+     *
+     * @param  variable The variable for which to get the {@link DataBuffer} constant, or {@code null}.
+     * @return The data type, or {@link DataBuffer#TYPE_UNDEFINED} if unknown.
+     */
+    private static int getRawDataType(final VariableIF variable) {
+        if (variable != null) {
+            final DataType type = variable.getDataType();
+            if (type != null) switch (type) {
+                case BOOLEAN: // Fall through
+                case BYTE:    return DataBuffer.TYPE_BYTE;
+                case SHORT:   return variable.isUnsigned() ? DataBuffer.TYPE_USHORT : DataBuffer.TYPE_SHORT;
+                case INT:     return DataBuffer.TYPE_INT;
+                case FLOAT:   return DataBuffer.TYPE_FLOAT;
+                case DOUBLE:  return DataBuffer.TYPE_DOUBLE;
+            }
+        }
+        return DataBuffer.TYPE_UNDEFINED;
+    }
+
+    /**
+     * Returns {@code true} if the given variable can be used for generating an image.
+     */
+    @Override
+    public boolean isCoverage(final int minSpan) {
+        int numVectors = 0; // Number of dimension having more than 1 value.
+        for (final int length : variable.getShape()) {
+            if (length >= minSpan) {
+                numVectors++;
+            }
+        }
+        if (numVectors >= MIN_DIMENSION && getRawDataType(variable) != DataBuffer.TYPE_UNDEFINED) {
+            final String name = getName();
+            for (final VariableIF var : all) {
+                if (var != variable) {
+                    Dimension dim;
+                    for (int d=0; (dim=var.getDimension(d)) != null; d++) {
+                        if (name.equals(dim.getShortName())) {
+                            // This variable is a dimension of another variable.
+                            return false;
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the names of the dimensions of this variable.
+     *
+     * @return The dimension names.
+     */
+    @Override
+    public List<String> getDimensions() {
+        final List<Dimension> dimensions = variable.getDimensions();
+        final List<String> names = new ArrayList<>(dimensions.size());
+        for (final Dimension dimension : dimensions) {
+            names.add(dimension.getShortName());
+        }
+        return names;
+    }
+
+    /**
+     * 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}.
+     */
+    @Override
+    public Object[] getAttributeValues(final String attributeName, final boolean numeric) {
+        final Attribute attribute = variable.findAttributeIgnoreCase(attributeName);
+        if (attribute != null) {
+            boolean hasValues = false;
+            final Object[] values = new Object[attribute.getLength()];
+            for (int i=0; i<values.length; i++) {
+                if (numeric) {
+                    if ((values[i] = attribute.getNumericValue(i)) != null) {
+                        hasValues = true;
+                    }
+                } else {
+                    String value = attribute.getStringValue(i);
+                    if (value != null && !(value = value.trim()).isEmpty()) {
+                        values[i] = value.replace('_', ' ');
+                        hasValues = true;
+                    }
+                }
+            }
+            if (hasValues) {
+                return values;
+            }
+        }
+        return CharSequences.EMPTY_ARRAY;
+    }
+}

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/VariableUCAR.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java?rev=1481889&view=auto
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java (added)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java [UTF-8] Mon May 13 14:44:15 2013
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.storage.netcdf;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.LogRecord;
+import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.logging.Logging;
+
+
+/**
+ * Base class of NetCDF classes which may produce warnings.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3 (derived from geotk-3.08)
+ * @version 0.3
+ * @module
+ */
+class WarningProducer {
+    /**
+     * Where to send the warnings, or {@code null} if none.
+     */
+    private final WarningProducer parent;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param parent Where to send the warnings, or {@code null} if none.
+     */
+    WarningProducer(final WarningProducer parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * Reports a warning represented by the given log record. The current implementation just logs the warning.
+     * However if we want to implement a listener mechanism in a future version, this could be done here.
+     *
+     * @param record The warning as a log record.
+     */
+    private void warning(final LogRecord record) {
+        if (parent != null) {
+            parent.warning(record);
+        } else {
+            final Logger logger = Logging.getLogger(WarningProducer.class);
+            record.setLoggerName(logger.getName());
+            logger.log(record);
+        }
+    }
+
+    /**
+     * Reports a warning represented by the given exception.
+     *
+     * @param methodName The name of the method in which the warning occurred.
+     * @param exception  The exception to log.
+     */
+    final void warning(final String methodName, final Exception exception) {
+        final LogRecord record = new LogRecord(Level.WARNING, Exceptions.formatChainedMessages(null, null, exception));
+        record.setSourceClassName(getClass().getCanonicalName());
+        record.setSourceMethodName(methodName);
+        warning(record);
+    }
+}

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/WarningProducer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message