You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2013/05/13 16:44:15 UTC
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/
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