cops = cop.getOperations();
expand(cops.toArray(new CoordinateOperation[cops.size()]), target, factory, false);
} else {
throw new IllegalArgumentException(Errors.format(ErrorKeys.ILLEGAL_CLASS_$2,
Classes.getClass(op), SingleOperation.class));
}
/*
* Check the CRS dimensions.
*/
if (i != 0) {
final CoordinateReferenceSystem previous = operations[i-1].getTargetCRS();
final CoordinateReferenceSystem next = op .getSourceCRS();
if (previous!=null && next!=null) {
final int dim1 = previous.getCoordinateSystem().getDimension();
final int dim2 = next.getCoordinateSystem().getDimension();
if (dim1 != dim2) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.MISMATCHED_DIMENSION_$2, dim1, dim2));
}
}
}
/*
* Concatenates the math transform.
*/
if (wantTransform) {
final MathTransform step = op.getMathTransform();
if (transform == null) {
transform = step;
} else if (factory != null) {
transform = factory.createConcatenatedTransform(transform, step);
} else {
transform = ConcatenatedTransform.create(transform, step);
}
}
}
if (wantTransform) {
final int size = target.size();
if (size <= 1) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.MISSING_PARAMETER_$1, "operations[" + size + ']'));
}
}
return transform;
}
/**
* If no accuracy were specified in the given properties map, add all accuracies found in the
* operation to concatenate. This method considers only {@link Transformation} components and
* ignores all conversions. According ISO 19111, the accuracy attribute is allowed only for
* transformations. However, this restriction is not enforced everywhere. The EPSG database
* declares an accuracy of 0 meters for conversions, which is conceptually exact. Ourself we
* are departing from the specification, since we are adding accuracy informations to a
* concatenated operation. This departure should be considered as a convenience feature
* only; accuracies are really relevant in transformations only.
*
* There is also a technical reasons for ignoring conversions. If a concatenated operation
* contains a datum shift (i.e. a transformation) with unknow accuracy, and a projection
* (i.e. a conversion) with a declared 0 meter error, we don't want to declare this 0 meter
* error as the concatenated operation's accuracy; it would be a false information.
*
* Note that a concatenated operation typically contains an arbitrary amount of conversions,
* but only one transformation. So considering transformation only usually means to pickup
* only one operation in the given {@code operations} list.
*
* @todo We should use a Map and merge only one accuracy for each specification.
*/
private static Map mergeAccuracy(final Map properties,
final List extends CoordinateOperation> operations)
{
if (!properties.containsKey(COORDINATE_OPERATION_ACCURACY_KEY)) {
Set accuracy = null;
for (final CoordinateOperation op : operations) {
if (op instanceof Transformation) {
// See javadoc for a rational why we take only transformations in account.
Collection candidates = op.getCoordinateOperationAccuracy();
if (candidates!=null && !candidates.isEmpty()) {
if (accuracy == null) {
accuracy = new LinkedHashSet();
}
accuracy.addAll(candidates);
}
}
}
if (accuracy != null) {
final Map merged = new HashMap(properties);
merged.put(COORDINATE_OPERATION_ACCURACY_KEY,
accuracy.toArray(new PositionalAccuracy[accuracy.size()]));
return merged;
}
}
return properties;
}
/**
* Returns the sequence of operations.
*/
public List getOperations() {
return operations;
}
/**
* Compare this concatenated operation with the specified object for equality.
* If {@code compareMetadata} is {@code true}, then all available properties are
* compared including {@linkplain #getValidArea valid area} and {@linkplain #getScope scope}.
*
* @param object The object to compare to {@code this}.
* @param compareMetadata {@code true} for performing a strict comparaison, or
* {@code false} for comparing only properties relevant to transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
if (object == this) {
return true; // Slight optimization.
}
if (super.equals(object, compareMetadata)) {
final DefaultConcatenatedOperation that = (DefaultConcatenatedOperation) object;
return equals(this.operations, that.operations, compareMetadata);
}
return false;
}
/**
* Returns a hash code value for this concatenated operation.
*/
@Override
public int hashCode() {
return operations.hashCode() ^ (int)serialVersionUID;
}
/**
* {@inheritDoc}
*/
@Override
protected String formatWKT(final Formatter formatter) {
final String label = super.formatWKT(formatter);
for (final Iterator it=operations.iterator(); it.hasNext();) {
formatter.append((CoordinateOperation) it.next());
}
return label;
}
}