/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2014, Geomatys
*
* 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.geotoolkit.coverage;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.geotoolkit.coverage.grid.GeneralGridEnvelope;
import org.geotoolkit.coverage.grid.GeneralGridGeometry;
import org.apache.sis.referencing.operation.transform.PassThroughTransform;
import org.geotoolkit.metadata.iso.spatial.PixelTranslation;
import org.geotoolkit.referencing.operation.transform.DimensionFilter;
import org.geotoolkit.referencing.operation.transform.LinearInterpolator1D;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.coverage.grid.GridNotEditableException;
import org.opengis.coverage.grid.GridPacking;
import org.opengis.coverage.grid.GridRange;
import org.opengis.coverage.grid.InvalidRangeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.*;
import org.opengis.util.FactoryException;
/**
* Subclass of CoverageStack for regular grid ND coverage.
*
*
* @author Johann Sorel (Geomatys)
* @author Quentin Boileau (Geomatys)
*/
public class GridCoverageStack extends CoverageStack implements GridCoverage {
private GridGeometry gridGeometry = null;
public GridCoverageStack(CharSequence name, final Collection<? extends GridCoverage> coverages)
throws IOException, TransformException, FactoryException {
super(name, coverages);
buildGridGeometry();
}
public GridCoverageStack(CharSequence name, final Collection<? extends GridCoverage> coverages, Integer zDimension)
throws IOException, TransformException, FactoryException {
super(name, coverages, zDimension);
buildGridGeometry();
}
public GridCoverageStack(CharSequence name, final CoordinateReferenceSystem crs, final Collection<? extends Element> elements)
throws IOException, TransformException, FactoryException {
super(name, crs, elements);
buildGridGeometry();
}
public GridCoverageStack(CharSequence name, final CoordinateReferenceSystem crs, final Collection<? extends Element> elements, Integer zDimension)
throws IOException, TransformException, FactoryException {
super(name, crs, elements, zDimension);
buildGridGeometry();
}
private void buildGridGeometry() throws IOException, TransformException, FactoryException{
final Element[] elements = getElements();
final CoordinateReferenceSystem crs = getCoordinateReferenceSystem();
final int nbDim = crs.getCoordinateSystem().getDimension();
if (elements.length == 0)
throw new IOException("Coverages list is empty");
//build the grid geometry
final int[] gridLower = new int[nbDim];
final int[] gridUpper = new int[nbDim];
final double[] zAxisSteps = new double[elements.length];
MathTransform baseGridToCRS = null;
int k=0;
for (Element element : elements) {
final GridCoverage coverage = (GridCoverage) element.getCoverage(null);
final GridGeometry gg = coverage.getGridGeometry();
final GridEnvelope ext = gg.getExtent();
//-- check extent pertinency
//we expect the axisIndex dimension to be a slice, low == high
if (ext.getLow(zDimension) != ext.getHigh(zDimension))
throw new IOException("Last dimension of the coverage is not a slice.");
if (baseGridToCRS == null) {
for (int i = 0; i < nbDim; i++) {
gridLower[i] = ext.getLow(i);
gridUpper[i] = ext.getHigh(i);
}
}
//-- check baseGridToCRS pertinency
baseGridToCRS = gg.getGridToCRS();
assert baseGridToCRS != null;
//-- pass gridToCRS into Corner
//-- GeoApi define that gridToCRS in Center
baseGridToCRS = PixelTranslation.translate(baseGridToCRS, PixelInCell.CELL_CENTER, PixelInCell.CELL_CORNER);
//find the real value
final double[] coord = new double[gridUpper.length];
for (int i = 0; i < gridUpper.length; i++) {
coord[i] = ext.getLow(i);
}
baseGridToCRS.transform(coord, 0, coord, 0, 1);
zAxisSteps[k] = coord[zDimension];
//increment number of slices
gridUpper[zDimension]++;
k++;
}
// reduce by one, values are inclusive
gridUpper[zDimension]--;
int remainingDimensions = nbDim - zDimension - 1;
/*
* Rebuild GridGeometry gridToCRS by extracting MT parts before and after MT1D on current zDimension axis.
* This is done in order to keep GridCoverage2D slice with all there dimension instead of truncate there dimensions
* on each CoverageStack level.
*
* TODO replace this hack by a GridCoverageStackBuilder that rebuild global gridGeometry and propagate it to every level
* and GridCoverage2D.
*/
//extract MT [0, zDim[
DimensionFilter df = new DimensionFilter(baseGridToCRS);
df.addSourceDimensionRange(0, zDimension);
MathTransform firstMT = df.separate();
firstMT = PassThroughTransform.create(0, firstMT, nbDim - zDimension);
//create dimension pass through transform with linear
final MathTransform lastAxisTrs;
if(zAxisSteps.length==1){
lastAxisTrs = MathTransforms.linear(1, zAxisSteps[0]);
}else{
lastAxisTrs = LinearInterpolator1D.create(zAxisSteps);
}
final MathTransform dimLinear = PassThroughTransform.create(zDimension, lastAxisTrs, remainingDimensions);
//extract MT [zDim+1, nbDim[
MathTransform lastPart = null;
if (remainingDimensions > 0) {
df = new DimensionFilter(baseGridToCRS);
df.addSourceDimensionRange(zDimension+1, nbDim);
lastPart = df.separate();
lastPart = PassThroughTransform.create(zDimension+1, lastPart, 0);
}
//build final gridToCRS
final MathTransform gridToCRS;
if (lastPart == null) {
gridToCRS = MathTransforms.concatenate(firstMT, dimLinear);
} else {
gridToCRS = MathTransforms.concatenate(firstMT, dimLinear, lastPart);
}
//build gridGeometry
final GeneralGridEnvelope gridEnv = new GeneralGridEnvelope(gridLower, gridUpper, true);
gridGeometry = new GeneralGridGeometry(gridEnv, PixelInCell.CELL_CORNER, gridToCRS, crs);
}
@Override
public boolean isDataEditable() {
return false;
}
@Override
public GridPacking getGridPacking() {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public GridGeometry getGridGeometry() {
return gridGeometry;
}
@Override
public int[] getOptimalDataBlockSizes() {
return null;
}
@Override
public int getNumOverviews() {
return 0;
}
@Override
public GridGeometry getOverviewGridGeometry(int index) throws IndexOutOfBoundsException {
throw new IndexOutOfBoundsException("No overviews available");
}
@Override
public GridCoverage getOverview(int index) throws IndexOutOfBoundsException {
throw new IndexOutOfBoundsException("No overviews available");
}
@Override
public List<GridCoverage> getSources() {
return Collections.EMPTY_LIST;
}
@Override
public boolean[] getDataBlock(GridRange range, boolean[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public byte[] getDataBlock(GridRange range, byte[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public short[] getDataBlock(GridRange range, short[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int[] getDataBlock(GridRange range, int[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public float[] getDataBlock(GridRange range, float[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public double[] getDataBlock(GridRange range, double[] destination) throws InvalidRangeException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public byte[] getPackedDataBlock(GridRange range) throws InvalidRangeException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, boolean[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, byte[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, short[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, int[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, float[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void setDataBlock(GridRange range, double[] values) throws InvalidRangeException, GridNotEditableException, ArrayIndexOutOfBoundsException {
throw new UnsupportedOperationException("Not yet implemented");
}
}