Search in sources :

Example 1 with EllipsoidalCS

use of org.opengis.referencing.cs.EllipsoidalCS in project sis by apache.

the class GeocentricAffine method createParameters.

/**
 * Returns the parameters for creating a datum shift operation.
 * The operation method will be one of the {@code GeocentricAffine} subclasses,
 * unless the specified {@code method} argument is {@link DatumShiftMethod#NONE}.
 * If no single operation method can be used, then this method returns {@code null}.
 *
 * <p>This method does <strong>not</strong> change the coordinate system type.
 * The source and target coordinate systems can be both {@code EllipsoidalCS} or both {@code CartesianCS}.
 * Any other type or mix of types (e.g. a {@code EllipsoidalCS} source and {@code CartesianCS} target)
 * will cause this method to return {@code null}. In such case, it is caller's responsibility to apply
 * the datum shift itself in Cartesian geocentric coordinates.</p>
 *
 * @param  sourceCS    the source coordinate system. Only the type and number of dimensions is checked.
 * @param  targetCS    the target coordinate system. Only the type and number of dimensions is checked.
 * @param  datumShift  the datum shift as a matrix, or {@code null} if there is no datum shift information.
 * @param  method      the preferred datum shift method. Note that {@code createParameters(…)} may overwrite.
 * @return the parameter values, or {@code null} if no single operation method can be found.
 */
public static ParameterValueGroup createParameters(final CoordinateSystem sourceCS, final CoordinateSystem targetCS, final Matrix datumShift, DatumShiftMethod method) {
    final boolean isEllipsoidal = (sourceCS instanceof EllipsoidalCS);
    if (!(isEllipsoidal ? (targetCS instanceof EllipsoidalCS) : (targetCS instanceof CartesianCS && sourceCS instanceof CartesianCS))) {
        // Coordinate systems are not two EllipsoidalCS or two CartesianCS.
        return null;
    }
    @SuppressWarnings("null") int dimension = sourceCS.getDimension();
    if (dimension != targetCS.getDimension()) {
        // Any value greater than 3 means "mismatched dimensions" for this method.
        dimension = 4;
    }
    if (method == DatumShiftMethod.NONE) {
        if (dimension <= 3) {
            return Affine.identity(dimension);
        } else if (isEllipsoidal) {
            final ParameterDescriptorGroup descriptor;
            switch(sourceCS.getDimension()) {
                case 2:
                    descriptor = Geographic2Dto3D.PARAMETERS;
                    break;
                case 3:
                    descriptor = Geographic3Dto2D.PARAMETERS;
                    break;
                default:
                    return null;
            }
            return descriptor.createValue();
        } else {
            return null;
        }
    }
    /*
         * Try to convert the matrix into (tX, tY, tZ, rX, rY, rZ, dS) parameters.
         * The matrix may not be convertible, in which case we will let the caller
         * uses the matrix directly in Cartesian geocentric coordinates.
         */
    final BursaWolfParameters parameters = new BursaWolfParameters(null, null);
    if (datumShift != null)
        try {
            parameters.setPositionVectorTransformation(datumShift, BURSAWOLF_TOLERANCE);
        } catch (IllegalArgumentException e) {
            log(Loggers.COORDINATE_OPERATION, "createParameters", e);
            return null;
        }
    else {
        /*
             * If there is no datum shift parameters (not to be confused with identity), then those parameters
             * are assumed unknown. Using the most accurate methods would give a false impression of accuracy,
             * so we use the fastest method instead. Since all parameter values are zero, Apache SIS should use
             * the AbridgedMolodenskyTransform2D optimization.
             */
        method = DatumShiftMethod.ABRIDGED_MOLODENSKY;
    }
    final boolean isTranslation = parameters.isTranslation();
    final ParameterDescriptorGroup descriptor;
    /*
         * Following "if" blocks are ordered from most accurate to less accurate datum shift method
         * supported by GeocentricAffine subclasses (except NONE which has already been handled).
         * Special cases:
         *
         *   - If the datum shift is applied between geocentric CRS, then the Molodensky approximations do not apply
         *     as they are designed for transformations between geographic CRS only. User preference is then ignored.
         *
         *   - Molodensky methods are approximations for datum shifts having only translation terms in their Bursa-Wolf
         *     parameters. If there is also a scale or rotation terms, then we can not use Molodensky methods. The user
         *     preference is then ignored.
         */
    if (!isEllipsoidal) {
        method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
        descriptor = isTranslation ? GeocentricTranslation.PARAMETERS : PositionVector7Param.PARAMETERS;
    } else if (!isTranslation) {
        method = DatumShiftMethod.GEOCENTRIC_DOMAIN;
        descriptor = (dimension >= 3) ? PositionVector7Param3D.PARAMETERS : PositionVector7Param2D.PARAMETERS;
    } else
        switch(method) {
            case GEOCENTRIC_DOMAIN:
                {
                    descriptor = (dimension >= 3) ? GeocentricTranslation3D.PARAMETERS : GeocentricTranslation2D.PARAMETERS;
                    break;
                }
            case MOLODENSKY:
                {
                    descriptor = Molodensky.PARAMETERS;
                    break;
                }
            case ABRIDGED_MOLODENSKY:
                {
                    descriptor = AbridgedMolodensky.PARAMETERS;
                    break;
                }
            default:
                throw new AssertionError(method);
        }
    /*
         * Following lines will set all Bursa-Wolf parameter values (scale, translation
         * and rotation terms). In the particular case of Molodensky method, we have an
         * additional parameter for the number of source and target dimensions (2 or 3).
         */
    final Parameters values = createParameters(descriptor, parameters, isTranslation);
    switch(method) {
        case MOLODENSKY:
        case ABRIDGED_MOLODENSKY:
            {
                if (dimension <= 3) {
                    values.getOrCreate(Molodensky.DIMENSION).setValue(dimension);
                }
                break;
            }
    }
    return values;
}
Also used : CartesianCS(org.opengis.referencing.cs.CartesianCS) Parameters(org.apache.sis.parameter.Parameters) BursaWolfParameters(org.apache.sis.referencing.datum.BursaWolfParameters) ParameterDescriptorGroup(org.opengis.parameter.ParameterDescriptorGroup) EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) BursaWolfParameters(org.apache.sis.referencing.datum.BursaWolfParameters)

