/* * 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.processing.coverage.mathcalc; import java.awt.Dimension; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Arrays; import org.apache.sis.geometry.GeneralDirectPosition; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.coverage.grid.GeneralGridGeometry; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.grid.GridCoverageBuilder; import org.geotoolkit.coverage.io.CoverageStoreException; import org.geotoolkit.coverage.io.GridCoverageReader; import org.geotoolkit.coverage.io.GridCoverageWriteParam; import org.geotoolkit.coverage.io.GridCoverageWriter; import org.geotoolkit.factory.FactoryFinder; import org.geotoolkit.geometry.HyperCubeIterator; import org.geotoolkit.referencing.operation.matrix.GeneralMatrix; import org.geotoolkit.image.BufferedImages; import org.geotoolkit.storage.coverage.*; import org.opengis.coverage.grid.GridEnvelope; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; import org.apache.sis.geometry.Envelopes; import org.apache.sis.util.Utilities; /** * TODO : This should be part of the CoverageWriter interface. * The current CoverageWriter interface is limited to slice writing as renderedImage, * this implies the callers know exactly the internal structure of the coverage. * * This class is a preview of a possible future design of the coverage writer, * unlike the writer approach where the user provide an image of the datas, here * the writer calls an evaluator on each coordinate. This allows to manipulate * N dimension writing and coverages which are not grids. * * * @author Johann Sorel (Geomatys) */ public class FillCoverage { /** * Fill coverage values on given envelope. * * @param evaluator , used to generate the new sample values. * @param env , envelope where new values will be evaluated. * @throws org.geotoolkit.coverage.io.CoverageStoreException */ public void fill(CoverageReference outRef, SampleEvaluator evaluator, Envelope env) throws DataStoreException { final GeneralGridGeometry gg; final GridCoverageWriter outWriter; final GridCoverageReader outReader; outReader = outRef.acquireReader(); outWriter = outRef.acquireWriter(); gg = outReader.getGridGeometry(outRef.getImageIndex()); outRef.recycle(outReader); // prepare dynamic pick object final GridEnvelope ge = gg.getExtent(); final int nbDim = ge.getDimension(); final DirectPosition positionGrid = new GeneralDirectPosition(gg.getCoordinateReferenceSystem()); final DirectPosition positionGeo = new GeneralDirectPosition(gg.getCoordinateReferenceSystem()); //calculate the hyper-cube where we will need to recalculate values final MathTransform gridToCrs = gg.getGridToCRS(); final int[] mins = new int[nbDim]; final int[] maxs = new int[nbDim]; for(int i=0;i<nbDim;i++){ mins[i] = ge.getLow(i); maxs[i] = ge.getHigh(i)+1; //high value is inclusive in grid envelopes } //adjust the writing hyper-cube if an envelope is provided if(env!=null){ if(!Utilities.equalsIgnoreMetadata(env.getCoordinateReferenceSystem(),gg.getCoordinateReferenceSystem())){ throw new CoverageStoreException("Envelope is not in data CRS."); } try { final MathTransform crsToGrid = gridToCrs.inverse(); // how do we round values ? cast, nearest, intersect, within ? GeneralEnvelope gridEnv = Envelopes.transform(crsToGrid, env); for(int i=0;i<nbDim;i++){ mins[i] = Math.max(mins[i], (int)gridEnv.getMinimum(i)); maxs[i] = Math.min(maxs[i], (int)gridEnv.getMaximum(i)); } } catch (TransformException ex) { throw new CoverageStoreException(ex.getMessage(), ex); } } //create iterator final int[] maxSize = new int[nbDim]; Arrays.fill(maxSize, 1); maxSize[0] = 256; maxSize[1] = 256; final HyperCubeIterator ite = new HyperCubeIterator(mins, maxs, maxSize); //loop on all slices pieces final MathTransformFactory mathFactory = FactoryFinder.getMathTransformFactory(null); while(ite.hasNext()){ final HyperCubeIterator.HyperCube cube = ite.next(); final int[] hcubeLower = cube.getLower(); final int[] hcubeUpper = cube.getUpper(); for(int i=2;i<nbDim;i++){ positionGrid.setOrdinate(i, hcubeLower[i]); } //create the slice coverage final BufferedImage zoneImage = BufferedImages.createImage( hcubeUpper[0]-hcubeLower[0], hcubeUpper[1]-hcubeLower[1], 1, DataBuffer.TYPE_DOUBLE); final WritableRaster raster = zoneImage.getRaster(); //loop on all pixels final double[] sampleData = new double[1]; try{ for(int x=hcubeLower[0],xn=hcubeUpper[0];x<xn;x++){ for(int y=hcubeLower[1],yn=hcubeUpper[1];y<yn;y++){ positionGrid.setOrdinate(0, x); positionGrid.setOrdinate(1, y); gridToCrs.transform(positionGrid, positionGeo); evaluator.evaluate(positionGeo, sampleData); raster.setSample(x-hcubeLower[0], y-hcubeLower[1], 0, sampleData[0]); } } }catch(TransformException ex){ throw new CoverageStoreException(ex.getMessage(), ex); } //Calculate grid to crs of this zone final GeneralMatrix matrix = new GeneralMatrix(nbDim+1); matrix.setIdentity(); for(int i=0;i<nbDim;i++){ matrix.setElement(i, nbDim, hcubeLower[i]); } final MathTransform cornerToGrid; try { cornerToGrid = mathFactory.createAffineTransform(matrix); } catch (FactoryException ex) { throw new CoverageStoreException(ex.getMessage(), ex); } final MathTransform concat = MathTransforms.concatenate(cornerToGrid, gridToCrs); final GridCoverageBuilder gcb = new GridCoverageBuilder(); gcb.setCoordinateReferenceSystem(gg.getCoordinateReferenceSystem()); gcb.setRenderedImage(zoneImage); gcb.setGridToCRS(concat); final GridCoverage2D zoneCoverage = gcb.getGridCoverage2D(); final GridCoverageWriteParam param = new GridCoverageWriteParam(); outWriter.write(zoneCoverage, param); } } /** * Fill given coverage reference, providing it with processed images. * * @param evaluator * @param outRef */ public static void fill(PyramidalCoverageReference outRef, SampleEvaluator evaluator) throws DataStoreException, TransformException, FactoryException { final ColorModel cm = outRef.getColorModel(); final SampleModel sm = outRef.getSampleModel(); for(Pyramid pyramid : outRef.getPyramidSet().getPyramids()){ for(GridMosaic mosaic : pyramid.getMosaics()){ final Dimension tileSize = mosaic.getTileSize(); final double[] upperLeftGeo = mosaic.getUpperLeftCorner().getCoordinate(); final Dimension gridSize = mosaic.getGridSize(); for(int y=0;y<gridSize.height;y++){ for(int x=0;x<gridSize.width;x++){ final MathTransform gridToCRS = AbstractGridMosaic.getTileGridToCRS(mosaic, new Point(x, y), PixelInCell.CELL_CENTER); final MathTransform crsToGrid = gridToCRS.inverse(); final double[] baseCoord = new double[upperLeftGeo.length]; crsToGrid.transform(upperLeftGeo, 0, baseCoord, 0, 1); final MathCalcImageEvaluator eval = new MathCalcImageEvaluator(baseCoord, gridToCRS, evaluator.copy()); final ProcessedRenderedImage image = new ProcessedRenderedImage(sm, cm, eval, tileSize.width, tileSize.height); outRef.writeTile(pyramid.getId(), mosaic.getId(), x, y, image); } } } } } /** * */ public static interface SampleEvaluator { /** * Evaluate the new sample values at given geographic coordinate. * * @param position , coordinate where to evaluate the sample. * @param sampleBuffer , new samples must be set in this buffer */ void evaluate(DirectPosition position, double[] sampleBuffer); SampleEvaluator copy() throws FactoryException; } }