Search in sources :

Example 1 with SpatialImageReader

use of org.geotoolkit.image.io.SpatialImageReader in project geotoolkit by Geomatys.

the class WorldFileImageReader method createMetadata.

/**
 * Creates a new stream or image metadata. This method first delegates to the main reader as
 * documented in the {@linkplain ImageReaderAdapter#createMetadata(int) super-class method},
 * then completes the metadata with information read from the <cite>World File</cite> and
 * <cite>Map Projection</cite> files.
 * <p>
 * The <cite>World File</cite> and <cite>Map Projection</cite> files are determined by calls
 * to the {@link #createInput(String)} method with {@code "tfw"} and {@code "prj"} argument
 * values. Subclasses can override the later method if they want to specify different files
 * to be read.
 */
@Override
protected SpatialMetadata createMetadata(final int imageIndex) throws IOException {
    SpatialMetadata metadata = super.createMetadata(imageIndex);
    if (imageIndex >= 0) {
        AffineTransform gridToCRS = null;
        CoordinateReferenceSystem crs = null;
        Object in = getVerifiedInput("tfw");
        if (in != null) {
            gridToCRS = SupportFiles.parseTFW(IOUtilities.open(in), in);
        }
        in = getVerifiedInput("prj");
        if (in != null) {
            crs = PrjFiles.read(IOUtilities.open(in), true);
        }
        /*
             * If we have found information in TFW or PRJ files, complete metadata.
             */
        if (gridToCRS != null || crs != null) {
            // -- if exist some metadata from sub reader complete them, else create new spatial metadata
            if (main instanceof SpatialImageReader) {
                metadata = ((SpatialImageReader) main).getImageMetadata(imageIndex);
            } else {
                metadata = new SpatialMetadata(false, this, null);
            }
            if (gridToCRS != null) {
                final int width = getWidth(imageIndex);
                final int height = getHeight(imageIndex);
                new GridDomainAccessor(metadata).setAll(gridToCRS, new Rectangle(width, height), null, PixelOrientation.UPPER_LEFT);
            }
            if (crs != null) {
                new ReferencingBuilder(metadata).setCoordinateReferenceSystem(crs);
            }
        }
    }
    return metadata;
}
Also used : SpatialMetadata(org.geotoolkit.image.io.metadata.SpatialMetadata) ReferencingBuilder(org.geotoolkit.image.io.metadata.ReferencingBuilder) Rectangle(java.awt.Rectangle) AffineTransform(java.awt.geom.AffineTransform) SpatialImageReader(org.geotoolkit.image.io.SpatialImageReader) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GridDomainAccessor(org.geotoolkit.internal.image.io.GridDomainAccessor)

Example 2 with SpatialImageReader

use of org.geotoolkit.image.io.SpatialImageReader in project geotoolkit by Geomatys.

the class ImageCoverageReader method getGridGeometry.

/**
 * Returns the grid geometry for the {@link GridCoverage2D} to be read at the given index.
 * The default implementation performs the following:
 * <p>
 * <ul>
 *   <li>The {@link GridExtent} is determined from the
 *       {@linkplain SpatialImageReader#getGridEnvelope(int) spatial image reader}
 *       if possible, or from the image {@linkplain ImageReader#getWidth(int) width}
 *       and {@linkplain ImageReader#getHeight(int) height} otherwise.</li>
 *   <li>The {@link CoordinateReferenceSystem} and the "<cite>grid to CRS</cite>" conversion
 *       are determined from the {@link SpatialMetadata} if any.</li>
 * </ul>
 *
 * @return The grid geometry for the {@link GridCoverage} at the specified index.
 * @throws IllegalStateException If the input source has not been set.
 * @throws IndexOutOfBoundsException If the supplied index is out of bounds.
 * @throws CoverageStoreException If an error occurs while reading the information from the input source.
 * @throws CancellationException If {@link #abort()} has been invoked in an other thread during
 *         the execution of this method.
 *
 * @see ImageReader#getWidth(int)
 * @see ImageReader#getHeight(int)
 */
