use of org.apache.sis.referencing.factory.InvalidGeodeticParameterException in project sis by apache.
the class Proj4Factory method name.
/**
* Returns the {@literal Proj.4} name of the given parameter value or parameter group.
*
* @param param the parameter value or parameter group for which to get the Proj.4 name.
* @param errorKey the resource key of the error message to produce if no Proj.4 name has been found.
* The message shall expect exactly one argument. This error key can be
* {@link Errors.Keys#UnsupportedOperation_1} or {@link Errors.Keys#UnexpectedParameter_1}.
* @return the Proj.4 name of the given object (never null).
* @throws FactoryException if the Proj.4 name has not been found.
*/
private String name(final IdentifiedObject param, final short errorKey) throws FactoryException {
String name = IdentifiedObjects.getName(param, Citations.PROJ4);
if (name == null) {
name = param.getName().getCode();
final String message = Errors.getResources(defaultProperties).getString(errorKey, name);
if (errorKey == Errors.Keys.UnsupportedOperation_1) {
throw new NoSuchIdentifierException(message, name);
} else {
throw new InvalidGeodeticParameterException(message);
}
}
return name;
}
use of org.apache.sis.referencing.factory.InvalidGeodeticParameterException in project sis by apache.
the class DefaultMathTransformFactoryTest method testAllMapProjections.
/**
* Tests the creation of all registered map projections.
* Only the semi-axis lengths are specified. For the rest, we rely on default values.
*
* @throws FactoryException if the construction of a map projection failed.
*
* @since 0.7
*/
@Test
public void testAllMapProjections() throws FactoryException {
/*
* Gets all map projections and creates a projection using the WGS84 ellipsoid
* and default parameter values.
*/
final Map<String, ?> dummyName = Collections.singletonMap(DefaultProjectedCRS.NAME_KEY, "Test");
final MathTransformFactory mtFactory = DefaultFactories.forBuildin(MathTransformFactory.class);
final Collection<OperationMethod> methods = mtFactory.getAvailableMethods(Projection.class);
for (final OperationMethod method : methods) {
final String classification = method.getName().getCode();
ParameterValueGroup param = mtFactory.getDefaultParameters(classification);
param.parameter("semi_major").setValue(6377563.396);
param.parameter("semi_minor").setValue(6356256.909237285);
final MathTransform mt;
try {
mt = mtFactory.createParameterizedTransform(param);
} catch (InvalidGeodeticParameterException e) {
/*
* Some map projections have mandatory parameters which we ignore for now
* except for a few well-known projection that we know should not fail.
*/
if (classification.contains("Mercator")) {
throw e;
}
out.print(classification);
out.print(CharSequences.spaces(42 - classification.length()));
out.print(": ");
out.println(e.getLocalizedMessage());
continue;
}
/*
* Verifies that the map projection properties are the ones that we specified.
* Note that the Equirectangular projection has been optimized as an affine transform, which we skip.
*/
if (mt instanceof LinearTransform) {
continue;
}
assertInstanceOf(classification, Parameterized.class, mt);
param = ((Parameterized) mt).getParameterValues();
assertEquals(classification, param.getDescriptor().getName().getCode());
assertEquals(classification, 6377563.396, param.parameter("semi_major").doubleValue(), 1E-4);
assertEquals(classification, 6356256.909237285, param.parameter("semi_minor").doubleValue(), 1E-4);
/*
* Creates a ProjectedCRS from the map projection. This part is more an integration test than
* a DefaultMathTransformFactory test. Again, the intent is to verify that the properties are
* the one that we specified.
*/
final DefaultProjectedCRS crs = new DefaultProjectedCRS(dummyName, CommonCRS.WGS84.normalizedGeographic(), new DefaultConversion(dummyName, method, mt, null), HardCodedCS.PROJECTED);
final Conversion projection = crs.getConversionFromBase();
assertSame(classification, mt, projection.getMathTransform());
assertEquals(classification, projection.getMethod().getName().getCode());
}
}
use of org.apache.sis.referencing.factory.InvalidGeodeticParameterException in project sis by apache.
the class CoordinateOperationRegistry method propagateVertical.
/**
* Appends a vertical axis in the source CRS of the first step {@code forward = true} or in
* the target CRS of the last step {@code forward = false} of the given operations chain.
*
* @param source3D the potentially three-dimensional source CRS
* @param target3D the potentially three-dimensional target CRS
* @param operations the chain of operations in which to add a vertical axis.
* @param forward {@code true} for adding the vertical axis at the beginning, or
* {@code false} for adding the vertical axis at the end.
* @return {@code true} on success.
* @throws IllegalArgumentException if the operation method can not have the desired number of dimensions.
*/
private boolean propagateVertical(final CoordinateReferenceSystem source3D, final CoordinateReferenceSystem target3D, final ListIterator<CoordinateOperation> operations, final boolean forward) throws IllegalArgumentException, FactoryException {
while (forward ? operations.hasNext() : operations.hasPrevious()) {
final CoordinateOperation op = forward ? operations.next() : operations.previous();
/*
* We will accept to increase the number of dimensions only for operations between geographic CRS.
* We do not increase the number of dimensions for operations between other kind of CRS because we
* would not know which value to give to the new dimension.
*/
CoordinateReferenceSystem sourceCRS, targetCRS;
if (!((sourceCRS = op.getSourceCRS()) instanceof GeodeticCRS && (targetCRS = op.getTargetCRS()) instanceof GeodeticCRS && sourceCRS.getCoordinateSystem() instanceof EllipsoidalCS && targetCRS.getCoordinateSystem() instanceof EllipsoidalCS)) {
break;
}
/*
* We can process mostly linear operations, otherwise it is hard to know how to add a dimension.
* Examples of linear operations are:
*
* - Longitude rotation (EPSG:9601). Note that this is a transformation rather than a conversion.
* - Geographic3D to 2D conversion (EPSG:9659).
*
* However there is a few special cases where we may be able to add a dimension in a non-linear operation.
* We can attempt those special cases by just giving the same parameters to the math transform factory
* together with the desired CRS. Examples of such special cases are:
*
* - Geocentric translations (geog2D domain)
* - Coordinate Frame Rotation (geog2D domain)
* - Position Vector transformation (geog2D domain)
*/
Matrix matrix = MathTransforms.getMatrix(op.getMathTransform());
if (matrix == null) {
if (SubTypes.isSingleOperation(op)) {
final MathTransformFactory mtFactory = factorySIS.getMathTransformFactory();
if (mtFactory instanceof DefaultMathTransformFactory) {
if (forward)
sourceCRS = toGeodetic3D(sourceCRS, source3D);
else
targetCRS = toGeodetic3D(targetCRS, target3D);
final MathTransform mt;
try {
mt = ((DefaultMathTransformFactory) mtFactory).createParameterizedTransform(((SingleOperation) op).getParameterValues(), ReferencingUtilities.createTransformContext(sourceCRS, targetCRS, null));
} catch (InvalidGeodeticParameterException e) {
log(null, e);
break;
}
operations.set(recreate(op, sourceCRS, targetCRS, mt, mtFactory.getLastMethodUsed()));
return true;
}
}
break;
}
/*
* We can process only one of the following cases:
*
* - Replace a 2D → 2D operation by a 3D → 3D one (i.e. add a passthrough operation).
* - Usually remove (or otherwise edit) the operation that change the number of dimensions
* between the 2D and 3D cases.
*/
final int numRow = matrix.getNumRow();
final int numCol = matrix.getNumCol();
// 2D → 2D operation.
final boolean is2D = (numCol == 3 && numRow == 3);
if (!(is2D || (// 2D → 3D operation.
forward ? // 2D → 3D operation.
(numCol == 3 && numRow == 4) : // 3D → 2D operation.
(numCol == 4 && numRow == 3)))) {
break;
}
matrix = Matrices.resizeAffine(matrix, 4, 4);
if (matrix.isIdentity()) {
operations.remove();
} else {
/*
* If we can not just remove the operation, build a new one with the expected number of dimensions.
* The new operation will inherit the same properties except the identifiers, since it is no longer
* conform to the definition provided by the authority.
*/
final MathTransform mt = factorySIS.getMathTransformFactory().createAffineTransform(matrix);
operations.set(recreate(op, toGeodetic3D(sourceCRS, source3D), toGeodetic3D(targetCRS, target3D), mt, null));
}
/*
* If we processed the operation that change the number of dimensions, we are done.
*/
if (!is2D) {
return true;
}
}
return false;
}
use of org.apache.sis.referencing.factory.InvalidGeodeticParameterException in project sis by apache.
the class DefaultCoordinateOperationFactory method createConcatenatedOperation.
/**
* Creates an ordered sequence of two or more single coordinate operations.
* The sequence of operations is constrained by the requirement that the source coordinate reference system
* of step (<var>n</var>+1) must be the same as the target coordinate reference system of step (<var>n</var>).
* The source coordinate reference system of the first step and the target coordinate reference system of the
* last step are the source and target coordinate reference system associated with the concatenated operation.
*
* <p>The properties given in argument follow the same rules than for any other
* {@linkplain AbstractCoordinateOperation#AbstractCoordinateOperation(Map, CoordinateReferenceSystem,
* CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransform) coordinate operation} constructor.
* The following table is a reminder of main (not all) properties:</p>
*
* <table class="sis">
* <caption>Recognized properties (non exhaustive list)</caption>
* <tr>
* <th>Property name</th>
* <th>Value type</th>
* <th>Returned by</th>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} or {@link String}</td>
* <td>{@link AbstractCoordinateOperation#getName()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td>
* <td>{@link AbstractCoordinateOperation#getIdentifiers()}</td>
* </tr>
* </table>
*
* @param properties the properties to be given to the identified object.
* @param operations the sequence of operations. Shall contains at least two operations.
* @return the concatenated operation created from the given arguments.
* @throws FactoryException if the object creation failed.
*/
@Override
public CoordinateOperation createConcatenatedOperation(final Map<String, ?> properties, final CoordinateOperation... operations) throws FactoryException {
/*
* If the user specified a single operation, there is no need to create a ConcatenatedOperation;
* the operation to return will be the specified one. The metadata given in argument are ignored
* on the assumption that the single operation has more complete metadata (in particular an EPSG
* code, in which case we do not want to modify any other metadata in order to stay compliant
* with EPSG definition).
*/
if (operations != null && operations.length == 1) {
return operations[0];
}
final ConcatenatedOperation op;
try {
op = new DefaultConcatenatedOperation(properties, operations, getMathTransformFactory());
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception.getLocalizedMessage(), exception);
}
/*
* Verifies again the number of single operations. We may have a singleton if some operations
* were omitted because their associated math transform were identity. This happen for example
* if a "Geographic 3D to 2D conversion" has been redimensioned to a "3D to 3D" operation.
*/
final List<? extends CoordinateOperation> co = op.getOperations();
if (co.size() != 1) {
return pool.unique(op);
}
final CoordinateOperation single = co.get(0);
assert op.getMathTransform().equals(single.getMathTransform()) : op;
if (!Objects.equals(single.getSourceCRS(), op.getSourceCRS()) || !Objects.equals(single.getTargetCRS(), op.getTargetCRS())) {
/*
* The CRS of the single operation may be different than the CRS of the concatenated operation
* if the first or the last operation was an identity operation. It happens for example if the
* sole purpose of an operation step was to change the longitude range from [-180 … +180]° to
* [0 … 360]°: the MathTransform is identity (because Apache SIS does not handle those changes
* in MathTransform; we handle that elsewhere, for example in the Envelopes utility class),
* but omitting the transform should not cause the lost of the CRS with desired longitude range.
*/
if (single instanceof SingleOperation) {
final Map<String, Object> merge = new HashMap<>(IdentifiedObjects.getProperties(single, CoordinateOperation.IDENTIFIERS_KEY));
merge.put(ReferencingServices.PARAMETERS_KEY, ((SingleOperation) single).getParameterValues());
if (single instanceof AbstractIdentifiedObject) {
merge.put(ReferencingServices.OPERATION_TYPE_KEY, ((AbstractIdentifiedObject) single).getInterface());
}
merge.putAll(properties);
return createSingleOperation(merge, op.getSourceCRS(), op.getTargetCRS(), AbstractCoordinateOperation.getInterpolationCRS(op), ((SingleOperation) single).getMethod(), single.getMathTransform());
}
}
return single;
}
use of org.apache.sis.referencing.factory.InvalidGeodeticParameterException in project sis by apache.
the class LinearTransformBuilder method create.
/**
* Creates a linear transform approximation from the source positions to the target positions.
* This method assumes that source positions are precise and that all uncertainty is in the target positions.
*
* @param factory the factory to use for creating the transform, or {@code null} for the default factory.
* The {@link MathTransformFactory#createAffineTransform(Matrix)} method of that factory
* shall return {@link LinearTransform} instances.
* @return the fitted linear transform.
* @throws FactoryException if the transform can not be created,
* for example because the source or target points have not be specified.
*
* @since 0.8
*/
@Override
@SuppressWarnings("serial")
public LinearTransform create(final MathTransformFactory factory) throws FactoryException {
if (transform == null) {
// Protect from changes.
final double[][] sources = this.sources;
final double[][] targets = this.targets;
if (targets == null) {
throw new InvalidGeodeticParameterException(noData());
}
final int sourceDim = (sources != null) ? sources.length : gridSize.length;
final int targetDim = targets.length;
correlation = new double[targetDim];
final MatrixSIS matrix = Matrices.create(targetDim + 1, sourceDim + 1, ExtendedPrecisionMatrix.ZERO);
matrix.setElement(targetDim, sourceDim, 1);
for (int j = 0; j < targetDim; j++) {
final double c;
switch(sourceDim) {
case 1:
{
final int row = j;
final Line line = new Line() {
@Override
public void setEquation(final Number slope, final Number y0) {
super.setEquation(slope, y0);
// Preserve the extended precision (double-double).
matrix.setNumber(row, 0, slope);
matrix.setNumber(row, 1, y0);
}
};
if (sources != null) {
c = line.fit(vector(sources[0]), vector(targets[j]));
} else {
c = line.fit(Vector.createSequence(0, 1, gridSize[0]), Vector.create(targets[j], false));
}
break;
}
case 2:
{
final int row = j;
final Plane plan = new Plane() {
@Override
public void setEquation(final Number sx, final Number sy, final Number z0) {
super.setEquation(sx, sy, z0);
// Preserve the extended precision (double-double).
matrix.setNumber(row, 0, sx);
matrix.setNumber(row, 1, sy);
matrix.setNumber(row, 2, z0);
}
};
if (sources != null) {
c = plan.fit(vector(sources[0]), vector(sources[1]), vector(targets[j]));
} else
try {
c = plan.fit(gridSize[0], gridSize[1], Vector.create(targets[j], false));
} catch (IllegalArgumentException e) {
// This may happen if the z vector still contain some "NaN" values.
throw new InvalidGeodeticParameterException(noData(), e);
}
break;
}
default:
{
throw new FactoryException(Errors.format(Errors.Keys.ExcessiveNumberOfDimensions_1, sourceDim));
}
}
correlation[j] = c;
}
transform = (LinearTransform) nonNull(factory).createAffineTransform(matrix);
}
return transform;
}
Aggregations