Package org.geotools.coverage.io.netcdf

Source Code of org.geotools.coverage.io.netcdf.NetCDFReader

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2007-2014, 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.coverage.io.netcdf;

import it.geosolutions.imageio.utilities.ImageIOUtilities;
import it.geosolutions.imageio.utilities.SoftValueHashMap;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BandedSampleModel;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.media.jai.ImageLayout;

import org.geotools.coverage.grid.GeneralGridGeometry;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.InvalidGridGeometryException;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.DimensionDescriptor;
import org.geotools.coverage.grid.io.GranuleSource;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.HarvestedSource;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.coverage.io.CoverageAccess;
import org.geotools.coverage.io.CoverageAccess.AccessType;
import org.geotools.coverage.io.CoverageReadRequest;
import org.geotools.coverage.io.CoverageResponse;
import org.geotools.coverage.io.CoverageSource;
import org.geotools.coverage.io.CoverageSource.AdditionalDomain;
import org.geotools.coverage.io.CoverageSource.TemporalDomain;
import org.geotools.coverage.io.CoverageSource.VerticalDomain;
import org.geotools.coverage.io.Driver.DriverCapabilities;
import org.geotools.coverage.io.FileDriver;
import org.geotools.coverage.io.GridCoverageResponse;
import org.geotools.coverage.io.RasterLayout;
import org.geotools.coverage.io.catalog.CoverageSlicesCatalog;
import org.geotools.coverage.io.catalog.CoverageSlicesCatalogSource;
import org.geotools.coverage.io.util.DateRangeTreeSet;
import org.geotools.coverage.io.util.DoubleRangeTreeSet;
import org.geotools.data.DataSourceException;
import org.geotools.data.DataUtilities;
import org.geotools.factory.Hints;
import org.geotools.feature.NameImpl;
import org.geotools.gce.imagemosaic.ImageMosaicFormat;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.imageio.netcdf.NetCDFImageReader;
import org.geotools.imageio.netcdf.VariableAdapter;
import org.geotools.imageio.netcdf.VariableAdapter.UnidataSpatialDomain;
import org.geotools.referencing.operation.transform.IdentityTransform;
import org.geotools.referencing.operation.transform.ProjectiveTransform;
import org.geotools.resources.coverage.CoverageUtilities;
import org.geotools.util.DateRange;
import org.geotools.util.NumberRange;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.Coverage;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.ReferenceIdentifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransform2D;
import org.opengis.referencing.operation.TransformException;

/**
* A NetCDF Reader implementation
*
* @author Daniele Romagnoli, GeoSolutions SAS
*
*/
public class NetCDFReader extends AbstractGridCoverage2DReader implements StructuredGridCoverage2DReader {

    static final String UNSPECIFIED = "_UN$PECIFIED_";

    static final String DOMAIN_SUFFIX = "_DOMAIN";

    static final String HAS_PREFIX = "HAS_";

    private static final String MINIMUM_SUFFIX = "_MAXIMUM";

    private static final String MAXIMUM_SUFFIX = "_MINIMUM";

    private static final String DATATYPE_SUFFIX = "_DATATYPE";

    private final static Logger LOGGER = Logging
            .getLogger("org.geotools.coverage.io.netcdf.NetCDFReader");

    static FileDriver DRIVER = new NetCDFDriver();

    private CoverageAccess access = null;

    private List<Name> names = null;

    private Set<String> setNames = null;

    private URL sourceURL;

    String defaultName = null;

    private SoftValueHashMap<String, CoverageSource> coverages = new SoftValueHashMap<String, CoverageSource>();

    public NetCDFReader(Object input, Hints uHints) throws DataSourceException{
        super(input, uHints);
        sourceURL = checkSource(input);

        if (!DRIVER.canProcess(DriverCapabilities.CONNECT, sourceURL, null)) {
            throw new DataSourceException("unable to connect to the specified source " + sourceURL);
        }

        // getting access to the source
        try {
            access = (NetCDFAccess) DRIVER.process(DriverCapabilities.CONNECT, sourceURL, null, uHints, null);
        } catch (IOException e) {
            throw new DataSourceException("Unable to connect", e);
        }
        if (access == null) {
            throw new DataSourceException("Unable to connect");
        }

        LOGGER.info("ACCEPTED: " + source.toString());

        // get the names
        names = access.getNames(null);
        setNames = new HashSet<String>();
        for (Name name: names) {
            String nameString = name.toString();
            if (defaultName == null) {
                defaultName = nameString;
            }
            setNames.add(nameString);
        }
    }

