Search in sources :

Example 1 with GridGeometry

use of org.apache.sis.coverage.grid.GridGeometry in project sis by apache.

the class ImageRequest method load.

/**
 * Loads the image. If the coverage has more than {@value #BIDIMENSIONAL} dimensions,
 * only two of them are taken for the image; for all other dimensions, only the values
 * at lowest index will be read.
 *
 * <p>If the {@link #coverage} field was null, it will be initialized as a side-effect.
 * No other fields will be modified.</p>
 *
 * <h4>Thread safety</h4>
 * This class does not need to be thread-safe because it should be used only once in a well-defined life cycle.
 * We nevertheless synchronize as a safety (e.g. user could give the same {@code ImageRequest} to two different
 * {@link CoverageExplorer} instances). In such case the {@link GridCoverage} will be loaded only once,
 * but no caching is done for the {@link RenderedImage} (because usually not needed).
 *
 * @param  task  the task invoking this method (for checking for cancellation).
 * @return the image loaded from the source given at construction time, or {@code null}
 *         if the task has been cancelled.
 * @throws DataStoreException if an error occurred while loading the grid coverage.
 */
final synchronized RenderedImage load(final FutureTask<?> task) throws DataStoreException {
    GridCoverage cv = coverage;
    final Long id = LogHandler.loadingStart(resource);
    try {
        if (cv == null) {
            cv = MultiResolutionImageLoader.getInstance(resource, null).getOrLoad(domain, range);
        }
        coverage = cv = cv.forConvertedValues(true);
        if (task.isCancelled()) {
            return null;
        }
        GridExtent ex = sliceExtent;
        if (ex == null) {
            final GridGeometry gg = cv.getGridGeometry();
            if (gg.getDimension() > MultiResolutionImageLoader.BIDIMENSIONAL) {
                ex = MultiResolutionImageLoader.slice(gg.derive(), gg.getExtent()).getIntersection();
            }
        }
        return cv.render(ex);
    } finally {
        LogHandler.loadingStop(id);
    }
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) GridExtent(org.apache.sis.coverage.grid.GridExtent) GridCoverage(org.apache.sis.coverage.grid.GridCoverage)

Example 2 with GridGeometry

use of org.apache.sis.coverage.grid.GridGeometry in project sis by apache.

the class MultiResolutionCoverageLoader method getOrLoad.

/**
 * Returns the coverage at the given level if it is present in the cache, or loads and caches it otherwise.
 *
 * @param  level  pyramid level of the desired coverage.
 * @return the coverage at the specified level (never null).
 * @throws DataStoreException if an error occurred while loading the coverage.
 */
public final GridCoverage getOrLoad(final int level) throws DataStoreException {
    synchronized (coverages) {
        final Reference<GridCoverage> ref = coverages[level];
        if (ref != null) {
            final GridCoverage coverage = ref.get();
            if (coverage != null)
                return coverage;
            coverages[level] = null;
        }
    }
    GridGeometry domain = null;
    if (resolutionSquared.length != 0) {
        final double[] resolutions = resolutionSquared[level].clone();
        for (int i = 0; i < resolutions.length; i++) {
            resolutions[i] = Math.sqrt(resolutions[i]);
        }
        final MathTransform gridToCRS = MathTransforms.scale(resolutions);
        domain = new GridGeometry(PixelInCell.CELL_CORNER, gridToCRS, areaOfInterest, GridRoundingMode.ENCLOSING);
    }
    final GridCoverage coverage = resource.read(getReadDomain(domain), readRanges);
    /*
         * Cache and return the coverage. The returned coverage may be a different instance
         * if another coverage has been cached concurrently for the same level.
         */
    synchronized (coverages) {
        final Reference<GridCoverage> ref = coverages[level];
        if (ref != null) {
            final GridCoverage c = ref.get();
            if (c != null)
                return c;
        }
        coverages[level] = new SoftReference<>(coverage);
    }
    return coverage;
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) GridCoverage(org.apache.sis.coverage.grid.GridCoverage) MathTransform(org.opengis.referencing.operation.MathTransform)

