/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2011-2012, 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.storage.coverage; import java.awt.Dimension; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.io.IOException; import java.util.*; import javax.imageio.ImageReader; import org.apache.sis.geometry.GeneralDirectPosition; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.ArgumentChecks; import org.geotoolkit.coverage.GridCoverageStack; import org.geotoolkit.coverage.finder.CoverageFinder; import org.geotoolkit.coverage.finder.StrictlyCoverageFinder; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.io.CoverageStoreException; import org.geotoolkit.image.io.XImageIO; import org.geotoolkit.internal.referencing.CRSUtilities; import org.geotoolkit.referencing.ReferencingUtilities; import org.opengis.coverage.SampleDimensionType; import org.opengis.coverage.grid.GridCoverage; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.SphericalCS; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; /** * Utility functions for coverage and mosaic. * * @author Johann Sorel (Geomatys) * @author RĂ©mi Marechal (Geomatys) * @author Quentin Boileau (Geomatys) * @module */ public final class CoverageUtilities { private CoverageUtilities(){} /** * Find the most appropriate pyramid in given pyramid set and given crs. * Returned Pyramid may not have the given crs. * * @param set : pyramid set to search in * @param crs searched crs * @return Pyramid, never null except if the pyramid set is empty * TODO : Is it really OK ? If we search a Lambert pyramid, and we've only got polar ones... * @deprecated use {@link org.geotoolkit.coverage.finder.StrictlyCoverageFinder#findPyramid(PyramidSet, org.opengis.referencing.crs.CoordinateReferenceSystem)} */ public static Pyramid findPyramid(final PyramidSet set, final CoordinateReferenceSystem crs) throws FactoryException { CoverageFinder finder = new StrictlyCoverageFinder(); return finder.findPyramid(set, crs); } /** * Find the most appropriate mosaic in the pyramid with the given information. * * @param pyramid * @param resolution * @param tolerance * @param env * @return GridMosaic * @deprecated use {@link org.geotoolkit.coverage.finder.StrictlyCoverageFinder#findMosaic(Pyramid, double, double, org.opengis.geometry.Envelope, Integer)} */ public static GridMosaic findMosaic(final Pyramid pyramid, final double resolution, final double tolerance, final Envelope env, int maxTileNumber) throws FactoryException{ CoverageFinder finder = new StrictlyCoverageFinder(); return finder.findMosaic(pyramid, resolution, tolerance, env, maxTileNumber); } /** * Search in the given pyramid all of the mosaics which fit the given parameters. 2 modes * are possible : * - Contains only : Suitable mosaics must be CONTAINED (or equal) to given spatial filter. * - Intersection : Suitable mosaics must INTERSECT given filter. * * TODO port this method in CoverageFinder * * @param toSearchIn The pyramid to get mosaics from. * @param filter The {@link Envelope} to use to specify spatial position of wanted mosaics. * @param containOnly True if you want 'Contains only' mode, false if you want 'Intersection' mode. * @return A list containing all the mosaics which fit the given envelope. Never null, but can be empty. * @throws TransformException If input filter {@link CoordinateReferenceSystem} is not compatible with * input mosaics one. */ public static List<GridMosaic> findMosaics(final Pyramid toSearchIn, Envelope filter, boolean containOnly) throws TransformException { final ArrayList<GridMosaic> result = new ArrayList<GridMosaic>(); //Rebuild filter envelope from pyramid CRS final GeneralEnvelope tmpFilter = new GeneralEnvelope( ReferencingUtilities.transform(filter, toSearchIn.getCoordinateReferenceSystem())); for (GridMosaic source : toSearchIn.getMosaics()) { final Envelope sourceEnv = source.getEnvelope(); if ((containOnly && tmpFilter.contains(sourceEnv, true)) || (!containOnly && tmpFilter.intersects(sourceEnv, true))) { result.add(source); } } return result; } /** * Compute ratio on each ordinate, not within 2D part of {@link CoordinateReferenceSystem}, * which represent recovery from each ordinate of searchEnvelope on gridEnvelope. * * @param searchEnvelope user coverage area search. * @param gridEnvelope mosaic envelope. * @return computed ratio. */ public static double getRatioND(Envelope searchEnvelope, Envelope gridEnvelope) { ArgumentChecks.ensureNonNull("gridEnvelope", gridEnvelope); ArgumentChecks.ensureNonNull("findEnvelope", searchEnvelope); final CoordinateReferenceSystem crs = gridEnvelope.getCoordinateReferenceSystem(); //find index ordinate of crs2D part of this crs. int minOrdinate2D = 0; boolean find = false; for(CoordinateReferenceSystem ccrrss : ReferencingUtilities.decompose(crs)) { final CoordinateSystem cs = ccrrss.getCoordinateSystem(); if((cs instanceof CartesianCS) || (cs instanceof SphericalCS) || (cs instanceof EllipsoidalCS)) { find = true; break; } minOrdinate2D += cs.getDimension(); } final int maxOrdinate2D = minOrdinate2D + 1; // compute distance if (!find) throw new IllegalArgumentException("CRS 2D part, not find"); final GeneralEnvelope intersection = new GeneralEnvelope(searchEnvelope); intersection.intersect(gridEnvelope); double sumRatio = 0; final int dimension = crs.getCoordinateSystem().getDimension(); for (int d = 0; d < dimension; d++) { if (d != minOrdinate2D && d != maxOrdinate2D) { final double ges = gridEnvelope.getSpan(d); // if intersect a slice part of gridEnvelope. // avoid divide by zero if (Math.abs(ges) <= 1E-12) continue; sumRatio += intersection.getSpan(d) / ges; } } return sumRatio; } /** * Retrieve index of the first axis of the geographic component in the input {@link CoordinateReferenceSystem}. * * @param crs {@link CoordinateReferenceSystem} which is analysed. * @return Index of the first horizontal axis in this CRS * @throws java.lang.IllegalArgumentException if input CRS has no horizontal component. * * @deprecated moved to {@link org.geotoolkit.internal.referencing.CRSUtilities#firstHorizontalAxis(org.opengis.referencing.crs.CoordinateReferenceSystem)} */ public static int getMinOrdinate(final CoordinateReferenceSystem crs) { int tempOrdinate = 0; for(CoordinateReferenceSystem ccrrss : ReferencingUtilities.decompose(crs)) { final CoordinateSystem cs = ccrrss.getCoordinateSystem(); if((cs instanceof CartesianCS) || (cs instanceof SphericalCS) || (cs instanceof EllipsoidalCS)) return tempOrdinate; tempOrdinate += cs.getDimension(); } throw new IllegalArgumentException("crs doesn't have any horizontal crs"); } /** * Copy a set of pyramid pointed by source coverage reference into destination * reference. * @param sourceRef The {@link PyramidalCoverageReference} to copy data from. * @param targetRef The {@link PyramidalCoverageReference} to copy data to. * @throws DataStoreException If a problem occurs at pyramid access. * @throws IOException If a problem occurs at image reading/writing. */ public static void copyPyramidReference(PyramidalCoverageReference sourceRef, PyramidalCoverageReference targetRef) throws DataStoreException, IOException { final Collection<Pyramid> pyramids = sourceRef.getPyramidSet().getPyramids(); //create pyramids for (Pyramid sP : pyramids) { final Pyramid tP = targetRef.createPyramid(sP.getCoordinateReferenceSystem()); //create mosaics for (GridMosaic sM : sP.getMosaics()) { final GridMosaic tM = targetRef.createMosaic(tP.getId(), sM.getGridSize(), sM.getTileSize(), sM.getUpperLeftCorner(), sM.getScale()); final int height = sM.getGridSize().height; final int width = sM.getGridSize().width; //Write tiles for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (!sM.isMissing(x, y)) { final TileReference sT = sM.getTile(x, y, null); final RenderedImage sourceImg; ImageReader reader = null; try { if (sT.getInput() instanceof RenderedImage) { sourceImg = (RenderedImage) sT.getInput(); } else { final int imgIdx = sT.getImageIndex(); reader = sT.getImageReader(); sourceImg = sT.getImageReader().read(imgIdx); } } finally { if (reader != null) { XImageIO.dispose(reader); } } targetRef.writeTile(tP.getId(), tM.getId(), x, y, sourceImg); } } } } } } public static int getDataType(SampleDimensionType sdt){ if(SampleDimensionType.REAL_32BITS.equals(sdt)){ return DataBuffer.TYPE_FLOAT; }else if(SampleDimensionType.REAL_64BITS.equals(sdt)){ return DataBuffer.TYPE_DOUBLE; }else if(SampleDimensionType.SIGNED_8BITS.equals(sdt)){ return DataBuffer.TYPE_BYTE; }else if(SampleDimensionType.SIGNED_16BITS.equals(sdt)){ return DataBuffer.TYPE_SHORT; }else if(SampleDimensionType.SIGNED_32BITS.equals(sdt)){ return DataBuffer.TYPE_INT; }else if(SampleDimensionType.UNSIGNED_1BIT.equals(sdt)){ return DataBuffer.TYPE_BYTE; }else if(SampleDimensionType.UNSIGNED_2BITS.equals(sdt)){ return DataBuffer.TYPE_BYTE; }else if(SampleDimensionType.UNSIGNED_4BITS.equals(sdt)){ return DataBuffer.TYPE_BYTE; }else if(SampleDimensionType.UNSIGNED_8BITS.equals(sdt)){ return DataBuffer.TYPE_BYTE; }else if(SampleDimensionType.UNSIGNED_16BITS.equals(sdt)){ return DataBuffer.TYPE_USHORT; }else if(SampleDimensionType.UNSIGNED_32BITS.equals(sdt)){ return DataBuffer.TYPE_INT; }else { throw new IllegalArgumentException("Unexprected data type : "+sdt); } } /** * Get or create a pyramid and it's mosaic for the given envelope and scales. * * @param container * @param envelope * @param tileSize * @param scales * @return * @throws DataStoreException */ public static Pyramid getOrCreatePyramid(PyramidalCoverageReference container, Envelope envelope, Dimension tileSize, double[] scales) throws DataStoreException{ //find if we already have a pyramid in the given CRS Pyramid pyramid = null; final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); for (Pyramid candidate : container.getPyramidSet().getPyramids()) { if (org.geotoolkit.referencing.CRS.equalsApproximatively(crs, candidate.getCoordinateReferenceSystem())) { pyramid = candidate; break; } } if (pyramid == null) { //we didn't find a pyramid, create one pyramid = container.createPyramid(crs); } //-- those parameters can change if another mosaic already exist final DirectPosition newUpperleft = new GeneralDirectPosition(crs); //-- We found the second horizontale axis dimension. final int maxHorizOrdinate = CRSUtilities.firstHorizontalAxis(crs) + 1; for (int d = 0; d < crs.getCoordinateSystem().getDimension(); d++) { final double v = (d == maxHorizOrdinate) ? envelope.getMaximum(d) : envelope.getMinimum(d); newUpperleft.setOrdinate(d, v); } //generate each mosaic for (final double scale : scales) { final double gridWidth = envelope.getSpan(0) / (scale*tileSize.width); final double gridHeight = envelope.getSpan(1) / (scale*tileSize.height); Dimension tileDim = tileSize; Dimension gridSize = new Dimension( (int)(Math.ceil(gridWidth)), (int)(Math.ceil(gridHeight))); //check if we already have a mosaic at this scale boolean mosaicFound = false; int index = 0; for (GridMosaic m : pyramid.getMosaics()) { if (m.getScale() == scale) { mosaicFound = true; break; } index++; } if (!mosaicFound) { //create a new mosaic container.createMosaic(pyramid.getId(),gridSize, tileDim, newUpperleft, scale); } } return pyramid; } /** * Extract recursively the first GridCoverage2D from a GridCoverage object. * * @param coverage a GridCoverage2D or GridCoverageStack * @return first GridCoverage2D. Can't be null. * @throws CoverageStoreException if GridCoverage2D not found or a empty GridCoverageStack */ public static GridCoverage2D firstSlice(GridCoverage coverage) throws CoverageStoreException { if (coverage instanceof GridCoverage2D) { return (GridCoverage2D) coverage; } else if (coverage instanceof GridCoverageStack) { GridCoverageStack coverageStack = (GridCoverageStack) coverage; if (coverageStack.getStackSize() > 0) { return firstSlice((GridCoverage) coverageStack.coverageAtIndex(0)); } else { throw new CoverageStoreException("Empty coverage list"); } } throw new CoverageStoreException("Unknown GridCoverage"); } /** * Compute Pyramid envelope. * * @param pyramid shouldn't be null * @return pyramid Envelope or null if no mosaic found. */ public static GeneralEnvelope getPyramidEnvelope(Pyramid pyramid) { ArgumentChecks.ensureNonNull("pyramid", pyramid); GeneralEnvelope pyramidEnv = null; for (GridMosaic mosaic : pyramid.getMosaics()) { if (pyramidEnv == null) { pyramidEnv = new GeneralEnvelope(mosaic.getEnvelope()); } else { pyramidEnv.add(mosaic.getEnvelope()); } } return pyramidEnv; } }