Andrea Aime ha scritto:
Hi,
after quite a bit of testing on our own and a cross review
in person during the FOSS4G-IT 2009 conference in Lugano
me and Simone have agreed it's time to turn the patch
into committed status.
Oh, just noticed the GeoTools patch was incomplete.
Here is a better one.
Cheers
Andrea
--
Andrea Aime
OpenGeo - http://opengeo.org
Expert service straight from the developers.
Index: src/main/java/org/geotools/renderer/lite/gridcoverage2d/GridCoverageRenderer.java
===================================================================
--- src/main/java/org/geotools/renderer/lite/gridcoverage2d/GridCoverageRenderer.java (revisione 34916)
+++ src/main/java/org/geotools/renderer/lite/gridcoverage2d/GridCoverageRenderer.java (copia locale)
@@ -18,6 +18,7 @@
// J2SE dependencies
import java.awt.AlphaComposite;
+import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.Rectangle;
@@ -33,9 +34,13 @@
import java.util.logging.Logger;
import javax.imageio.ImageIO;
+import javax.media.jai.ImageLayout;
import javax.media.jai.Interpolation;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.JAI;
+import javax.media.jai.operator.AffineDescriptor;
+import javax.media.jai.operator.ScaleDescriptor;
+import javax.media.jai.operator.TranslateDescriptor;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
@@ -166,34 +171,7 @@
/** Parameters used to control the {...@link Scale} operation. */
private static final Resample resampleFactory = new Resample();
- /**
- * Creates a new {...@link GridCoverageRenderer} object.
- *
- * @param destinationCRS
- * the CRS of the {...@link GridCoverage2D} to render.
- * @param envelope
- * delineating the area to be rendered.
- * @param screenSize
- * at which we want to rendere the source
- * {...@link GridCoverage2D}.
- * @param worldToScreen if not <code>null</code> and if it contains a rotation,
- * this Affine Tranform is used directly to convert from world coordinates
- * to screen coordinates. Otherwise, a standard {...@link GridToEnvelopeMapper}
- * is used to calculate the affine transform.
- *
- * @throws TransformException
- * @throws NoninvertibleTransformException
- * @deprecated Use {...@link GridCoverageRenderer#GridCoverageRenderer(CoordinateReferenceSystem, Envelope, Rectangle, AffineTransform, RenderingHints)}
- * instead
- */
- public GridCoverageRenderer(final CoordinateReferenceSystem destinationCRS,
- final Envelope envelope, Rectangle screenSize)
- throws TransformException, NoninvertibleTransformException {
- this(destinationCRS, envelope, screenSize, null, null);
-
- }
-
/**
* Creates a new {...@link GridCoverageRenderer} object.
*
@@ -220,29 +198,8 @@
}
- /**
- * Creates a new {...@link GridCoverageRenderer} object.
- *
- * @param destinationCRS
- * the CRS of the {...@link GridCoverage2D} to render.
- * @param envelope
- * delineating the area to be rendered.
- * @param screenSize
- * at which we want to rendere the source
- * {...@link GridCoverage2D}.
- * @param java2dHints
- * to control this rendering process.
- * @throws TransformException
- * @throws NoninvertibleTransformException
- * @deprecated Use {...@link GridCoverageRenderer#GridCoverageRenderer(CoordinateReferenceSystem, Envelope, Rectangle, AffineTransform, RenderingHints)}
- * instead
- */
- public GridCoverageRenderer(final CoordinateReferenceSystem destinationCRS,
- final Envelope envelope, Rectangle screenSize, RenderingHints java2dHints) throws TransformException,
- NoninvertibleTransformException {
- this(destinationCRS, envelope, screenSize, null,java2dHints);
- }
+
/**
* Creates a new {...@link GridCoverageRenderer} object.
*
@@ -379,36 +336,48 @@
}
/**
- * Paint this grid coverage. The caller must ensure that
- * <code>graphics</code> has an affine transform mapping "real world"
- * coordinates in the coordinate system given by {...@link
- * #getCoordinateSystem}.
+ * Write the provided {...@link RenderedImage} in the debug directory with the provided file name.
*
- * @param graphics
- * the {...@link Graphics2D} context in which to paint.
- * @param metaBufferedEnvelope
+ * @param raster
+ * the {...@link RenderedImage} that we have to write.
+ * @param fileName
+ * a {...@link String} indicating where we should write it.
+ */
+ static void writeRenderedImage(final RenderedImage raster, final String fileName) {
+ if (debugDir == null)
+ throw new NullPointerException(
+ "Unable to write the provided coverage in the debug directory");
+ if (DEBUG == false)
+ throw new IllegalStateException(
+ "Unable to write the provided coverage since we are not in debug mode");
+ try {
+ ImageIO.write(raster, "tiff", new File(debugDir, fileName + ".tiff"));
+ } catch (IOException e) {
+ LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+ }
+ }
+
+ /**
+ * Builds a (RenderedImage, AffineTransform) pair that can be used for rendering onto a
+ * {...@link Graphics2D} or as the basis to build a final image. Will return null if there is
+ * nothing to render.
+ *
+ * @param gridCoverage
+ * @param symbolizer
+ * @return
* @throws FactoryException
* @throws TransformException
* @throws NoninvertibleTransformException
- * @throws Exception
- * @throws UnsupportedOperationException
- * if the transformation from grid to coordinate system in
- * the GridCoverage is not an AffineTransform
*/
- public void paint(
- final Graphics2D graphics,
- final GridCoverage2D gridCoverage,
+ private Object[] prepareFinalImage(final GridCoverage2D gridCoverage,
final RasterSymbolizer symbolizer)
throws FactoryException, TransformException,
NoninvertibleTransformException {
-
// ///////////////////////////////////////////////////////////////////
//
// Initial checks
//
// ///////////////////////////////////////////////////////////////////
- if(graphics==null)
- throw new NullPointerException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1,"graphics"));
if(gridCoverage==null)
throw new NullPointerException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1,"gridCoverage"));
@@ -548,7 +517,7 @@
LOGGER
.info("The destination envelope does not intersect the envelope of the source coverage.");
}
- return;
+ return null;
}
@@ -570,7 +539,7 @@
if (LOGGER.isLoggable(Level.FINE))
LOGGER.fine(
new StringBuilder("Skipping current coverage because cropped to an empty area").toString());
- return;
+ return null;
}
}catch (Throwable t) {
////
@@ -657,11 +626,119 @@
// //
final AffineTransform clonedFinalWorldToGrid = (AffineTransform) finalWorldToGrid.clone();
clonedFinalWorldToGrid.concatenate(finalGCgridToWorld);
+
+ return new Object[] {finalImage, clonedFinalWorldToGrid};
+ }
+
+ /**
+ * Turns the coverage into a rendered image applying the necessary transformations and the
+ * symbolizer
+ *
+ * @param gridCoverage
+ * @param symbolizer
+ * @return The transformed image, or null if the coverage does not lie within the rendering
+ * bounds
+ * @throws FactoryException
+ * @throws TransformException
+ * @throws NoninvertibleTransformException
+ */
+ public RenderedImage renderImage(final GridCoverage2D gridCoverage,
+ final RasterSymbolizer symbolizer, Interpolation interpolation, Color background,
+ int tileSizeX, int tileSizeY) throws FactoryException, TransformException,
+ NoninvertibleTransformException {
+
+ // Build the final image and the transformation
+ Object[] couple = prepareFinalImage(gridCoverage, symbolizer);
+ if (couple == null)
+ return null;
+
+ RenderedImage finalImage = (RenderedImage) couple[0];
+ AffineTransform clonedFinalWorldToGrid = (AffineTransform) couple[1];
+
+ // TODO: optimize translate/scale transformations
+ // TODO: use mosaic to merge with a background respecting alpha and transparency
+ // TODO: check tolerance value
+ // TODO: do we need to pass in any hints?
+ final double tolerance = 1e-6;
+
+ if (XAffineTransform.isIdentity(clonedFinalWorldToGrid, tolerance)) {
+ return finalImage;
+ }
+
+ // TODO: make this check a little more general, AffineTransform type won't use tolerances,
+ // but this will do for the moment, in our tests the common case fits with AffineTransform
+ // expectations
+ final ImageLayout layout = new ImageLayout(finalImage);
+ layout.setTileGridXOffset(0).setTileGridYOffset(0).setTileHeight(tileSizeY).setTileWidth(
+ tileSizeX);
+ final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
+ float sx = (float) clonedFinalWorldToGrid.getScaleX();
+ float sy = (float) clonedFinalWorldToGrid.getScaleY();
+ float tx = (float) clonedFinalWorldToGrid.getTranslateX();
+ float ty = (float) clonedFinalWorldToGrid.getTranslateY();
+ int txType = clonedFinalWorldToGrid.getType();
+ if (txType == AffineTransform.TYPE_TRANSLATION) {
+ return TranslateDescriptor.create(finalImage, tx, ty, interpolation, hints);
+ } else if (txType == AffineTransform.TYPE_GENERAL_SCALE
+ || txType == AffineTransform.TYPE_UNIFORM_SCALE
+ || txType == (AffineTransform.TYPE_GENERAL_SCALE | AffineTransform.TYPE_TRANSLATION)
+ || txType == (AffineTransform.TYPE_UNIFORM_SCALE | AffineTransform.TYPE_TRANSLATION)) {
+ return ScaleDescriptor.create(finalImage, sx, sy, tx, ty, interpolation, hints);
+ } else {
+ return AffineDescriptor.create(finalImage, clonedFinalWorldToGrid, interpolation, null,
+ hints);
+ }
+ }
+
+ /**
+ * Paint this grid coverage. The caller must ensure that
+ * <code>graphics</code> has an affine transform mapping "real world"
+ * coordinates in the coordinate system given by {...@link
+ * #getCoordinateSystem}.
+ *
+ * @param graphics
+ * the {...@link Graphics2D} context in which to paint.
+ * @param metaBufferedEnvelope
+ * @throws FactoryException
+ * @throws TransformException
+ * @throws NoninvertibleTransformException
+ * @throws Exception
+ * @throws UnsupportedOperationException
+ * if the transformation from grid to coordinate system in
+ * the GridCoverage is not an AffineTransform
+ */
+ public void paint(
+ final Graphics2D graphics,
+ final GridCoverage2D gridCoverage,
+ final RasterSymbolizer symbolizer)
+ throws FactoryException, TransformException,
+ NoninvertibleTransformException {
+
+ // ///////////////////////////////////////////////////////////////////
+ //
+ // Initial checks
+ //
+ // ///////////////////////////////////////////////////////////////////
+ if(graphics==null)
+ throw new NullPointerException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1,"graphics"));
+ if(gridCoverage==null)
+ throw new NullPointerException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1,"gridCoverage"));
+
if (LOGGER.isLoggable(Level.FINE))
- LOGGER.fine(new StringBuilder("clonedFinalWorldToGrid ").append(clonedFinalWorldToGrid.toString()).toString());
+ LOGGER.fine(new StringBuilder("Drawing coverage ").append(gridCoverage.toString()).toString());
+
final RenderingHints oldHints = graphics.getRenderingHints();
graphics.setRenderingHints(this.hints);
+
+
+ // Build the final image and the transformation
+ Object[] couple = prepareFinalImage(gridCoverage, symbolizer);
+ if (couple == null)
+ return;
+ RenderedImage finalImage = (RenderedImage) couple[0];
+ AffineTransform clonedFinalWorldToGrid = (AffineTransform) couple[1];
+
// //
// Opacity
// //
@@ -669,10 +746,10 @@
final Composite oldAlphaComposite = graphics.getComposite();
graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
try {
- //debug
+ //debug
if (DEBUG) {
writeRenderedImage(finalImage,"final");
- }
+ }
// //
// Drawing the Image
@@ -681,26 +758,26 @@
} catch (Throwable t) {
try {
- //log the error
- if(LOGGER.isLoggable(Level.FINE))
- LOGGER.log(Level.FINE,t.getLocalizedMessage(),t);
+ //log the error
+ if(LOGGER.isLoggable(Level.FINE))
+ LOGGER.log(Level.FINE,t.getLocalizedMessage(),t);
// /////////////////////////////////////////////////////////////
- // this is a workaround for a bug in Java2D, we need to convert
- // the image to component color model to make it work just fine.
+ // this is a workaround for a bug in Java2D, we need to convert
+ // the image to component color model to make it work just fine.
// /////////////////////////////////////////////////////////////
if(t instanceof IllegalArgumentException){
if (DEBUG) {
writeRenderedImage(finalImage,"preWORKAROUND1");
}
- final RenderedImage image = new ImageWorker(finalImage).forceComponentColorModel(true).getRenderedImage();
-
+ final RenderedImage image = new ImageWorker(finalImage).forceComponentColorModel(true).getRenderedImage();
+
if (DEBUG) {
- writeRenderedImage(image,"WORKAROUND1");
+ writeRenderedImage(image,"WORKAROUND1");
}
graphics.drawRenderedImage(image, clonedFinalWorldToGrid);
-
+
}
else if(t instanceof ImagingOpException)
// /////////////////////////////////////////////////////////////
@@ -719,10 +796,10 @@
// LARGE IMAGES.
// /////////////////////////////////////////////////////////////
{
- BufferedImage buf =
- finalImage.getColorModel().hasAlpha()?
- new BufferedImage(finalImage.getWidth(), finalImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR):
- new BufferedImage(finalImage.getWidth(), finalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
+ BufferedImage buf =
+ finalImage.getColorModel().hasAlpha()?
+ new BufferedImage(finalImage.getWidth(), finalImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR):
+ new BufferedImage(finalImage.getWidth(), finalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
if (DEBUG) {
writeRenderedImage(buf,"preWORKAROUND2");
}
@@ -731,18 +808,18 @@
g.drawRenderedImage(finalImage, AffineTransform.getTranslateInstance(-translationX, -translationY));
g.dispose();
if (DEBUG) {
- writeRenderedImage(buf,"WORKAROUND2");
+ writeRenderedImage(buf,"WORKAROUND2");
}
clonedFinalWorldToGrid.concatenate(AffineTransform.getTranslateInstance(translationX, translationY));
graphics.drawImage(buf, clonedFinalWorldToGrid, null);
//release
buf.flush();
- buf=null;
+ buf=null;
}
else
- //log the error
- if(LOGGER.isLoggable(Level.FINE))
- LOGGER.fine("Unable to renderer this raster, no workaround found");
+ //log the error
+ if(LOGGER.isLoggable(Level.FINE))
+ LOGGER.fine("Unable to renderer this raster, no workaround found");
} catch (Throwable t1) {
@@ -762,24 +839,4 @@
}
- /**
- * Write the provided {...@link RenderedImage} in the debug directory with the provided file name.
- * @param raster the {...@link RenderedImage} that we have to write.
- * @param fileName a {...@link String} indicating where we should write it.
- */
- private void writeRenderedImage(final RenderedImage raster,final String fileName) {
- if(debugDir==null)
- throw new NullPointerException("Unable to write the provided coverage in the debug directory");
- if(DEBUG==false)
- throw new IllegalStateException("Unable to write the provided coverage since we are not in debug mode");
- try {
- ImageIO.write(
- raster,
- "tiff",
- new File(debugDir,fileName+".tiff"));
- } catch (IOException e) {
- LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e);
- }
- }
-
}
------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
Geoserver-devel mailing list
Geoserver-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/geoserver-devel