Example 3 with GridGeometry

use of org.apache.sis.coverage.grid.GridGeometry in project sis by apache.

the class RenderingData method ensureImageLoaded.

/**
 * Fetches the rendered image if {@linkplain #data} is null. This method needs to be invoked at least
 * once after {@link #setCoverageSpace(GridGeometry, List)}. The {@code coverage} given in argument
 * may be the value returned by {@link #ensureCoverageLoaded(LinearTransform, DirectPosition)}.
 *
 * @param  coverage     the coverage from which to read data, or {@code null} if the coverage did not changed.
 * @param  sliceExtent  a subspace of the grid coverage extent where all dimensions except two have a size of 1 cell.
 *         May be {@code null} if this grid coverage has only two dimensions with a size greater than 1 cell.
 * @return whether the {@linkpalin #data} changed.
 * @throws FactoryException if the CRS changed but the transform from old to new CRS can not be determined.
 * @throws TransformException if an error occurred while transforming coordinates from old to new CRS.
 */
public final boolean ensureImageLoaded(GridCoverage coverage, final GridExtent sliceExtent) throws FactoryException, TransformException {
    if (data != null || coverage == null) {
        return false;
    }
    coverage = coverage.forConvertedValues(true);
    final GridGeometry old = dataGeometry;
    final List<SampleDimension> ranges = coverage.getSampleDimensions();
    final RenderedImage image = coverage.render(sliceExtent);
    final Object value = image.getProperty(PlanarImage.GRID_GEOMETRY_KEY);
    final GridGeometry domain = (value instanceof GridGeometry) ? (GridGeometry) value : new ImageRenderer(coverage, sliceExtent).getImageGeometry(BIDIMENSIONAL);
    setCoverageSpace(domain, ranges);
    data = image;
    /*
         * Update the transforms in a way that preserve the current zoom level, translation, etc.
         * We compute the change in the "data grid to objective CRS" transforms caused by the change
         * in data grid geometry, then we concatenate that change to the existing transforms.
         * That way, the objective CRS is kept unchanged.
         */
    if (old != null && cornerToObjective != null && objectiveToCenter != null) {
        MathTransform toNew = null, toOld = null;
        if (old.isDefined(GridGeometry.CRS) && domain.isDefined(GridGeometry.CRS)) {
            final CoordinateReferenceSystem oldCRS = old.getCoordinateReferenceSystem();
            final CoordinateReferenceSystem newCRS = dataGeometry.getCoordinateReferenceSystem();
            if (newCRS != oldCRS) {
                // Quick check for the vast majority of cases.
                /*
                     * Transform computed below should always be the identity transform,
                     * but we check anyway as a safety. A non-identity transform would be
                     * a pyramid where the CRS changes according the pyramid level.
                     */
                final GeographicBoundingBox areaOfInterest = Extents.union(dataGeometry.getGeographicExtent().orElse(null), old.getGeographicExtent().orElse(null));
                toNew = CRS.findOperation(oldCRS, newCRS, areaOfInterest).getMathTransform();
                toOld = toNew.inverse();
            }
        }
        final MathTransform forward = concatenate(PixelInCell.CELL_CORNER, dataGeometry, old, toOld);
        final MathTransform inverse = concatenate(PixelInCell.CELL_CENTER, old, dataGeometry, toNew);
        cornerToObjective = MathTransforms.concatenate(forward, cornerToObjective);
        objectiveToCenter = MathTransforms.concatenate(objectiveToCenter, inverse);
    }
    return true;
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) ImageRenderer(org.apache.sis.coverage.grid.ImageRenderer) MathTransform(org.opengis.referencing.operation.MathTransform) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeographicBoundingBox(org.opengis.metadata.extent.GeographicBoundingBox) RenderedImage(java.awt.image.RenderedImage) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 4 with GridGeometry

use of org.apache.sis.coverage.grid.GridGeometry in project sis by apache.

the class RasterReader method readAsCoverage.

