/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2009, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*/
package org.geotools.arcsde.raster.gce;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.media.jai.ImageLayout;
import javax.media.jai.InterpolationNearest;
import javax.media.jai.JAI;
import javax.media.jai.OpImage;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.operator.FormatDescriptor;
import javax.media.jai.operator.MosaicDescriptor;
import org.geotools.arcsde.raster.info.RasterDatasetInfo;
import org.geotools.arcsde.raster.info.RasterQueryInfo;
import org.geotools.arcsde.raster.info.RasterUtils;
import org.geotools.arcsde.raster.io.RasterReaderFactory;
import org.geotools.arcsde.raster.io.TiledRasterReader;
import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.GridSampleDimension;
import org.geotools.coverage.TypeMap;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.data.DefaultServiceInfo;
import org.geotools.data.ServiceInfo;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.ColorInterpretation;
import org.opengis.coverage.grid.Format;
import org.opengis.coverage.grid.GridCoverage;
import org.opengis.coverage.grid.GridCoverageReader;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValue;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
/**
*
* @author Gabriel Roldan (OpenGeo)
* @since 2.5.4
* @version $Id$
*
* @source $URL$
* http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org
* /geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java $
*/
@SuppressWarnings("nls")
public final class ArcSDEGridCoverage2DReaderJAI extends AbstractGridCoverage2DReader {
private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce");
/**
* @see LoggingHelper#log(RenderedImage, Long, String)
*/
private static final boolean DEBUG_TO_DISK = Boolean
.getBoolean("org.geotools.arcsde.gce.debug");
private final ArcSDERasterFormat parent;
private final RasterDatasetInfo rasterInfo;
private DefaultServiceInfo serviceInfo;
private RasterReaderFactory rasterReaderFactory;
public ArcSDEGridCoverage2DReaderJAI(final ArcSDERasterFormat parent,
final RasterReaderFactory rasterReaderFactory, final RasterDatasetInfo rasterInfo,
final Hints hints) throws IOException {
// check it's a supported format
{
final int bitsPerSample = rasterInfo.getBand(0, 0).getCellType().getBitsPerSample();
if (rasterInfo.getNumBands() > 1 && (bitsPerSample == 1 || bitsPerSample == 4)) {
throw new IllegalArgumentException(bitsPerSample
+ "-bit rasters with more than one band are not supported");
}
}
this.parent = parent;
this.rasterReaderFactory = rasterReaderFactory;
this.rasterInfo = rasterInfo;
super.hints = hints;
super.coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints);
super.crs = rasterInfo.getCoverageCrs();
super.originalEnvelope = rasterInfo.getOriginalEnvelope(PixelInCell.CELL_CENTER);
GridEnvelope gridRange = rasterInfo.getOriginalGridRange();
// super.originalGridRange = new GeneralGridRange(gridRange.toRectangle());
super.originalGridRange = gridRange;
super.coverageName = rasterInfo.getRasterTable();
final int numLevels = rasterInfo.getNumPyramidLevels(0);
// level 0 is not an overview, but the raster itself
super.numOverviews = numLevels - 1;
// ///
//
// setting the higher resolution avalaible for this coverage
//
// ///
highestRes = super.getResolution(originalEnvelope,
new Rectangle(originalGridRange.getLow(0), originalGridRange.getLow(1),
originalGridRange.getSpan(0), originalGridRange.getSpan(1)), crs);
// //
//
// get information for the successive images
//
// //
// REVISIT may the different rasters in the raster dataset have different pyramid levels? I
// guess so
if (numOverviews > 0) {
overViewResolutions = new double[numOverviews][2];
for (int pyramidLevel = 1; pyramidLevel <= numOverviews; pyramidLevel++) {
GridEnvelope levelGridRange = rasterInfo.getGridRange(0, pyramidLevel);
GeneralEnvelope levelEnvelope = rasterInfo.getGridEnvelope(0, pyramidLevel);
Rectangle2D levelGridRangeRect = new Rectangle2D.Double(levelGridRange.getLow(0),
levelGridRange.getLow(1), levelGridRange.getSpan(0),
levelGridRange.getSpan(1));
overViewResolutions[pyramidLevel - 1] = super.getResolution(levelEnvelope,
levelGridRangeRect, crs);
}
} else {
overViewResolutions = null;
}
}
/**
* @see GridCoverageReader#getFormat()
*/
public Format getFormat() {
return parent;
}
@Override
public ServiceInfo getInfo() {
if (serviceInfo == null) {
serviceInfo = new DefaultServiceInfo();
serviceInfo.setTitle(rasterInfo.getRasterTable());
serviceInfo.setDescription(rasterInfo.toString());
Set<String> keywords = new HashSet<String>();
keywords.add("ArcSDE");
serviceInfo.setKeywords(keywords);
}
return serviceInfo;
}
/**
* @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;
}
private GeneralEnvelope toPixelCenter(double[] resolution, GeneralEnvelope pixelCornerEnv) {
double deltaX = resolution[0] / 2;
double deltaY = resolution[1] / 2;
GeneralEnvelope env = new GeneralEnvelope(pixelCornerEnv.getCoordinateReferenceSystem());
env.setEnvelope(pixelCornerEnv.getMinimum(0) + deltaX, pixelCornerEnv.getMinimum(1)
+ deltaY, pixelCornerEnv.getMaximum(0) - deltaX, pixelCornerEnv.getMaximum(1)
- deltaY);
return env;
}
private GridSampleDimension[] getSampleDimensions(final RenderedImage coverageRaster)
throws IOException {
GridSampleDimension[] bands = rasterInfo.getGridSampleDimensions();
// may the image have been promoted? build the correct band info then
final int imageBands = coverageRaster.getSampleModel().getNumBands();
if (bands.length == 1 && imageBands > 1) {
LOGGER.fine(coverageName + " was promoted from 1 to "
+ coverageRaster.getSampleModel().getNumBands()
+ " bands, returning an appropriate set of GridSampleDimension");
// stolen from super.createCoverage:
final ColorModel cm = coverageRaster.getColorModel();
bands = new GridSampleDimension[imageBands];
// setting bands names.
for (int i = 0; i < imageBands; i++) {
final ColorInterpretation colorInterpretation;
colorInterpretation = TypeMap.getColorInterpretation(cm, i);
if (colorInterpretation == null) {
throw new IOException("Unrecognized sample dimension type");
}
bands[i] = new GridSampleDimension(colorInterpretation.name()).geophysics(true);
}
}
return bands;
}
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);
// }
}
}
}
private List<RasterQueryInfo> findMatchingRasters(final GeneralEnvelope requestedEnvelope,
final GridEnvelope requestedDim, final OverviewPolicy overviewPolicy) {
final List<RasterQueryInfo> matchingQueries;
matchingQueries = RasterUtils.findMatchingRasters(rasterInfo, requestedEnvelope,
requestedDim, overviewPolicy);
if (matchingQueries.isEmpty()) {
return matchingQueries;
}
for (RasterQueryInfo match : matchingQueries) {
RasterUtils.fitRequestToRaster(requestedEnvelope, rasterInfo, match);
}
return matchingQueries;
}
private GeneralEnvelope getResultEnvelope(final List<RasterQueryInfo> queryInfos,
final GridEnvelope mosaicGeometry) {
// use the same queryInfo used by setMosaicLocations
final RasterQueryInfo baseQueryInfo = RasterUtils.findLowestResolution(queryInfos);
GeneralEnvelope finalEnvelope = null;
// if (queryInfos.size() == 1) {
// finalEnvelope = queryInfos.get(0).getResultEnvelope();
// } else {
int rasterIndex = baseQueryInfo.getRasterIndex();
int pyramidLevel = baseQueryInfo.getPyramidLevel();
MathTransform rasterToModel = rasterInfo.getRasterToModel(rasterIndex, pyramidLevel);
CoordinateReferenceSystem coverageCrs = rasterInfo.getCoverageCrs();
GeneralEnvelope mosaicGeometryEnv = new GeneralEnvelope(coverageCrs);
mosaicGeometryEnv.setEnvelope(mosaicGeometry.getLow(0), mosaicGeometry.getLow(1),
1 + mosaicGeometry.getHigh(0), 1 + mosaicGeometry.getHigh(1));
try {
finalEnvelope = CRS.transform(rasterToModel, mosaicGeometryEnv);
finalEnvelope.setCoordinateReferenceSystem(coverageCrs);
} catch (TransformException e) {
throw new RuntimeException(e);
}
// }
// double[] resolution = baseQueryInfo.getResolution();
// finalEnvelope = toPixelCenter(resolution, finalEnvelope);
return finalEnvelope;
}
/**
* 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;
}
/**
* Crops the image representing a full tile set to the required dimension and returns it, but
* keeps minx and miny being zero.
*
* @param fullTilesRaster
* @param tiledImageGridRange
* @param cropTo
* @return
*/
private RenderedImage cropToRequiredDimension(final RenderedImage fullTilesRaster,
final GridEnvelope cropTo) {
GridEnvelope2D crop = new GridEnvelope2D(cropTo.getLow(0), cropTo.getLow(1),
cropTo.getSpan(0), cropTo.getSpan(1));
GridEnvelope2D origDim = new GridEnvelope2D(fullTilesRaster.getMinX(),
fullTilesRaster.getMinY(), fullTilesRaster.getWidth(), fullTilesRaster.getHeight());
if (!origDim.contains(crop)) {
throw new IllegalArgumentException("Original image (" + origDim
+ ") does not contain desired dimension (" + crop + ")");
} else if (origDim.equals(crop)) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("No need to crop image, full tiled dimension and target one "
+ "do match: original: " + fullTilesRaster.getWidth() + "x"
+ fullTilesRaster.getHeight() + ", target: " + crop.getSpan(0) + "x"
+ crop.getSpan(1));
}
return fullTilesRaster;
}
ParameterBlock cropParams = new ParameterBlock();
cropParams.addSource(fullTilesRaster);// Source
// cropParams.add(Float.valueOf(cropTo.x - tiledImageGridRange.x)); // x origin for each
// band
// cropParams.add(Float.valueOf(cropTo.y - tiledImageGridRange.y)); // y origin for each
// band
cropParams.add(Float.valueOf(crop.getLow(0))); // x origin for each band
cropParams.add(Float.valueOf(crop.getLow(1))); // y origin for each band
cropParams.add(Float.valueOf(crop.getSpan(0)));// width for each band
cropParams.add(Float.valueOf(crop.getSpan(1)));// height for each band
final RenderingHints hints = new RenderingHints(JAI.KEY_OPERATION_BOUND,
OpImage.OP_NETWORK_BOUND);
RenderedImage image = JAI.create("Crop", cropParams, hints);
// assert cropTo.x - tiledImageGridRange.x == image.getMinX();
// assert cropTo.y - tiledImageGridRange.y == image.getMinY();
assert crop.getLow(0) == image.getMinX();
assert crop.getLow(1) == image.getMinY();
assert crop.getSpan(0) == image.getWidth();
assert crop.getSpan(1) == image.getHeight();
// assert cropTo.x == image.getMinX();
// assert cropTo.y == image.getMinY();
// assert cropTo.width == image.getWidth();
// assert cropTo.height == image.getHeight();
return image;
}
static class ReadParameters {
GeneralEnvelope requestedEnvelope;
GridEnvelope dim;
OverviewPolicy overviewPolicy;
}
private ArcSDEGridCoverage2DReaderJAI.ReadParameters parseReadParams(
final GeneralParameterValue[] params) throws IllegalArgumentException {
if (params == null) {
throw new IllegalArgumentException("No GeneralParameterValue given to read operation");
}
GeneralEnvelope reqEnvelope = null;
GridEnvelope dim = null;
OverviewPolicy overviewPolicy = null;
// /////////////////////////////////////////////////////////////////////
//
// Checking params
//
// /////////////////////////////////////////////////////////////////////
for (int i = 0; i < params.length; i++) {
final ParameterValue<?> param = (ParameterValue<?>) params[i];
final String name = param.getDescriptor().getName().getCode();
if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString())) {
final GridGeometry2D gg = (GridGeometry2D) param.getValue();
reqEnvelope = new GeneralEnvelope((Envelope) gg.getEnvelope2D());
final GeneralEnvelope coverageEnvelope = getOriginalEnvelope();
CoordinateReferenceSystem nativeCrs = coverageEnvelope
.getCoordinateReferenceSystem();
CoordinateReferenceSystem requestCrs = reqEnvelope.getCoordinateReferenceSystem();
if (!CRS.equalsIgnoreMetadata(nativeCrs, requestCrs)) {
LOGGER.fine("Request CRS and native CRS differ, "
+ "reprojecting request envelope to native CRS");
ReferencedEnvelope nativeCrsEnv;
nativeCrsEnv = toNativeCrs(reqEnvelope, nativeCrs);
reqEnvelope = new GeneralEnvelope(nativeCrsEnv);
}
dim = gg.getGridRange2D();
continue;
}
if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName().toString())) {
overviewPolicy = (OverviewPolicy) param.getValue();
continue;
}
}
if (reqEnvelope == null && dim == null) {
reqEnvelope = getOriginalEnvelope();
dim = getOriginalGridRange();
}
if (reqEnvelope == null) {
reqEnvelope = getOriginalEnvelope();
}
if (dim == null) {
final GeneralEnvelope adjustedGRange;
try {
MathTransform gridToWorld = getOriginalGridToWorld(PixelInCell.CELL_CENTER);
MathTransform worldToGrid = gridToWorld.inverse();
adjustedGRange = CRS.transform(worldToGrid, reqEnvelope);
} catch (Exception e) {
throw new RuntimeException(e);
}
int xmin = (int) Math.floor(adjustedGRange.getMinimum(0));
int ymin = (int) Math.floor(adjustedGRange.getMinimum(1));
int xmax = (int) Math.ceil(adjustedGRange.getMaximum(0));
int ymax = (int) Math.ceil(adjustedGRange.getMaximum(1));
dim = new GridEnvelope2D(xmin, ymin, xmax - xmin, ymax - ymin);
}
if (!reqEnvelope.intersects(getOriginalEnvelope(), true)) {
throw new IllegalArgumentException(
"The requested extend does not overlap the coverage extent: "
+ getOriginalEnvelope());
}
if (dim.getSpan(0) <= 0 || dim.getSpan(1) <= 0) {
throw new IllegalArgumentException("The requested coverage dimension can't be null: "
+ dim);
}
if (overviewPolicy == null) {
overviewPolicy = OverviewPolicy.NEAREST;
LOGGER.finer("No overview policy requested, defaulting to " + overviewPolicy);
}
LOGGER.fine("Overview policy is " + overviewPolicy);
ArcSDEGridCoverage2DReaderJAI.ReadParameters parsedParams = new ArcSDEGridCoverage2DReaderJAI.ReadParameters();
parsedParams.requestedEnvelope = reqEnvelope;
parsedParams.dim = dim;
parsedParams.overviewPolicy = overviewPolicy;
return parsedParams;
}
private static ReferencedEnvelope toNativeCrs(final GeneralEnvelope requestedEnvelope,
final CoordinateReferenceSystem nativeCRS) throws IllegalArgumentException {
ReferencedEnvelope reqEnv = toReferencedEnvelope(requestedEnvelope);
if (!CRS.equalsIgnoreMetadata(nativeCRS, reqEnv.getCoordinateReferenceSystem())) {
// we're being reprojected. We'll need to reproject reqEnv into
// our native coordsys
try {
// ReferencedEnvelope origReqEnv = reqEnv;
reqEnv = reqEnv.transform(nativeCRS, true);
} catch (FactoryException fe) {
// unable to reproject?
throw new IllegalArgumentException("Unable to find a reprojection from requested "
+ "coordsys to native coordsys for this request", fe);
} catch (TransformException te) {
throw new IllegalArgumentException("Unable to perform reprojection from requested "
+ "coordsys to native coordsys for this request", te);
}
}
return reqEnv;
}
private static ReferencedEnvelope toReferencedEnvelope(GeneralEnvelope envelope) {
double minx = envelope.getMinimum(0);
double maxx = envelope.getMaximum(0);
double miny = envelope.getMinimum(1);
double maxy = envelope.getMaximum(1);
CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem();
ReferencedEnvelope refEnv = new ReferencedEnvelope(minx, maxx, miny, maxy, crs);
return refEnv;
}
/**
* A simple helper class to guard and easy logging the mosaic geometries in both geographical
* and pixel ranges
*/
private static class LoggingHelper {
private static final File debugDir = new File(System.getProperty("user.home")
+ File.separator + "arcsde_test");
static {
if (DEBUG_TO_DISK) {
debugDir.mkdir();
}
}
public Level GEOM_LEVEL = Level.FINER;
public static String REQ_ENV = "Requested envelope";
public static String RES_ENV = "Resulting envelope";
public static String MOSAIC_ENV = "Resulting mosaiced envelopes";
public static String MOSAIC_EXPECTED = "Expected mosaic layout (in pixels)";
public static String MOSAIC_RESULT = "Resulting image mosaic layout (in pixels)";
private Map<String, StringBuilder> geoms = null;
LoggingHelper() {
// not much to to
}
private StringBuilder getGeom(String geomName) {
if (geoms == null) {
geoms = new HashMap<String, StringBuilder>();
}
StringBuilder sb = geoms.get(geomName);
if (sb == null) {
sb = new StringBuilder("MULTIPOLYGON(\n");
geoms.put(geomName, sb);
}
return sb;
}
public void appendLoggingGeometries(String geomName, RenderedImage img) {
if (LOGGER.isLoggable(GEOM_LEVEL)) {
appendLoggingGeometries(geomName, new GridEnvelope2D(img.getMinX(), img.getMinY(),
img.getWidth(), img.getHeight()));
}
}
public void appendLoggingGeometries(String geomName, GridEnvelope env) {
if (LOGGER.isLoggable(GEOM_LEVEL)) {
appendLoggingGeometries(geomName,
new GeneralEnvelope(new Rectangle2D.Double(env.getLow(0), env.getLow(1),
env.getSpan(0), env.getSpan(1))));
}
}
public void appendLoggingGeometries(String geomName, GeneralEnvelope env) {
if (LOGGER.isLoggable(GEOM_LEVEL)) {
StringBuilder sb = getGeom(geomName);
sb.append(" ((" + env.getMinimum(0) + " " + env.getMinimum(1) + ", "
+ env.getMaximum(0) + " " + env.getMinimum(1) + ", " + env.getMaximum(0)
+ " " + env.getMaximum(1) + ", " + env.getMinimum(0) + " "
+ env.getMaximum(1) + ", " + env.getMinimum(0) + " " + env.getMinimum(1)
+ ")),");
}
}
public void log(String geomName) {
if (LOGGER.isLoggable(GEOM_LEVEL)) {
StringBuilder sb = getGeom(geomName);
sb.setLength(sb.length() - 1);
sb.append("\n)");
LOGGER.log(GEOM_LEVEL, geomName + ":\n" + sb.toString());
}
}
public void log(RenderedImage image, Long rasterId, String fileName) {
if (DEBUG_TO_DISK) {
LOGGER.warning("BEWARE THE DEBUG FLAG IS TURNED ON! "
+ "IF IN PRODUCTION THIS IS A SEVERE MISTAKE!!!");
// ImageIO.write(FormatDescriptor.create(image,
// Integer.valueOf(DataBuffer.TYPE_BYTE),
// null), "TIFF", new File(debugDir, rasterId.longValue() + fileName + ".tiff"));
try {
ImageIO.write(image, "TIFF", new File(debugDir, rasterId.longValue() + fileName
+ ".tiff"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}