/* * Geotoolkit - 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.storage.coverage; import java.awt.Dimension; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.imageio.ImageReader; import javax.swing.ProgressMonitor; import javax.xml.bind.annotation.XmlTransient; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.coverage.GridSampleDimension; import org.geotoolkit.coverage.grid.GeneralGridEnvelope; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.grid.GridCoverageBuilder; import org.geotoolkit.coverage.grid.GridGeometry2D; import org.geotoolkit.coverage.grid.ViewType; import org.geotoolkit.coverage.io.CoverageStoreException; import org.geotoolkit.coverage.io.GridCoverageReader; import org.geotoolkit.coverage.io.GridCoverageWriter; import org.geotoolkit.image.io.XImageIO; import org.opengis.util.GenericName; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; import org.apache.sis.util.logging.Logging; /** * Abstract pyramidal coverage reference. * All methods return null values if authorized and writing operations raise exceptions. * * @author Johann Sorel (Geomatys) */ @XmlTransient public abstract class AbstractPyramidalCoverageReference extends AbstractCoverageReference implements PyramidalCoverageReference { protected final int imageIndex; public AbstractPyramidalCoverageReference(CoverageStore store, GenericName name,int imageIndex) { super(store, name); this.imageIndex = imageIndex; } @Override public int getImageIndex() { return imageIndex; } @Override public boolean isWritable() throws CoverageStoreException { return false; } @Override public GridCoverageReader acquireReader() throws CoverageStoreException { final PyramidalModelReader reader = new PyramidalModelReader(); reader.setInput(this); return reader; } @Override public GridCoverageWriter acquireWriter() throws CoverageStoreException { if(isWritable()){ return new PyramidalModelWriter(this); }else{ throw new CoverageStoreException("Pyramid is not writable"); } } @Override public Image getLegend() throws DataStoreException { return null; } @Override public ViewType getPackMode() throws DataStoreException { return ViewType.RENDERED; } @Override public void setPackMode(ViewType packMode) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public List<GridSampleDimension> getSampleDimensions() throws DataStoreException { return null; } @Override public void setSampleDimensions(List<GridSampleDimension> dimensions) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public ColorModel getColorModel() throws DataStoreException { return null; } @Override public void setColorModel(ColorModel colorModel) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public SampleModel getSampleModel() throws DataStoreException { return null; } @Override public void setSampleModel(SampleModel sampleModel) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public Pyramid createPyramid(CoordinateReferenceSystem crs) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public void deletePyramid(String pyramidId) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public GridMosaic createMosaic(String pyramidId, Dimension gridSize, Dimension tilePixelSize, DirectPosition upperleft, double pixelscale) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public void deleteMosaic(String pyramidId, String mosaicId) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } /** * {@inheritDoc }. */ @Override public void writeTiles(final String pyramidId, final String mosaicId, final RenderedImage image, final boolean onlyMissing, final ProgressMonitor monitor) throws DataStoreException { final Rectangle fullArea = new Rectangle(image.getNumXTiles(), image.getNumYTiles()); writeTiles(pyramidId, mosaicId, image, fullArea, onlyMissing, monitor); } /** * {@inheritDoc }. */ @Override public void writeTiles(final String pyramidId, final String mosaicId, final RenderedImage image, final Rectangle area, final boolean onlyMissing, final ProgressMonitor monitor) throws DataStoreException { if(!isWritable()){ throw new DataStoreException("Pyramid writing not supported."); } final int offsetX = image.getMinTileX(); final int offsetY = image.getMinTileY(); final int startX = (int)area.getMinX(); final int startY = (int)area.getMinY(); final int endX = (int)area.getMaxX(); final int endY = (int)area.getMaxY(); assert startX >= 0; assert startY >= 0; assert endX > startX && endX <= image.getNumXTiles(); assert endY > startY && endY <= image.getNumYTiles(); final RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.CallerRunsPolicy(); final BlockingQueue queue = new ArrayBlockingQueue(Runtime.getRuntime().availableProcessors()); final ThreadPoolExecutor executor = new ThreadPoolExecutor( 0, Runtime.getRuntime().availableProcessors(), 1, TimeUnit.MINUTES, queue, rejectHandler); for(int y=startY; y<endY;y++){ for(int x=startX;x<endX;x++){ final Raster raster = image.getTile(offsetX+x, offsetY+y); final RenderedImage img = new BufferedImage(image.getColorModel(), (WritableRaster)raster, image.getColorModel().isAlphaPremultiplied(), null); final int tx = offsetX+x; final int ty = offsetY+y; executor.submit(new Runnable() { @Override public void run() { if (monitor != null && monitor.isCanceled()) { return; } try { writeTile(pyramidId, mosaicId, tx, ty, img); } catch (DataStoreException ex) { Logging.getLogger("org.geotoolkit.storage.coverage").log(Level.WARNING, ex.getMessage(), ex); } } }); } } } @Override public void writeTile(String pyramidId, String mosaicId, int tileX, int tileY, RenderedImage image) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } @Override public void deleteTile(String pyramidId, String mosaicId, int tileX, int tileY) throws DataStoreException { throw new DataStoreException("Pyramid writing not supported."); } /** * Get a tile as coverage. * @param pyramidId * @param mosaicId * @param tileX * @param tileY * @return GridCoverage2D */ public static GridCoverage2D getTileAsCoverage(PyramidalCoverageReference covRef, String pyramidId, String mosaicId, int tileX, int tileY) throws DataStoreException { TileReference tile = null; final Pyramid pyramid = covRef.getPyramidSet().getPyramid(pyramidId); if(pyramid==null){ throw new DataStoreException("Invalid pyramid reference : "+pyramidId); } GridMosaic mosaic = null; for(GridMosaic gm : pyramid.getMosaics()){ if(gm.getId().equals(mosaicId)){ mosaic = gm; tile = gm.getTile(tileX, tileY, null); } } if(tile==null){ throw new DataStoreException("Invalid tile reference : "+pyramidId+" "+mosaicId+" "+tileX+" "+tileY); } return getTileAsCoverage(covRef, pyramidId, mosaicId, tile); } /** * Get a tile as coverage. * @param covRef * @param pyramidId * @param mosaicId * @param tile * @return GridCoverage2D * @throws org.apache.sis.storage.DataStoreException */ public static GridCoverage2D getTileAsCoverage(PyramidalCoverageReference covRef, String pyramidId, String mosaicId, TileReference tile) throws DataStoreException { final Pyramid pyramid = covRef.getPyramidSet().getPyramid(pyramidId); if(pyramid==null){ throw new DataStoreException("Invalid pyramid reference : "+pyramidId); } GridMosaic mosaic = null; for(GridMosaic gm : pyramid.getMosaics()){ if(gm.getId().equals(mosaicId)){ mosaic = gm; } } Object input = tile.getInput(); RenderedImage image; if(input instanceof RenderedImage){ image = (RenderedImage) input; }else{ ImageReader reader = null; try { reader = tile.getImageReader(); image = reader.read(tile.getImageIndex()); } catch (IOException ex) { throw new DataStoreException(ex.getMessage(),ex); } finally { //dispose reader and substream XImageIO.disposeSilently(reader); } } //build the coverage --------------------------------------------------- final GridCoverageBuilder gcb = new GridCoverageBuilder(); gcb.setName("tile"); final CoordinateReferenceSystem tileCRS = pyramid.getCoordinateReferenceSystem(); final MathTransform gridToCrs = AbstractGridMosaic.getTileGridToCRS(mosaic,tile.getPosition()); final GeneralGridEnvelope ge = new GeneralGridEnvelope( new Rectangle(image.getWidth(), image.getHeight()),tileCRS.getCoordinateSystem().getDimension()); final GridGeometry2D gridgeo = new GridGeometry2D(ge, PixelInCell.CELL_CORNER, gridToCrs, tileCRS, null); gcb.setGridGeometry(gridgeo); gcb.setRenderedImage(image); final List<GridSampleDimension> dimensions = covRef.getSampleDimensions(); if(dimensions!=null){ gcb.setSampleDimensions(dimensions.toArray(new GridSampleDimension[0])); } return (GridCoverage2D) gcb.build(); } }