/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 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.display2d.container.statefull; import java.awt.Graphics2D; import java.awt.Point; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageReader; import javax.vecmath.Point3d; import org.geotoolkit.image.io.XImageIO; import org.geotoolkit.storage.coverage.AbstractGridMosaic; import org.geotoolkit.storage.coverage.GridMosaic; import org.geotoolkit.storage.coverage.TileReference; import org.geotoolkit.coverage.grid.GridCoverage2D; import org.geotoolkit.coverage.grid.GridCoverageBuilder; import org.geotoolkit.coverage.grid.GridEnvelope2D; import org.geotoolkit.coverage.grid.GridGeometry2D; import org.geotoolkit.coverage.grid.ViewType; import org.geotoolkit.display2d.GO2Utilities; import org.geotoolkit.display2d.canvas.J2DCanvas; import org.geotoolkit.display2d.canvas.RenderingContext2D; import org.geotoolkit.display2d.style.CachedRule; import org.geotoolkit.display2d.style.CachedSymbolizer; import org.geotoolkit.display2d.style.renderer.DefaultRasterSymbolizerRenderer; import org.geotoolkit.internal.referencing.CRSUtilities; import org.geotoolkit.map.CoverageMapLayer; import org.geotoolkit.map.MapItem; import org.geotoolkit.process.ProcessException; import org.apache.sis.internal.referencing.j2d.AffineTransform2D; import org.apache.sis.util.logging.Logging; import org.geotoolkit.map.ElevationModel; 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.style.RasterSymbolizer; import org.apache.sis.util.Utilities; /** * * @author Johann Sorel (Geomatys) */ public class StatefullTileJ2D extends StatefullMapItemJ2D<MapItem> { private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.display2d.container.statefull"); private final GridMosaic mosaic; private final Point3d coordinate; private final CachedRule[] rules; protected volatile GridCoverage2D buffer = null; protected AffineTransform2D trs = null; protected RenderedImage img = null; private volatile Updater updater = null; private final AtomicBoolean needUpdate = new AtomicBoolean(); private Envelope env2d; private volatile boolean obsoleted = false; private boolean loaded = false; public StatefullTileJ2D(GridMosaic mosaic, Point3d coordinate, J2DCanvas canvas, CoverageMapLayer item, CachedRule[] rules) { super(canvas, item, false); this.mosaic = mosaic; this.coordinate = coordinate; this.rules = rules; } public GridMosaic getMosaic() { return mosaic; } public Point3d getCoordinate() { return coordinate; } public boolean isLoaded(){ return loaded; } public void setObsoleted(boolean obsoleted) { this.obsoleted = obsoleted; } @Override public void paint(RenderingContext2D renderingContext) { GridCoverage2D coverage = this.buffer; if(coverage == null){ final Envelope env2d = renderingContext.getCanvasObjectiveBounds2D(); updateRequest(env2d); return; } //we must switch to objectiveCRS for grid coverage renderingContext.switchToDisplayCRS(); final AffineTransform objToDisp = renderingContext.getObjectiveToDisplay(); final AffineTransform comp = new AffineTransform(objToDisp); comp.concatenate(trs); final Graphics2D g = renderingContext.getGraphics(); g.drawRenderedImage(img, comp); } private void updateRequest(Envelope env2d){ if(obsoleted) return; boolean mustUpdate = false; if(this.env2d == null || !this.env2d.equals(env2d)){ mustUpdate = true; loaded = false; } this.env2d = env2d; if(mustUpdate){ needUpdate.set(true); checkUpdater(); } } private synchronized void checkUpdater(){ if(obsoleted) return; if(needUpdate.get() && updater == null){ needUpdate.set(false); if(env2d != null){ updater = new Updater(env2d); getExecutor().execute(updater); } } } protected class Updater implements Runnable{ private Envelope env2d; private Updater(Envelope env2d){ this.env2d = env2d; } @Override public void run() { if(obsoleted || loaded){ updater = null; return; } try{ final MathTransform trs = AbstractGridMosaic.getTileGridToCRS( mosaic, new Point((int)coordinate.x, (int)coordinate.y)); final TileReference tr = mosaic.getTile((int)coordinate.x, (int)coordinate.y, null); final CoordinateReferenceSystem pyramidCRS2D = CRSUtilities.getCRS2D(mosaic.getPyramid().getCoordinateReferenceSystem()); final GridCoverage2D coverage = prepareTile(env2d.getCoordinateReferenceSystem(), pyramidCRS2D, tr, trs); if(coverage != null){ //we copy the image in a buffered image with a well knowed data typeand color model //this can significantly improve performances. RenderedImage ri = null; for(final CachedRule rule : rules){ for(final CachedSymbolizer symbol : rule.symbolizers()){ if(symbol.getSource() instanceof RasterSymbolizer){ // todo appeler method getElevationmodel le coverage a deja ete reprojeté final CoverageMapLayer layer = (CoverageMapLayer) getUserObject(); final ElevationModel elevMod = layer.getElevationModel(); final GridCoverage2D dem = DefaultRasterSymbolizerRenderer.getDEMCoverage(coverage, elevMod); ri = DefaultRasterSymbolizerRenderer.applyStyle(null,coverage, dem, (RasterSymbolizer)symbol.getSource()); break; } } } if(ri == null){ //should not happen ri = coverage.getRenderedImage(); } final BufferedImage img = new BufferedImage(ri.getWidth(), ri.getHeight(), BufferedImage.TYPE_INT_ARGB); img.createGraphics().drawRenderedImage(ri, new AffineTransform()); StatefullTileJ2D.this.img = img; StatefullTileJ2D.this.trs = (AffineTransform2D) coverage.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT); StatefullTileJ2D.this.buffer = coverage; } getCanvas().repaint(); }catch(Exception ex){ ex.printStackTrace(); }finally{ loaded = true; } updater = null; checkUpdater(); } } private static GridCoverage2D prepareTile(final CoordinateReferenceSystem objCRS2D, final CoordinateReferenceSystem tileCRS ,final TileReference tile, MathTransform trs) { Object input = tile.getInput(); RenderedImage image = null; if(input instanceof RenderedImage){ image = (RenderedImage) input; }else{ ImageReader reader = null; try { reader = tile.getImageReader(); if(reader == null) return null; image = reader.read(tile.getImageIndex()); } catch (IOException ex) { LOGGER.log(Level.INFO, ex.getMessage()); return null; } finally { XImageIO.disposeSilently(reader); } } final boolean needReproject = !Utilities.equalsIgnoreMetadata(tileCRS,objCRS2D); if (needReproject) { //will be reprojected, we must check that image has alpha support //otherwise we will have black borders after reprojection if (!image.getColorModel().hasAlpha()) { final BufferedImage buffer = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); buffer.createGraphics().drawRenderedImage(image, new AffineTransform()); image = buffer; } } //build the coverage final GridCoverageBuilder gcb = new GridCoverageBuilder(); final GridEnvelope2D ge = new GridEnvelope2D(0, 0, image.getWidth(), image.getHeight()); final GridGeometry2D gridgeo = new GridGeometry2D(ge, PixelOrientation.UPPER_LEFT, trs, tileCRS, null); gcb.setName("tile"); gcb.setGridGeometry(gridgeo); gcb.setRenderedImage(image); GridCoverage2D coverage = (GridCoverage2D) gcb.build(); if (needReproject) { try { coverage = GO2Utilities.resample(coverage.view(ViewType.NATIVE),objCRS2D); } catch (ProcessException ex) { LOGGER.log(Level.SEVERE, null, ex); } } return coverage; } }