blob: 5c9d5808993b7f5d0724017a92d0ae7970e82b84 [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.internal.referencing.provider;
import javax.xml.bind.annotation.XmlTransient;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.apache.sis.referencing.operation.transform.EllipsoidToCentricTransform;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.parameter.ParameterBuilder;
import org.apache.sis.internal.util.Constants;
import org.apache.sis.parameter.Parameters;
import org.apache.sis.measure.Units;
/**
* The base class of operation methods performing an affine operation in geocentric coordinates
* concatenated with conversion from/to geographic coordinates. This base class is also used for
* operation methods performing <em>approximation</em> of above, even if they do not really pass
* through geocentric coordinates.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.8
* @since 0.7
* @module
*/
@XmlTransient
public abstract class GeocentricAffineBetweenGeographic extends GeocentricAffine {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -6202315859507526222L;
/**
* The operation parameter descriptor for the number of source and target geographic dimensions (2 or 3).
* This is an OGC-specific parameter for the {@link Molodensky} and {@link AbridgedMolodensky} operations,
* but Apache SIS uses it also for internal parameters of Geographic/Geocentric.
*
* <p>We do not provide default value for this parameter (neither we do for other OGC-specific parameters
* in this class) because this parameter is used with both two- and three-dimensional operation methods.
* If we want to provide a default value, we could but it would complicate a little bit the code since we
* could no longer reuse the same {@code PARAMETERS} constant for operation methods of any number of dimensions.
* Furthermore it would not solve the case where the number of input dimensions is different than the number of
* output dimensions. We can not afford to have wrong default values since it would confuse our interpretation
* of user's parameters in {@link #createMathTransform(MathTransformFactory, ParameterValueGroup)}.</p>
*
* @see GeographicToGeocentric#DIMENSION
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
* <caption>Parameter names</caption>
* <tr><td> OGC: </td><td> dim </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
* <li>Value domain: [2…3]</li>
* <li>No default value</li>
* <li>Optional</li>
* </ul>
*/
public static final ParameterDescriptor<Integer> DIMENSION;
/**
* The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
* Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
* <caption>Parameter names</caption>
* <tr><td> OGC: </td><td> src_semi_major </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
* <li>Value domain: (0.0 … ∞) m</li>
* <li>No default value</li>
* </ul>
*/
public static final ParameterDescriptor<Double> SRC_SEMI_MAJOR;
/**
* The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
* Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
* <caption>Parameter names</caption>
* <tr><td> OGC: </td><td> src_semi_minor </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
* <li>Value domain: (0.0 … ∞) m</li>
* <li>No default value</li>
* </ul>
*/
public static final ParameterDescriptor<Double> SRC_SEMI_MINOR;
/**
* The operation parameter descriptor for the {@code "src_semi_major"} optional parameter value.
* Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
* <caption>Parameter names</caption>
* <tr><td> OGC: </td><td> tgt_semi_major </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
* <li>Value domain: (0.0 … ∞) m</li>
* <li>No default value</li>
* </ul>
*/
public static final ParameterDescriptor<Double> TGT_SEMI_MAJOR;
/**
* The operation parameter descriptor for the {@code "src_semi_minor"} optional parameter value.
* Valid values range from 0 to infinity. Units are {@linkplain Units#METRE metres}.
*
* <!-- Generated by ParameterNameTableGenerator -->
* <table class="sis">
* <caption>Parameter names</caption>
* <tr><td> OGC: </td><td> tgt_semi_minor </td></tr>
* </table>
* <b>Notes:</b>
* <ul>
* <li>Value domain: (0.0 … ∞) m</li>
* <li>No default value</li>
* </ul>
*/
public static final ParameterDescriptor<Double> TGT_SEMI_MINOR;
static {
final ParameterBuilder builder = builder().setCodeSpace(Citations.OGC, Constants.OGC);
SRC_SEMI_MAJOR = builder.addName("src_semi_major").createStrictlyPositive(Double.NaN, Units.METRE);
SRC_SEMI_MINOR = builder.addName("src_semi_minor").createStrictlyPositive(Double.NaN, Units.METRE);
TGT_SEMI_MAJOR = builder.addName("tgt_semi_major").createStrictlyPositive(Double.NaN, Units.METRE);
TGT_SEMI_MINOR = builder.addName("tgt_semi_minor").createStrictlyPositive(Double.NaN, Units.METRE);
DIMENSION = builder.addName("dim").setRequired(false).createBounded(Integer.class, 2, 3, null);
}
/**
* Constructs a provider with the specified parameters.
*
* @param sourceDimensions number of dimensions in the source CRS of this operation method.
* @param targetDimensions number of dimensions in the target CRS of this operation method.
* @param parameters description of parameters expected by this operation.
* @param redimensioned providers for all combinations between 2D and 3D cases, or {@code null}.
*/
GeocentricAffineBetweenGeographic(int sourceDimensions, int targetDimensions,
ParameterDescriptorGroup parameters, GeodeticOperation[] redimensioned)
{
super(sourceDimensions, targetDimensions, parameters, redimensioned);
}
/**
* Notifies {@code DefaultMathTransformFactory} that map projections require values for the
* {@code "src_semi_major"}, {@code "src_semi_minor"} , {@code "tgt_semi_major"} and
* {@code "tgt_semi_minor"} parameters.
*
* @return 3, meaning that the operation requires source and target ellipsoids.
*/
@Override
public final int getEllipsoidsMask() {
return 3;
}
/**
* Creates a math transform from the specified group of parameter values.
* This method wraps the affine operation into Geographic/Geocentric conversions.
*
* @param factory the factory to use for creating concatenated transforms.
* @param values the group of parameter values.
* @return the created math transform.
* @throws FactoryException if a transform can not be created.
*/
@Override
public MathTransform createMathTransform(final MathTransformFactory factory, final ParameterValueGroup values)
throws FactoryException
{
final Parameters pv = Parameters.castOrWrap(values);
final MathTransform affine = super.createMathTransform(factory, pv);
/*
* Create a "Geographic to Geocentric" conversion with ellipsoid axis length units converted to metres
* (the unit implied by SRC_SEMI_MAJOR) because it is the unit of Bursa-Wolf parameters that we created above.
*/
MathTransform toGeocentric = EllipsoidToCentricTransform.createGeodeticConversion(factory,
pv.doubleValue(SRC_SEMI_MAJOR),
pv.doubleValue(SRC_SEMI_MINOR),
Units.METRE, getSourceDimensions() >= 3,
EllipsoidToCentricTransform.TargetType.CARTESIAN);
/*
* Create a "Geocentric to Geographic" conversion with ellipsoid axis length units converted to metres
* because this is the unit of the Geocentric CRS used above.
*/
MathTransform toGeographic = EllipsoidToCentricTransform.createGeodeticConversion(factory,
pv.doubleValue(TGT_SEMI_MAJOR),
pv.doubleValue(TGT_SEMI_MINOR),
Units.METRE, getTargetDimensions() >= 3,
EllipsoidToCentricTransform.TargetType.CARTESIAN);
try {
toGeographic = toGeographic.inverse();
} catch (NoninvertibleTransformException e) {
throw new FactoryException(e); // Should never happen with SIS implementation.
}
/*
* The Geocentric → Affine → Geographic chain.
*/
return factory.createConcatenatedTransform(toGeocentric,
factory.createConcatenatedTransform(affine, toGeographic));
}
}