use of org.opengis.referencing.operation.CoordinateOperation in project sis by apache.
the class Envelopes method transform.
/**
* Transforms the given envelope to the specified CRS. If any argument is null, or if the
* {@linkplain GeneralEnvelope#getCoordinateReferenceSystem() envelope CRS} is null or the
* same instance than the given target CRS, then the given envelope is returned unchanged.
* Otherwise a new transformed envelope is returned.
*
* <div class="section">Performance tip</div>
* If there is many envelopes to transform with the same source and target CRS, then it is more efficient
* to get the {@link CoordinateOperation} or {@link MathTransform} instance once and invoke one of the
* others {@code transform(…)} methods.
*
* @param envelope the envelope to transform (may be {@code null}).
* @param targetCRS the target CRS (may be {@code null}).
* @return a new transformed envelope, or directly {@code envelope} if no change was required.
* @throws TransformException if a transformation was required and failed.
*
* @since 0.5
*/
public static Envelope transform(Envelope envelope, final CoordinateReferenceSystem targetCRS) throws TransformException {
if (envelope != null && targetCRS != null) {
final CoordinateReferenceSystem sourceCRS = envelope.getCoordinateReferenceSystem();
if (sourceCRS != targetCRS) {
if (sourceCRS == null) {
// Slight optimization: just copy the given Envelope.
envelope = new GeneralEnvelope(envelope);
((GeneralEnvelope) envelope).setCoordinateReferenceSystem(targetCRS);
} else {
final CoordinateOperation operation;
try {
operation = CoordinateOperations.factory().createOperation(sourceCRS, targetCRS);
} catch (FactoryException exception) {
throw new TransformException(Errors.format(Errors.Keys.CanNotTransformEnvelope), exception);
}
envelope = transform(operation, envelope);
}
assert Utilities.deepEquals(targetCRS, envelope.getCoordinateReferenceSystem(), ComparisonMode.DEBUG);
}
}
return envelope;
}
use of org.opengis.referencing.operation.CoordinateOperation in project sis by apache.
the class Formatter method appendForSubtypes.
/**
* Appends the anchor, scope and domain of validity of the given object. Those information are available
* only for {@link ReferenceSystem}, {@link Datum} and {@link CoordinateOperation} objects.
*/
private void appendForSubtypes(final IdentifiedObject object) {
final InternationalString anchor, scope;
final Extent area;
if (object instanceof ReferenceSystem) {
anchor = null;
scope = ((ReferenceSystem) object).getScope();
area = ((ReferenceSystem) object).getDomainOfValidity();
} else if (object instanceof Datum) {
anchor = ((Datum) object).getAnchorPoint();
scope = ((Datum) object).getScope();
area = ((Datum) object).getDomainOfValidity();
} else if (object instanceof CoordinateOperation) {
anchor = null;
scope = ((CoordinateOperation) object).getScope();
area = ((CoordinateOperation) object).getDomainOfValidity();
} else {
return;
}
appendOnNewLine(WKTKeywords.Anchor, anchor, null);
appendOnNewLine(WKTKeywords.Scope, scope, ElementKind.SCOPE);
if (area != null) {
appendOnNewLine(WKTKeywords.Area, area.getDescription(), ElementKind.EXTENT);
append(Extents.getGeographicBoundingBox(area), BBOX_ACCURACY);
appendVerticalExtent(Extents.getVerticalRange(area));
appendTemporalExtent(Extents.getTimeRange(area));
}
}
use of org.opengis.referencing.operation.CoordinateOperation in project sis by apache.
the class AbstractCoordinateOperation method equals.
/**
* Compares this coordinate operation with the specified object for equality. If the {@code mode} argument
* is {@link ComparisonMode#STRICT} or {@link ComparisonMode#BY_CONTRACT BY_CONTRACT}, then all available
* properties are compared including the {@linkplain #getDomainOfValidity() domain of validity} and the
* {@linkplain #getScope() scope}.
*
* @param object the object to compare to {@code this}.
* @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
* {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for ignoring properties
* that do not make a difference in the numerical results of coordinate operations.
* @return {@code true} if both objects are equal for the given comparison mode.
*/
@Override
public boolean equals(final Object object, ComparisonMode mode) {
if (super.equals(object, mode)) {
if (mode == ComparisonMode.STRICT) {
final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
if (Objects.equals(sourceCRS, that.sourceCRS) && Objects.equals(interpolationCRS, that.interpolationCRS) && Objects.equals(transform, that.transform) && Objects.equals(scope, that.scope) && Objects.equals(domainOfValidity, that.domainOfValidity) && Objects.equals(coordinateOperationAccuracy, that.coordinateOperationAccuracy)) {
// Check against never-ending recursivity with DerivedCRS.
if (Semaphores.queryAndSet(Semaphores.CONVERSION_AND_CRS)) {
return true;
} else
try {
return Objects.equals(targetCRS, that.targetCRS);
} finally {
Semaphores.clear(Semaphores.CONVERSION_AND_CRS);
}
}
} else {
/*
* This block is for all ComparisonModes other than STRICT. At this point we know that the metadata
* properties (class, name, identifiers, etc.) match the criterion of the given comparison mode.
* Before to continue perform the following checks:
*
* - Scope, domain and accuracy properties only if NOT in "ignore metadata" mode.
* - Interpolation CRS in all cases (regardless if ignoring metadata or not).
*/
final CoordinateOperation that = (CoordinateOperation) object;
if ((mode.isIgnoringMetadata() || (deepEquals(getScope(), that.getScope(), mode) && deepEquals(getDomainOfValidity(), that.getDomainOfValidity(), mode) && deepEquals(getCoordinateOperationAccuracy(), that.getCoordinateOperationAccuracy(), mode))) && deepEquals(getInterpolationCRS(), getInterpolationCRS(that), mode)) {
/*
* At this point all metadata match or can be ignored. First, compare the targetCRS.
* We need to perform this comparison only if this 'equals(…)' method is not invoked
* from AbstractDerivedCRS, otherwise we would fall in an infinite recursive loop
* (because targetCRS is the DerivedCRS, which in turn wants to compare this operation).
*
* We also opportunistically use this "anti-recursivity" check for another purpose.
* The Semaphores.COMPARING flag should be set only when AbstractDerivedCRS is comparing
* its "from base" conversion. The flag should never be set in any other circumstance,
* since this is an internal Apache SIS mechanism. If we know that we are comparing the
* AbstractDerivedCRS.fromBase conversion, then (in the way Apache SIS is implemented)
* this.sourceCRS == AbstractDerivedCRS.baseCRS. Consequently we can relax the check of
* sourceCRS axis order if the mode is ComparisonMode.IGNORE_METADATA.
*/
if (Semaphores.queryAndSet(Semaphores.CONVERSION_AND_CRS)) {
if (mode.isIgnoringMetadata()) {
mode = ComparisonMode.ALLOW_VARIANT;
}
} else
try {
if (!deepEquals(getTargetCRS(), that.getTargetCRS(), mode)) {
return false;
}
} finally {
Semaphores.clear(Semaphores.CONVERSION_AND_CRS);
}
/*
* Now compare the sourceCRS, potentially with a relaxed ComparisonMode (see above comment).
* If the comparison mode allows the two CRS to have different axis order and units, then we
* need to take in account those difference before to compare the MathTransform. We proceed
* by modifying 'tr2' as if it was a MathTransform with crs1 as the source instead of crs2.
*/
final CoordinateReferenceSystem crs1 = this.getSourceCRS();
final CoordinateReferenceSystem crs2 = that.getSourceCRS();
if (deepEquals(crs1, crs2, mode)) {
MathTransform tr1 = this.getMathTransform();
MathTransform tr2 = that.getMathTransform();
if (mode == ComparisonMode.ALLOW_VARIANT)
try {
final MathTransform before = MathTransforms.linear(CoordinateSystems.swapAndScaleAxes(crs1.getCoordinateSystem(), crs2.getCoordinateSystem()));
final MathTransform after = MathTransforms.linear(CoordinateSystems.swapAndScaleAxes(that.getTargetCRS().getCoordinateSystem(), this.getTargetCRS().getCoordinateSystem()));
tr2 = MathTransforms.concatenate(before, tr2, after);
} catch (IncommensurableException | RuntimeException e) {
Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION), AbstractCoordinateOperation.class, "equals", e);
}
return deepEquals(tr1, tr2, mode);
}
}
}
}
return false;
}
use of org.opengis.referencing.operation.CoordinateOperation in project sis by apache.
the class DefaultConcatenatedOperation method initialize.
/**
* Performs the part of {@code DefaultConcatenatedOperations} construction that requires an iteration over
* the sequence of coordinate operations. This method performs the following processing:
*
* <ul>
* <li>Verify the validity of the {@code operations} argument.</li>
* <li>Add the single operations in the {@code flattened} array.</li>
* <li>Set the {@link #transform} field to the concatenated transform.</li>
* <li>Set the {@link #coordinateOperationAccuracy} field, but only if {@code setAccuracy} is {@code true}.</li>
* </ul>
*
* This method invokes itself recursively if there is nested {@code ConcatenatedOperation} instances
* in the given list. This should not happen according ISO 19111 standard, but we try to be safe.
*
* <div class="section">How coordinate operation accuracy is determined</div>
* If {@code setAccuracy} is {@code true}, then this method copies accuracy information found in the single
* {@link Transformation} instance. This method ignores instances of other kinds for the following reason:
* some {@link Conversion} instances declare an accuracy, which is typically close to zero. If a concatenated
* operation contains such conversion together with a transformation with unknown accuracy, then we do not want
* to declare "0 meter" as the concatenated operation accuracy; it would be a false information.
* An other reason is that a concatenated operation typically contains an arbitrary amount of conversions,
* but only one transformation. So considering only transformations usually means to pickup only one operation
* in the given {@code operations} list, which make things clearer.
*
* <div class="note"><b>Note:</b>
* according ISO 19111, the accuracy attribute is allowed only for transformations. However this restriction
* is not enforced everywhere. For example the EPSG database declares an accuracy of 0 meter for conversions,
* which is conceptually exact. In this class we are departing from strict interpretation of 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.</div>
*
* @param properties the properties specified at construction time, or {@code null} if unknown.
* @param operations the operations to concatenate.
* @param flattened the destination list in which to add the {@code SingleOperation} instances.
* @param mtFactory the math transform factory to use, or {@code null} for not performing concatenation.
* @param setSource {@code true} for setting the {@link #sourceCRS} on the very first CRS (regardless if null or not).
* @param setAccuracy {@code true} for setting the {@link #coordinateOperationAccuracy} field.
* @param setDomain {@code true} for setting the {@link #domainOfValidity} field.
* @return the last target CRS, regardless if null or not.
* @throws FactoryException if the factory can not concatenate the math transforms.
*/
private CoordinateReferenceSystem initialize(final Map<String, ?> properties, final CoordinateOperation[] operations, final List<CoordinateOperation> flattened, final MathTransformFactory mtFactory, boolean setSource, boolean setAccuracy, boolean setDomain) throws FactoryException {
CoordinateReferenceSystem previous = null;
for (int i = 0; i < operations.length; i++) {
final CoordinateOperation op = operations[i];
ArgumentChecks.ensureNonNullElement("operations", i, op);
/*
* Verify consistency of user argument: for each coordinate operation, the number of dimensions of the
* source CRS shall be equals to the number of dimensions of the target CRS in the previous operation.
*/
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.getResources(properties).getString(Errors.Keys.MismatchedDimension_3, "operations[" + i + "].sourceCRS", dim1, dim2));
}
}
if (setSource) {
setSource = false;
// Take even if null.
sourceCRS = next;
}
// For next iteration cycle.
previous = op.getTargetCRS();
/*
* Now that we have verified the CRS dimensions, we should be able to concatenate the transforms.
* If an operation is a nested ConcatenatedOperation (not allowed by ISO 19111, but we try to be
* safe), we will first try to use the ConcatenatedOperation.transform as a whole. Only if that
* concatenated operation does not provide a transform we will concatenate its components. Note
* however that we traverse nested concatenated operations unconditionally at least for checking
* its consistency.
*/
MathTransform step = op.getMathTransform();
if (op instanceof ConcatenatedOperation) {
final List<? extends CoordinateOperation> children = ((ConcatenatedOperation) op).getOperations();
@SuppressWarnings("SuspiciousToArrayCall") final CoordinateOperation[] asArray = children.toArray(new CoordinateOperation[children.size()]);
initialize(properties, asArray, flattened, (step == null) ? mtFactory : null, false, setAccuracy, setDomain);
} else if (!step.isIdentity()) {
flattened.add(op);
}
if (mtFactory != null && step != null) {
transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step;
}
/*
* Optionally copy the coordinate operation accuracy from the transformation (or from a concatenated
* operation on the assumption that its accuracy was computed by the same algorithm than this method).
* See javadoc for a rational about why we take only transformations in account. If more than one
* transformation is found, clear the collection and abandon the attempt to set the accuracy information.
* Instead the user will get a better result by invoking PositionalAccuracyConstant.getLinearAccuracy(…)
* since that method conservatively computes the sum of all linear accuracy.
*/
if (setAccuracy && (op instanceof Transformation || op instanceof ConcatenatedOperation) && (PositionalAccuracyConstant.getLinearAccuracy(op) != 0)) {
if (coordinateOperationAccuracy == null) {
coordinateOperationAccuracy = op.getCoordinateOperationAccuracy();
} else {
coordinateOperationAccuracy = null;
setAccuracy = false;
}
}
/*
* Optionally copy the domain of validity, provided that it is the same for all component.
* Current implementation does not try to compute the intersection of all components.
*/
if (setDomain) {
final Extent domain = op.getDomainOfValidity();
if (domain != null) {
if (domainOfValidity == null) {
domainOfValidity = domain;
} else if (!domain.equals(domainOfValidity)) {
domainOfValidity = null;
setDomain = false;
}
}
}
}
return previous;
}
use of org.opengis.referencing.operation.CoordinateOperation in project sis by apache.
the class DefaultConcatenatedOperation method formatTo.
/**
* Formats this coordinate operation in pseudo-WKT. This is specific to Apache SIS since
* there is no concatenated operation in the Well Known Text (WKT) version 2 format.
*
* @param formatter the formatter to use.
* @return {@code "ConcatenatedOperation"}.
*/
@Override
protected String formatTo(final Formatter formatter) {
super.formatTo(formatter);
for (final CoordinateOperation component : operations) {
formatter.newLine();
formatter.append(castOrCopy(component));
}
formatter.setInvalidWKT(this, null);
return "ConcatenatedOperation";
}
Aggregations