/**
 * Parses a raster from the given input stream and returns as a coverage.
 *
 * @param  input  source of bytes to read.
 * @return the raster as a coverage, or {@code null} if the raster is empty.
 * @throws Exception in an error occurred while reading from the given input or creating the coverage.
 *         Exception type may be I/O, SQL, factory, data store, arithmetic, raster format, <i>etc.</i>,
 *         too numerous for enumerating them all.
 */
public GridCoverage readAsCoverage(final ChannelDataInput input) throws Exception {
    final BufferedImage image = readAsImage(input);
    if (image == null) {
        return null;
    }
    CoordinateReferenceSystem crs = null;
    final int srid = getSRID();
    if (spatialRefSys != null) {
        crs = spatialRefSys.fetchCRS(srid);
    } else if (srid > 0) {
        crs = CRS.forCode(Constants.EPSG + ':' + srid);
    }
    if (crs == null) {
        crs = defaultCRS;
    }
    final GridExtent extent = new GridExtent(image.getWidth(), image.getHeight());
    final GridGeometry domain = new GridGeometry(extent, ANCHOR, getGridToCRS(), crs);
    /*
         * Create pseudo-categories with a transfer function if we need to specify "no data" value,
         * or the sign of stored data do not match the sign of expected values.
         */
    List<SampleDimension> range = null;
    if (needsTransferFunction()) {
        final SampleDimension[] sd = new SampleDimension[bands.length];
        final SampleDimension.Builder builder = new SampleDimension.Builder();
        for (int b = 0; b < sd.length; b++) {
            final Band band = bands[b];
            if ((band.getDataBufferType() & OPPOSITE_SIGN) != 0) {
                // See `Band.OPPOSITE_SIGN` javadoc for more information on this limitation.
                throw new RasterFormatException("Data type not yet supported.");
            }
            sd[b] = builder.setName(b + 1).setBackground(band.noDataValue).build();
            builder.clear();
        }
        range = Arrays.asList(sd);
    }
    return new GridCoverage2D(domain, range, image);
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) GridExtent(org.apache.sis.coverage.grid.GridExtent) GridCoverage2D(org.apache.sis.coverage.grid.GridCoverage2D) SampleDimension(org.apache.sis.coverage.SampleDimension) BufferedImage(java.awt.image.BufferedImage) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) RasterFormatException(java.awt.image.RasterFormatException)

Example 5 with GridGeometry

use of org.apache.sis.coverage.grid.GridGeometry in project sis by apache.

the class MemoryGridResource method read.

/**
 * Returns a subset of the wrapped grid coverage. If a non-null grid geometry is specified, then
 * this method tries to return a grid coverage matching the given grid geometry on a best-effort basis.
 * In current implementation this is either a {@link org.apache.sis.coverage.grid.GridCoverage2D} or
 * the original grid coverage.
 *
 * @param  domain  desired grid extent and resolution, or {@code null} for the whole domain.
 * @param  range   0-based indices of sample dimensions to read, or {@code null} or an empty sequence for reading them all.
 * @return the grid coverage for the specified domain and range.
 */
