/* (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);
}
}