/** * Scale an image up/down to the desired width and height, while maintaining the image's aspect * ratio (if requested). * * @param image the source image to scale * @param scaleWidth the new width to scale to * @param scaleHeight the new height to scale to * @param keepAspect true if the aspect ratio should be maintained. * @param color the color to fill borders when maintaining aspect ratio (0 = black, 255 = white). * @return a scaled image as RenderedOp * @see #scaleImage(RenderedOp, int, int) */ public static RenderedOp scaleImage( RenderedOp image, int scaleWidth, int scaleHeight, boolean keepAspect, double color) { float xScale = (float) scaleWidth / (float) image.getWidth(); float yScale = (float) scaleHeight / (float) image.getHeight(); boolean resize = false; if (keepAspect) { resize = Math.abs(xScale - yScale) < .0000001; xScale = Math.min(xScale, yScale); yScale = xScale; } ParameterBlock params = new ParameterBlock(); params.addSource(image); params.add(xScale); // x scale factor params.add(yScale); // y scale factor params.add(0.0F); // x translate params.add(0.0F); // y translate params.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); RenderedOp result = JAI.create("scale", params, ImageToolkit.NOCACHE_HINT); // $NON-NLS-1$ if (resize) { result = resizeImage(result, scaleWidth, scaleHeight, color); } return result; }
// This method creates a surrogate image -- one which is // used to display data which originally was outside the // [0-255] range. private RenderedImage createSurrogate(RenderedImage image, double maxValue) { ParameterBlock pb = new ParameterBlock(); pb.addSource(image); RenderedOp extrema = JAI.create("extrema", pb); // Must get the extrema of all bands. double[] allMins = (double[]) extrema.getProperty("minimum"); // double[] allMaxs = (double[])extrema.getProperty("maximum"); double minValue = allMins[0]; // double maxValue = allMaxs[0]; for (int v = 1; v < allMins.length; v++) { if (allMins[v] < minValue) minValue = allMins[v]; // if (allMaxs[v] > maxValue) maxValue = allMaxs[v]; } // Rescale the image with the parameters double[] multiplyBy = new double[1]; multiplyBy[0] = Model_Main.MAXPIXELVALUE / (maxValue - minValue); double[] addThis = new double[1]; addThis[0] = -minValue * multiplyBy[0]; // Now we can rescale the pixels gray levels: ParameterBlock pbSub = new ParameterBlock(); pbSub.addSource(image); pbSub.add(addThis); RenderedImage surrogateImage = (PlanarImage) JAI.create("subtractconst", pbSub, null); ParameterBlock pbMult = new ParameterBlock(); pbMult.addSource(surrogateImage); pbMult.add(multiplyBy); surrogateImage = (PlanarImage) JAI.create("multiplyconst", pbMult, null); return surrogateImage; }
/** * Test Read exploiting JAI-ImageIO tools capabilities and GDAL warp. * * @throws FileNotFoundException * @throws IOException */ @Test public void readWithWarp() throws FileNotFoundException, IOException { if (!isGDALAvailable) { return; } final ParameterBlockJAI pbjImageRead; String fileName = "utmByte.tif"; final File file = TestData.file(this, fileName); SpatialReference destinationReference = new SpatialReference(); destinationReference.SetProjCS("UTM 17 (WGS84) in northern hemisphere."); destinationReference.SetWellKnownGeogCS("WGS84"); destinationReference.SetUTM(17, 1); GDALImageReadParam readParam = new GDALImageReadParam(); readParam.setDestinationWkt(destinationReference.ExportToWkt()); readParam.setResampleAlgorithm(ResampleAlgorithm.CUBIC); pbjImageRead = new ParameterBlockJAI("ImageRead"); pbjImageRead.setParameter("Input", new FileImageInputStreamExtImpl(file)); pbjImageRead.setParameter("Reader", new GeoTiffImageReaderSpi().createReaderInstance()); pbjImageRead.setParameter("ReadParam", readParam); RenderedOp image = JAI.create("ImageRead", pbjImageRead); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, "", true); else Assert.assertNotNull(image.getTiles()); }
// If defaultNewIsNull is true, then newIsNull is ignored and it should behave as // if newIsNull were false. private void runTest( WritableRaster source, double sourceNoData, double newValue, boolean defaultNewIsNull, boolean newIsNull) { ColorModel cm1 = RasterUtils.createColorModel(source); java.util.Hashtable<String, Object> arg1Properties = new java.util.Hashtable<String, Object>(); arg1Properties.put(OpImageUtils.NODATA_PROPERTY, sourceNoData); BufferedImage s1 = new BufferedImage(cm1, source, false, arg1Properties); RenderedOp op = null; if (defaultNewIsNull) { op = ReplaceNullDescriptor.create(s1, newValue, null); } else { op = ReplaceNullDescriptor.create(s1, newValue, newIsNull, null); } Raster r = op.getData(); if (!defaultNewIsNull && newIsNull) { Assert.assertEquals(newValue, OpImageUtils.getNoData(op, Double.NaN), EPSILON); } else { Assert.assertTrue(Double.isNaN(OpImageUtils.getNoData(op, Double.NaN))); } Assert.assertEquals(width, r.getWidth()); Assert.assertEquals(height, r.getHeight()); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { double v = r.getSampleDouble(x, y, 0); int pixelId = getPixelId(x, y, width); Assert.assertEquals(pixelId, v, EPSILON); } } }
private RenderedOp createScaledImage( RenderedImage sourceImage, S2Resolution resolution, int level) { int sourceWidth = sourceImage.getWidth(); int sourceHeight = sourceImage.getHeight(); int targetWidth = imageLayouts[0].width >> level; int targetHeight = imageLayouts[0].height >> level; float scaleX = (float) targetWidth / (float) sourceWidth; float scaleY = (float) targetHeight / (float) sourceHeight; float corrFactorX = resolution == S2Resolution.R20M ? R20M_X_FACTOR : R60M_X_FACTOR; float corrFactorY = resolution == S2Resolution.R20M ? R20M_Y_FACTOR : R60M_Y_FACTOR; final Dimension tileDim = getTileDim(targetWidth, targetHeight); ImageLayout imageLayout = new ImageLayout(); imageLayout.setTileWidth(tileDim.width); imageLayout.setTileHeight(tileDim.height); RenderingHints renderingHints = new RenderingHints( JAI.KEY_BORDER_EXTENDER, BorderExtender.createInstance(BorderExtender.BORDER_ZERO)); renderingHints.put(JAI.KEY_IMAGE_LAYOUT, imageLayout); RenderedOp scaledImage = ScaleDescriptor.create( sourceImage, scaleX * corrFactorX, scaleY * corrFactorY, 0F, 0F, Interpolation.getInstance(Interpolation.INTERP_NEAREST), renderingHints); if (scaledImage.getWidth() != targetWidth || scaledImage.getHeight() != targetHeight) { return CropDescriptor.create( scaledImage, 0.0F, 0.0F, (float) targetWidth, (float) targetHeight, null); } else { return scaledImage; } }
/** * Test Writing capabilities. * * @throws FileNotFoundException * @throws IOException */ @Test public void write() throws IOException, FileNotFoundException { if (!isGDALAvailable) { return; } final File outputFile = TestData.temp(this, "writetest.tif", false); outputFile.deleteOnExit(); final File inputFile = TestData.file(this, "utmByte.tif"); ImageReadParam rparam = new ImageReadParam(); rparam.setSourceRegion(new Rectangle(1, 1, 300, 500)); rparam.setSourceSubsampling(1, 2, 0, 0); ImageReader reader = new GeoTiffImageReaderSpi().createReaderInstance(); reader.setInput(inputFile); final IIOMetadata metadata = reader.getImageMetadata(0); final ParameterBlockJAI pbjImageRead = new ParameterBlockJAI("ImageRead"); pbjImageRead.setParameter("Input", inputFile); pbjImageRead.setParameter("reader", reader); pbjImageRead.setParameter("readParam", rparam); final ImageLayout l = new ImageLayout(); l.setTileGridXOffset(0).setTileGridYOffset(0).setTileHeight(256).setTileWidth(256); RenderedOp image = JAI.create("ImageRead", pbjImageRead, new RenderingHints(JAI.KEY_IMAGE_LAYOUT, l)); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, "geotiff"); // //////////////////////////////////////////////////////////////// // preparing to write // //////////////////////////////////////////////////////////////// final ParameterBlockJAI pbjImageWrite = new ParameterBlockJAI("ImageWrite"); ImageWriter writer = new GeoTiffImageWriterSpi().createWriterInstance(); pbjImageWrite.setParameter("Output", outputFile); pbjImageWrite.setParameter("writer", writer); pbjImageWrite.setParameter("ImageMetadata", metadata); pbjImageWrite.setParameter("Transcode", false); ImageWriteParam param = new ImageWriteParam(Locale.getDefault()); param.setSourceRegion(new Rectangle(10, 10, 100, 100)); param.setSourceSubsampling(2, 1, 0, 0); pbjImageWrite.setParameter("writeParam", param); pbjImageWrite.addSource(image); final RenderedOp op = JAI.create("ImageWrite", pbjImageWrite); final ImageWriter writer2 = (ImageWriter) op.getProperty(ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER); writer2.dispose(); // //////////////////////////////////////////////////////////////// // preparing to read again // //////////////////////////////////////////////////////////////// final ParameterBlockJAI pbjImageReRead = new ParameterBlockJAI("ImageRead"); pbjImageReRead.setParameter("Input", outputFile); pbjImageReRead.setParameter("Reader", new GeoTiffImageReaderSpi().createReaderInstance()); final RenderedOp image2 = JAI.create("ImageRead", pbjImageReRead); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image2, "geotif2"); else Assert.assertNotNull(image2.getTiles()); }
/** * Test Read on a Paletted Image * * @throws FileNotFoundException * @throws IOException */ @Test public void palette() throws FileNotFoundException, IOException { if (!isGDALAvailable) { return; } final File outputFile = TestData.temp(this, "writetest.tif", false); outputFile.deleteOnExit(); final File inputFile = TestData.file(this, "paletted.tif"); ImageReader reader = new GeoTiffImageReaderSpi().createReaderInstance(); reader.setInput(inputFile); final IIOMetadata metadata = reader.getImageMetadata(0); final ParameterBlockJAI pbjImageRead = new ParameterBlockJAI("ImageRead"); pbjImageRead.setParameter("Input", inputFile); pbjImageRead.setParameter("reader", reader); final ImageLayout l = new ImageLayout(); l.setTileGridXOffset(0).setTileGridYOffset(0).setTileHeight(256).setTileWidth(256); RenderedOp image = JAI.create("ImageRead", pbjImageRead, new RenderingHints(JAI.KEY_IMAGE_LAYOUT, l)); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, "Paletted image read"); // //////////////////////////////////////////////////////////////// // preparing to write // //////////////////////////////////////////////////////////////// final ParameterBlockJAI pbjImageWrite = new ParameterBlockJAI("ImageWrite"); ImageWriter writer = new GeoTiffImageWriterSpi().createWriterInstance(); pbjImageWrite.setParameter("Output", outputFile); pbjImageWrite.setParameter("writer", writer); pbjImageWrite.setParameter("ImageMetadata", metadata); pbjImageWrite.setParameter("Transcode", false); pbjImageWrite.addSource(image); final RenderedOp op = JAI.create("ImageWrite", pbjImageWrite); final ImageWriter writer2 = (ImageWriter) op.getProperty(ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER); writer2.dispose(); // //////////////////////////////////////////////////////////////// // preparing to read again // //////////////////////////////////////////////////////////////// final ParameterBlockJAI pbjImageReRead = new ParameterBlockJAI("ImageRead"); pbjImageReRead.setParameter("Input", outputFile); pbjImageReRead.setParameter("Reader", new GeoTiffImageReaderSpi().createReaderInstance()); final RenderedOp image2 = JAI.create("ImageRead", pbjImageReRead); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image2, "Paletted image read back after writing"); else Assert.assertNotNull(image2.getTiles()); ImageIOUtilities.disposeImage(image2); ImageIOUtilities.disposeImage(image); }
@Test public void rectanglePolyAcrossTiles() throws Exception { final int margin = 3; final int Ntiles = 3; int minx = margin; int miny = minx; int maxx = TILE_WIDTH * Ntiles - 2 * margin; int maxy = maxx; String wkt = String.format( "POLYGON((%d %d, %d %d, %d %d, %d %d, %d %d))", minx, miny, minx, maxy, maxx, maxy, maxx, miny, minx, miny); Polygon poly = (Polygon) reader.read(wkt); ParameterBlockJAI pb = new ParameterBlockJAI("VectorBinarize"); pb.setParameter("width", Ntiles * TILE_WIDTH); pb.setParameter("height", Ntiles * TILE_WIDTH); pb.setParameter("geometry", poly); RenderedOp dest = JAI.create("VectorBinarize", pb); CoordinateSequence2D testPointCS = new CoordinateSequence2D(1); Point testPoint = gf.createPoint(testPointCS); for (int ytile = 0; ytile < Ntiles; ytile++) { for (int xtile = 0; xtile < Ntiles; xtile++) { Raster tile = dest.getTile(xtile, ytile); for (int y = tile.getMinY(), iy = 0; iy < tile.getHeight(); y++, iy++) { testPointCS.setY(0, y + 0.5); for (int x = tile.getMinX(), ix = 0; ix < tile.getWidth(); x++, ix++) { testPointCS.setX(0, x + 0.5); testPoint.geometryChanged(); int expected = poly.intersects(testPoint) ? 1 : 0; assertEquals( "Failed test at position " + x + ", " + y + ", " + "expected " + expected + " but got " + tile.getSample(x, y, 0), expected, tile.getSample(x, y, 0)); } } } } }
protected RenderedOp resizeJAI( int thumbWidth, int thumbHeight, RenderedOp image, boolean scaleComputed) { double scale = 1.0; if (!scaleComputed) { int imageWidth = image.getWidth(); int imageHeight = image.getHeight(); scale = computeEvenResizeScale(thumbWidth, thumbHeight, imageWidth, imageHeight); } RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); return JAI.create("SubsampleAverage", image, scale, scale, qualityHints); }
/** * Crop down an image to smaller dimensions. Used by resizeImage() when an image dimension is * smaller. * * @param image the source image to crop * @param toWidth the new width to crop to * @param toHeight the new height to crop to * @return a cropped image as RenderedOp * @see #resizeImage(RenderedOp, int, int) */ public static RenderedOp cropImage(RenderedOp image, int toWidth, int toHeight) { int width = image.getWidth(); int height = image.getHeight(); int xOffset = (width - toWidth) / 2; int yOffset = (height - toHeight) / 2; ParameterBlock params = new ParameterBlock(); params.addSource(image); params.add((float) xOffset); // x origin params.add((float) yOffset); // y origin params.add((float) toWidth); // width params.add((float) toHeight); // height return JAI.create("crop", params, null); // $NON-NLS-1$ }
@Test public void testRoundTripTiledImage() throws Exception { BufferedImage input = ImageIO.read(sourceFile); // prepare a tiled image layout ImageLayout il = new ImageLayout(input); il.setTileWidth(8); il.setTileHeight(8); RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il); RenderedOp tiled = FormatDescriptor.create(input, input.getSampleModel().getDataType(), hints); assertEquals(8, tiled.getTileWidth()); assertEquals(8, tiled.getTileHeight()); roundTripPNGJ(input, tiled); }
/** * Test Read exploiting JAI-ImageIO tools capabilities * * @throws FileNotFoundException * @throws IOException */ @Test public void read() throws FileNotFoundException, IOException { if (!isGDALAvailable) { return; } final ParameterBlockJAI pbjImageRead; String fileName = "utmByte.tif"; final File file = TestData.file(this, fileName); pbjImageRead = new ParameterBlockJAI("ImageRead"); pbjImageRead.setParameter("Input", new FileImageInputStreamExtImpl(file)); pbjImageRead.setParameter("Reader", new GeoTiffImageReaderSpi().createReaderInstance()); RenderedOp image = JAI.create("ImageRead", pbjImageRead); if (TestData.isInteractiveTest()) Viewer.visualizeAllInformation(image, "", true); else Assert.assertNotNull(image.getTiles()); }
// 이미지 폭 public int getImageWidth(String pathname) { int width = -1; File file = new File(pathname); if (!file.exists()) return width; ParameterBlock pb = new ParameterBlock(); pb.add(pathname); RenderedOp rOp = JAI.create("fileload", pb); BufferedImage bi = rOp.getAsBufferedImage(); width = bi.getWidth(); return width; }
// 이미지 높이 public int getImageHeight(String pathname) { int height = -1; File file = new File(pathname); if (!file.exists()) return height; ParameterBlock pb = new ParameterBlock(); pb.add(pathname); RenderedOp rOp = JAI.create("fileload", pb); BufferedImage bi = rOp.getAsBufferedImage(); height = bi.getHeight(); return height; }
private void extremaOp(TiffMeta surface, GridCoverage2D gridCoverage2D) { double min = Double.MAX_VALUE; double max = Double.MIN_VALUE; RenderedImage img = gridCoverage2D.getRenderedImage(); RenderedOp extremaOp = ExtremaDescriptor.create(img, null, 10, 10, false, 1, null); double[] allMins = (double[]) extremaOp.getProperty("minimum"); min = Doubles.min(allMins); double[] allMaxs = (double[]) extremaOp.getProperty("maximum"); max = Doubles.max(allMaxs); surface.setMaxVal(max); surface.setMinVal(min); }
public static void main(String[] args) { final File swapDir = new File("swap"); swapDir.mkdir(); final Logger logger = Logger.getAnonymousLogger(); final ConsoleHandler consoleHandler = new ConsoleHandler(); consoleHandler.setLevel(Level.ALL); logger.addHandler(consoleHandler); logger.setLevel(Level.ALL); final DefaultSwapSpace swapSpace = new DefaultSwapSpace(swapDir, logger); final SwappingTileCache tileCache = new SwappingTileCache(64 * M, swapSpace); JAI.getDefaultInstance().setTileCache(tileCache); RenderedImage sourceImage; if (args.length == 0) { sourceImage = DFTTestMain.createTestImage(512, 512); } else { sourceImage = FileLoadDescriptor.create(args[0], null, false, null); } sourceImage = DFTConvolveRIF.toFloat(sourceImage, null); BorderExtender extender = BorderExtender.createInstance(BorderExtender.BORDER_COPY); RenderingHints hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); KernelJAI kernel = createBlurrKernel(33); RenderedOp convolvedImage = ConvolveDescriptor.create(sourceImage, kernel, hints); RenderedOp dftConvolvedImage = DFTConvolveDescriptor.create(sourceImage, kernel, null, hints); RenderedOp deltaImage = MultiplyConstDescriptor.create( SubtractDescriptor.create(convolvedImage, dftConvolvedImage, null), new double[] {2}, null); showImage(sourceImage, "sourceImage"); showImage(convolvedImage, "convolvedImage"); showImage(dftConvolvedImage, "dftConvolvedImage"); showImage(deltaImage, "deltaImage"); final Object o = dftConvolvedImage.getProperty("kernelFT"); System.out.println("o = " + o); System.out.println("Kernel\tConvolve\tDFTConvolve\tPerfGain"); for (int i = 3; i <= 201; i += 2) { KernelJAI k = createBlurrKernel(i); double t1 = getRenderTime(ConvolveDescriptor.create(sourceImage, k, hints)); double t2 = getRenderTime(DFTConvolveDescriptor.create(sourceImage, k, null, hints)); System.out.println(i + "\t" + t1 + "\t" + t2 + "\t" + t1 / t2); } }
/** * Resize an image to the new dimensions - no scaling is performed on the image, but the canvas * size is changed. Any empty areas are filled with white. * * @param image the source image to resize * @param toWidth the new width to resize to * @param toHeight the new height to resize to * @param color the color to fill borders when resizing up. * @return the resized image as RenderedOp */ public static RenderedOp resizeImage(RenderedOp image, int toWidth, int toHeight, double color) { int width = image.getWidth(); int height = image.getHeight(); if (width > toWidth || height > toHeight) { image = cropImage(image, Math.min(width, toWidth), Math.min(height, toHeight)); } if (width < toWidth || height < toHeight) { int w = Math.max((toWidth - width) / 2, 0); int h = Math.max((toHeight - height) / 2, 0); image = borderImage(image, w, w, h, h, color); } return image; }
@Test public void test2BandsBug() { // build a transparent image BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_BYTE_GRAY); RenderedOp img = BandMergeDescriptor.create(null, 0, false, null, image, image); image = img.getAsBufferedImage(); // create a palette out of it RenderedImage indexed = quantize(image); assertTrue(indexed.getColorModel() instanceof IndexColorModel); IndexColorModel icm = (IndexColorModel) indexed.getColorModel(); // png encoder go mad if they get a one element palette, we need at // least two assertEquals(2, icm.getMapSize()); }
/** * This method downscales a band by a given factor * * @param inputBand - the input band * @param scalingFactor - the scaling factor * @return Band - the downscaled band */ public static Band downscaleBand(Band inputBand, float scalingFactor) { final RenderedImage sourceImage = inputBand.getSourceImage(); final RenderedOp downscaledImage = ScaleDescriptor.create( sourceImage, 1.0f / scalingFactor, 1.0f / scalingFactor, 0.0f, 0.0f, Interpolation.getInstance(Interpolation.INTERP_NEAREST), null); Band downscaledBand = new Band( inputBand.getName(), inputBand.getDataType(), downscaledImage.getWidth(), downscaledImage.getHeight()); downscaledBand.setSourceImage(downscaledImage); return downscaledBand; }
public static ImageHeader load(Properties imageProperties, File imageDir) throws IOException { int dataType = Integer.parseInt(imageProperties.getProperty("dataType")); int minX = Integer.parseInt(imageProperties.getProperty("minX", "0")); int minY = Integer.parseInt(imageProperties.getProperty("minY", "0")); int width = Integer.parseInt(imageProperties.getProperty("width")); int height = Integer.parseInt(imageProperties.getProperty("height")); int tileGridXOffset = Integer.parseInt(imageProperties.getProperty("tileGridXOffset", "0")); int tileGridYOffset = Integer.parseInt(imageProperties.getProperty("tileGridYOffset", "0")); int tileWidth = Integer.parseInt(imageProperties.getProperty("tileWidth")); int tileHeight = Integer.parseInt(imageProperties.getProperty("tileHeight")); int numberOfBits = Integer.parseInt(imageProperties.getProperty("numberOfBits", "0")); String tileFormat = imageProperties.getProperty("tileFormat", "raw.zip"); SampleModel sampleModel; ColorModel colorModel; if (tileFormat.startsWith("raw")) { if (numberOfBits == 1 || numberOfBits == 2 || numberOfBits == 4) { sampleModel = new MultiPixelPackedSampleModel(dataType, tileWidth, tileHeight, numberOfBits); } else { sampleModel = ImageUtils.createSingleBandedSampleModel(dataType, tileWidth, tileHeight); } colorModel = null; } else { RenderedOp tile00 = FileLoadDescriptor.create( new File(imageDir, "0-0." + tileFormat).getPath(), null, true, null); sampleModel = tile00.getSampleModel().createCompatibleSampleModel(tileWidth, tileHeight); colorModel = tile00.getColorModel(); } ImageLayout imageLayout = new ImageLayout( minX, minY, width, height, tileGridXOffset, tileGridYOffset, tileWidth, tileHeight, sampleModel, colorModel); return new ImageHeader(imageLayout, tileFormat); }
protected boolean writeImage(String dstPath, String type, RenderedOp image) { boolean success = false; // JAI doesn't natively support GIF encoding, but Java ImageIO does. if (type.toLowerCase().equals("gif")) { File dst = new File(dstPath); // if the file doesn't exist, create it and make sure we can write to it. if (!dst.exists()) { try { dst.createNewFile(); } catch (IOException ioe) { this.core.app.logError( ErrorReporter.errorMsg(this.getClass(), "resizeImg") + "Failed to create tmp img at \"" + dstPath + "\".", ioe); } } if (!dst.canWrite()) { dst.setWritable(true); } try { ImageIO.write(image, type.toUpperCase(), dst); } catch (IOException ioe) { this.core.app.logError( ErrorReporter.errorMsg(this.getClass(), "resizeImg") + "Failed to write image data to \"" + dstPath + "\".", ioe); } // clean up file handle dst = null; } else { if (type.toLowerCase().equals("jpg")) { type = "jpeg"; } JAI.create("filestore", image, dstPath, type); } if (image != null && new File(dstPath).exists()) { success = true; } // JAI Cleanup image.dispose(); image = null; type = null; return success; }
@Test public void testAddAndGetTile() throws InterruptedException, FileNotFoundException, IOException { // Input stream to use ImageInputStream stream_in = null; try { stream_in = new FileImageInputStream(TestData.file(this, "world.tiff")); // Input RenderedImage to use final RenderedOp input = ImageReadDescriptor.create( stream_in, 0, false, false, false, null, null, null, null, null); // Boolean used for checking if the conditions are passed final AtomicBoolean passed = new AtomicBoolean(true); // Cache creation final ConcurrentTileCacheMultiMap cache = new ConcurrentTileCacheMultiMap(1000 * 1000, false, 1f, 4); // Selection of one tile from the image Raster data = input.getTile(input.getMinTileX(), input.getMinTileY()); // Setting the tile inside the cache cache.add(input, input.getMinTileX(), input.getMinTileY(), data); // Thread pool to use for doing concurrent access on the cache ThreadPoolExecutor executor = new ThreadPoolExecutor( TOTAL, TOTAL, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000000)); // Latch used for waiting all the threads to end their work final CountDownLatch latch = new CountDownLatch(TOTAL); // Cycle for launching various requests int counter = TOTAL; while (counter > 0) { executor.execute( new Runnable() { public void run() { // Get the tile to use Raster data = cache.getTile(input, input.getMinTileX(), input.getMinTileY()); if (data == null) { passed.getAndSet(false); } latch.countDown(); } }); // Counter update counter--; } // Waiting all threads to finish latch.await(); // Ensure that all the threads have found the tile Assert.assertTrue(passed.get()); } finally { try { if (stream_in != null) { stream_in.flush(); stream_in.close(); } } catch (Throwable t) { // } } }
@Test public void testOptimizedWarp() throws Exception { // do it again, make sure the image does not turn black since GridCoverage2D ushortCoverage = EXAMPLES.get(5); GridCoverage2D coverage = project(ushortCoverage, CRS.parseWKT(GOOGLE_MERCATOR_WKT), null, "nearest", null, true); RenderedImage ri = coverage.getRenderedImage(); ImageWorker.WARP_REDUCTION_ENABLED = false; AffineTransform at = new AffineTransform(0.4, 0, 0, 0.5, -200, -200); RenderedOp fullChain = (RenderedOp) new ImageWorker(ri) .affine( at, Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0}) .getRenderedImage(); assertEquals("Scale", fullChain.getOperationName()); fullChain.getTiles(); ImageWorker.WARP_REDUCTION_ENABLED = true; RenderedOp reduced = (RenderedOp) new ImageWorker(ri) .affine( at, Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0}) .getRenderedImage(); // force computation, to make sure it does not throw exceptions reduced.getTiles(); // check the chain has been reduced assertEquals("Warp", reduced.getOperationName()); assertEquals(1, reduced.getSources().size()); assertSame(ushortCoverage.getRenderedImage(), reduced.getSourceImage(0)); // check the bounds of the output image has not changed assertEquals(fullChain.getBounds(), reduced.getBounds()); }
public static RenderedImage getImageOp(RenderedImage image, String opName) { if (image instanceof RenderedOp) { RenderedOp op = (RenderedOp) image; if (op.getOperationName().equalsIgnoreCase(opName)) { return image; } while (op.getNumSources() > 0) { try { PlanarImage img = op.getSourceImage(0); if (image instanceof RenderedOp) { RenderedOp op2 = (RenderedOp) img; if (op2.getOperationName().equalsIgnoreCase(opName)) { return img; } } } catch (Exception ex) { return null; } } } return null; }
// This method is similar to the testBandMerge method but it tests the ExtendedBandMergeOpImage // class private void testExtendedBandMerge(RenderedImage[] sources, boolean noDataUsed, boolean roiUsed) { // Optional No Data Range used Range[] noData; // Source image data type int dataType = sources[0].getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } // ROI to use ROI roi = null; if (roiUsed) { roi = roiData; } // New array ofr the transformed source images RenderedOp[] translated = new RenderedOp[sources.length]; List<AffineTransform> transform = new ArrayList<AffineTransform>(); for (int i = 0; i < sources.length; i++) { // Translation coefficients int xTrans = (int) (Math.random() * 10); int yTrans = (int) (Math.random() * 10); // Translation operation AffineTransform tr = AffineTransform.getTranslateInstance(xTrans, yTrans); // Addition to the transformations list transform.add(tr); // Translation of the image translated[i] = TranslateDescriptor.create(sources[i], (float) xTrans, (float) yTrans, null, null); } // Definition of the final image dimensions ImageLayout layout = new ImageLayout(); layout.setMinX(sources[0].getMinX()); layout.setMinY(sources[0].getMinY()); layout.setWidth(sources[0].getWidth()); layout.setHeight(sources[0].getHeight()); RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); // BandMerge operation RenderedOp merged = BandMergeDescriptor.create(noData, destNoData, hints, transform, roi, translated); Assert.assertNotNull(merged.getTiles()); // Check if the bands number is the same assertEquals(BAND_NUMBER, merged.getNumBands()); // Upper-Left tile indexes int minTileX = merged.getMinTileX(); int minTileY = merged.getMinTileY(); // Raster object Raster upperLeftTile = merged.getTile(minTileX, minTileY); // Tile bounds int minX = upperLeftTile.getMinX(); int minY = upperLeftTile.getMinY(); int maxX = upperLeftTile.getWidth() + minX; int maxY = upperLeftTile.getHeight() + minY; // Source corners final int dstMinX = merged.getMinX(); final int dstMinY = merged.getMinY(); final int dstMaxX = merged.getMaxX(); final int dstMaxY = merged.getMaxY(); Point2D ptDst = new Point2D.Double(0, 0); Point2D ptSrc = new Point2D.Double(0, 0); // Cycle on all the tile Bands for (int b = 0; b < BAND_NUMBER; b++) { RandomIter iter = RandomIterFactory.create(translated[b], null); // Source corners final int srcMinX = translated[b].getMinX(); final int srcMinY = translated[b].getMinY(); final int srcMaxX = translated[b].getMaxX(); final int srcMaxY = translated[b].getMaxY(); // Cycle on the y-axis for (int x = minX; x < maxX; x++) { // Cycle on the x-axis for (int y = minY; y < maxY; y++) { // Calculated value double value = upperLeftTile.getSampleDouble(x, y, b); // If the tile pixels are outside the image bounds, then no data is set. if (x < dstMinX || x >= dstMaxX || y < dstMinY || y >= dstMaxY) { value = destNoData; } // Set the x,y destination pixel location ptDst.setLocation(x, y); // Map destination pixel to source pixel transform.get(b).transform(ptDst, ptSrc); // Source pixel indexes int srcX = round(ptSrc.getX()); int srcY = round(ptSrc.getY()); double valueOld = destNoData; // Check if the pixel is inside the source bounds if (!(srcX < srcMinX || srcX >= srcMaxX || srcY < srcMinY || srcY >= srcMaxY)) { // Old band value valueOld = iter.getSampleDouble(srcX, srcY, 0); } // ROI CHECK boolean contained = true; if (roiUsed) { if (!roi.contains(x, y)) { contained = false; // Comparison if the final value is not inside a ROI assertEquals(value, destNoData, TOLERANCE); } } if (contained) { // If no Data are present, no data check is performed if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: byte sampleB = ImageUtil.clampRoundByte(value); byte sampleBOld = ImageUtil.clampRoundByte(valueOld); if (noData[0].contains(sampleBOld)) { assertEquals(sampleB, destNoData, TOLERANCE); } else { assertEquals(sampleB, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_USHORT: short sampleUS = ImageUtil.clampRoundUShort(value); short sampleUSOld = ImageUtil.clampRoundUShort(valueOld); if (noData[0].contains(sampleUSOld)) { assertEquals(sampleUS, destNoData, TOLERANCE); } else { assertEquals(sampleUS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_SHORT: short sampleS = ImageUtil.clampRoundShort(value); short sampleSOld = ImageUtil.clampRoundShort(valueOld); if (noData[0].contains(sampleSOld)) { assertEquals(sampleS, destNoData, TOLERANCE); } else { assertEquals(sampleS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_INT: int sampleI = ImageUtil.clampRoundInt(value); int sampleIOld = ImageUtil.clampRoundInt(valueOld); if (noData[0].contains(sampleIOld)) { assertEquals(sampleI, destNoData, TOLERANCE); } else { assertEquals(sampleI, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_FLOAT: float sampleF = ImageUtil.clampFloat(value); float sampleFOld = ImageUtil.clampFloat(valueOld); if (noData[0].contains(sampleFOld)) { assertEquals(sampleF, destNoData, TOLERANCE); } else { assertEquals(sampleF, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: if (noData[0].contains(valueOld)) { assertEquals(value, destNoData, TOLERANCE); } else { assertEquals(value, valueOld, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } else { // Else a simple value comparison is done assertEquals(value, valueOld, TOLERANCE); } } } } } // Disposal of the output image merged.dispose(); }
private void testBandMerge(RenderedImage[] sources, boolean noDataUsed, boolean roiUsed) { // Optional No Data Range used Range[] noData; // Source image data type int dataType = sources[0].getSampleModel().getDataType(); // If no Data are present, the No Data Range associated is used if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: noData = noDataByte; break; case DataBuffer.TYPE_USHORT: noData = noDataUShort; break; case DataBuffer.TYPE_SHORT: noData = noDataShort; break; case DataBuffer.TYPE_INT: noData = noDataInt; break; case DataBuffer.TYPE_FLOAT: noData = noDataFloat; break; case DataBuffer.TYPE_DOUBLE: noData = noDataDouble; break; default: throw new IllegalArgumentException("Wrong data type"); } } else { noData = null; } // ROI to use ROI roi = null; if (roiUsed) { roi = roiData; } // BandMerge operation RenderedOp merged = BandMergeDescriptor.create(noData, destNoData, null, null, roi, sources); // Check if the bands number is the same assertEquals(BAND_NUMBER, merged.getNumBands()); // Upper-Left tile indexes int minTileX = merged.getMinTileX(); int minTileY = merged.getMinTileY(); // Raster object Raster upperLeftTile = merged.getTile(minTileX, minTileY); // Tile bounds int minX = upperLeftTile.getMinX(); int minY = upperLeftTile.getMinY(); int maxX = upperLeftTile.getWidth() + minX; int maxY = upperLeftTile.getHeight() + minY; // Cycle on all the tile Bands for (int b = 0; b < BAND_NUMBER; b++) { // Selection of the source raster associated with the band Raster bandRaster = sources[b].getTile(minTileX, minTileY); // Cycle on the y-axis for (int x = minX; x < maxX; x++) { // Cycle on the x-axis for (int y = minY; y < maxY; y++) { // Calculated value double value = upperLeftTile.getSampleDouble(x, y, b); // Old band value double valueOld = bandRaster.getSampleDouble(x, y, 0); // ROI CHECK boolean contained = true; if (roiUsed) { if (!roi.contains(x, y)) { contained = false; // Comparison if the final value is not inside a ROI assertEquals(value, destNoData, TOLERANCE); } } if (contained) { // If no Data are present, no data check is performed if (noDataUsed) { switch (dataType) { case DataBuffer.TYPE_BYTE: byte sampleB = ImageUtil.clampRoundByte(value); byte sampleBOld = ImageUtil.clampRoundByte(valueOld); if (noData[0].contains(sampleBOld)) { assertEquals(sampleB, destNoData, TOLERANCE); } else { assertEquals(sampleB, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_USHORT: short sampleUS = ImageUtil.clampRoundUShort(value); short sampleUSOld = ImageUtil.clampRoundUShort(valueOld); if (noData[0].contains(sampleUSOld)) { assertEquals(sampleUS, destNoData, TOLERANCE); } else { assertEquals(sampleUS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_SHORT: short sampleS = ImageUtil.clampRoundShort(value); short sampleSOld = ImageUtil.clampRoundShort(valueOld); if (noData[0].contains(sampleSOld)) { assertEquals(sampleS, destNoData, TOLERANCE); } else { assertEquals(sampleS, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_INT: int sampleI = ImageUtil.clampRoundInt(value); int sampleIOld = ImageUtil.clampRoundInt(valueOld); if (noData[0].contains(sampleIOld)) { assertEquals(sampleI, destNoData, TOLERANCE); } else { assertEquals(sampleI, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_FLOAT: float sampleF = ImageUtil.clampFloat(value); float sampleFOld = ImageUtil.clampFloat(valueOld); if (noData[0].contains(sampleFOld)) { assertEquals(sampleF, destNoData, TOLERANCE); } else { assertEquals(sampleF, valueOld, TOLERANCE); } break; case DataBuffer.TYPE_DOUBLE: if (noData[0].contains(valueOld)) { assertEquals(value, destNoData, TOLERANCE); } else { assertEquals(value, valueOld, TOLERANCE); } break; default: throw new IllegalArgumentException("Wrong data type"); } } else { // Else a simple value comparison is done assertEquals(value, valueOld, TOLERANCE); } } } } } // Disposal of the output image merged.dispose(); }
@DescribeResult(name = "result", description = "The contours feature collection") public SimpleFeatureCollection execute( @DescribeParameter(name = "data", description = "The raster to be used as the source") GridCoverage2D gc2d, @DescribeParameter( name = "band", description = "The source image band to process", min = 0, max = 1) Integer band, @DescribeParameter(name = "levels", description = "Values for which to generate contours") double[] levels, @DescribeParameter( name = "interval", description = "Interval between contour values (ignored if levels arg is supplied)", min = 0) Double interval, @DescribeParameter( name = "simplify", description = "Values for which to generate contours", min = 0) Boolean simplify, @DescribeParameter( name = "smooth", description = "Values for which to generate contours", min = 0) Boolean smooth, @DescribeParameter( name = "roi", description = "The geometry used to delineate the area of interest in model space", min = 0) Geometry roi, ProgressListener progressListener) throws ProcessException { // // initial checks // if (gc2d == null) { throw new ProcessException("Invalid input, source grid coverage should be not null"); } if (band != null && (band < 0 || band >= gc2d.getNumSampleDimensions())) { throw new ProcessException("Invalid input, invalid band number:" + band); } boolean hasValues = !(levels == null || levels.length == 0); if (!hasValues && interval == null) { throw new ProcessException("One between interval and values must be valid"); } // switch to geophisics if necessary gc2d = gc2d.view(ViewType.GEOPHYSICS); // // GRID TO WORLD preparation // final AffineTransform mt2D = (AffineTransform) gc2d.getGridGeometry().getGridToCRS2D(PixelOrientation.CENTER); // get the list of nodata, if any List<Object> noDataList = new ArrayList<Object>(); for (GridSampleDimension sd : gc2d.getSampleDimensions()) { // grab all the explicit nodata final double[] sdNoData = sd.getNoDataValues(); if (sdNoData != null) { for (double nodata : sdNoData) { noDataList.add(nodata); } } // handle also readers setting up nodata in a category with a specific name if (sd.getCategories() != null) { for (Category cat : sd.getCategories()) { if (cat.getName().equals(NO_DATA)) { final NumberRange<? extends Number> catRange = cat.getRange(); if (catRange.getMinimum() == catRange.getMaximum()) { noDataList.add(catRange.getMinimum()); } else { Range<Double> noData = new Range<Double>( catRange.getMinimum(), catRange.isMinIncluded(), catRange.getMaximum(), catRange.isMaxIncluded()); noDataList.add(noData); } } } } } // get the rendered image final RenderedImage raster = gc2d.getRenderedImage(); // perform jai operation ParameterBlockJAI pb = new ParameterBlockJAI("Contour"); pb.setSource("source0", raster); if (roi != null) { pb.setParameter("roi", CoverageUtilities.prepareROI(roi, mt2D)); } if (band != null) { pb.setParameter("band", band); } if (interval != null) { pb.setParameter("interval", interval); } else { final ArrayList<Double> elements = new ArrayList<Double>(levels.length); for (double level : levels) elements.add(level); pb.setParameter("levels", elements); } if (simplify != null) { pb.setParameter("simplify", simplify); } if (smooth != null) { pb.setParameter("smooth", smooth); } if (noDataList != null) { pb.setParameter("nodata", noDataList); } final RenderedOp dest = JAI.create("Contour", pb); @SuppressWarnings("unchecked") final Collection<LineString> prop = (Collection<LineString>) dest.getProperty(ContourDescriptor.CONTOUR_PROPERTY_NAME); // wrap as a feature collection and return final SimpleFeatureType schema = CoverageUtilities.createFeatureType(gc2d, LineString.class); final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema); int i = 0; final ListFeatureCollection featureCollection = new ListFeatureCollection(schema); final AffineTransformation jtsTransformation = new AffineTransformation( mt2D.getScaleX(), mt2D.getShearX(), mt2D.getTranslateX(), mt2D.getShearY(), mt2D.getScaleY(), mt2D.getTranslateY()); for (LineString line : prop) { // get value Double value = (Double) line.getUserData(); line.setUserData(null); // filter coordinates in place line.apply(jtsTransformation); // create feature and add to list builder.set("the_geom", line); builder.set("value", value); featureCollection.add(builder.buildFeature(String.valueOf(i++))); } // return value return featureCollection; }
/** * Quick method for populating the {@link CUDABean} instance provided. * * @param bean * @param reference * @param coverage * @param geo * @param transform * @throws IOException * @throws MismatchedDimensionException * @throws TransformException */ private void populateBean( CUDABean bean, boolean reference, GridCoverage2D coverage, Geometry geo, MathTransform transform, int buffer) throws IOException, MismatchedDimensionException, TransformException { RenderedImage image = coverage.getRenderedImage(); // 0) Check if a buffer must be applied Geometry originalGeo = (Geometry) geo.clone(); if (buffer > 0) { try { if (!"EPSG:4326" .equals(CRS.lookupIdentifier(coverage.getCoordinateReferenceSystem(), false))) { geo = geo.buffer(buffer); } else { geo = geo.buffer(buffer / 111.128); } } catch (FactoryException e) { geo = geo.buffer(buffer); } } // 1) Crop the two coverages with the selected Geometry GridCoverage2D crop = CROP.execute(coverage, geo, null); transform = ProjectiveTransform.create( (AffineTransform) crop.getGridGeometry().getGridToCRS(PixelInCell.CELL_CORNER)) .inverse(); // 2) Extract the BufferedImage from each image image = crop.getRenderedImage(); Rectangle rectIMG = new Rectangle(image.getMinX(), image.getMinY(), image.getWidth(), image.getHeight()); ImageWorker w = new ImageWorker(image); BufferedImage buf = w.getBufferedImage(); if (image instanceof RenderedOp) { ((RenderedOp) image).dispose(); } // 3) Generate an array of data from each image Raster data = buf.getData(); final DataBufferByte db = (DataBufferByte) data.getDataBuffer(); byte[] byteData = db.getData(); if (reference) { // 4) Transform the Geometry to Raster space Geometry rs = JTS.transform(geo, transform); Geometry rsFilter = JTS.transform(geo.difference(originalGeo), transform); ROI roiGeo = new ROIGeometry(rs); ROI roiFilter = new ROIGeometry(rsFilter); // 5) Extract an array of data from the transformed ROI byte[] roiData = getROIData((buffer > 0 ? roiFilter : roiGeo), rectIMG); bean.setRoi(roiData); bean.setRoiObj(roiGeo); // 6) Setting the Coverage data array bean.setReferenceImage(byteData); // 7) Setting the Image dimensions bean.setHeight(rectIMG.height); bean.setWidth(rectIMG.width); bean.setMinX(rectIMG.x); bean.setMinY(rectIMG.y); } else { // 6) Setting the Coverage data array bean.setCurrentImage(byteData); } // 7) Store the Reference Covergae containing the geospatial info bean.setReferenceCoverage(coverage); }
/** * Overrides to use the same method to slice the tiles than {@code MetatileMapOutputFormat} so the * GeoServer settings such as use native accel are leveraged in the same way when calling {@link * RenderedImageMapResponse#formatImageOutputStream}, * * @see org.geowebcache.layer.MetaTile#createTile(int, int, int, int) */ @Override public RenderedImage createTile( final int x, final int y, final int tileWidth, final int tileHeight) { // check image type final int type; if (metaTileImage instanceof PlanarImage) { type = 1; } else if (metaTileImage instanceof BufferedImage) { type = 2; } else { type = 0; } // now do the splitting RenderedImage tile; switch (type) { case 0: // do a crop, and then turn it into a buffered image so that we can release // the image chain RenderedOp cropped = GTCropDescriptor.create( metaTileImage, Float.valueOf(x), Float.valueOf(y), Float.valueOf(tileWidth), Float.valueOf(tileHeight), NO_CACHE); tile = cropped.getAsBufferedImage(); disposeLater(cropped); break; case 1: final PlanarImage pImage = (PlanarImage) metaTileImage; final WritableRaster wTile = WritableRaster.createWritableRaster( pImage.getSampleModel().createCompatibleSampleModel(tileWidth, tileHeight), new Point(x, y)); Rectangle sourceArea = new Rectangle(x, y, tileWidth, tileHeight); sourceArea = sourceArea.intersection(pImage.getBounds()); // copying the data to ensure we don't have side effects when we clean the cache pImage.copyData(wTile); if (wTile.getMinX() != 0 || wTile.getMinY() != 0) { tile = new BufferedImage( pImage.getColorModel(), (WritableRaster) wTile.createTranslatedChild(0, 0), pImage.getColorModel().isAlphaPremultiplied(), null); } else { tile = new BufferedImage( pImage.getColorModel(), wTile, pImage.getColorModel().isAlphaPremultiplied(), null); } break; case 2: final BufferedImage image = (BufferedImage) metaTileImage; tile = image.getSubimage(x, y, tileWidth, tileHeight); break; default: throw new IllegalStateException( Errors.format( ErrorKeys.ILLEGAL_ARGUMENT_$2, "metaTile class", metaTileImage.getClass().toString())); } return tile; }
/** * Executes the raster to vector process. * * @param coverage the input grid coverage * @param band the coverage band to process; defaults to 0 if {@code null} * @param insideEdges whether boundaries between raster regions with data values (ie. not NODATA) * should be returned; defaults to {@code true} if {@code null} * @param roi optional polygonal {@code Geometry} to define a sub-area within which vectorizing * will be done * @param noDataValues optional list of values to treat as NODATA; regions with these values will * not be represented in the returned features; if {@code null}, 0 is used as the single * NODATA value; ignored if {@code classificationRanges} is provided * @param classificationRanges optional list of {@code Range} objects to pre-classify the input * coverage prior to vectorizing; values not included in the list will be treated as NODATA; * values in the first {@code Range} are classified to 1, those in the second {@code Range} to * 2 etc. * @param progressListener an optional listener * @return a feature collection where each feature has a {@code Polygon} ("the_geom") and an * attribute "value" with value of the corresponding region in either {@code coverage} or the * classified coverage (when {@code classificationRanges} is used) * @throws ProcessException */ @DescribeResult(name = "result", description = "The polygon feature collection") public SimpleFeatureCollection execute( @DescribeParameter(name = "data", description = "The raster to be used as the source") GridCoverage2D coverage, @DescribeParameter( name = "band", description = "(Integer, default=0) the source image band to process", min = 0) Integer band, @DescribeParameter( name = "insideEdges", description = "(Boolean, default=true) whether to vectorize boundaries between adjacent regions with non-outside values", min = 0) Boolean insideEdges, @DescribeParameter( name = "roi", description = "The geometry used to delineate the area of interest in model space", min = 0) Geometry roi, @DescribeParameter( name = "nodata", description = "Collection<Number>, default={0}) values to treat as NODATA", collectionType = Number.class, min = 0) Collection<Number> noDataValues, @DescribeParameter( name = "ranges", description = "The list of ranges to be applied. \n" + "Each range is expressed as 'OPEN START ; END CLOSE'\n" + "where 'OPEN:=(|[, CLOSE=)|]',\n " + "START is the low value, or nothing to imply -INF,\n" + "CLOSE is the biggest value, or nothing to imply +INF", collectionType = Range.class, min = 0) List<Range> classificationRanges, ProgressListener progressListener) throws ProcessException { // // initial checks // if (coverage == null) { throw new ProcessException("Invalid input, source grid coverage should be not null"); } if (band == null) { band = 0; } else if (band < 0 || band >= coverage.getNumSampleDimensions()) { throw new ProcessException("Invalid input, invalid band number:" + band); } // do we have classification ranges? boolean hasClassificationRanges = classificationRanges != null && classificationRanges.size() > 0; // apply the classification by setting 0 as the default value and using 1, ..., numClasses for // the other classes. // we use 0 also as the noData for the resulting coverage. if (hasClassificationRanges) { final RangeLookupProcess lookup = new RangeLookupProcess(); coverage = lookup.execute(coverage, band, classificationRanges, progressListener); } // Use noDataValues to set the "outsideValues" parameter of the Vectorize // operation unless classificationRanges are in use, in which case the // noDataValues arg is ignored. List<Number> outsideValues = new ArrayList<Number>(); if (noDataValues != null && !hasClassificationRanges) { outsideValues.addAll(noDataValues); } else { outsideValues.add(0); } // // GRID TO WORLD preparation // final AffineTransform mt2D = (AffineTransform) coverage.getGridGeometry().getGridToCRS2D(PixelOrientation.UPPER_LEFT); // get the rendered image final RenderedImage raster = coverage.getRenderedImage(); // perform jai operation ParameterBlockJAI pb = new ParameterBlockJAI("Vectorize"); pb.setSource("source0", raster); if (roi != null) { pb.setParameter("roi", CoverageUtilities.prepareROI(roi, mt2D)); } pb.setParameter("band", band); pb.setParameter("outsideValues", outsideValues); if (insideEdges != null) { pb.setParameter("insideEdges", insideEdges); } // pb.setParameter("removeCollinear", false); final RenderedOp dest = JAI.create("Vectorize", pb); @SuppressWarnings("unchecked") final Collection<Polygon> prop = (Collection<Polygon>) dest.getProperty(VectorizeDescriptor.VECTOR_PROPERTY_NAME); // wrap as a feature collection and return final SimpleFeatureType featureType = CoverageUtilities.createFeatureType(coverage, Polygon.class); final SimpleFeatureBuilder builder = new SimpleFeatureBuilder(featureType); int i = 0; final ListFeatureCollection featureCollection = new ListFeatureCollection(featureType); final AffineTransformation jtsTransformation = new AffineTransformation( mt2D.getScaleX(), mt2D.getShearX(), mt2D.getTranslateX(), mt2D.getShearY(), mt2D.getScaleY(), mt2D.getTranslateY()); for (Polygon polygon : prop) { // get value Double value = (Double) polygon.getUserData(); polygon.setUserData(null); // filter coordinates in place polygon.apply(jtsTransformation); // create feature and add to list builder.set("the_geom", polygon); builder.set("value", value); featureCollection.add(builder.buildFeature(String.valueOf(i++))); } // return value return featureCollection; }