public GridGeometry getGridGeometry() throws DataStoreException {
    final int index = 0;
    GridGeometry gridGeometry = getCached(gridGeometries, index);
    if (gridGeometry == null) {
        // Protect from changes.
        final ImageReader imageReader = this.imageReader;
        if (imageReader == null) {
            throw new IllegalStateException(formatErrorMessage(Errors.Keys.NoImageInput));
        }
        /*
             * Get the required information from the SpatialMetadata, if any.
             * For now we just collect them - they will be processed later.
             */
        CoordinateReferenceSystem crs = null;
        MathTransform gridToCRS = null;
        PixelOrientation pointInPixel = null;
        final int width, height;
        try {
            width = imageReader.getWidth(index);
            height = imageReader.getHeight(index);
            final SpatialMetadata metadata = getImageMetadata(imageReader, index);
            if (metadata != null) {
                crs = metadata.getInstanceForType(CoordinateReferenceSystem.class);
                if (crs == null) {
                    crs = PredefinedCRS.GRID_2D;
                }
                if (crs instanceof GridGeometry) {
                    // Some formats (e.g. NetCDF) do that.
                    gridToCRS = ((GridGeometry) crs).getGridToCRS(PixelInCell.CELL_CENTER);
                } else {
                    final RectifiedGrid grid = metadata.getInstanceForType(RectifiedGrid.class);
                    if (grid != null) {
                        gridToCRS = getMetadataHelper().getGridToCRS(grid);
                    }
                    final Georectified georect = metadata.getInstanceForType(Georectified.class);
                    if (georect != null) {
                        pointInPixel = georect.getPointInPixel();
                    }
                }
            }
        } catch (IOException e) {
            throw new CoverageStoreException(formatErrorMessage(e), e);
        }
        /*
             * If any metadata are still null, replace them by their default values. Those default
             * values are selected in order to be as neutral as possible: An ImageCRS which is not
             * convertible to GeodeticCRS, an identity "grid to CRS" conversion, a PixelOrientation
             * equivalent to performing no shift at all in the "grid to CRS" conversion.
             */
        if (crs == null) {
            crs = PredefinedCRS.GRID_2D;
        }
        final int dimension = crs.getCoordinateSystem().getDimension();
        if (gridToCRS == null) {
            gridToCRS = MathTransforms.identity(dimension);
        }
        if (pointInPixel == null) {
            pointInPixel = PixelOrientation.CENTER;
        }
        /*
             * Now build the grid geometry. Note that the grid extent spans shall be set to 1
             * for all dimensions other than X and Y, even if the original file has more data,
             * since this is a GridGeometry2D requirement.
             */
        final long[] lower = new long[dimension];
        final long[] upper = new long[dimension];
        Arrays.fill(upper, 1);
        upper[X_DIMENSION] = width;
        upper[Y_DIMENSION] = height;
        final GridExtent gridExtent = new GridExtent(null, lower, upper, false);
        final PixelInCell pixelInCell = pointInPixel == PixelOrientation.CENTER ? PixelInCell.CELL_CENTER : PixelInCell.CELL_CORNER;
        gridGeometry = new GridGeometry(gridExtent, pixelInCell, gridToCRS, crs);
        Map.Entry<Map<Integer, GridGeometry>, GridGeometry> entry = setCached(gridGeometry, gridGeometries, index);
        gridGeometries = entry.getKey();
        gridGeometry = entry.getValue();
    }
    return gridGeometry;
}
Also used : PixelOrientation(org.opengis.metadata.spatial.PixelOrientation) GridGeometry(org.apache.sis.coverage.grid.GridGeometry) GridExtent(org.apache.sis.coverage.grid.GridExtent) MathTransform(org.opengis.referencing.operation.MathTransform) SpatialMetadata(org.geotoolkit.image.io.metadata.SpatialMetadata) IOException(java.io.IOException) Georectified(org.opengis.metadata.spatial.Georectified) PixelInCell(org.opengis.referencing.datum.PixelInCell) RectifiedGrid(org.opengis.coverage.grid.RectifiedGrid) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) ImageReader(javax.imageio.ImageReader) SpatialImageReader(org.geotoolkit.image.io.SpatialImageReader) Map(java.util.Map) HashMap(java.util.HashMap)

Example 3 with SpatialImageReader

use of org.geotoolkit.image.io.SpatialImageReader in project geotoolkit by Geomatys.

the class ImageCoverageReader method read.