Example 2 with EllipsoidalCS

use of org.opengis.referencing.cs.EllipsoidalCS in project sis by apache.

the class MathTransformContext method getMatrix.

/**
 * Returns the normalization or denormalization matrix.
 */
@Override
@SuppressWarnings("fallthrough")
public Matrix getMatrix(final MatrixRole role) throws FactoryException {
    final CoordinateSystem cs;
    boolean inverse = false;
    double rotation;
    switch(role) {
        default:
            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "role", role));
        // Fall through
        case INVERSE_NORMALIZATION:
            inverse = true;
        case NORMALIZATION:
            rotation = sourceMeridian;
            cs = getSourceCS();
            break;
        // Fall through
        case INVERSE_DENORMALIZATION:
            inverse = true;
        case DENORMALIZATION:
            inverse = !inverse;
            rotation = targetMeridian;
            cs = getTargetCS();
            break;
    }
    Matrix matrix = super.getMatrix(role);
    if (rotation != 0) {
        if (inverse)
            rotation = -rotation;
        MatrixSIS cm = MatrixSIS.castOrCopy(matrix);
        if (cs instanceof CartesianCS) {
            rotation = Math.toRadians(rotation);
            final Matrix4 rot = new Matrix4();
            rot.m00 = rot.m11 = Math.cos(rotation);
            rot.m01 = -(rot.m10 = Math.sin(rotation));
            if (inverse) {
                // Apply the rotation after denormalization.
                matrix = Matrices.multiply(rot, cm);
            } else {
                // Apply the rotation before normalization.
                matrix = cm.multiply(rot);
            }
        } else if (cs == null || cs instanceof EllipsoidalCS || cs instanceof SphericalCS) {
            final Double value = rotation;
            if (inverse) {
                // Longitude is the first axis in normalized CS.
                cm.convertBefore(0, null, value);
            } else {
                cm.convertAfter(0, null, value);
            }
            matrix = cm;
        } else {
            throw new FactoryException(Errors.format(Errors.Keys.UnsupportedCoordinateSystem_1, cs.getName()));
        }
    }
    return matrix;
}
Also used : CartesianCS(org.opengis.referencing.cs.CartesianCS) SphericalCS(org.opengis.referencing.cs.SphericalCS) Matrix(org.opengis.referencing.operation.Matrix) FactoryException(org.opengis.util.FactoryException) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) MatrixSIS(org.apache.sis.referencing.operation.matrix.MatrixSIS) Matrix4(org.apache.sis.referencing.operation.matrix.Matrix4)

Example 3 with EllipsoidalCS

use of org.opengis.referencing.cs.EllipsoidalCS in project sis by apache.

the class CommonCRSTest method testGeographic3D.

/**
 * Tests the {@link CommonCRS#geographic3D()} method.
 */
