private void readAllTiledRasters( final List<RasterQueryInfo> queries, final TiledRasterReader rasterReader, final LoggingHelper log) throws IOException { for (RasterQueryInfo queryInfo : queries) { final Long rasterId = queryInfo.getRasterId(); final RenderedImage rasterImage; try { final int pyramidLevel = queryInfo.getPyramidLevel(); final GridEnvelope matchingTiles = queryInfo.getMatchingTiles(); // final Point imageLocation = queryInfo.getTiledImageSize().getLocation(); rasterImage = rasterReader.read(rasterId, pyramidLevel, matchingTiles); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Fetching data for " + queryInfo.toString(), e); throw e; } queryInfo.setResultImage(rasterImage); { LOGGER.finer(queryInfo.toString()); log.appendLoggingGeometries(LoggingHelper.MOSAIC_EXPECTED, queryInfo.getMosaicLocation()); log.appendLoggingGeometries(LoggingHelper.MOSAIC_ENV, queryInfo.getResultEnvelope()); // final Rectangle tiledImageSize = queryInfo.getTiledImageSize(); // int width = rasterImage.getWidth(); // int height = rasterImage.getHeight(); // if (tiledImageSize.width != width || tiledImageSize.height != height) { // throw new IllegalStateException( // "Read image is not of the expected size. Image=" + width + "x" + height // + ", expected: " + tiledImageSize.width + "x" // + tiledImageSize.height); // } } } }
/** * For each raster: crop->scale->translate->add to mosaic * * @param queries * @param mosaicGeometry * @return * @throws IOException */ private RenderedImage createMosaic( final List<RasterQueryInfo> queries, final GridEnvelope mosaicGeometry, final LoggingHelper log) throws IOException { List<RenderedImage> transformed = new ArrayList<RenderedImage>(queries.size()); /* * Do we need to expand to RGB color space and then create a new colormapped image with the * whole mosaic? */ boolean expandCM = queries.size() > 1 && rasterInfo.isColorMapped(); if (expandCM) { LOGGER.fine( "Creating mosaic out of " + queries.size() + " colormapped rasters. The mosaic tiles will be expanded to " + "\nRGB space and the resulting mosaic reduced to a new IndexColorModel"); } for (RasterQueryInfo query : queries) { RenderedImage image = query.getResultImage(); log.log(image, query.getRasterId(), "01_original"); if (expandCM) { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer( "Creating color expanded version of tile for raster #" + query.getRasterId()); } /* * reformat the image as a 4 band rgba backed by byte data */ image = FormatDescriptor.create(image, Integer.valueOf(DataBuffer.TYPE_BYTE), null); log.log(image, query.getRasterId(), "04_1_colorExpanded"); } image = cropToRequiredDimension(image, query.getResultGridRange()); log.log(image, query.getRasterId(), "02_crop"); // Raster data = image.getData(); // image = new BufferedImage(image.getColorModel(), (WritableRaster) data, false, null); if (queries.size() == 1) { return image; } final GridEnvelope mosaicLocation = query.getMosaicLocation(); // scale Float scaleX = Float.valueOf(((float) mosaicLocation.getSpan(0) / image.getWidth())); Float scaleY = Float.valueOf(((float) mosaicLocation.getSpan(1) / image.getHeight())); Float translateX = Float.valueOf(0); Float translateY = Float.valueOf(0); if (!(Float.valueOf(1.0F).equals(scaleX) && Float.valueOf(1.0F).equals(scaleY))) { ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(scaleX); pb.add(scaleY); pb.add(translateX); pb.add(translateY); pb.add(new InterpolationNearest()); image = JAI.create("scale", pb); log.log(image, query.getRasterId(), "03_scale"); int width = image.getWidth(); int height = image.getHeight(); assert mosaicLocation.getSpan(0) == width; assert mosaicLocation.getSpan(1) == height; } if (image.getMinX() != mosaicLocation.getLow(0) || image.getMinY() != mosaicLocation.getLow(1)) { // translate ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(Float.valueOf(mosaicLocation.getLow(0) - image.getMinX())); pb.add(Float.valueOf(mosaicLocation.getLow(1) - image.getMinY())); pb.add(null); image = JAI.create("translate", pb); log.log(image, query.getRasterId(), "04_translate"); assert image.getMinX() == mosaicLocation.getLow(0) : image.getMinX() + " != " + mosaicLocation.getLow(0); assert image.getMinY() == mosaicLocation.getLow(1) : image.getMinY() + " != " + mosaicLocation.getLow(1); assert image.getWidth() == mosaicLocation.getSpan(0) : image.getWidth() + " != " + mosaicLocation.getSpan(0); assert image.getHeight() == mosaicLocation.getSpan(1) : image.getHeight() + " != " + mosaicLocation.getSpan(1); } transformed.add(image); } final RenderedImage mosaic; if (queries.size() == 1) { /* * This is besides a very slight perf improvement needed because the JAI mosaic * operation truncates floating point raster values to 0 and 1. REVISIT: If there's no * workaround for that we should prevent raster catalogs made of floating point rasters * and throw an exception as we could not really support that. */ mosaic = transformed.get(0); } else { /* * adapted from RasterLayerResponse.java in the imagemosaic module */ ParameterBlockJAI mosaicParams = new ParameterBlockJAI("Mosaic"); mosaicParams.setParameter("mosaicType", MosaicDescriptor.MOSAIC_TYPE_OVERLAY); // set background values to raster's no-data double[] backgroundValues; if (expandCM) { backgroundValues = new double[] {0, 0, 0, 0}; } else { final int numBands = rasterInfo.getNumBands(); backgroundValues = new double[numBands]; final int rasterIndex = 0; Number noDataValue; for (int bn = 0; bn < numBands; bn++) { noDataValue = rasterInfo.getNoDataValue(rasterIndex, bn); backgroundValues[bn] = noDataValue.doubleValue(); } } mosaicParams.setParameter("backgroundValues", backgroundValues); final ImageLayout layout = new ImageLayout( mosaicGeometry.getLow(0), mosaicGeometry.getLow(1), mosaicGeometry.getSpan(0), mosaicGeometry.getSpan(1)); final int tileWidth = rasterInfo.getTileDimension(0).width; final int tileHeight = rasterInfo.getTileDimension(0).height; layout.setTileWidth(tileWidth); layout.setTileHeight(tileHeight); final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); for (RenderedImage img : transformed) { mosaicParams.addSource(img); log.appendLoggingGeometries(LoggingHelper.MOSAIC_RESULT, img); } log.log(LoggingHelper.MOSAIC_RESULT); LOGGER.fine("Creating mosaic out of " + queries.size() + " raster tiles"); mosaic = JAI.create("Mosaic", mosaicParams, hints); log.log(mosaic, 0L, "05_mosaic_result"); } return mosaic; }
/** * @see GridCoverageReader#read(GeneralParameterValue[]) * @return A new {@linkplain GridCoverage grid coverage} from the input source, or {@code null} if * the requested envelope is outside the data bounds */ public GridCoverage2D read(GeneralParameterValue[] params) throws IOException { final GeneralEnvelope requestedEnvelope; final GridEnvelope requestedDim; final OverviewPolicy overviewPolicy; { final ReadParameters opParams = parseReadParams(params); overviewPolicy = opParams.overviewPolicy; requestedEnvelope = opParams.requestedEnvelope; requestedDim = opParams.dim; } /* * For each raster in the raster dataset, obtain the tiles, pixel range, and resulting * envelope */ final List<RasterQueryInfo> queries; queries = findMatchingRasters(requestedEnvelope, requestedDim, overviewPolicy); if (queries.isEmpty()) { if (requestedEnvelope.intersects(getOriginalEnvelope(), true)) { /* * No matching rasters but envelopes intersect, meaning it's a raster catalog with * irregular coverage and the request lies on an area with no coverage */ ImageTypeSpecifier imageTypeSpecifier; imageTypeSpecifier = RasterUtils.createFullImageTypeSpecifier(rasterInfo, 0); SampleModel sampleModel = imageTypeSpecifier.getSampleModel(); Point location = new Point(0, 0); WritableRaster raster = Raster.createWritableRaster(sampleModel, location); GridCoverage2D emptyCoverage; emptyCoverage = coverageFactory.create(coverageName, raster, requestedEnvelope); return emptyCoverage; } /* * none of the rasters match the requested envelope. */ return null; } final LoggingHelper log = new LoggingHelper(); /* * Once we collected the matching rasters and their image subsets, find out where in the * overall resulting mosaic they fit. If the rasters does not share the spatial resolution, * the QueryInfo.resultDimension and QueryInfo.mosaicLocation width or height won't match */ final GridEnvelope mosaicGeometry; mosaicGeometry = RasterUtils.setMosaicLocations(rasterInfo, queries); if (mosaicGeometry.getSpan(0) == 0 || mosaicGeometry.getSpan(1) == 0) { LOGGER.finer( "Mosaic geometry width or height is zero," + " returning fake coverage for pixels " + mosaicGeometry); return null; } /* * Gather the rendered images for each of the rasters that match the requested envelope */ final TiledRasterReader rasterReader = rasterReaderFactory.create(rasterInfo); try { readAllTiledRasters(queries, rasterReader, log); } finally { // rasterReader.dispose(); } log.log(LoggingHelper.REQ_ENV); log.log(LoggingHelper.RES_ENV); log.log(LoggingHelper.MOSAIC_ENV); log.log(LoggingHelper.MOSAIC_EXPECTED); final RenderedImage coverageRaster = createMosaic(queries, mosaicGeometry, log); assert mosaicGeometry.getSpan(0) == coverageRaster.getWidth(); assert mosaicGeometry.getSpan(1) == coverageRaster.getHeight(); /* * BUILDING COVERAGE */ GridSampleDimension[] bands = getSampleDimensions(coverageRaster); final GeneralEnvelope resultEnvelope = getResultEnvelope(queries, mosaicGeometry); log.appendLoggingGeometries(LoggingHelper.REQ_ENV, requestedEnvelope); log.appendLoggingGeometries(LoggingHelper.RES_ENV, resultEnvelope); GridCoverage2D resultCoverage = coverageFactory.create(coverageName, coverageRaster, resultEnvelope, bands, null, null); // MathTransform gridToCRS = rasterInfo.getRasterToModel(queries.get(0).getRasterIndex(), // queries.get(0).getPyramidLevel()); // // GridGeometry2D gridGeometry = new GridGeometry2D(PixelInCell.CELL_CORNER, gridToCRS, // resultEnvelope, hints); // // GridCoverage[] sources = null; // Map<?, ?> properties = null; // GridCoverage2D resultCoverage = coverageFactory.create(coverageName, coverageRaster, // gridGeometry, bands, sources, properties); return resultCoverage; }