/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.imageio.netcdf; import it.geosolutions.imageio.ndplugin.BaseImageMetadata; import it.geosolutions.imageio.plugins.netcdf.NetCDFImageReader; import it.geosolutions.imageio.plugins.netcdf.NetCDFUtilities; import it.geosolutions.imageio.plugins.netcdf.NetCDFUtilities.CheckType; import it.geosolutions.imageio.utilities.SoftValueHashMap; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageReadParam; import javax.imageio.ImageTypeSpecifier; import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageReaderSpi; import org.geotools.imageio.DefaultSliceDescriptor; import org.geotools.imageio.SliceDescriptor; import org.geotools.imageio.SpatioTemporalImageReader; import org.geotools.imageio.metadata.SpatioTemporalMetadata; import ucar.ma2.InvalidRangeException; import ucar.ma2.Range; import ucar.nc2.Variable; import ucar.nc2.dataset.CoordinateAxis1D; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.dataset.VariableDS; /** * Class implementing a {@link SpatioTemporalImageReader} to handle NetCDF-CF * data source. * * @author Daniele Romagnoli, GeoSolutions * * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/coverage-experiment/netcdf/src/main/java/org/geotools/imageio/netcdf/NetCDFSpatioTemporalImageReader.java $ */ public class NetCDFSpatioTemporalImageReader extends SpatioTemporalImageReader { private final static Logger LOGGER = Logger .getLogger("org.geotools.imageio.netcdf"); private SoftValueHashMap<Integer, IIOMetadata> metadataMap = new SoftValueHashMap<Integer, IIOMetadata>(); /** Inner map to cache the SpatioTemporal metadata instances */ private SoftValueHashMap<Integer, SpatioTemporalMetadata> spatioTemporalMetadataMap = new SoftValueHashMap<Integer, SpatioTemporalMetadata>(); /** The underlying direct imageio reader to be used by this spatiotemporal one */ private NetCDFImageReader directReader; /** An inner map useful to associate NetCDF Variable with imageIndex */ private Map<Range, Variable> variableMap; /** An inner map useful to associate NetCDF Variable with imageIndex */ private Map<String, Variable> coordinatesMap; private NetcdfDataset dataset; protected NetCDFSpatioTemporalImageReader(ImageReaderSpi originatingProvider) { super(originatingProvider); directReader = new NetCDFImageReader(originatingProvider); } @Override public int getHeight(int imageIndex) throws IOException { return directReader.getHeight(imageIndex); } @Override public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException { return directReader.getImageTypes(imageIndex); } @Override public int getNumImages(boolean allowSearch) throws IOException { return directReader.getNumImages(allowSearch); } @Override public IIOMetadata getStreamMetadata() throws IOException { return directReader.getStreamMetadata(); } @Override public int getWidth(int imageIndex) throws IOException { return directReader.getWidth(imageIndex); } @Override public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { return directReader.read(imageIndex, param); } public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { try { super.setInput(input, seekForwardOnly, ignoreMetadata); dataset = NetCDFUtilities.getDataset(input); if (dataset == null) throw new IllegalArgumentException("Error occurred during NetCDF file parsing: obtained a null netcdf dataset"); int numImages = 0; final CheckType checkType = NetCDFUtilities.getCheckType(dataset); variableMap = new HashMap<Range, Variable>(); coordinatesMap = new HashMap<String, Variable>(); if (dataset != null) { final List<Variable> variables = dataset.getVariables(); if (variables != null) { for (final Variable variable : variables) { if (variable != null && variable instanceof VariableDS) { String varName = variable.getName(); if (variable.isCoordinateVariable()) coordinatesMap.put(varName, variable); if (variable instanceof CoordinateAxis1D){ //Due to a netCDF library bug, coordinates values need to //be read to be properly obtained afterwards. //Otherwise it may return NaN in some coords. if (varName.equalsIgnoreCase(NetCDFUtilities.LAT)|| varName.equalsIgnoreCase(NetCDFUtilities.LATITUDE)|| varName.equalsIgnoreCase(NetCDFUtilities.LON)|| varName.equalsIgnoreCase(NetCDFUtilities.LONGITUDE)){ variable.read(); } } if (!NetCDFUtilities.isVariableAccepted(variable, checkType)) continue; int[] shape = variable.getShape(); switch (shape.length) { case 2: variableMap.put(new Range(numImages, numImages + 1), variable); numImages++; break; case 3: variableMap.put(new Range(numImages, numImages + shape[0]), variable); numImages += shape[0]; break; case 4: variableMap.put(new Range(numImages, numImages + shape[0] * shape[1]), variable); numImages += shape[0] * shape[1]; break; } } } } } } catch (IOException e) { throw new IllegalArgumentException( "Error occurred during NetCDF file parsing", e); } catch (InvalidRangeException e) { throw new IllegalArgumentException( "Error occurred during NetCDF file parsing", e); } directReader.setInput(input, seekForwardOnly, ignoreMetadata); } @Override public void setInput(Object input, boolean seekForwardOnly) { this.setInput(input, seekForwardOnly, true); } @Override public void setInput(Object input) { this.setInput(input, true, true); } @Override public void dispose() { directReader.dispose(); directReader = null; metadataMap.clear(); metadataMap = null; variableMap.clear(); variableMap = null; coordinatesMap.clear(); coordinatesMap = null; spatioTemporalMetadataMap.clear(); spatioTemporalMetadataMap = null; try { dataset.close(); } catch (IOException e) { if (LOGGER.isLoggable(Level.FINE)) LOGGER.log(Level.FINE, "Error closing the netcdf dataset",e); } } @Override public void reset() { this.dispose(); } /** * Returns a {@link SliceDescriptor} instance for the specified imageIndex. * * @param imageIndex * the index of the specified 2D raster. * @see SpatioTemporalImageReader#getSliceDescriptor(int) */ public SliceDescriptor getSliceDescriptor(int imageIndex) throws IOException { return new DefaultSliceDescriptor(getSpatioTemporalMetadata(imageIndex)); } /** * Returns the ImageMetadata obtained from the underlying flatReader * * @param imageIndex * the index of the specified 2D raster. */ public IIOMetadata getImageMetadata(int imageIndex) throws IOException { IIOMetadata metadata; synchronized (metadataMap) { if (!metadataMap.containsKey(imageIndex)) { metadata = directReader.getImageMetadata(imageIndex); metadataMap.put(imageIndex, metadata); } else { metadata = (BaseImageMetadata) metadataMap.get(imageIndex); if (metadata == null) { metadata = directReader.getImageMetadata(imageIndex); metadataMap.put(imageIndex, metadata); } } } return metadata; } /** * Returns a {@link SpatioTemporalMetadata} instance for the specified * imageIndex. * * @param imageIndex * the index of the specified 2D raster. * @see SpatioTemporalImageReader#getSpatioTemporalMetadata(int) */ public SpatioTemporalMetadata getSpatioTemporalMetadata(int imageIndex) { SpatioTemporalMetadata metadata; synchronized (spatioTemporalMetadataMap) { if (!spatioTemporalMetadataMap.containsKey(imageIndex)) { metadata = new NetCDFSpatioTemporalMetadata(this, imageIndex); spatioTemporalMetadataMap.put(imageIndex, metadata); } else { metadata = spatioTemporalMetadataMap.get(imageIndex); if (metadata == null) { metadata = new NetCDFSpatioTemporalMetadata(this, imageIndex); spatioTemporalMetadataMap.put(imageIndex, metadata); } } } return metadata; } // ///////////////////////////////////////////////////////////////////////// // // Set of Package private methods to ease the access to NetCDF variables and // Ranges without needs to re-create a mapping. They will be used by the // metadata class. // // ///////////////////////////////////////////////////////////////////////// /** * Package private method simply returning the netCDF Variable related to * the specified imageIndex. * * @param imageIndex * @return the requested Variable. */ Variable getVariable(final int imageIndex) { Variable var = null; Range rangeFound = getRange(imageIndex); if (rangeFound != null) var = variableMap.get(rangeFound); return var; } /** * Package private method simply returning the netCDF Variable related to * the specified Range. * * @param range * @return the requested Variable. */ Variable getVariable(final Range range) { Variable var = null; if (range != null && variableMap.containsKey(range)) var = variableMap.get(range); return var; } /** * Package private method simply returning the range containing the * specified imageIndex. * * @param imageIndex * @return the requested Range. */ Range getRange(int imageIndex) { Range rangeFound = null; for (Range range : variableMap.keySet()) { if (range.contains(imageIndex) && range.first() <= imageIndex && imageIndex < range.last()) { rangeFound = range; break; } } return rangeFound; } Variable getCoordinate(final String coordName){ if (coordName!=null && coordName.length()>0 && coordinatesMap.containsKey(coordName)) return coordinatesMap.get(coordName); return null; } }