From: <svn...@os...> - 2012-03-08 14:14:59
|
Author: aaime Date: 2012-03-08 06:14:48 -0800 (Thu, 08 Mar 2012) New Revision: 38618 Modified: branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/GranuleDescriptor.java branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/RasterLayerResponse.java branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/ReadType.java branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/Utils.java branches/2.7.x/modules/plugin/imagemosaic/src/test/java/org/geotools/gce/imagemosaic/ImageMosaicReaderTest.java Log: [GEOT-4066] ImageMosaic does not satisfy the specified background values when the request this a hole sitting on the side of the mosaic Modified: branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/GranuleDescriptor.java =================================================================== --- branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/GranuleDescriptor.java 2012-03-08 14:14:21 UTC (rev 38617) +++ branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/GranuleDescriptor.java 2012-03-08 14:14:48 UTC (rev 38618) @@ -342,9 +342,9 @@ reader = cachedReaderSPI.createReaderInstance(); if(reader == null) throw new IllegalArgumentException("Unable to get an ImageReader for the provided file "+granuleUrl.toString()); - + reader.setInput(inStream); //get selected level and base level dimensions - final Rectangle originalDimension = Utils.getDimension(0,inStream, reader); + final Rectangle originalDimension = Utils.getDimension(0, reader); // build the g2W for this tile, in principle we should get it // somehow from the tile itself or from the index, but at the moment @@ -624,6 +624,7 @@ inStream = cachedStreamSPI.createInputStreamInstance(granuleUrl, ImageIO.getUseCache(), ImageIO.getCacheDirectory()); if(inStream==null) return null; + // get a reader and try to cache the relevant SPI if(cachedReaderSPI==null){ @@ -662,7 +663,7 @@ } //get selected level and base level dimensions - final GranuleOverviewLevelDescriptor selectedlevel= getLevel(imageIndex,reader,inStream); + final GranuleOverviewLevelDescriptor selectedlevel= getLevel(imageIndex,reader); // now create the crop grid to world which can be used to decide @@ -924,13 +925,10 @@ } } - private GranuleOverviewLevelDescriptor getLevel(final int index, final ImageReader reader, final ImageInputStream inStream) { + private GranuleOverviewLevelDescriptor getLevel(final int index, final ImageReader reader) { if(reader==null) - throw new NullPointerException("Null reader passed to the internal GranuleOverviewLevelDescriptor method"); - if(inStream==null) - throw new NullPointerException("Null stream passed to the internal GranuleOverviewLevelDescriptor method"); - + throw new NullPointerException("Null reader passed to the internal GranuleOverviewLevelDescriptor method"); synchronized (granuleLevels) { if(granuleLevels.containsKey(Integer.valueOf(index))) return granuleLevels.get(Integer.valueOf(index)); @@ -944,7 +942,7 @@ // //get selected level and base level dimensions - final Rectangle levelDimension = Utils.getDimension(index,inStream, reader); + final Rectangle levelDimension = Utils.getDimension(index, reader); final GranuleOverviewLevelDescriptor baseLevel= granuleLevels.get(0); final double scaleX=baseLevel.width/(1.0*levelDimension.width); @@ -992,9 +990,10 @@ reader=cachedReaderSPI.createReaderInstance(); if(reader==null) throw new IllegalArgumentException("Unable to get an ImageReader for the provided file "+granuleUrl.toString()); + reader.setInput(inStream); // call internal method which will close everything - return getLevel(index, reader, inStream); + return getLevel(index, reader); } catch (IllegalStateException e) { Modified: branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/RasterLayerResponse.java =================================================================== --- branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/RasterLayerResponse.java 2012-03-08 14:14:21 UTC (rev 38617) +++ branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/RasterLayerResponse.java 2012-03-08 14:14:48 UTC (rev 38618) @@ -23,11 +23,8 @@ import java.awt.Dimension; import java.awt.Rectangle; import java.awt.RenderingHints; -import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.PathIterator; -import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; @@ -63,6 +60,7 @@ import javax.media.jai.operator.ConstantDescriptor; import javax.media.jai.operator.FormatDescriptor; import javax.media.jai.operator.MosaicDescriptor; +import javax.media.jai.operator.TranslateDescriptor; import org.apache.commons.io.FilenameUtils; import org.geotools.coverage.Category; @@ -1019,11 +1017,14 @@ final Number[] values = Utils.getBackgroundValues(rasterManager.defaultSM, backgroundValues); // create a constant image with a proper layout - final RenderedImage finalImage = ConstantDescriptor.create( + RenderedImage finalImage = ConstantDescriptor.create( Float.valueOf(rasterBounds.width), Float.valueOf(rasterBounds.height), values, null); + if (rasterBounds.x != 0 || rasterBounds.y != 0) { + finalImage = TranslateDescriptor.create(finalImage, Float.valueOf(rasterBounds.x), Float.valueOf(rasterBounds.y), Interpolation.getInstance(Interpolation.INTERP_NEAREST), null); + } if(rasterManager.defaultCM!=null){ final ImageLayout2 il= new ImageLayout2(); il.setColorModel(rasterManager.defaultCM); @@ -1165,19 +1166,19 @@ // // SPECIAL CASE // 1 single tile, we try not do a mosaic. + final ROI[] sourceRoi = visitor.sourceRoi; if(visitor.granulesNumber==1 && Utils.OPTIMIZE_CROP){ // the roi is exactly equal to the final ROI roi = visitor.rois.get(0); - Rectangle bounds = toRectangle(roi.getAsShape()); + Rectangle bounds = Utils.toRectangle(roi.getAsShape()); if (bounds != null) { - final RenderedImage image= visitor.getSourcesAsArray()[0]; - final Rectangle imageBounds= PlanarImage.wrapRenderedImage(image).getBounds(); + RenderedImage image= visitor.getSourcesAsArray()[0]; + Rectangle imageBounds= PlanarImage.wrapRenderedImage(image).getBounds(); if(imageBounds.equals(bounds)){ - // do we need to crop - if(!imageBounds.equals(rasterBounds)){ + // do we need to crop? (image is bigger than requested?) + if(!rasterBounds.contains(imageBounds)){ // we have to crop - XRectangle2D.intersect(imageBounds, rasterBounds, imageBounds); if(imageBounds.isEmpty()){ @@ -1188,15 +1189,28 @@ ImageWorker iw = new ImageWorker(image); iw.setRenderingHints(localHints); iw.crop(imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height); - return iw.getRenderedImage(); + + image = iw.getRenderedImage(); + imageBounds = PlanarImage.wrapRenderedImage(image).getBounds(); } + + // and, do we need to add a border around the image? + if(!imageBounds.contains(rasterBounds)) { + image = MosaicDescriptor.create( + new RenderedImage[] {image}, + request.isBlend()? MosaicDescriptor.MOSAIC_TYPE_BLEND: MosaicDescriptor.MOSAIC_TYPE_OVERLAY, + (alphaIn || visitor.doInputTransparency) ? visitor.alphaChannels : null, sourceRoi, + visitor.sourceThreshold, + backgroundValues, + localHints); + } + return image; } } } - final ROI[] sourceRoi = visitor.sourceRoi; final RenderedImage mosaic = MosaicDescriptor.create( visitor.getSourcesAsArray(), request.isBlend()? MosaicDescriptor.MOSAIC_TYPE_BLEND: MosaicDescriptor.MOSAIC_TYPE_OVERLAY, @@ -1231,91 +1245,6 @@ } /** - * Checks if the Shape equates to a Rectangle, if it does it performs a conversion, otherwise - * returns null - * @param shape - * @return - */ - Rectangle toRectangle(Shape shape) { - if(shape instanceof Rectangle) { - return (Rectangle) shape; - } - - if(shape == null) { - return null; - } - - // check if it's equivalent to a rectangle - PathIterator iter = shape.getPathIterator(new AffineTransform()); - double[] coords = new double[2]; - - // not enough points? - if(iter.isDone()) { - return null; - } - - // get the first and init the data structures - iter.next(); - int action = iter.currentSegment(coords); - if(action != PathIterator.SEG_MOVETO && action != PathIterator.SEG_LINETO) { - return null; - } - double minx = coords[0]; - double miny = coords[1]; - double maxx = minx; - double maxy = miny; - double prevx = minx; - double prevy = miny; - int i = 0; - - // at most 4 steps, if more it's not a strict rectangle - for (; i < 4 && !iter.isDone(); i++) { - iter.next(); - action = iter.currentSegment(coords); - - if(action == PathIterator.SEG_CLOSE) { - break; - } - if(action != PathIterator.SEG_LINETO) { - return null; - } - - // check orthogonal step (x does not change and y does, or vice versa) - double x = coords[0]; - double y = coords[1]; - if(!(prevx == x && prevy != y) && - !(prevx != x && prevy == y)) { - return null; - } - - // update mins and maxes - if(x < minx) { - minx = x; - } else if(x > maxx) { - maxx = x; - } - if(y < miny) { - miny = y; - } else if(y > maxy) { - maxy = y; - } - - // keep track of prev step - prevx = x; - prevy = y; - } - - // if more than 4 other points it's not a standard rectangle - iter.next(); - if(!iter.isDone() || i != 3) { - return null; - } - - // turn it into a rectangle - return new Rectangle2D.Double(minx, miny, maxx - minx, maxy - miny).getBounds(); - } - - /** * This method is responsible for creating a coverage from the supplied {@link RenderedImage}. * * @param image @@ -1424,12 +1353,11 @@ null ).geophysics(true); } - return coverageFactory.create( rasterManager.getCoverageIdentifier(), image, new GridGeometry2D( - new GridEnvelope2D(PlanarImage.wrapRenderedImage(image).getBounds()), + new GridEnvelope2D(PlanarImage.wrapRenderedImage(image).getBounds()), PixelInCell.CELL_CORNER, finalGridToWorldCorner, this.mosaicBBox.getCoordinateReferenceSystem(), Modified: branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/ReadType.java =================================================================== --- branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/ReadType.java 2012-03-08 14:14:21 UTC (rev 38617) +++ branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/ReadType.java 2012-03-08 14:14:48 UTC (rev 38618) @@ -143,6 +143,7 @@ // check input stream final ImageInputStream inStream=(ImageInputStream) reader.getInput(); // read data + inStream.seek(0); final RenderedOp raster = ImageReadDescriptor.create( inStream, imageIndex, Modified: branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/Utils.java =================================================================== --- branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/Utils.java 2012-03-08 14:14:21 UTC (rev 38617) +++ branches/2.7.x/modules/plugin/imagemosaic/src/main/java/org/geotools/gce/imagemosaic/Utils.java 2012-03-08 14:14:48 UTC (rev 38618) @@ -18,7 +18,9 @@ import java.awt.Color; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.geom.AffineTransform; +import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; @@ -805,16 +807,12 @@ * in case the {@link ImageReader} or the * {@link ImageInputStream} fail. */ - static Rectangle getDimension(final int imageIndex, - final ImageInputStream inStream, final ImageReader reader) + static Rectangle getDimension(final int imageIndex,final ImageReader reader) throws IOException { - Utilities.ensureNonNull("inStream", inStream); Utilities.ensureNonNull("reader", reader); if (imageIndex < 0) throw new IllegalArgumentException(Errors.format( ErrorKeys.INDEX_OUT_OF_BOUNDS_$1, imageIndex)); - inStream.reset(); - reader.setInput(inStream); return new Rectangle(0, 0, reader.getWidth(imageIndex), reader .getHeight(imageIndex)); } @@ -1561,4 +1559,89 @@ return true; return false; } + + /** + * Checks if the Shape equates to a Rectangle, if it does it performs a conversion, otherwise + * returns null + * @param shape + * @return + */ + static Rectangle toRectangle(Shape shape) { + if(shape instanceof Rectangle) { + return (Rectangle) shape; + } + + if(shape == null) { + return null; + } + + // check if it's equivalent to a rectangle + PathIterator iter = shape.getPathIterator(new AffineTransform()); + double[] coords = new double[2]; + + // not enough points? + if(iter.isDone()) { + return null; + } + + // get the first and init the data structures + iter.next(); + int action = iter.currentSegment(coords); + if(action != PathIterator.SEG_MOVETO && action != PathIterator.SEG_LINETO) { + return null; + } + double minx = coords[0]; + double miny = coords[1]; + double maxx = minx; + double maxy = miny; + double prevx = minx; + double prevy = miny; + int i = 0; + + // at most 4 steps, if more it's not a strict rectangle + for (; i < 4 && !iter.isDone(); i++) { + iter.next(); + action = iter.currentSegment(coords); + + if(action == PathIterator.SEG_CLOSE) { + break; + } + if(action != PathIterator.SEG_LINETO) { + return null; + } + + // check orthogonal step (x does not change and y does, or vice versa) + double x = coords[0]; + double y = coords[1]; + if(!(prevx == x && prevy != y) && + !(prevx != x && prevy == y)) { + return null; + } + + // update mins and maxes + if(x < minx) { + minx = x; + } else if(x > maxx) { + maxx = x; + } + if(y < miny) { + miny = y; + } else if(y > maxy) { + maxy = y; + } + + // keep track of prev step + prevx = x; + prevy = y; + } + + // if more than 4 other points it's not a standard rectangle + iter.next(); + if(!iter.isDone() || i != 3) { + return null; + } + + // turn it into a rectangle + return new Rectangle2D.Double(minx, miny, maxx - minx, maxy - miny).getBounds(); + } } Modified: branches/2.7.x/modules/plugin/imagemosaic/src/test/java/org/geotools/gce/imagemosaic/ImageMosaicReaderTest.java =================================================================== --- branches/2.7.x/modules/plugin/imagemosaic/src/test/java/org/geotools/gce/imagemosaic/ImageMosaicReaderTest.java 2012-03-08 14:14:21 UTC (rev 38617) +++ branches/2.7.x/modules/plugin/imagemosaic/src/test/java/org/geotools/gce/imagemosaic/ImageMosaicReaderTest.java 2012-03-08 14:14:48 UTC (rev 38618) @@ -19,6 +19,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileNotFoundException; @@ -34,7 +36,9 @@ import java.util.TimeZone; import java.util.logging.Logger; +import javax.imageio.ImageIO; import javax.media.jai.PlanarImage; +import javax.media.jai.iterator.RectIterFactory; import javax.media.jai.widget.ScrollingImagePanel; import javax.swing.JFrame; import javax.swing.SwingUtilities; @@ -52,6 +56,7 @@ import org.geotools.coverage.grid.io.OverviewPolicy; import org.geotools.coverage.grid.io.UnknownFormat; import org.geotools.factory.Hints; +import org.geotools.geometry.Envelope2D; import org.geotools.geometry.GeneralEnvelope; import org.geotools.parameter.Parameter; import org.geotools.referencing.CRS; @@ -61,8 +66,8 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; +import org.opengis.geometry.Envelope; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterValue; @@ -868,7 +873,80 @@ checkCoverage(reader, new GeneralParameterValue[] { gg, outTransp },title); } + + @Test + public void testRequestInHole() throws Exception { + final AbstractGridFormat format = getFormat(rgbAURL); + final ImageMosaicReader reader = getReader(rgbAURL, format); + assertNotNull(reader); + + // ask to extract an area that is inside the coverage bbox, but in a hole (no data) + final ParameterValue<GridGeometry2D> ggp = AbstractGridFormat.READ_GRIDGEOMETRY2D.createValue(); + Envelope2D env = new Envelope2D(reader.getCrs(), 500000, 3200000, 1000, 1000); + GridGeometry2D gg = new GridGeometry2D(new GridEnvelope2D(0, 0, 100, 100), (Envelope) env); + ggp.setValue(gg); + + // red background + final ParameterValue<double[]> bgp = ImageMosaicFormat.BACKGROUND_VALUES.createValue(); + bgp.setValue(new double[] {255, 0, 0, 255}); + + // read and check we actually got a coverage in the requested area + GridCoverage2D coverage = reader.read(new GeneralParameterValue[] {ggp, bgp}); + assertNotNull(coverage); + assertTrue(coverage.getEnvelope2D().intersects(env)); + + // and that the color is the expected one given the background values provided + RenderedImage ri = coverage.getRenderedImage(); + int[] pixel = new int[4]; + Raster tile = ri.getTile(ri.getMinTileX() + 1, ri.getMinTileY() + 1); + tile.getPixel(tile.getMinX(), tile.getMinY(), pixel); + assertEquals(255, pixel[0]); + assertEquals(0, pixel[1]); + assertEquals(0, pixel[2]); + assertEquals(255, pixel[3]); + } + + @Test + public void testRequestInOut() throws Exception { + final AbstractGridFormat format = getFormat(rgbAURL); + final ImageMosaicReader reader = getReader(rgbAURL, format); + + assertNotNull(reader); + + // ask to extract an area that is inside the coverage bbox, so that the area is partly + // inside the raster, and partly outside + final ParameterValue<GridGeometry2D> ggp = AbstractGridFormat.READ_GRIDGEOMETRY2D.createValue(); + Envelope2D env = new Envelope2D(reader.getCrs(), 64887, 2499342, 646897 - 64887 , 3155705 - 2499342); + GridGeometry2D gg = new GridGeometry2D(new GridEnvelope2D(0, 0, 100, 100), (Envelope) env); + ggp.setValue(gg); + + // red background + final ParameterValue<double[]> bgp = ImageMosaicFormat.BACKGROUND_VALUES.createValue(); + bgp.setValue(new double[] {255, 0, 0, 255}); + + // read and check we actually got a coverage in the requested area + GridCoverage2D coverage = reader.read(new GeneralParameterValue[] {ggp, bgp}); + assertNotNull(coverage); + System.out.println(coverage.getEnvelope2D()); + System.out.println(env); + assertTrue(coverage.getEnvelope2D().contains(env)); + + // and that the color is the expected one given the background values provided + RenderedImage ri = coverage.getRenderedImage(); + ImageIO.write(ri, "PNG", new File("/tmp/mix.png")); + System.out.println(ri.getNumXTiles()); + System.out.println(ri.getNumYTiles()); + int[] pixel = new int[4]; + Raster tile = ri.getTile(ri.getMinTileX() + ri.getNumXTiles() - 1, + ri.getMinTileY() + ri.getNumYTiles() - 1); + tile.getPixel(tile.getWidth() / 2, tile.getHeight() / 2, pixel); + assertEquals(255, pixel[0]); + assertEquals(0, pixel[1]); + assertEquals(0, pixel[2]); + assertEquals(255, pixel[3]); + } + /** * @param args */ |