Package org.geotools.coverage.grid.io

Source Code of org.geotools.coverage.grid.io.ReadResolutionCalculator

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2014, 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.coverage.grid.io;

import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.data.DataSourceException;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.LinearTransform;
import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
import org.geotools.referencing.operation.matrix.XAffineTransform;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;

/**
* Class that supports readers in computing the proper reading resolution for a given grid geometry
*/
public class ReadResolutionCalculator {

    static final Logger LOGGER = Logging.getLogger(ReadResolutionCalculator.class);

    private ReferencedEnvelope requestedBBox;

    private Rectangle requestedRasterArea;

    private AffineTransform requestedGridToWorld;

    private double[] fullResolution;

    private boolean accurateResolution;

    private MathTransform destinationToSourceTransform;

    public ReadResolutionCalculator(GridGeometry2D requestedGridGeometry,
            CoordinateReferenceSystem nativeCrs, double[] fullResolution) throws FactoryException {
        Utilities.ensureNonNull("gridGeometry", requestedGridGeometry);
        this.requestedBBox = new ReferencedEnvelope(
                (Envelope) requestedGridGeometry.getEnvelope2D());
        this.requestedRasterArea = requestedGridGeometry.getGridRange2D().getBounds();
        this.requestedGridToWorld = (AffineTransform) requestedGridGeometry.getGridToCRS2D();
        this.fullResolution = fullResolution;
        // the reader might not know (e.g., wms cascading reader) in this case we
        // pick the classic computation results, it's better than nothing
        if (fullResolution == null) {
            this.fullResolution = computeClassicResolution();
        }
        CoordinateReferenceSystem requestedCRS = requestedGridGeometry
                .getCoordinateReferenceSystem();
        if (!CRS.equalsIgnoreMetadata(nativeCrs, requestedCRS)) {
            this.destinationToSourceTransform = CRS.findMathTransform(requestedCRS, nativeCrs);
        }
    }

    /**
     * Computes the requested resolution which is going to be used for selecting overviews and or
     * deciding decimation factors on the target coverage.
     *
     * <p>
     * In case the requested envelope is in the same {@link CoordinateReferenceSystem} of the
     * coverage we compute the resolution using the requested {@link MathTransform}. Notice that it
     * must be a {@link LinearTransform} or else we fail.
     *
     * <p>
     * In case the requested envelope is not in the same {@link CoordinateReferenceSystem} of the
     * coverage we
     *
     * @throws DataSourceException in case something bad happens during reprojections and/or
     *         intersections.
     */
    public double[] computeRequestedResolution(ReferencedEnvelope readBounds) {
        try {

            // let's try to get the resolution from the requested gridToWorld
            if (requestedGridToWorld instanceof LinearTransform) {

                //
                // the crs of the request and the one of the coverage are NOT the
                // same and the conversion is not , we can get the resolution from envelope + raster
                // directly
                //
                if (destinationToSourceTransform != null
                        && !destinationToSourceTransform.isIdentity()) {
                    if (accurateResolution) {
                        return computeAccurateResolution(readBounds);
                    } else {
                        return computeClassicResolution();
                    }
                } else {
                    // the crs of the request and the one of the coverage are the
                    // same, we can get the resolution from the grid to world
                    return new double[] { XAffineTransform.getScaleX0(requestedGridToWorld),
                            XAffineTransform.getScaleY0(requestedGridToWorld) };
                }
            } else {
                // should not happen
                throw new UnsupportedOperationException(Errors.format(
                        ErrorKeys.UNSUPPORTED_OPERATION_$1, requestedGridToWorld.toString()));
            }
        } catch (Throwable e) {
            if (LOGGER.isLoggable(Level.INFO))
                LOGGER.log(Level.INFO, "Unable to compute requested resolution", e);
        }

        //
        // use the coverage resolution since we cannot compute the requested one
        //
        LOGGER.log(Level.WARNING,
                "Unable to compute requested resolution, the reader will pick the native one");
        return fullResolution;
    }

    /**
     * Classic way of computing the requested resolution
     *
     * @return
     */
    private double[] computeClassicResolution() {
        final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(new GridEnvelope2D(
                requestedRasterArea), requestedBBox);
        final AffineTransform tempTransform = geMapper.createAffineTransform();

        return new double[] { XAffineTransform.getScaleX0(tempTransform),
                XAffineTransform.getScaleY0(tempTransform) };
    }

    /**
     * Compute the resolutions through a more accurate logic: Compute the resolution in 9 points,
     * the corners of the requested area and the middle points and take the better one. This will
     * provide better results for cases where there is a lot more deformation on a subregion
     * (top/bottom/sides) of the requested bbox with respect to others.
     *
     * @return
     * @throws TransformException
     * @throws NoninvertibleTransformException
     * @throws FactoryException
     */
    private double[] computeAccurateResolution(ReferencedEnvelope readBBox)
            throws TransformException, NoninvertibleTransformException, FactoryException {
        if (!CRS.equalsIgnoreMetadata(readBBox.getCoordinateReferenceSystem(),
                requestedBBox.getCoordinateReferenceSystem())) {
            readBBox = readBBox.transform(requestedBBox.getCoordinateReferenceSystem(), true);
        }
        double resX = XAffineTransform.getScaleX0(requestedGridToWorld);
        double resY = XAffineTransform.getScaleY0(requestedGridToWorld);
        GeneralEnvelope cropBboxTarget = CRS.transform(readBBox,
                requestedBBox.getCoordinateReferenceSystem());
        double[] points = new double[36];
        for (int i = 0; i < 3; i++) {
            double x;
            if (i == 0) {
                x = cropBboxTarget.getMinimum(0) + resX / 2;
            } else if (i == 1) {
                x = cropBboxTarget.getMedian(0);
            } else {
                x = cropBboxTarget.getMaximum(0) - resX / 2;
            }
            for (int j = 0; j < 3; j++) {
                double y;
                if (j == 0) {
                    y = cropBboxTarget.getMinimum(1) + resY / 2;
                } else if (j == 1) {
                    y = cropBboxTarget.getMedian(1);
                } else {
                    y = cropBboxTarget.getMaximum(1) - resY / 2;
                }

                int k = (i * 3 + j) * 4;
                points[k] = x - resX / 2;
                points[k + 1] = y - resY / 2;
                points[k + 2] = x + resX / 2;
                points[k + 3] = y + resY / 2;
            }
        }
        destinationToSourceTransform.transform(points, 0, points, 0, 18);

        double minDistance = Double.MAX_VALUE;
        for (int i = 0; i < 36 && minDistance > 0; i += 4) {
            double dx = points[i + 2] - points[i];
            double dy = points[i + 3] - points[i + 1];
            double d = Math.sqrt(dx * dx + dy * dy);
            if (d < minDistance) {
                minDistance = d;
            }
        }

        // reprojection can turn a segment into a zero lenght one, in that case, fall back on
        // the full resolution in that case
        return new double[] { Math.max(minDistance, fullResolution[0]),
                Math.max(minDistance, fullResolution[1]) };
    }

    public boolean isAccurateResolution() {
        return accurateResolution;
    }

    public void setAccurateResolution(boolean accurateResolution) {
        this.accurateResolution = accurateResolution;
    }
}
TOP

Related Classes of org.geotools.coverage.grid.io.ReadResolutionCalculator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.