blob: 7d720b7d8108feccd57857a21e0e5bd5a5374817 [file] [log] [blame]
/*
* 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;
}
}