/**
 * Converts geodetic parameters to image parameters, reads the image and wraps it in a
 * grid coverage. First, this method creates an initially empty block of image parameters
 * by invoking the {@link #createImageReadParam(int)} method. The image parameter
 * {@linkplain ImageReadParam#setSourceRegion source region},
 * {@linkplain ImageReadParam#setSourceSubsampling source subsampling} and
 * {@linkplain ImageReadParam#setSourceBands source bands} are computed from the
 * parameter given to this {@code read} method. Then, the following image parameters
 * are set (if the image parameter class allows such settings):
 * <p>
 * <ul>
 *   <li><code>{@linkplain SpatialImageReadParam#setSampleConversionAllowed
 *       setSampleConversionAllowed}({@linkplain SampleConversionType#REPLACE_FILL_VALUES
 *       REPLACE_FILL_VALUES}, true)</code> in order to allow the replacement of
 *       fill values by {@link Float#NaN NaN}.</li>
 *
 *   <li><code>{@linkplain SpatialImageReadParam#setSampleConversionAllowed
 *       setSampleConversionAllowed}({@linkplain SampleConversionType#SHIFT_SIGNED_INTEGERS
 *       SHIFT_SIGNED_INTEGERS}, true)</code> if the sample dimensions declare an unsigned
 *       range of sample values.</li>
 *
 *   <li><code>{@linkplain MosaicImageReadParam#setSubsamplingChangeAllowed
 *       setSubsamplingChangeAllowed}(true)</code> in order to allow {@link MosaicImageReader}
 *       to use a different resolution than the requested one. This is crucial from a
 *       performance point of view. Since the {@code GridCoverageReader} contract does not
 *       guarantee that the grid geometry of the returned coverage is the requested geometry,
 *       we are allowed to do that.</li>
 * </ul>
 * <p>
 * Finally, the image is read and wrapped in a {@link GridCoverage2D} using the
 * information provided by {@link #getGridGeometry(int)} and {@link #getSampleDimensions(int)}.
 *
 * /!\ If {@link org.geotoolkit.coverage.io.GridCoverageReadParam#setDeferred(boolean)} parameter is set to true, the
 * returned coverage will rely on the current reader to cache it's data on the fly, so you CANNOT dispose of the current
 * reader while your using the resulting coverage.
 *
 * @param  param Optional parameters used to control the reading process, or {@code null}.
 * @return The {@link GridCoverage} at the specified index.
 * @throws IllegalStateException if the input source has not been set.
 * @throws IndexOutOfBoundsException if the supplied index is out of bounds.
 * @throws CoverageStoreException If an error occurs while reading the information from the input source.
 * @throws CancellationException If {@link #abort()} has been invoked in an other thread during
 *         the execution of this method.
 *
 * @see ImageReader#read(int)
 */