@Test
@DependsOnMethod("testGeographic")
public void testGeographic3D() {
    final GeographicCRS crs = CommonCRS.WGS72.geographic3D();
    Validators.validate(crs);
    assertEquals("WGS 72", crs.getName().getCode());
    assertSame(CommonCRS.WGS72.geographic().getDatum(), crs.getDatum());
    assertNotSame(CommonCRS.WGS84.geographic().getDatum(), crs.getDatum());
    final EllipsoidalCS cs = crs.getCoordinateSystem();
    final String name = cs.getName().getCode();
    assertTrue(name, name.startsWith("Ellipsoidal 3D"));
    assertEquals("dimension", 3, cs.getDimension());
    assertAxisDirectionsEqual(name, cs, AxisDirection.NORTH, AxisDirection.EAST, AxisDirection.UP);
    assertSame("Cached value", crs, CommonCRS.WGS72.geographic3D());
}
Also used : EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) GeographicCRS(org.opengis.referencing.crs.GeographicCRS) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 4 with EllipsoidalCS

use of org.opengis.referencing.cs.EllipsoidalCS in project sis by apache.

the class DefaultGeographicCRSTest method testConventionalOrientation.

/**
 * Tests the {@link DefaultGeographicCRS#forConvention(AxesConvention)} method
 * for {@link AxesConvention#CONVENTIONALLY_ORIENTED}.
 */
@Test
public void testConventionalOrientation() {
    final DefaultGeographicCRS crs = DefaultGeographicCRS.castOrCopy(CommonCRS.WGS84.geographic3D());
    final DefaultGeographicCRS normalized = crs.forConvention(AxesConvention.CONVENTIONALLY_ORIENTED);
    assertNotSame(crs, normalized);
    final EllipsoidalCS cs = normalized.getCoordinateSystem();
    final EllipsoidalCS ref = crs.getCoordinateSystem();
    assertSame("longitude", ref.getAxis(1), cs.getAxis(0));
    assertSame("latitude", ref.getAxis(0), cs.getAxis(1));
    assertSame("height", ref.getAxis(2), cs.getAxis(2));
}
Also used : EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) Test(org.junit.Test)

Example 5 with EllipsoidalCS

use of org.opengis.referencing.cs.EllipsoidalCS in project sis by apache.

the class DefaultGeodeticCRS method formatTo.

/**
 * Formats this CRS as a <cite>Well Known Text</cite> {@code GeodeticCRS[…]} element.
 * More information about the WKT format is documented in subclasses.
 *
 * @return {@code "GeodeticCRS"} (WKT 2) or {@code "GeogCS"}/{@code "GeocCS"} (WKT 1).
 */
