All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotoolkit.referencing.operation.projection.Assertions Maven / Gradle / Ivy

/*
 *    Geotoolkit.org - An Open Source Java GIS Toolkit
 *    http://www.geotoolkit.org
 *
 *    (C) 1999-2012, Open Source Geospatial Foundation (OSGeo)
 *    (C) 2009-2012, Geomatys
 *
 *    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.geotoolkit.referencing.operation.projection;

import org.opengis.referencing.operation.Matrix;

import org.geotoolkit.lang.Static;
import org.geotoolkit.resources.Errors;

import static java.lang.Math.*;
import static java.lang.Double.POSITIVE_INFINITY;
import static org.geotoolkit.referencing.operation.projection.UnitaryProjection.*;


/**
 * Static methods for assertions. This is used only when Java 1.4 assertions are enabled.
 * When a point has been projected using spherical formulas, compare with the same point
 * transformed using spherical formulas and throw an exception if the result differ.
 *
 * @author Martin Desruisseaux (Geomatys)
 * @author Rémi Maréchal (Geomatys)
 * @version 3.20
 *
 * @since 2.0
 * @module
 */
final class Assertions extends Static {
    /**
     * Maximum difference allowed when comparing the result of an inverse projections,
     * in radians. A value of 1E-7 radians is approximatively 0.5 kilometres. Note that
     * inverse projections are typically less accurate than forward projections. This
     * tolerance is set to such high value for avoiding too intrusive assertion errors.
     * This is okay only for catching gross programming errors.
     */
    private static final double INVERSE_TOLERANCE = 1E-7;

    /**
     * Maximum difference allowed when comparing the result of forward projections,
     * in distance on the unit ellipse. A value of 1E-7 is approximatively 0.1 metres.
     */
    private static final double FORWARD_TOLERANCE = 1E-7;

    /**
     * Maximum difference allowed between spherical and ellipsoidal formulas when
     * comparing derivatives. Units are metres.
     */
    private static final double DERIVATIVE_TOLERANCE = 1E-1;

    /**
     * Do not allows instantiation of this class.
     */
    private Assertions() {
    }

    /**
     * Checks if transform using spherical formulas produces the same result
     * than ellipsoidal formulas. This method is invoked during assertions only.
     *
     * @param  expected The (easting,northing) computed by ellipsoidal formulas.
     * @param  offset Index of the coordinate in the {@code expected} array.
     * @param  x The easting computed by spherical formulas on the unit sphere.
     * @param  y The northing computed by spherical formulas on the unit sphere.
     * @param  tolerance The tolerance (optional).
     * @return Always {@code true} if the {@code tolerance} value is valid.
     * @throws ProjectionException if the comparison failed.
     */
    static boolean checkTransform(final double[] expected, final int offset,
            final double x, final double y, final double tolerance)
            throws ProjectionException
    {
        if (expected != null) {
            compare("x", expected[offset  ], x, tolerance);
            compare("y", expected[offset+1], y, tolerance);
        }
        return tolerance < POSITIVE_INFINITY;
    }

    /**
     * Default version of {@link #checkTransform(double,double,double[],int)}.
     */
    static boolean checkTransform(final double[] expected, final int offset, final double x, final double y)
            throws ProjectionException
    {
        return checkTransform(expected, offset, x, y, FORWARD_TOLERANCE);
    }

    /**
     * Checks if inverse transform using spherical formulas produces the same result
     * than ellipsoidal formulas. This method is invoked during assertions only.
     *
     * {@note This method ignores the longitude if the latitude is at a pole,
     *        because in such case the longitude is meanless.}
     *
     * @param  expected  The (longitude,latitude) computed by ellipsoidal formulas.
     * @param  offset    Index of the coordinate in the {@code expected} array.
     * @param  λ         The longitude computed by spherical formulas, in radians.
     * @param  φ         The latitude computed by spherical formulas, in radians.
     * @param  tolerance The tolerance (optional).
     * @return Always {@code true} if the {@code tolerance} value is valid.
     * @throws ProjectionException if the comparison failed.
     */
    static boolean checkInverseTransform(final double[] expected, final int offset,
            final double λ, final double φ, final double tolerance)
            throws ProjectionException
    {
        compare("latitude", expected[offset+1], φ, tolerance);
        if (abs(PI/2 - abs(φ)) > ANGLE_TOLERANCE) {
            compare("longitude", expected[offset], λ, tolerance);
        }
        return tolerance < POSITIVE_INFINITY;
    }

    /**
     * Default version of {@link #checkInverseTransform(double,double,double[],int,double)}.
     */
    static boolean checkInverseTransform(final double[] expected, final int offset, final double λ, final double φ)
            throws ProjectionException
    {
        return checkInverseTransform(expected, offset, λ, φ, INVERSE_TOLERANCE);
    }

    /**
     * Checks if derivatives using spherical formulas produces the same result than ellipsoidal
     * formulas. This method is invoked during assertions only. The spherical formulas are used
     * for the "expected" results since they are simpler than the ellipsoidal formulas.
     *
     * @since 3.18
     */
    static boolean checkDerivative(final Matrix spherical, final Matrix ellipsoidal)
            throws ProjectionException
    {
        if (spherical != null || ellipsoidal != null) { // NullPointerException is ok if only one is null.
            compare("m00", spherical.getElement(0,0), ellipsoidal.getElement(0,0), DERIVATIVE_TOLERANCE);
            compare("m01", spherical.getElement(0,1), ellipsoidal.getElement(0,1), DERIVATIVE_TOLERANCE);
            compare("m10", spherical.getElement(1,0), ellipsoidal.getElement(1,0), DERIVATIVE_TOLERANCE);
            compare("m11", spherical.getElement(1,1), ellipsoidal.getElement(1,1), DERIVATIVE_TOLERANCE);
        }
        return true;
    }

    /**
     * Compares two value for equality up to some tolerance threshold. This is used during
     * assertions only. The comparison does not fail if at least one value to compare is
     * {@link Double#NaN} or infinity.
     * 

* Hack: if the {@code variable} name starts by lower-case {@code L} * (as in "longitude" and "latitude"), then the value is assumed to be an angle in * radians. This is used for formatting an error message, if needed. * * @throws ProjectionException if the comparison failed. */ private static void compare(final String variable, double expected, double actual, final double tolerance) throws ProjectionException { final double delta = abs(expected - actual); if (delta > tolerance) { if (variable.charAt(0) == 'l') { actual = toDegrees(actual); expected = toDegrees(expected); } else if (abs(actual) > 30 && abs(expected) > 30) { /* * If the projected point tend toward infinity, treats the value as if is was * really infinity. Note that 30 is considered as "close to infinity" because * of the result we get when projecting 90°N using Mercator spherical formula: * * y = log(tan(π/4 + φ/2)) * * Because there is no exact representation of π/2 in base 2, the tangent * function gives 1.6E+16 instead of infinity, which leads the logarithmic * function to give us 37.3. * * This behavior is tested in MercatorTest.testSphericalFormulas(). */ if (signum(actual) == signum(expected)) { return; } } throw new ProjectionError(Errors.format(Errors.Keys.TEST_FAILURE_$3, variable, String.valueOf(expected), String.valueOf(actual)) + // Force full precision. "(Δ" + variable + '=' + delta + " ε=" + tolerance + ')'); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy