/* * 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.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Arrays; import java.util.List; import javax.measure.IncommensurableException; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.storage.coverage.CoverageReference; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.coverage.grid.GeneralGridGeometry; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.io.CoverageReader; import org.geotoolkit.coverage.io.DisjointCoverageDomainException; import org.geotoolkit.coverage.io.GridCoverageReadParam; import org.geotoolkit.coverage.io.GridCoverageReader; import org.geotoolkit.display.PortrayalException; 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.image.BufferedImages; import org.geotoolkit.image.internal.ImageUtilities; import org.opengis.geometry.Envelope; import org.opengis.metadata.spatial.PixelOrientation; 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; /** * * @author Thomas Rouby (Geomatys) * @author Johann Sorel (Geomatys) */ public class DefaultElevationLoader extends AbstractElevationLoader { private final CoverageReference coverageRef; private final GeneralGridGeometry gridGeom; private final double minElevation; private final double maxElevation; private CoordinateReferenceSystem outputCrs; private MathTransform coverageToOutput, outputToCoverage; public DefaultElevationLoader(CoverageReference ref) throws FactoryException, IncommensurableException, DataStoreException { this.coverageRef = ref; final GridCoverageReader reader = coverageRef.acquireReader(); this.gridGeom = reader.getGridGeometry(ref.getImageIndex()); final List<GridSampleDimension> dimensions = reader.getSampleDimensions(ref.getImageIndex()); if(dimensions!=null && !dimensions.isEmpty()){ final GridSampleDimension elevationDim = dimensions.get(0).geophysics(true); this.minElevation = elevationDim.getMinimumValue(); this.maxElevation = elevationDim.getMaximumValue(); }else{ this.minElevation = 0; this.maxElevation = 1000; } ref.recycle(reader); } @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; } @Override 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 = gridGeom.getCoordinateReferenceSystem(); coverageToOutput = CRS.findOperation(crsImg, outputCrs, null).getMathTransform(); try { outputToCoverage = coverageToOutput.inverse(); } catch (NoninvertibleTransformException ex) { outputToCoverage = CRS.findOperation(outputCrs, crsImg, null).getMathTransform(); } } } @Override 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()); } try{ final CoverageReader reader = coverageRef.acquireReader(); final GridCoverageReadParam params = new GridCoverageReadParam(); params.setEnvelope(outputEnv); try{ final GridCoverage2D coverage = (GridCoverage2D)reader.read(coverageRef.getImageIndex(), params); return extractTileImage(outputEnv, coverage, outputToCoverage, outputDimension); }catch(DisjointCoverageDomainException de){ //tile outside of the coverage, it's possible //create a fake tile at minimum elevation final BufferedImage img = BufferedImages.createImage( outputDimension.width, outputDimension.height, 1, DataBuffer.TYPE_DOUBLE); ImageUtilities.fill(img, minElevation); return img; } }catch(Exception ex){ throw new PortrayalException(ex); } } private static BufferedImage extractTileImage(final Envelope tileEnvelope, final GridCoverage2D coverage, final MathTransform outputCRSToCoverageCRS, final Dimension tileSize) throws TransformException { final RenderedImage dataRenderedImage = coverage.getRenderedImage(); if (dataRenderedImage == null) { return null; } final double targetTileWidth = tileSize.width; final double targetTileHeight = tileSize.height; final MathTransform coverageCRSToImageGrid = coverage.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT).inverse(); final AffineTransform2D tileGridToOutputCRS = 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( tileGridToOutputCRS, outputCRSToCoverageCRS, coverageCRSToImageGrid); //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; } }