@Override
protected String formatTo(final Formatter formatter) {
    WKTUtilities.appendName(this, formatter, null);
    CoordinateSystem cs = getCoordinateSystem();
    final Convention convention = formatter.getConvention();
    final boolean isWKT1 = (convention.majorVersion() == 1);
    final boolean isGeographicWKT1 = isWKT1 && (cs instanceof EllipsoidalCS);
    if (isGeographicWKT1 && cs.getDimension() == 3) {
        /*
             * Version 1 of WKT format did not have three-dimensional GeographicCRS. Instead, such CRS were formatted
             * as a CompoundCRS made of a two-dimensional GeographicCRS with a VerticalCRS for the ellipsoidal height.
             * Note that such compound is illegal in WKT 2 and ISO 19111 standard, as ellipsoidal height shall not be
             * separated from the geographic component. So we perform this separation only at WKT 1 formatting time.
             */
        SingleCRS first = CRS.getHorizontalComponent(this);
        SingleCRS second = CRS.getVerticalComponent(this, true);
        if (first != null && second != null) {
            // Should not be null, but we are paranoiac.
            if (AxisDirection.UP.equals(AxisDirections.absolute(cs.getAxis(0).getDirection()))) {
                // It is very unusual to have VerticalCRS first, but our code tries to be robust.
                final SingleCRS t = first;
                first = second;
                second = t;
            }
            formatter.newLine();
            formatter.append(WKTUtilities.toFormattable(first));
            formatter.newLine();
            formatter.append(WKTUtilities.toFormattable(second));
            formatter.newLine();
            return WKTKeywords.Compd_CS;
        }
    }
    /*
         * Unconditionally format the datum element, followed by the prime meridian.
         * The prime meridian is part of datum according ISO 19111, but is formatted
         * as a sibling (rather than a child) element in WKT for historical reasons.
         */
    // Gives subclasses a chance to override.
    final GeodeticDatum datum = getDatum();
    formatter.newLine();
    formatter.append(WKTUtilities.toFormattable(datum));
    formatter.newLine();
    final PrimeMeridian pm = datum.getPrimeMeridian();
    final Unit<Angle> angularUnit = AxisDirections.getAngularUnit(cs, null);
    if (// Really this specific enum, not Convention.isSimplified().
    convention != Convention.WKT2_SIMPLIFIED || ReferencingUtilities.getGreenwichLongitude(pm, Units.DEGREE) != 0) {
        final Unit<Angle> oldUnit = formatter.addContextualUnit(angularUnit);
        formatter.indent(1);
        formatter.append(WKTUtilities.toFormattable(pm));
        formatter.indent(-1);
        formatter.newLine();
        formatter.restoreContextualUnit(angularUnit, oldUnit);
    }
    /*
         * Get the coordinate system to format. This will also determine the units to write and the keyword to
         * return in WKT 1 format. Note that for the WKT 1 format, we need to replace the coordinate system by
         * an instance conform to the legacy conventions.
         *
         * We can not delegate the work below to subclasses,  because XML unmarshalling of a geodetic CRS will
         * NOT create an instance of a subclass (because the distinction between geographic and geocentric CRS
         * is not anymore in ISO 19111:2007).
         */
    final boolean isBaseCRS;
    if (isWKT1) {
        if (!isGeographicWKT1) {
            // If not geographic, then presumed geocentric.
            if (cs instanceof CartesianCS) {
                cs = Legacy.forGeocentricCRS((CartesianCS) cs, true);
            } else {
                // SphericalCS was not supported in WKT 1.
                formatter.setInvalidWKT(cs, null);
            }
        }
        isBaseCRS = false;
    } else {
        isBaseCRS = isBaseCRS(formatter);
    }
    /*
         * Format the coordinate system, except if this CRS is the base CRS of an AbstractDerivedCRS in WKT 2 format.
         * This is because ISO 19162 omits the coordinate system definition of enclosed base CRS in order to simplify
         * the WKT. The 'formatCS(…)' method may write axis unit before or after the axes depending on whether we are
         * formatting WKT version 1 or 2 respectively.
         *
         * Note that even if we do not format the CS, we may still write the units if we are formatting in "simplified"
         * mode (as opposed to the more verbose mode). This looks like the opposite of what we would expect, but this is
         * because formatting the unit here allow us to avoid repeating the unit in projection parameters when this CRS
         * is part of a ProjectedCRS. Note however that in such case, the units to format are the angular units because
         * the linear units will be formatted in the enclosing PROJCS[…] element.
         */
    if (!isBaseCRS || convention == Convention.INTERNAL) {
        // Will also format the axes unit.
        formatCS(formatter, cs, ReferencingUtilities.getUnit(cs), isWKT1);
    } else if (convention.isSimplified()) {
        formatter.append(formatter.toContextualUnit(angularUnit));
    }
    /*
         * For WKT 1, the keyword depends on the subclass: "GeogCS" for GeographicCRS or "GeocCS" for GeocentricCRS.
         * However we can not rely on the subclass for choosing the keyword, because after XML unmarhaling we only
         * have a GeodeticCRS. We need to make the choice in this base class. The CS type is a sufficient criterion.
         */
    if (isWKT1) {
        return isGeographicWKT1 ? WKTKeywords.GeogCS : WKTKeywords.GeocCS;
    } else {
        return isBaseCRS ? WKTKeywords.BaseGeodCRS : formatter.shortOrLong(WKTKeywords.GeodCRS, WKTKeywords.GeodeticCRS);
    }
}
Also used : SingleCRS(org.opengis.referencing.crs.SingleCRS) CartesianCS(org.opengis.referencing.cs.CartesianCS) Convention(org.apache.sis.io.wkt.Convention) Angle(javax.measure.quantity.Angle) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) GeodeticDatum(org.opengis.referencing.datum.GeodeticDatum) PrimeMeridian(org.opengis.referencing.datum.PrimeMeridian)

Aggregations

EllipsoidalCS (org.opengis.referencing.cs.EllipsoidalCS)16 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)9 CartesianCS (org.opengis.referencing.cs.CartesianCS)7 Test (org.junit.Test)5 GeodeticCRS (org.opengis.referencing.crs.GeodeticCRS)5 CoordinateSystemAxis (org.opengis.referencing.cs.CoordinateSystemAxis)5 GeographicCRS (org.opengis.referencing.crs.GeographicCRS)4 ProjectedCRS (org.opengis.referencing.crs.ProjectedCRS)4 GeodeticDatum (org.opengis.referencing.datum.GeodeticDatum)4 DependsOnMethod (org.apache.sis.test.DependsOnMethod)3 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)3 SphericalCS (org.opengis.referencing.cs.SphericalCS)3 PrimeMeridian (org.opengis.referencing.datum.PrimeMeridian)3 Unit (javax.measure.Unit)2 Angle (javax.measure.quantity.Angle)2 BursaWolfParameters (org.apache.sis.referencing.datum.BursaWolfParameters)2 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)2 IdentifiedObject (org.opengis.referencing.IdentifiedObject)2 EngineeringCRS (org.opengis.referencing.crs.EngineeringCRS)2 VerticalCRS (org.opengis.referencing.crs.VerticalCRS)2