    private URL checkSource(Object input) {
        URL sourceURL = null;
        if (input instanceof URL) {
            sourceURL = (URL) input;
        } else if (input instanceof File) {
            sourceURL = DataUtilities.fileToURL((File) input);
        }
        return sourceURL;
    }

    @Override
    public Format getFormat() {
        return new NetCDFFormat();
    }

    @Override
    public String[] getMetadataNames(String coverageName) {
        checkIsSupported(coverageName);
        final List<String> metadataNames = new ArrayList<String>();
       
        // standard metadata
        metadataNames.add(GridCoverage2DReader.HAS_TIME_DOMAIN);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_MINIMUM);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_MAXIMUM);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN_RESOLUTION);
        metadataNames.add(GridCoverage2DReader.TIME_DOMAIN + DATATYPE_SUFFIX);

        metadataNames.add(GridCoverage2DReader.HAS_ELEVATION_DOMAIN);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_MINIMUM);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_MAXIMUM);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN_RESOLUTION);
        metadataNames.add(GridCoverage2DReader.ELEVATION_DOMAIN + DATATYPE_SUFFIX);

        // additional domains
        addAdditionalMetadata(metadataNames, coverageName);
        return metadataNames.toArray(new String[metadataNames.size()]);
    }

    private void addAdditionalMetadata(List<String> metadataNames, String coverageName) {
        NetCDFSource source = null;
        try {
            source = (NetCDFSource) getGridCoverageSource(coverageName);
            List<AdditionalDomain> domains = source.getAdditionalDomains();
            if (domains != null && !domains.isEmpty()) {
                for (AdditionalDomain domain: domains) {
                    String domainName = domain.getName().toUpperCase();
                    metadataNames.add(HAS_PREFIX + domainName+ DOMAIN_SUFFIX);
                    metadataNames.add(domainName + DOMAIN_SUFFIX);
                    metadataNames.add(domainName + DOMAIN_SUFFIX + MINIMUM_SUFFIX);
                    metadataNames.add(domainName + DOMAIN_SUFFIX + MAXIMUM_SUFFIX);
                    metadataNames.add(domainName + DOMAIN_SUFFIX + DATATYPE_SUFFIX);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getMetadataValue(String coverageName, String name) {
        String value = null;
        NetCDFSource source = null;
        try {
            source = (NetCDFSource) getGridCoverageSource(coverageName);
            final TemporalDomain timeDomain = source.getTemporalDomain();
            final VerticalDomain verticalDomain = source.getVerticalDomain();
           
            final List<AdditionalDomain> additionalDomains = source.getAdditionalDomains();
            final boolean hasAdditionalDomains = additionalDomains != null && !additionalDomains.isEmpty();
            final boolean hasTimeDomain = timeDomain != null;
            final boolean hasElevationDomain = verticalDomain != null;

            if (name.equalsIgnoreCase(GridCoverage2DReader.HAS_ELEVATION_DOMAIN))
                return String.valueOf(hasElevationDomain);

            if (name.equalsIgnoreCase(GridCoverage2DReader.HAS_TIME_DOMAIN)) {
                return String.valueOf(hasTimeDomain);
            }

            // NOT supported
            if (name.equalsIgnoreCase(GridCoverage2DReader.TIME_DOMAIN_RESOLUTION)) {
                return null;
            }
            // NOT supported
            if (name.equalsIgnoreCase(GridCoverage2DReader.ELEVATION_DOMAIN_RESOLUTION)) {
                return null;
            }

            if (hasTimeDomain) {
                if (name.equalsIgnoreCase("time_domain")) {
                    return parseDomain(name, timeDomain);
                }
                if ((name.equalsIgnoreCase("time_domain_minimum") || name
                        .equalsIgnoreCase("time_domain_maximum"))) {
                    return parseDomain(name, timeDomain);
                }
                if (name.equalsIgnoreCase("time_domain_datatype")) {
                    return parseDomain(name, timeDomain);
                }
            }

            if (hasElevationDomain) {
                if (name.equalsIgnoreCase("elevation_domain")) {
                    return parseDomain(name, verticalDomain);
                }

                if (name.equalsIgnoreCase("elevation_domain_minimum")
                        || name.equalsIgnoreCase("elevation_domain_maximum")) {
                    return parseDomain(name, verticalDomain);
                }
                if (name.equalsIgnoreCase("elevation_domain_datatype")) {
                    return parseDomain(name, verticalDomain);
                }
            }
           
            if (hasAdditionalDomains) {
                if (name.startsWith(HAS_PREFIX)) {
                    final String substring = name.substring(HAS_PREFIX.length(), name.length() - DOMAIN_SUFFIX.length());
                    for (AdditionalDomain additionalDomain: additionalDomains) {
                        if (additionalDomain.getName().toUpperCase().contains(substring)) {
                            return "true";
                        }
                    }
                    return "false";
                   
                } else if (name.contains(DOMAIN_SUFFIX)) {
                    for (AdditionalDomain additionalDomain: additionalDomains) {
                        if (name.toUpperCase().startsWith(additionalDomain.getName().toUpperCase() + "_DOMAIN")) {
                            return parseDomain(name, additionalDomain);
                        }
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //
        return value;
    }

    @Override
    public String[] getGridCoverageNames() {
        return (String[]) setNames.toArray(new String[setNames.size()]);
    }

    @Override
    public Set<ParameterDescriptor<List>> getDynamicParameters(String coverageName)
            throws IOException {
        NetCDFSource source = (NetCDFSource) getGridCoverageSource(coverageName);
        if (source != null) {
            return source.getDynamicParameters();
        }
        return null;
    }

    @Override
    public int getGridCoverageCount() {
        return setNames.size();
    }

    /**
     * Parse a domain
     * @param name
     * @param domain
     * @return
     * @throws IOException
     */
    private String parseDomain(String name, Object domain) throws IOException {
        name = name.toLowerCase();
        if (domain instanceof VerticalDomain) {
           
            // Vertical domain management
            VerticalDomain verticalDomain = (VerticalDomain) domain;
            if (name.endsWith("domain")) {
               
                // global domain
                SortedSet<? extends NumberRange<Double>> verticalElements = verticalDomain.getVerticalElements(true, null);
                return buildVerticalList(verticalElements);
            } else if (name.endsWith("datatype")) {
                return Double.class.getName();
            } else {
                // min or max requests
                SortedSet<? extends NumberRange<Double>> verticalElements = verticalDomain.getVerticalElements(false, null);
                NumberRange<Double> overall = verticalElements.iterator().next();
                if (name.endsWith("maximum")) {
                    return Double.toString(overall.getMaximum());
                } else if (name.endsWith("minimum")) {
                    return Double.toString(overall.getMinimum());
                } else {
                    throw new IllegalArgumentException("Unsupported metadata name");
                }
            }
        } else if (domain instanceof TemporalDomain) {
           
            // Temporal domain management
            TemporalDomain temporalDomain = (TemporalDomain) domain;
            if (name.endsWith("domain")) {
                // global domain
                SortedSet<? extends DateRange> temporalElements = temporalDomain.getTemporalElements(true, null);
                return buildTemporalList(temporalElements);
            } else if (name.endsWith("datatype")) {
                return Date.class.getName();
            } else {
                SortedSet<? extends DateRange> temporalElements = temporalDomain.getTemporalElements(false, null);
                DateRange overall = temporalElements.iterator().next();
                // min or max requests
                if (name.endsWith("maximum")) {
                    return ConvertersHack.convert(overall.getMaxValue(), String.class);
                } else if (name.endsWith("minimum")) {
                    return ConvertersHack.convert(overall.getMinValue(), String.class);
                } else {
                    throw new IllegalArgumentException("Unsupported metadata name");
                }
            }
        } else if (domain instanceof AdditionalDomain) {
           
            // Vertical domain management
            AdditionalDomain additionalDomain = (AdditionalDomain) domain;
            if (name.endsWith("domain")) {
                Set<Object> elements = additionalDomain.getElements(false, null);
                return buildElementsList(elements);
            } else {
                // min or max requests
                Set<Object> elements = additionalDomain.getElements(true, null);
                NumberRange<Double> range = (NumberRange<Double>) elements.iterator().next();
                if (name.endsWith("maximum")) {
                    return Double.toString(range.getMaximum());
                } else if (name.endsWith("minimum")) {
                    return Double.toString(range.getMinimum());
                } else {
                    throw new IllegalArgumentException("Unsupported metadata name");
                }
            }
        } else {
            throw new IllegalArgumentException("Unsupported domain ");
        }
    }

    private String buildTemporalList(SortedSet<? extends DateRange> temporalElements) {
        Iterator<DateRange> iterator = (Iterator<DateRange>) temporalElements.iterator();
//        LinkedHashSet<String> result = new LinkedHashSet<String>();

        final StringBuilder buff = new StringBuilder("");
        while (iterator.hasNext()) {
            DateRange range = iterator.next();
            buff.append(ConvertersHack.convert(range.getMinValue(), String.class) + "/" + ConvertersHack.convert(range.getMaxValue(), String.class));
            if (iterator.hasNext()) {
                buff.append(",");
            }
        }
        return buff.toString();
    }

    /**
     * Setup a String containing vertical domain by doing a scan of a set of vertical Elements
     * @param verticalElements
     * @return
     */
    private String buildVerticalList(SortedSet<? extends NumberRange<Double>> verticalElements) {
        Iterator<NumberRange<Double>> iterator = (Iterator<NumberRange<Double>>) verticalElements.iterator();
        LinkedHashSet<String> ranges = new LinkedHashSet<String>();

        while (iterator.hasNext()) {
            NumberRange<Double> range = iterator.next();
            ranges.add((range.getMinValue() + "/" + range.getMaxValue()));
        }
        return buildResultsString(ranges);
    }

    /**
     * Setup a String containing additional domain by doing a scan of a set of Elements
     * @param elements
     * @return
     * TODO: improve that to deal with multiple types
     */
    private String buildElementsList(Set<Object> elements) {
        Iterator<Object> iterator = (Iterator<Object>) elements.iterator();
        LinkedHashSet<String> ranges = new LinkedHashSet<String>();

        while (iterator.hasNext()) {
            Number value = (Number) iterator.next();
            ranges.add(value.toString());
        }
        return buildResultsString(ranges);
    }

   
    @Override
    public GridCoverage2D read(String coverageName, GeneralParameterValue[] parameters)
            throws IllegalArgumentException, IOException {
        final CoverageSource gridSource = getGridCoverageSource(coverageName);
        final CoverageReadRequest request = setupCoverageRequest(parameters, gridSource);
        final CoverageResponse result = gridSource.read(request, null);
        Collection<? extends Coverage> results = result.getResults(null);
        if (results == null || results.isEmpty()) {
            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine("No results have been found");
            }
            return null;
        }
        GridCoverageResponse resp = (GridCoverageResponse) results.iterator().next();
        return resp.getGridCoverage2D();
    }

    /**
     * Create the coverageReadRequest on top of the specified read params
     * @param params
     * @return
     * @throws IOException
     */
    private CoverageReadRequest setupCoverageRequest(GeneralParameterValue[] params, final CoverageSource gridSource) throws IOException {
        CoverageReadRequest request = new CoverageReadRequest();
        if (params != null) {
            for (GeneralParameterValue gParam : params) {
                if (gParam instanceof ParameterValue<?>) {
                    final ParameterValue<?> param = (ParameterValue<?>) gParam;
                    final ReferenceIdentifier name = param.getDescriptor().getName();
                    try {
                        extractParameter(param, name, request, gridSource);
                    } catch (Exception e) {
                        throw new IOException(e);
                    }
                }
            }
        }
        return request;
    }

    private void extractParameter(ParameterValue<?> param, ReferenceIdentifier name,
            CoverageReadRequest request, CoverageSource gridSource) throws MismatchedDimensionException,
            InvalidGridGeometryException, TransformException, IOException {

        // //
        //
        // Requested GridGeometry2D parameter
        //
        // //
        if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
            final Object value = param.getValue();
            if (value == null)
                return;
            final GridGeometry2D gg = (GridGeometry2D) value;
            request.setDomainSubset(gg.getGridRange2D(), gg.getGridToCRS2D(),
                    gg.getCoordinateReferenceSystem());
            return;
        }

        // //
        //
        // Time parameter
        //
        // //
        if (name.equals(ImageMosaicFormat.TIME.getName())) {
            final Object value = param.getValue();
            if (value == null) {
                return;
            }
            final List<?> dates = (List<?>) value;
            if (dates != null && !dates.isEmpty()) {
                SortedSet<DateRange> requestedTemporalSubset = new DateRangeTreeSet();
                for (Object val : dates) {
                    if (val instanceof Date) {
                        requestedTemporalSubset.add(new DateRange((Date) val, (Date) val));
                    } else if (val instanceof DateRange) {
                        requestedTemporalSubset.add((DateRange)val);
                    }
                }
               
                // TODO IMPROVE THAT TO DEAL ON RANGES
                request.setTemporalSubset(requestedTemporalSubset);
            }
            return;

        }

        //
        //
        // Elevation parameter
        //
        // //
        if (name.equals(ImageMosaicFormat.ELEVATION.getName())) {
            final Object value = param.getValue();
            if (value == null)
                return;
            List<?> values = (List<?>) value;
            if (values != null && !values.isEmpty()) {
                Set<NumberRange<Double>> verticalSubset = new DoubleRangeTreeSet();
                for (Object val : values) {
                    if (val instanceof Number) {
                        verticalSubset.add(new NumberRange<Double>(Double.class, ((Number) val).doubleValue(), ((Number) val).doubleValue()));
                    } else if (val instanceof NumberRange) {
                        verticalSubset.add((NumberRange<Double>)val);
                    }
                }
                // TODO IMPROVE THAT TO DEAL ON RANGES
                request.setVerticalSubset(verticalSubset);
            }
            return;
        }

        if (name.equals(ImageMosaicFormat.FILTER.getName())) {
            final Object value = param.getValue();
            if (value == null)
                return;
            request.setFilter((Filter) value);
            return;
        }

        String paramName = name.getCode();
        if (((NetCDFSource) gridSource).isParameterSupported(name)) {
            final Object value = param.getValue();
            if (value == null){
                return;
            }
            final Set values= new HashSet();
            if (value instanceof Collection) {
                values.addAll((Collection)value); // we are assuming it is a list !!!

            } else {
                values.add(value);
            }
            // remove last comma
            Map<String, Set<?>> domainsSubset = request.getAdditionalDomainsSubset();
            if (domainsSubset == null) {
                domainsSubset = new HashMap<String, Set<?>>();
                request.setAdditionalDomainsSubset(domainsSubset);
            }
            domainsSubset.put(paramName, values);           
            return;
        }
    }

    /**
     * Return a {@link CoverageSource} related to the specified coverageName
     * @param coverageName
     * @return
     * @throws IOException
     */
    private CoverageSource getGridCoverageSource(final String coverageName) throws IOException {
        // Preliminar check on name availability
        checkIsSupported(coverageName);
        synchronized (coverages) {
            if (coverages.containsKey(coverageName)) {
                return coverages.get(coverageName);
            }

            // create, cache and return
            CoverageSource source = access.access(new NameImpl(coverageName), null,
                    AccessType.READ_ONLY, null, null);
            coverages.put(coverageName, source);
            return source;
        }
    }

    /**
     * Check whether the specified coverageName is one of the coverage available for the reader
     * @param coverageName
     */
    private void checkIsSupported(final String coverageName) {
        if (!setNames.contains(coverageName)) {
            throw new IllegalArgumentException("the specified coverage is not available: "
                    + coverageName);
        }
    }

    /**
     * Read a GridCoverage2D base on the specified read parameters.
     */
    @Override
    public GridCoverage2D read(GeneralParameterValue[] parameters) throws IllegalArgumentException,
            IOException {
        if (!names.isEmpty()) {
            if (names.size() > 1) {
                throw new IllegalArgumentException("You need to specify a coverageName");
            } else {
                return read(names.get(0).toString(), parameters);
            }
        }
        return null;
    }

    @Override
    public void dispose() {
        super.dispose();
        synchronized (this) {
            if (coverages != null && !coverages.isEmpty()) {
                Iterator<String> keysIt = coverages.keySet().iterator();
                while (keysIt.hasNext()) {
                    String key = keysIt.next();
                    CoverageSource sourceCov = coverages.get(key);
                    sourceCov.dispose();
                }
            }
            coverages.clear();
            coverages = null;
        }
        if (access != null) {
            try {
                access.dispose();
            } catch (Throwable t) {
                if (LOGGER.isLoggable(Level.FINE)) {
                    LOGGER.log(Level.FINE, t.getLocalizedMessage(), t);
                }
            }
        }
    }

    /**
     * Build a String containing comma separated values from the result set
     * @param result
     * @return
     */
    private String buildResultsString(Set<String> result) {
        if (result.size() <= 0) {
            return "";
        }

        final StringBuilder buff = new StringBuilder();
        for (Iterator<String> it = result.iterator(); it.hasNext();) {
            buff.append(ConvertersHack.convert(it.next(), String.class));
            if (it.hasNext()) {
                buff.append(",");
            }
        }
        return buff.toString();
    }

    private String checkUnspecifiedCoverage(String coverageName) {
        if (coverageName.equalsIgnoreCase(UNSPECIFIED)) {
            if (getGridCoverageCount() > 1) {
                throw new IllegalArgumentException(
                        "Need to specify the coverageName for a reader related to multiple coverages");
            } else {
                return defaultName;
            }
        } else {
            if (!setNames.contains(coverageName)) {
                throw new IllegalArgumentException("The specified coverageName is unavailable");
            } else {
                return coverageName;
            }
        }
    }

    @Override
    public GeneralEnvelope getOriginalEnvelope() {
        return getOriginalEnvelope(UNSPECIFIED);
    }

    @Override
    public GeneralEnvelope getOriginalEnvelope(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            CoverageSource source = getGridCoverageSource(coverageName);
            VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
            GeneralEnvelope generalEnvelope = new GeneralEnvelope(spatialDomain.getReferencedEnvelope());
            generalEnvelope.setCoordinateReferenceSystem(spatialDomain.getCoordinateReferenceSystem2D());
            return generalEnvelope;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public GridEnvelope getOriginalGridRange() {
        return getOriginalGridRange(UNSPECIFIED);
    }

    @Override
    public GridEnvelope getOriginalGridRange(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            final CoverageSource source = getGridCoverageSource(coverageName);
            VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
            return spatialDomain.getGridGeometry().getGridRange2D();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public double[] getReadingResolutions(OverviewPolicy policy,
            double[] requestedResolution) {
        try {
            return getReadingResolutions(UNSPECIFIED, policy, requestedResolution);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }
   
    @Override
    public double[] getReadingResolutions(String coverageName, OverviewPolicy policy,
            double[] requestedResolution) throws IOException {
            // Currently we have no overviews support so we will return the highest resolution
        coverageName = checkUnspecifiedCoverage(coverageName);
        final CoverageSource source = getGridCoverageSource(coverageName);
        VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
        GeneralGridGeometry gridGeometry2D = spatialDomain.getGridGeometry();
        AffineTransform gridToCRS = (AffineTransform) gridGeometry2D.getGridToCRS();
        return CoverageUtilities.getResolution(gridToCRS);
    }

    @Override
    public int getNumOverviews() {
        return getNumOverviews(UNSPECIFIED);
       
    }
   
    @Override
    public int getNumOverviews(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            final CoverageSource source = getGridCoverageSource(coverageName);
            // Make sure that coverageName exists
            return 0;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public double[][] getResolutionLevels() throws IOException {
        return getResolutionLevels(UNSPECIFIED);
    }

    @Override
    public double[][] getResolutionLevels(String coverageName) throws IOException {
        coverageName = checkUnspecifiedCoverage(coverageName);
        double[][] res = new double[1][2];
        double[] readRes = getReadingResolutions(coverageName, null, null);
        res[0][0] = readRes[0];
        res[0][1] = readRes[1];
        return res;
    }

    @Override
    public ImageLayout getImageLayout() throws IOException {
        return getImageLayout(UNSPECIFIED);
    }

    @Override
    public ImageLayout getImageLayout(String coverageName) throws IOException {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            final CoverageSource source = getGridCoverageSource(coverageName);
            VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
            GridEnvelope2D gridRange = spatialDomain.getGridGeometry().getGridRange2D();
            RasterLayout rasterElement = spatialDomain.getRasterElements(false, null).iterator().next();
            SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_DOUBLE, (int)gridRange.getWidth(), (int)gridRange.getHeight(), 1);
            ColorModel colorModel = ImageIOUtilities.createColorModel(sampleModel);
            Rectangle rect = rasterElement.toRectangle();
            ImageLayout layout = new ImageLayout(rect.x, rect.y, rect.width, rect.height);
            layout.setSampleModel(sampleModel);
            layout.setColorModel(colorModel);
            return layout;
           
            //TODO: DR: need to define a real ImageLayout. We may consider adding ImageLayout to RasterLayout
           
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
       
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        return getCoordinateReferenceSystem(UNSPECIFIED);
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem(String coverageName) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            final CoverageSource source = getGridCoverageSource(coverageName);
            VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
            return spatialDomain.getCoordinateReferenceSystem2D();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public MathTransform getOriginalGridToWorld(PixelInCell pixInCell) {
        return getOriginalGridToWorld(UNSPECIFIED, pixInCell);
    }
   
    @Override
    public MathTransform getOriginalGridToWorld(String coverageName, PixelInCell pixInCell) {
        coverageName = checkUnspecifiedCoverage(coverageName);
        try {
            final CoverageSource source = getGridCoverageSource(coverageName);
            VariableAdapter.UnidataSpatialDomain spatialDomain = (UnidataSpatialDomain) source.getSpatialDomain();
            MathTransform2D gridToWorld = spatialDomain.getGridToWorldTransform(null);
            if (pixInCell == PixelInCell.CELL_CENTER) {
                return gridToWorld;
            }

            // we do have to change the pixel datum
            if (gridToWorld instanceof AffineTransform) {
                final AffineTransform tr = new AffineTransform((AffineTransform) gridToWorld);
                tr.concatenate(AffineTransform.getTranslateInstance(-0.5, -0.5));
                return ProjectiveTransform.create(tr);
            }

            if(gridToWorld instanceof IdentityTransform){
                final AffineTransform tr= new AffineTransform(1,0,0,1,0,0);
                tr.concatenate(AffineTransform.getTranslateInstance(-0.5,-0.5));
                return ProjectiveTransform.create(tr);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        throw new IllegalStateException("This reader's grid to world transform is invalid!");
    }

    @Override
    public GranuleSource getGranules(String coverageName, boolean readOnly) throws IOException,
            UnsupportedOperationException {
        NetCDFImageReader reader = (NetCDFImageReader) ((NetCDFAccess)access).reader;
        final CoverageSlicesCatalog catalog = reader.getCatalog();
        return new CoverageSlicesCatalogSource(catalog, coverageName);
    }
   
    @Override
    public boolean isReadOnly() {
        return true;
    }

    @Override
    public void createCoverage(String coverageName, SimpleFeatureType schema/*,
            Set<DimensionDescriptor> dimensions*/) throws IOException, UnsupportedOperationException {
        throw new UnsupportedOperationException("This operation is not supported on this reader");
    }

    @Override
    public List<HarvestedSource> harvest(String defaultCoverage, Object source, Hints hints) throws IOException, UnsupportedOperationException {
        throw new UnsupportedOperationException("This operation is not supported on this reader");
    }

    @Override
    public List<DimensionDescriptor> getDimensionDescriptors(String coverageName) throws IOException {
        final CoverageSource source = (CoverageSource) getGridCoverageSource(coverageName);
        return source.getDimensionDescriptors();
    }

    @Override
    public boolean removeCoverage(String coverageName, boolean forceDelete) throws IOException,
            UnsupportedOperationException {
        if (setNames.contains(coverageName)) {
            setNames.remove(coverageName);
        }
        Name name = new NameImpl(coverageName);
        if (names.contains(name)) {
            access.delete(name, null, hints);
        } else {
            return false;
        }
        if (defaultName == coverageName) {
            Iterator<Name> iterator = names.iterator();
            if (iterator.hasNext()) {
                defaultName = iterator.next().toString();
            } else {
                defaultName = null;
            }
        }
        if (forceDelete) {
            delete(true);
        }
        return true;
    }

    @Override
    public void delete(boolean deleteData) throws IOException {
        ((NetCDFAccess) access).purge();
    }

    @Override
    public boolean removeCoverage(String coverageName) throws IOException, UnsupportedOperationException {
        return removeCoverage(coverageName, false);
    }
}
TOP

Related Classes of org.geotools.coverage.io.netcdf.NetCDFReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.