/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, 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.display3d.scene.loader;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import java.util.Collection;
import javax.measure.IncommensurableException;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.ArgumentChecks;
import org.geotoolkit.coverage.*;
import org.geotoolkit.image.interpolation.Interpolation;
import org.geotoolkit.image.interpolation.InterpolationCase;
import org.geotoolkit.image.interpolation.Resample;
import org.geotoolkit.image.iterator.PixelIterator;
import org.geotoolkit.image.iterator.PixelIteratorFactory;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.geotoolkit.display.PortrayalException;
import org.geotoolkit.display3d.utils.TextureUtils;
import org.geotoolkit.image.internal.ImageUtilities;
import org.geotoolkit.storage.coverage.*;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
import org.apache.sis.geometry.Envelopes;
/**
*
* @author Thomas Rouby (Geomatys))
*/
public class PyramidElevationLoader extends AbstractElevationLoader {
private final PyramidalCoverageReference coverageRef;
private Pyramid dataSource;
private final double minElevation;
private final double maxElevation;
private GridMosaicRenderedImage dataRenderedImage = null;
private CoordinateReferenceSystem outputCrs;
private MathTransform transformToOutput, transformFromOutput;
public PyramidElevationLoader(final PyramidalCoverageReference ref) throws FactoryException, IncommensurableException, DataStoreException {
ArgumentChecks.ensureNonNull("pyramid reference", ref);
this.coverageRef = ref;
final Collection<Pyramid> pyramidsMNT = (Collection<Pyramid>) ref.getPyramidSet().getPyramids();
if (!pyramidsMNT.isEmpty()){
dataSource = pyramidsMNT.iterator().next();
}
ArgumentChecks.ensureNonNull("pyramid", dataSource);
final GridSampleDimension elevationDim = ref.getSampleDimensions().get(0).geophysics(true);
this.minElevation = elevationDim.getMinimumValue();
this.maxElevation = elevationDim.getMaximumValue();
}
@Override
public double getMinimumElevation() {
return minElevation;
}
@Override
public double getMaximumElevation() {
return maxElevation;
}
/**
* Return the current outputCRS if set, else null
* @return
*/
public CoordinateReferenceSystem getOutputCRS() {
return outputCrs;
}
public void setOutputCRS(CoordinateReferenceSystem outputCrs) throws PortrayalException {
this.outputCrs = outputCrs;
try {
createTransformOutput();
} catch (FactoryException | IncommensurableException ex) {
throw new PortrayalException(ex);
}
}
/**
* Internal only, use setOutputCRS to recalculate output transform
*/
private void createTransformOutput() throws FactoryException, IncommensurableException {
if (outputCrs != null){
final CoordinateReferenceSystem crsImg = this.dataSource.getCoordinateReferenceSystem();
transformToOutput = CRS.findOperation(crsImg, outputCrs, null).getMathTransform();
try {
transformFromOutput = transformToOutput.inverse();
} catch (NoninvertibleTransformException ex) {
transformFromOutput = CRS.findOperation(outputCrs, crsImg, null).getMathTransform();
}
}
}
public BufferedImage getBufferedImageOf(final Envelope outputEnv, final Dimension outputDimension) throws PortrayalException {
if(outputCrs == null){
throw new PortrayalException("Output crs has not been set");
}
if (!org.geotoolkit.referencing.CRS.equalsApproximatively(outputEnv.getCoordinateReferenceSystem(), outputCrs)){
this.setOutputCRS(outputEnv.getCoordinateReferenceSystem());
}
final Envelope env;
try {
env = Envelopes.transform(transformFromOutput, outputEnv);
} catch (TransformException ex) {
throw new PortrayalException(ex);
}
final double scale = env.getSpan(0)/outputDimension.width;
final int indexImg = TextureUtils.getNearestScaleIndex(dataSource.getScales(), scale);
if (dataRenderedImage != null) {
final GridMosaic gridMosaic = dataRenderedImage.getGridMosaic();
final double mosaicScale = gridMosaic.getScale();
final double mosaicIndex = TextureUtils.getNearestScaleIndex(dataSource.getScales(), mosaicScale);
if (gridMosaic.getPyramid() != dataSource || mosaicIndex != indexImg) {
final Collection<GridMosaic> mosaics = dataSource.getMosaics(indexImg);
if (!mosaics.isEmpty()) {
dataRenderedImage = new GridMosaicRenderedImage(mosaics.iterator().next());
} else {
dataRenderedImage = null;
return null;
}
}
} else {
final Collection<GridMosaic> mosaics = dataSource.getMosaics(indexImg);
if (!mosaics.isEmpty()) {
dataRenderedImage = new GridMosaicRenderedImage(mosaics.iterator().next());
} else {
dataRenderedImage = null;
return null;
}
}
try {
return extractTileImage(outputEnv, dataRenderedImage, transformFromOutput, outputDimension);
} catch (TransformException ex) {
throw new PortrayalException(ex);
}
}
private static BufferedImage extractTileImage(final Envelope tileEnvelope,
final GridMosaicRenderedImage dataRenderedImage, final MathTransform transformFromOutput,
final Dimension tileSize) throws TransformException {
if (dataRenderedImage == null) {
return null;
}
final double targetTileWidth = tileSize.width;
final double targetTileHeight = tileSize.height;
final GridMosaic gridmosaic = dataRenderedImage.getGridMosaic();
final MathTransform mosaicCrsToMosaicGrid = AbstractGridMosaic.getTileGridToCRS(gridmosaic, new Point(0, 0)).inverse();
final AffineTransform2D targetGridToTargetCrs = new AffineTransform2D(
tileEnvelope.getSpan(0)/targetTileWidth,
0, 0,
-tileEnvelope.getSpan(1)/targetTileHeight,
tileEnvelope.getMinimum(0),
tileEnvelope.getMaximum(1));
final ColorModel sourceColorModel = dataRenderedImage.getColorModel();
final Raster prototype = dataRenderedImage.getData(new Rectangle(1, 1));
//prepare the output image
final WritableRaster targetRaster = prototype.createCompatibleWritableRaster(tileSize.width, tileSize.height);
final BufferedImage targetImage = new BufferedImage(sourceColorModel, targetRaster, sourceColorModel.isAlphaPremultiplied(), null);
ImageUtilities.fill(targetImage, Double.NaN);
final MathTransform sourceToTarget = MathTransforms.concatenate(
targetGridToTargetCrs, transformFromOutput, mosaicCrsToMosaicGrid);
//resample image
final double[] fillValue = new double[targetImage.getData().getNumBands()];
Arrays.fill(fillValue, Double.NaN);
final PixelIterator it = PixelIteratorFactory.createRowMajorIterator(dataRenderedImage);
final Interpolation interpol = Interpolation.create(it, InterpolationCase.NEIGHBOR, 2);
final Resample resampler = new Resample(sourceToTarget, targetImage, interpol, fillValue);
resampler.fillImage();
return targetImage;
}
}