org.geoserver.catalog.CoverageViewReader.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.catalog.CoverageViewReader.java

Source

/* (c) 2014-2015 Open Source Geospatial Foundation - all rights reserved
 * (c) 2014 OpenPlans
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.catalog;

import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.SampleModel;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.RasterFactory;

import org.apache.commons.lang.ArrayUtils;
import org.geoserver.catalog.CoverageView.CoverageBand;
import org.geoserver.catalog.CoverageView.InputCoverageBand;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridCoverageFactory;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.GridCoverage2DReader;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.grid.io.StructuredGridCoverage2DReader;
import org.geotools.coverage.processing.CoverageProcessor;
import org.geotools.data.DataSourceException;
import org.geotools.data.ResourceInfo;
import org.geotools.data.ServiceInfo;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.parameter.DefaultParameterDescriptorGroup;
import org.geotools.parameter.ParameterGroup;
import org.geotools.parameter.Parameters;
import org.geotools.referencing.CRS;
import it.geosolutions.jaiext.utilities.ImageLayout2;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.filter.FilterFactory2;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;

import it.geosolutions.imageio.maskband.DatasetLayout;
import it.geosolutions.imageio.utilities.ImageIOUtilities;

/**
 * A {@link CoverageView} reader which takes care of doing underlying coverage read operations and recompositions.
 * 
 * @author Daniele Romagnoli, GeoSolutions SAS
 * 
 */
public class CoverageViewReader implements GridCoverage2DReader {

    public final static FilterFactory2 FF = CommonFactoryFinder.getFilterFactory2();

    private static final CoverageProcessor PROCESSOR = CoverageProcessor.getInstance();

    /**
     * A CoveragesConsistencyChecker checks if the composing coverages respect the constraints which currently are:
     * <UL>
     * <LI>same CRS</LI>
     * <LI>same resolution</LI>
     * <LI>same bbox</LI>
     * <LI>same data type</LI>
     * <LI>same dimensions (same number of dimension, same type, and same name)</LI>
     * </UL>
     */
    static class CoveragesConsistencyChecker {

        private static double DELTA = 1E-10;

        private Set<ParameterDescriptor<List>> dynamicParameters;

        private String[] metadataNames;

        private GridEnvelope gridRange;

        private GeneralEnvelope envelope;

        private CoordinateReferenceSystem crs;

        private ImageLayout layout;

        public CoveragesConsistencyChecker(GridCoverage2DReader reader) throws IOException {
            envelope = reader.getOriginalEnvelope();
            gridRange = reader.getOriginalGridRange();
            crs = reader.getCoordinateReferenceSystem();
            metadataNames = reader.getMetadataNames();
            dynamicParameters = reader.getDynamicParameters();
            layout = reader.getImageLayout();
        }

        /**
         * Check whether the coverages associated to the provided reader is consistent with the reference coverage.
         * 
         * @param reader
         * @throws IOException
         */
        public void checkConsistency(GridCoverage2DReader reader) throws IOException {
            GeneralEnvelope envelope = reader.getOriginalEnvelope();
            GridEnvelope gridRange = reader.getOriginalGridRange();
            CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem();
            String[] metadataNames = reader.getMetadataNames();
            Set<ParameterDescriptor<List>> dynamicParameters = reader.getDynamicParameters();

            // Checking envelope equality
            if (!envelope.equals(this.envelope, DELTA, true)) {
                throw new IllegalArgumentException("The coverage envelope must be the same");
            }

            // Checking gridRange equality
            final Rectangle thisRectangle = new Rectangle(this.gridRange.getLow(0), this.gridRange.getLow(1),
                    this.gridRange.getSpan(0), this.gridRange.getSpan(1));
            final Rectangle thatRectangle = new Rectangle(gridRange.getLow(0), gridRange.getLow(1),
                    gridRange.getSpan(0), gridRange.getSpan(1));
            if (!thisRectangle.equals(thatRectangle)) {
                throw new IllegalArgumentException("The coverage gridRange should be the same");
            }

            // Checking dimensions
            if (metadataNames.length != this.metadataNames.length) {
                throw new IllegalArgumentException("The coverage metadataNames should have the same size");
            }

            final Set<String> metadataSet = new HashSet<String>(Arrays.asList(metadataNames));
            for (String metadataName : this.metadataNames) {
                if (!metadataSet.contains(metadataName)) {
                    throw new IllegalArgumentException("The coverage metadata are different");
                }
            }

            // TODO: Add check for dynamic parameters

            // Checking CRS
            MathTransform destinationToSourceTransform = null;
            if (!CRS.equalsIgnoreMetadata(crs, this.crs)) {
                try {
                    destinationToSourceTransform = CRS.findMathTransform(crs, this.crs, true);
                } catch (FactoryException e) {
                    throw new DataSourceException("Unable to inspect request CRS", e);
                }
            }

            // now transform the requested envelope to source crs
            if (destinationToSourceTransform != null && !destinationToSourceTransform.isIdentity()) {
                throw new IllegalArgumentException("The coverage coordinateReferenceSystem should be the same");
            }

            // Checking data type
            if (layout.getSampleModel(null).getDataType() != this.layout.getSampleModel(null).getDataType()) {
                throw new IllegalArgumentException("The coverage dataType should be the same");
            }

        }
    }

