| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.referencing.operation; |
| |
| import org.opengis.util.FactoryException; |
| import org.opengis.referencing.cs.CartesianCS; |
| import org.opengis.referencing.cs.SphericalCS; |
| import org.opengis.referencing.cs.EllipsoidalCS; |
| import org.opengis.referencing.cs.CoordinateSystem; |
| import org.opengis.referencing.datum.GeodeticDatum; |
| import org.opengis.referencing.operation.Matrix; |
| import org.apache.sis.referencing.privy.ReferencingUtilities; |
| import org.apache.sis.referencing.operation.matrix.Matrices; |
| import org.apache.sis.referencing.operation.matrix.Matrix4; |
| import org.apache.sis.referencing.operation.matrix.MatrixSIS; |
| import org.apache.sis.referencing.operation.transform.ContextualParameters.MatrixRole; |
| import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory.Context; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.measure.Units; |
| |
| |
| /** |
| * Information about the context in which a {@code MathTransform} is created. |
| * This class performs the same normalization as the super-class (namely axis swapping and unit conversions), |
| * with the addition of longitude rotation for supporting change of prime meridian. This latter change is not |
| * applied by the super-class because prime meridian is part of geodetic datum, and the public math transform |
| * factory know nothing about datum (on design, for separation of concerns). |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| */ |
| final class MathTransformContext extends Context { |
| /** |
| * For cross-version compatibility. |
| */ |
| private static final long serialVersionUID = 8765209303733056283L; |
| |
| /** |
| * The longitude of the source and target prime meridian, in number of degrees East of Greenwich. |
| */ |
| private double sourceMeridian, targetMeridian; |
| |
| /** |
| * Creates a new context which add some datum-related information in addition |
| * to the information provided by the super-class. |
| */ |
| MathTransformContext(final GeodeticDatum source, final GeodeticDatum target) { |
| final double rs = ReferencingUtilities.getGreenwichLongitude(source.getPrimeMeridian(), Units.DEGREE); |
| final double rt = ReferencingUtilities.getGreenwichLongitude(target.getPrimeMeridian(), Units.DEGREE); |
| if (rs != rt) { |
| sourceMeridian = rs; |
| targetMeridian = rt; |
| } |
| } |
| |
| /** |
| * 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)); |
| case INVERSE_NORMALIZATION: inverse = true; // Fall through |
| case NORMALIZATION: rotation = sourceMeridian; |
| cs = getSourceCS(); |
| break; |
| case INVERSE_DENORMALIZATION: inverse = true; // Fall through |
| 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) { |
| matrix = Matrices.multiply(rot, cm); // Apply the rotation after denormalization. |
| } else { |
| matrix = cm.multiply(rot); // Apply the rotation before normalization. |
| } |
| } else if (cs == null || cs instanceof EllipsoidalCS || cs instanceof SphericalCS) { |
| final Double value = rotation; |
| if (inverse) { |
| cm.convertBefore(0, null, value); // Longitude is the first axis in normalized CS. |
| } else { |
| cm.convertAfter(0, null, value); |
| } |
| matrix = cm; |
| } else { |
| throw new FactoryException(Errors.format(Errors.Keys.UnsupportedCoordinateSystem_1, cs.getName())); |
| } |
| } |
| return matrix; |
| } |
| } |