public GridCoverage2D read(final GridCoverageReadParam param) throws DataStoreException, CancellationException {
    final int index = 0;
    final boolean loggingEnabled = isLoggable();
    long fullTime = (loggingEnabled) ? System.nanoTime() : 0;
    ignoreGridTransforms = !loggingEnabled;
    /*
         * Parameters check.
         */
    abortRequested = false;
    // Protect from changes.
    final ImageReader imageReader = this.imageReader;
    if (imageReader == null) {
        throw new IllegalStateException(formatErrorMessage(Errors.Keys.NoImageInput));
    }
    GridGeometry gridGeometry = getGridGeometry();
    final int gridDimensionX, gridDimensionY;
    if (gridGeometry.getDimension() == 2) {
        gridDimensionX = 0;
        gridDimensionY = 1;
    } else if (gridGeometry.isDefined(GridGeometry.EXTENT)) {
        final int[] subSpace = gridGeometry.getExtent().getSubspaceDimensions(2);
        gridDimensionX = subSpace[0];
        gridDimensionY = subSpace[1];
    } else
        throw new DataStoreException("Cannot determine X and Y axis of the coverage");
    checkAbortState();
    final ImageReadParam imageParam;
    try {
        imageParam = createImageReadParam(0);
    } catch (IOException e) {
        throw new CoverageStoreException(formatErrorMessage(e), e);
    }
    final int[] srcBands;
    final int[] dstBands;
    MathTransform2D destToExtractedGrid = null;
    boolean supportBandSelection = true;
    if (param != null) {
        srcBands = param.getSourceBands();
        dstBands = param.getDestinationBands();
        if (srcBands != null && dstBands != null && srcBands.length != dstBands.length) {
            throw new IllegalArgumentException(Errors.getResources(locale).getString(Errors.Keys.MismatchedArrayLength_2, "sourceBands", "destinationBands"));
        }
        /*
             * Convert geodetic envelope and resolution to pixel coordinates.
             * Store the result of the above conversions in the ImageReadParam object.
             */
        destToExtractedGrid = geodeticToPixelCoordinates(gridGeometry, param, imageParam, false);
        /*
             * Conceptually we could compute right now:
             *
             *     AffineTransform change = new AffineTransform();
             *     change.translate(sourceRegion.x, sourceRegion.y);
             *     change.scale(xSubsampling, ySubsampling);
             *
             * However this implementation will scale only after the image has been read,
             * because the MosaicImageReader may have changed the subsampling to more
             * efficient values if it was authorized to make such change.
             */
        if (srcBands != null) {
            // -- particulaity case for tiff image reader which does not support setbands indexes.
            try {
                imageParam.setSourceBands(srcBands);
            } catch (UnsupportedOperationException ex) {
                LOGGER.log(Level.WARNING, ex.getMessage() + "Read coverage without set any source bands.");
                supportBandSelection = false;
            }
        }
        if (dstBands != null) {
            try {
                imageParam.setDestinationBands(dstBands);
            } catch (UnsupportedOperationException ex) {
                LOGGER.log(Level.WARNING, ex.getMessage() + "Read coverage without set any destination bands.");
            }
        }
    } else {
        srcBands = null;
        dstBands = null;
    }
    /*
         * Next, check if we should allow the image reader to add an offset to signed intergers
         * in order to make them unsigned. We will allow such offset if the SampleDimensions
         * declare unsigned range of sample values.
         */
    boolean usePaletteFactory = false;
    final SampleDimension[] bands = getSampleDimensions(index, supportBandSelection ? srcBands : null, supportBandSelection ? dstBands : null);
    if (imageParam instanceof SpatialImageReadParam) {
        final SpatialImageReadParam sp = (SpatialImageReadParam) imageParam;
        if (!isRangeSigned(bands)) {
            sp.setSampleConversionAllowed(SampleConversionType.SHIFT_SIGNED_INTEGERS, true);
        }
        sp.setSampleConversionAllowed(SampleConversionType.REPLACE_FILL_VALUES, true);
        /*
             * If the image does not have its own color palette, provides a palette factory
             * which will create the IndexColorModel (if needed) from the SampleDimension.
             */
        if (bands != null && imageReader instanceof SpatialImageReader)
            try {
                usePaletteFactory = !((SpatialImageReader) imageReader).hasColors(index);
            } catch (IOException e) {
                throw new CoverageStoreException(formatErrorMessage(e), e);
            }
        /*
             * If there is supplemental dimensions (over the usual 2 dimensions) and the subclass
             * implementation did not defined explicitely some dimension slices, then convert the
             * envelope bounds in those supplemental dimensions to slices index.
             *
             * TODO: there is some duplication between this code and the work done in the parent
             *       class. We need to refactor geodeticToPixelCoordinates(…) in a new helper
             *       class for making easier to divide the work in smaller parts.
             */
        if (param != null && !sp.hasDimensionSlices()) {
            final int gridDim = gridGeometry.getDimension();
            if (gridDim > 2) {
                // max(X_DIMENSION, Y_DIMENSION) + 1
                final CoordinateReferenceSystem crs = gridGeometry.getCoordinateReferenceSystem();
                final int geodeticDim = crs.getCoordinateSystem().getDimension();
                if (geodeticDim > 2) {
                    Envelope envelope = param.getEnvelope();
                    if (envelope != null && envelope.getDimension() > 2)
                        try {
                            if (crs instanceof CompoundCRS) {
                                envelope = CRSUtilities.appendMissingDimensions(envelope, (CompoundCRS) crs);
                            }
                            envelope = Envelopes.transform(envelope, crs);
                            final double[] median = new double[geodeticDim];
                            for (int i = 0; i < geodeticDim; i++) {
                                median[i] = envelope.getMedian(i);
                            }
                            final double[] indices = new double[gridDim];
                            gridGeometry.getGridToCRS(PixelInCell.CELL_CENTER).inverse().transform(median, 0, indices, 0, 1);
                            final GridExtent gridExtent;
                            if (crs instanceof GridGeometry) {
                                gridExtent = ((GridGeometry) crs).getExtent();
                            } else {
                                // We can not fallback on gridGeometry.getExtent(), because
                                // GridGeometry2D contract forces all extra dimensions to have
                                // a span of 1.
                                gridExtent = null;
                            }
                            for (int i = 0; i < gridDim; i++) {
                                if (i != gridDimensionX && i != gridDimensionY) {
                                    final double sliceIndex = indices[i];
                                    if (!Double.isNaN(sliceIndex)) {
                                        final DimensionSlice slice = sp.newDimensionSlice();
                                        slice.addDimensionId(i);
                                        slice.setSliceIndex((int) Math.round(Math.max(gridExtent != null ? gridExtent.getLow(i) : Integer.MIN_VALUE, Math.min(gridExtent != null ? gridExtent.getHigh(i) : Integer.MAX_VALUE, sliceIndex))));
                                    }
                                }
                            }
                        } catch (TransformException e) {
                            throw new CoverageStoreException(formatErrorMessage(e), e);
                        }
                }
            }
        }
    }
    checkAbortState();
    /*
         * Read the image using the ImageReader.read(...) method.  We could have used
         * ImageReader.readAsRenderedImage(...) instead in order to give the reader a
         * chance to return a tiled image,  but experience with some formats suggests
         * that it requires to keep the ImageReader with its input stream open.
         */
    final String name;
    RenderedImage image;
    try {
        if (usePaletteFactory) {
            SampleDimensionPalette.BANDS.set(bands);
            ((SpatialImageReadParam) imageParam).setPaletteFactory(SampleDimensionPalette.FACTORY);
        }
        if (param != null && param.isDeferred()) {
            image = new LargeRenderedImage(imageReader.getOriginatingProvider(), imageParam, imageReader.getInput(), index, null);
        } else {
            image = imageReader.read(index, imageParam);
        }
    } catch (IOException | IllegalArgumentException e) {
        throw new CoverageStoreException(formatErrorMessage(e), e);
    } finally {
        if (usePaletteFactory) {
            SampleDimensionPalette.BANDS.remove();
        }
    }
    /*
         * If the grid geometry changed as a result of subsampling or reading a smaller region,
         * update the grid geometry. The (xmin, ymin) values are usually (0,0), but we take
         * them in account anyway as a paranoiac safety (a previous version of this code used
         * the 'readAsRenderedImage(...)' method, which could have shifted the image).
         */
    if (param != null) {
        final Rectangle sourceRegion = imageParam.getSourceRegion();
        final AffineTransform change = AffineTransform.getTranslateInstance(sourceRegion.x, sourceRegion.y);
        change.scale(imageParam.getSourceXSubsampling(), imageParam.getSourceYSubsampling());
        final int xmin = image.getMinX();
        final int ymin = image.getMinY();
        final int xi = gridDimensionX;
        final int yi = gridDimensionY;
        final MathTransform gridToCRS = gridGeometry.getGridToCRS(PixelInCell.CELL_CORNER);
        MathTransform newGridToCRS = gridToCRS;
        if (!change.isIdentity()) {
            final int gridDimension = gridToCRS.getSourceDimensions();
            final Matrix matrix = Matrices.createIdentity(gridDimension + 1);
            matrix.setElement(xi, xi, change.getScaleX());
            matrix.setElement(yi, yi, change.getScaleY());
            matrix.setElement(xi, gridDimension, change.getTranslateX() - xmin);
            matrix.setElement(yi, gridDimension, change.getTranslateY() - ymin);
            newGridToCRS = MathTransforms.concatenate(MathTransforms.linear(matrix), gridToCRS);
        }
        final GridExtent gridExtent = gridGeometry.getExtent();
        final long[] low = GridGeometryIterator.getLow(gridExtent);
        final long[] high = GridGeometryIterator.getHigh(gridExtent);
        low[xi] = xmin;
        high[xi] = xmin + image.getWidth() - 1;
        low[yi] = ymin;
        high[yi] = ymin + image.getHeight() - 1;
        if (imageParam instanceof SpatialImageReadParam) {
            for (final DimensionSlice slice : ((SpatialImageReadParam) imageParam).getDimensionSlices()) {
                for (final Object id : slice.getDimensionIds()) {
                    if (id instanceof Integer) {
                        final int dim = (Integer) id;
                        low[dim] = high[dim] = slice.getSliceIndex();
                    }
                }
            }
        }
        final GridExtent newGridRange = new GridExtent(null, low, high, true);
        if (newGridToCRS != gridToCRS || !newGridRange.equals(gridExtent)) {
            gridGeometry = new GridGeometry(newGridRange, PixelInCell.CELL_CORNER, newGridToCRS, gridGeometry.getCoordinateReferenceSystem());
        }
    }
    final GridCoverage2D coverage = new org.apache.sis.coverage.grid.GridCoverage2D(gridGeometry, Arrays.asList(bands), image);
    if (loggingEnabled) {
        fullTime = System.nanoTime() - fullTime;
        final Level level = getLogLevel(fullTime);
        if (LOGGER.isLoggable(level)) {
            ImageCoverageStore.logOperation(level, locale, ImageCoverageReader.class, false, input, index, coverage, null, null, destToExtractedGrid, fullTime);
        }
    }
    return coverage;
}
Also used : GridExtent(org.apache.sis.coverage.grid.GridExtent) MathTransform(org.opengis.referencing.operation.MathTransform) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) Rectangle(java.awt.Rectangle) Envelope(org.opengis.geometry.Envelope) ImageReadParam(javax.imageio.ImageReadParam) SpatialImageReadParam(org.geotoolkit.image.io.SpatialImageReadParam) Matrix(org.opengis.referencing.operation.Matrix) DimensionSlice(org.geotoolkit.image.io.DimensionSlice) SpatialImageReader(org.geotoolkit.image.io.SpatialImageReader) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) ImageReader(javax.imageio.ImageReader) SpatialImageReader(org.geotoolkit.image.io.SpatialImageReader) SpatialImageReadParam(org.geotoolkit.image.io.SpatialImageReadParam) GridGeometry(org.apache.sis.coverage.grid.GridGeometry) DataStoreException(org.apache.sis.storage.DataStoreException) GridCoverage2D(org.apache.sis.coverage.grid.GridCoverage2D) TransformException(org.opengis.referencing.operation.TransformException) IOException(java.io.IOException) SampleDimension(org.apache.sis.coverage.SampleDimension) LargeRenderedImage(org.geotoolkit.image.io.large.LargeRenderedImage) AffineTransform(java.awt.geom.AffineTransform) Level(java.util.logging.Level) MathTransform2D(org.opengis.referencing.operation.MathTransform2D) LargeRenderedImage(org.geotoolkit.image.io.large.LargeRenderedImage) RenderedImage(java.awt.image.RenderedImage)

Aggregations

SpatialImageReader (org.geotoolkit.image.io.SpatialImageReader)3 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)3 Rectangle (java.awt.Rectangle)2 AffineTransform (java.awt.geom.AffineTransform)2 IOException (java.io.IOException)2 ImageReader (javax.imageio.ImageReader)2 GridExtent (org.apache.sis.coverage.grid.GridExtent)2 GridGeometry (org.apache.sis.coverage.grid.GridGeometry)2 SpatialMetadata (org.geotoolkit.image.io.metadata.SpatialMetadata)2 MathTransform (org.opengis.referencing.operation.MathTransform)2 RenderedImage (java.awt.image.RenderedImage)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Level (java.util.logging.Level)1 ImageReadParam (javax.imageio.ImageReadParam)1 SampleDimension (org.apache.sis.coverage.SampleDimension)1 GridCoverage2D (org.apache.sis.coverage.grid.GridCoverage2D)1 DataStoreException (org.apache.sis.storage.DataStoreException)1 DimensionSlice (org.geotoolkit.image.io.DimensionSlice)1 SpatialImageReadParam (org.geotoolkit.image.io.SpatialImageReadParam)1