@Override
public GridCoverage read(GridGeometry domain, final int... range) {
    List<SampleDimension> bands = coverage.getSampleDimensions();
    final RangeArgument rangeIndices = validateRangeArgument(bands.size(), range);
    /*
         * The given `domain` may use arbitrary `gridToCRS` and `CRS` properties.
         * For this simple implementation we need the same `gridToCRS` and `CRS`
         * than the wrapped coverage; only domain `extent` is allowed to differ.
         * Subsampling is ignored for now because it is an expensive operation.
         * Clipping and range selection are light and do not copy any data.
         *
         * TODO: a future implementation may apply subsampling efficiently,
         *       by adjusting the pixel stride in SampleModel.
         */
    GridExtent intersection = null;
    final GridGeometry source = coverage.getGridGeometry();
    if (domain == null) {
        domain = source;
    } else {
        intersection = source.derive().rounding(GridRoundingMode.ENCLOSING).subgrid(domain).getIntersection();
        if (intersection.equals(source.getExtent())) {
            // Will request the whole image.
            intersection = null;
            domain = source;
        }
    }
    /*
         * Quick check before to invoke the potentially costly `coverage.render(…)` method.
         */
    final boolean sameBands = rangeIndices.isIdentity();
    if (sameBands && intersection == null) {
        return coverage;
    }
    /*
         * After `render(…)` execution, the (minX, minY) image coordinates are the differences between
         * the extent that we requested and the one that we got. If that differences is not zero, then
         * we need to translate the `GridExtent` in order to make it matches what we got. But before to
         * apply that translation, we adjust the grid size (because it may add another translation).
         */
    RenderedImage data = coverage.render(intersection);
    if (intersection != null) {
        final int[] sd = intersection.getSubspaceDimensions(2);
        final int dimX = sd[0];
        final int dimY = sd[1];
        final long ox = intersection.getLow(dimX);
        final long oy = intersection.getLow(dimY);
        final long[] changes = new long[Math.max(dimX, dimY) + 1];
        for (int i = changes.length; --i >= 0; ) {
            // We need only the dimensions that may change.
            changes[i] = intersection.getSize(i);
        }
        changes[dimX] = data.getWidth();
        changes[dimY] = data.getHeight();
        intersection = intersection.resize(changes);
        /*
             * Apply the translation after we resized the grid extent, because the resize operation
             * may have caused an additional translation. We cancel that translation with terms that
             * restore the (ox,oy) lower coordinates before to add the data minimum X,Y.
             */
        Arrays.fill(changes, 0);
        changes[dimX] = Math.addExact(ox - intersection.getLow(dimX), data.getMinX());
        changes[dimY] = Math.addExact(oy - intersection.getLow(dimY), data.getMinX());
        intersection = intersection.translate(changes);
        /*
             * If the result is the same intersection than the source coverage,
             * we may be able to return that coverage directly.
             */
        if (intersection.equals(source.getExtent())) {
            if (sameBands) {
                return coverage;
            }
            domain = source;
        } else {
            domain = new GridGeometry(intersection, PixelInCell.CELL_CORNER, source.getGridToCRS(PixelInCell.CELL_CORNER), source.getCoordinateReferenceSystem());
        }
    }
    if (!sameBands) {
        data = new ImageProcessor().selectBands(data, range);
        bands = UnmodifiableArrayList.wrap(rangeIndices.select(bands));
    }
    return new GridCoverageBuilder().setValues(data).setRanges(bands).setDomain(domain).build();
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) GridExtent(org.apache.sis.coverage.grid.GridExtent) SampleDimension(org.apache.sis.coverage.SampleDimension) ImageProcessor(org.apache.sis.image.ImageProcessor) GridCoverageBuilder(org.apache.sis.coverage.grid.GridCoverageBuilder) RenderedImage(java.awt.image.RenderedImage)

Aggregations

GridGeometry (org.apache.sis.coverage.grid.GridGeometry)25 GridExtent (org.apache.sis.coverage.grid.GridExtent)10 SampleDimension (org.apache.sis.coverage.SampleDimension)6 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)6 GridCoverage (org.apache.sis.coverage.grid.GridCoverage)5 MathTransform (org.opengis.referencing.operation.MathTransform)5 Test (org.junit.Test)4 TransformException (org.opengis.referencing.operation.TransformException)4 GridDerivation (org.apache.sis.coverage.grid.GridDerivation)3 Point (java.awt.Point)2 RenderedImage (java.awt.image.RenderedImage)2 GridCoverage2D (org.apache.sis.coverage.grid.GridCoverage2D)2 LinearTransform (org.apache.sis.referencing.operation.transform.LinearTransform)2 DataStoreContentException (org.apache.sis.storage.DataStoreContentException)2 FactoryException (org.opengis.util.FactoryException)2 AffineTransform (java.awt.geom.AffineTransform)1 BufferedImage (java.awt.image.BufferedImage)1 DataBuffer (java.awt.image.DataBuffer)1 RasterFormatException (java.awt.image.RasterFormatException)1 IOException (java.io.IOException)1