    /** The CoverageView containing definition */
    CoverageView coverageView;

    /** The name of the reference coverage, we can remove/revisit it once we relax some constraint */
    String referenceName;

    private String coverageName;

    private GridCoverage2DReader delegate;

    private Hints hints;

    /** The CoverageInfo associated to the CoverageView */
    private CoverageInfo coverageInfo;

    private GridCoverageFactory coverageFactory;

    private ImageLayout imageLayout;

    public CoverageViewReader(GridCoverage2DReader delegate, CoverageView coverageView, CoverageInfo coverageInfo,
            Hints hints) {
        this.coverageName = coverageView.getName();
        this.delegate = delegate;
        this.coverageView = coverageView;
        this.coverageInfo = coverageInfo;
        this.hints = hints;
        // Refactor this once supporting heterogeneous elements
        referenceName = coverageView.getBand(0).getInputCoverageBands().get(0).getCoverageName();
        if (this.hints != null && this.hints.containsKey(Hints.GRID_COVERAGE_FACTORY)) {
            final Object factory = this.hints.get(Hints.GRID_COVERAGE_FACTORY);
            if (factory != null && factory instanceof GridCoverageFactory) {
                this.coverageFactory = (GridCoverageFactory) factory;
            }
        }
        if (this.coverageFactory == null) {
            this.coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints);
        }
        ImageLayout layout;
        try {
            layout = delegate.getImageLayout(referenceName);
            List<CoverageBand> bands = coverageView.getCoverageBands();
            SampleModel originalSampleModel = layout.getSampleModel(null);
            SampleModel sampleModel = RasterFactory.createBandedSampleModel(originalSampleModel.getDataType(),
                    originalSampleModel.getWidth(), originalSampleModel.getHeight(), bands.size());

            ColorModel colorModel = ImageIOUtilities.createColorModel(sampleModel);
            this.imageLayout = new ImageLayout2(layout.getMinX(null), layout.getMinY(null),
                    originalSampleModel.getWidth(), originalSampleModel.getHeight());
            imageLayout.setSampleModel(sampleModel);
            imageLayout.setColorModel(colorModel);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public GridCoverage2D read(GeneralParameterValue[] parameters) throws IllegalArgumentException, IOException {

        List<CoverageBand> bands = coverageView.getCoverageBands();
        List<GridCoverage2D> coverages = new ArrayList<GridCoverage2D>();

        CoveragesConsistencyChecker checker = null;

        int coverageBandsSize = bands.size();

        // Check params, populate band indices to read if BANDS param has been defined
        ArrayList<Integer> selectedBandIndices = new ArrayList<Integer>();
        for (int m = 0; m < coverageBandsSize; m++) {
            selectedBandIndices.add(m);
        }

        if (parameters != null) {
            for (int i = 0; i < parameters.length; i++) {
                final ParameterValue param = (ParameterValue) parameters[i];
                if (AbstractGridFormat.BANDS.getName().equals(param.getDescriptor().getName())) {
                    int[] bandIndicesParam = (int[]) param.getValue();
                    if (bandIndicesParam != null) {
                        selectedBandIndices = new ArrayList<Integer>();
                        for (int bIdx = 0; bIdx < bandIndicesParam.length; bIdx++) {
                            selectedBandIndices.add(bandIndicesParam[bIdx]);
                        }
                        break;
                    }
                }
            }
        }

        // Since composition of a raster band using a formula applied on individual bands has not
        // been implemented, the normal case is that each CoverageBand is in fact a single band from
        // an input coverage. When band composition will be implemented, this will be the point where
        // band composition will occur, before the final BandSelect/BandMerge ops

        // This is a good spot to read coverages. Reading a coverage is done only once, it is
        // cached to be used for its other bands that possibly take part in the CoverageView definition
        HashMap<String, GridCoverage2D> inputCoverages = new HashMap<String, GridCoverage2D>();
        GridCoverage2D dynamicAlphaSource = null;
        for (int bIdx : selectedBandIndices) {
            CoverageBand band = bands.get(bIdx);
            List<InputCoverageBand> selectedBands = band.getInputCoverageBands();

            // Peek for coverage name
            String coverageName = selectedBands.get(0).getCoverageName();
            if (!inputCoverages.containsKey(coverageName)) {
                GridCoverage2DReader reader = SingleGridCoverage2DReader.wrap(delegate, coverageName);
                // Remove this when removing constraints
                if (checker == null) {
                    checker = new CoveragesConsistencyChecker(reader);
                } else {
                    checker.checkConsistency(reader);
                }
                // bands selection parameter inside on final bands so they should not be propagated to the delegate reader
                GeneralParameterValue[] filteredParameters = parameters;
                if (parameters != null) {
                    // creating a copy of parameters excluding the bands parameter
                    filteredParameters = Arrays.stream(parameters)
                            .filter(parameter -> !parameter.getDescriptor().getName()
                                    .equals(AbstractGridFormat.BANDS.getName()))
                            .toArray(GeneralParameterValue[]::new);
                }
                final GridCoverage2D coverage = reader.read(filteredParameters);
                if (coverage == null) {
                    continue;
                }
                if (dynamicAlphaSource == null && hasDynamicAlpha(coverage, reader)) {
                    dynamicAlphaSource = coverage;
                }
                inputCoverages.put(coverageName, coverage);
            }
        }

        // all readers returned null?
        if (inputCoverages.isEmpty()) {
            return null;
        }

        // Group together bands that come from the same coverage
        ArrayList<CoverageBand> mergedBands = new ArrayList<CoverageBand>();

        int idx = 0;
        CoverageBand mBand = null;
        while (idx < selectedBandIndices.size()) {

            if (mBand == null) {
                // Create a temporary CoverageBand, to use later for SelectSampleDimension operations
                mBand = new CoverageBand();
                mBand.setInputCoverageBands(bands.get(selectedBandIndices.get(idx)).getInputCoverageBands());
            }

            // peek to the next band. Is it from the same coverage?
            String coverageName = bands.get(selectedBandIndices.get(idx)).getInputCoverageBands().get(0)
                    .getCoverageName();

            if (idx + 1 < selectedBandIndices.size() && bands.get(selectedBandIndices.get(idx + 1))
                    .getInputCoverageBands().get(0).getCoverageName().equals(coverageName)) {
                // Same coverage, add its bands to the previous
                ArrayList<InputCoverageBand> groupBands = new ArrayList<InputCoverageBand>();
                groupBands.addAll(mBand.getInputCoverageBands());
                groupBands.addAll(bands.get(selectedBandIndices.get(idx + 1)).getInputCoverageBands());
                mBand.setInputCoverageBands(groupBands);
            } else {
                mergedBands.add(mBand);
                mBand = null;
            }
            idx++;
        }

        // perform the band selects as needed
        for (CoverageBand band : mergedBands) {
            List<InputCoverageBand> selectedBands = band.getInputCoverageBands();

            // Peek for coverage name
            String coverageName = selectedBands.get(0).getCoverageName();

            // Get band indices for band selection
            ArrayList<Integer> bandIndices = new ArrayList<Integer>(selectedBands.size());
            for (InputCoverageBand icb : selectedBands) {
                int bandIdx = 0;
                final String bandString = icb.getBand();
                if (bandString != null && !bandString.isEmpty()) {
                    bandIdx = Integer.parseInt(bandString);
                }
                bandIndices.add(bandIdx);
            }

            GridCoverage2D coverage = inputCoverages.get(coverageName);

            // special case for dynamic alpha on single input, no need to actually select away the alpha
            Hints localHints = new Hints(hints);
            if (dynamicAlphaSource != null && mergedBands.size() == 1
                    && (bandIndices.size() == 1 || bandIndices.size() == 3)) {
                final int alphaBandIndex = getAlphaBandIndex(coverage);
                addAlphaColorModelHint(localHints, bandIndices.size());
                bandIndices.add(alphaBandIndex);
            }

            coverage = retainBands(bandIndices, coverage, localHints);
            coverages.add(coverage);
        }

        GridCoverage2D result;
        if (coverages.size() > 1) {
            // dynamic alpha but more than one source
            Hints localHints = new Hints(hints);
            if (dynamicAlphaSource != null) {
                int currentBandCount = countBands(coverages);
                // and the output is suitable for getting an alpha band
                if (currentBandCount == 1 || currentBandCount == 3) {
                    final int alphaBandIndex = getAlphaBandIndex(dynamicAlphaSource);
                    GridCoverage2D alphaBandCoverage = retainBands(Arrays.asList(alphaBandIndex),
                            dynamicAlphaSource, hints);
                    coverages.add(alphaBandCoverage);

                    addAlphaColorModelHint(localHints, currentBandCount);
                }
            }

            // perform final band merge
            final ParameterValueGroup param = PROCESSOR.getOperation("BandMerge").getParameters();
            param.parameter("sources").setValue(coverages);
            result = (GridCoverage2D) PROCESSOR.doOperation(param, localHints);
        } else {
            // optimize out, no need to do a band merge
            result = coverages.get(0);
        }

        return result;
    }

    private void addAlphaColorModelHint(Hints localHints, int currentBandCount) {
        ImageLayout layout = new ImageLayout();
        ColorModel alphaModel = getColorModelWithAlpha(currentBandCount);
        layout.setColorModel(alphaModel);
        localHints.put(JAI.KEY_IMAGE_LAYOUT, layout);
    }

    private ColorModel getColorModelWithAlpha(int currentBandCount) {
        if (currentBandCount == 3) {
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
            int[] nBits = { 8, 8, 8, 8 };
            return new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
        } else if (currentBandCount == 1) {
            ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
            int[] nBits = { 8, 8 };
            return new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
        } else {
            throw new IllegalArgumentException("Cannot create a color model with alpha" + "support starting with "
                    + currentBandCount + " bands");
        }
    }

    private int countBands(List<GridCoverage2D> coverages) {
        int count = 0;
        for (GridCoverage2D coverage : coverages) {
            count += coverage.getRenderedImage().getSampleModel().getNumBands();
        }
        return count;
    }

    private int getAlphaBandIndex(GridCoverage2D coverage) {
        final ColorModel cm = coverage.getRenderedImage().getColorModel();
        if (!cm.hasAlpha() || cm.getNumComponents() == cm.getNumColorComponents()) {
            throw new IllegalArgumentException(
                    "The source coverage does not have an alpha band, cannot extract an alpha band");
        }
        // the alpha band is always the last (see ComponentColorModel.getAlphaRaster or the getAlpha(object) code
        if (cm.getNumColorComponents() == 1) {
            // gray-alpha
            return 1;
        } else {
            // rgba/argb
            return 3;
        }
    }

    private GridCoverage2D retainBands(List<Integer> bandIndices, GridCoverage2D coverage, Hints hints) {
        final ParameterValueGroup param = PROCESSOR.getOperation("SelectSampleDimension").getParameters();
        param.parameter("Source").setValue(coverage);
        final int[] sampleDimensionArray = ArrayUtils
                .toPrimitive(bandIndices.toArray(new Integer[bandIndices.size()]));
        param.parameter("SampleDimensions").setValue(sampleDimensionArray);
        coverage = (GridCoverage2D) PROCESSOR.doOperation(param, hints);
        return coverage;
    }

    /**
     * Checks if a reader added a alpha channel on the fly as a result of a read parameter. We want to preserve
     * this alpha channel because the user never got a chance to select its presence in the output (e.g. 
     * footprint management in mosaic)
     * @param coverage
     * @param reader
     * @return
     * @throws IOException 
     */
    private boolean hasDynamicAlpha(GridCoverage2D coverage, GridCoverage2DReader reader) throws IOException {
        // check if we have an alpha band in the coverage to stat with
        if (coverage == null) {
            return false;
        }
        ColorModel dynamicCm = coverage.getRenderedImage().getColorModel();
        if (!dynamicCm.hasAlpha() || !hasAlphaBand(dynamicCm)) {
            return false;
        }

        // check if we did not have one in the original layout
        ImageLayout readerLayout = reader.getImageLayout();
        if (readerLayout == null) {
            return false;
        }
        ColorModel nativeCm = readerLayout.getColorModel(null);
        if (nativeCm == null || nativeCm.hasAlpha()) {
            return false;
        }

        // the coverage has an alpha band, but the original reader does not advertise one? 
        return !hasAlphaBand(nativeCm);

    }

    private boolean hasAlphaBand(ColorModel cm) {
        // num components returns the alpha, num _color_ components does not
        return (cm.getNumComponents() == 2 && cm.getNumColorComponents() == 1)
                /* gray-alpha case */ || (cm.getNumComponents() == 4
                        && cm.getNumColorComponents() == 3) /* rgba case */;
    }

    /**
     * @param coverageName
     */
    protected void checkCoverageName(String coverageName) {
        if (!this.coverageName.equalsIgnoreCase(coverageName)) {
            throw new IllegalArgumentException("The specified coverageName isn't the one of this coverageView");
        }
    }

    public void dispose() throws IOException {
        delegate.dispose();
    }

    /**
     * Get a {@link GridCoverage2DReader} wrapping the provided delegate reader
     */
    public static GridCoverage2DReader wrap(GridCoverage2DReader reader, CoverageView coverageView,
            CoverageInfo coverageInfo, Hints hints) {
        if (reader instanceof StructuredGridCoverage2DReader) {
            return new StructuredCoverageViewReader((StructuredGridCoverage2DReader) reader, coverageView,
                    coverageInfo, hints);
        } else {
            return new CoverageViewReader(reader, coverageView, coverageInfo, hints);
        }
    }

    @Override
    public Format getFormat() {
        return new Format() {

            private final Format delegateFormat = delegate.getFormat();

            @Override
            public ParameterValueGroup getWriteParameters() {
                return delegateFormat.getWriteParameters();
            }

            @Override
            public String getVersion() {
                return delegateFormat.getVersion();
            }

            @Override
            public String getVendor() {
                return delegateFormat.getVendor();
            }

            @Override
            public ParameterValueGroup getReadParameters() {
                HashMap<String, String> info = new HashMap<String, String>();

                info.put("name", getName());
                info.put("description", getDescription());
                info.put("vendor", getVendor());
                info.put("docURL", getDocURL());
                info.put("version", getVersion());

                List<GeneralParameterDescriptor> delegateFormatParams = new ArrayList<GeneralParameterDescriptor>();
                delegateFormatParams.addAll(delegateFormat.getReadParameters().getDescriptor().descriptors());
                // add bands parameter descriptor only if the delegate reader doesn't have it already
                if (!checkIfDelegateReaderSupportsBands()) {
                    delegateFormatParams.add(AbstractGridFormat.BANDS);
                }

                return new ParameterGroup(new DefaultParameterDescriptorGroup(info,
                        delegateFormatParams.toArray(new GeneralParameterDescriptor[delegateFormatParams.size()])));
            }

            @Override
            public String getName() {
                return delegateFormat.getName();
            }

            @Override
            public String getDocURL() {
                return delegateFormat.getDocURL();
            }

            @Override
            public String getDescription() {
                return delegateFormat.getDescription();
            }

        };
    }

    /**
     * Helper method that checks if the delegate reader support bands selection.
     */
    private boolean checkIfDelegateReaderSupportsBands() {
        List<GeneralParameterDescriptor> parameters = delegate.getFormat().getReadParameters().getDescriptor()
                .descriptors();
        for (GeneralParameterDescriptor parameterDescriptor : parameters) {
            if (parameterDescriptor.getName().equals(AbstractGridFormat.BANDS.getName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Object getSource() {
        return delegate.getSource();
    }

    @Override
    public String[] getMetadataNames(String coverageName) throws IOException {
        checkCoverageName(coverageName);
        return delegate.getMetadataNames(referenceName);
    }

    @Override
    public String getMetadataValue(String coverageName, String name) throws IOException {
        checkCoverageName(coverageName);
        return delegate.getMetadataValue(referenceName, name);
    }

    @Override
    public String[] listSubNames() throws IOException {
        return delegate.listSubNames();
    }

    @Override
    public String[] getGridCoverageNames() throws IOException {
        return delegate.getGridCoverageNames();
    }

    @Override
    public int getGridCoverageCount() throws IOException {
        return delegate.getGridCoverageCount();
    }

    @Override
    public String getCurrentSubname() throws IOException {
        return delegate.getCurrentSubname();
    }

    @Override
    public boolean hasMoreGridCoverages() throws IOException {
        return delegate.hasMoreGridCoverages();
    }

    @Override
    public void skip() throws IOException {
        delegate.skip();

    }

    @Override
    public GeneralEnvelope getOriginalEnvelope(String coverageName) {
        checkCoverageName(coverageName);
        return delegate.getOriginalEnvelope(referenceName);
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem(String coverageName) {
        checkCoverageName(coverageName);
        return delegate.getCoordinateReferenceSystem(referenceName);
    }

    @Override
    public GridEnvelope getOriginalGridRange(String coverageName) {
        checkCoverageName(coverageName);
        return delegate.getOriginalGridRange(referenceName);
    }

    @Override
    public MathTransform getOriginalGridToWorld(String coverageName, PixelInCell pixInCell) {
        checkCoverageName(coverageName);
        return delegate.getOriginalGridToWorld(referenceName, pixInCell);
    }

    @Override
    public GridCoverage2D read(String coverageName, GeneralParameterValue[] parameters) throws IOException {
        checkCoverageName(coverageName);
        return read(parameters);
    }

    @Override
    public Set<ParameterDescriptor<List>> getDynamicParameters(String coverageName) throws IOException {
        checkCoverageName(coverageName);
        return delegate.getDynamicParameters(referenceName);
    }

    @Override
    public double[] getReadingResolutions(String coverageName, OverviewPolicy policy, double[] requestedResolution)
            throws IOException {
        checkCoverageName(coverageName);
        return delegate.getReadingResolutions(referenceName, policy, requestedResolution);
    }

    @Override
    public int getNumOverviews(String coverageName) {
        checkCoverageName(coverageName);
        return delegate.getNumOverviews(referenceName);
    }

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

    @Override
    public ImageLayout getImageLayout(String coverageName) throws IOException {
        checkCoverageName(coverageName);
        return imageLayout;
    }

    @Override
    public double[][] getResolutionLevels(String coverageName) throws IOException {
        checkCoverageName(coverageName);
        return delegate.getResolutionLevels(referenceName);
    }

    @Override
    public String[] getMetadataNames() throws IOException {
        return delegate.getMetadataNames(referenceName);
    }

    @Override
    public String getMetadataValue(String name) throws IOException {
        return delegate.getMetadataValue(referenceName, name);
    }

    @Override
    public GeneralEnvelope getOriginalEnvelope() {
        return delegate.getOriginalEnvelope(referenceName);
    }

    @Override
    public GridEnvelope getOriginalGridRange() {
        return delegate.getOriginalGridRange(referenceName);
    }

    @Override
    public MathTransform getOriginalGridToWorld(PixelInCell pixInCell) {
        return delegate.getOriginalGridToWorld(referenceName, pixInCell);
    }

    @Override
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        return delegate.getCoordinateReferenceSystem(referenceName);
    }

    @Override
    public Set<ParameterDescriptor<List>> getDynamicParameters() throws IOException {
        return delegate.getDynamicParameters(referenceName);
    }

    @Override
    public double[] getReadingResolutions(OverviewPolicy policy, double[] requestedResolution) throws IOException {
        return delegate.getReadingResolutions(referenceName, policy, requestedResolution);
    }

    @Override
    public int getNumOverviews() {
        return delegate.getNumOverviews(referenceName);
    }

    @Override
    public double[][] getResolutionLevels() throws IOException {
        return delegate.getResolutionLevels(referenceName);
    }

    @Override
    public DatasetLayout getDatasetLayout() {
        return delegate.getDatasetLayout();
    }

    @Override
    public DatasetLayout getDatasetLayout(String coverageName) {
        return delegate.getDatasetLayout(coverageName);
    }

    @Override
    public ServiceInfo getInfo() {
        return delegate.getInfo();
    }

    @Override
    public ResourceInfo getInfo(String coverageName) {
        return delegate.getInfo(coverageName);
    }
}