Search in sources :

Example 26 with CoordinateOperation

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;
}
Also used : FactoryException(org.opengis.util.FactoryException) NoninvertibleTransformException(org.opengis.referencing.operation.NoninvertibleTransformException) TransformException(org.opengis.referencing.operation.TransformException) AbstractCoordinateOperation(org.apache.sis.referencing.operation.AbstractCoordinateOperation) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 27 with CoordinateOperation

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));
    }
}
Also used : Datum(org.opengis.referencing.datum.Datum) InternationalString(org.opengis.util.InternationalString) TemporalExtent(org.opengis.metadata.extent.TemporalExtent) SimpleExtent(org.apache.sis.internal.simple.SimpleExtent) VerticalExtent(org.opengis.metadata.extent.VerticalExtent) Extent(org.opengis.metadata.extent.Extent) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) ReferenceSystem(org.opengis.referencing.ReferenceSystem)

Example 28 with CoordinateOperation

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;
}
Also used : MathTransform(org.opengis.referencing.operation.MathTransform) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 29 with CoordinateOperation

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;
}
Also used : Transformation(org.opengis.referencing.operation.Transformation) MathTransform(org.opengis.referencing.operation.MathTransform) Extent(org.opengis.metadata.extent.Extent) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation)

Example 30 with CoordinateOperation

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";
}
Also used : CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation)

Aggregations

CoordinateOperation (org.opengis.referencing.operation.CoordinateOperation)45 Test (org.junit.Test)32 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)24 DependsOnMethod (org.apache.sis.test.DependsOnMethod)21 CompoundCRS (org.opengis.referencing.crs.CompoundCRS)6 GeographicCRS (org.opengis.referencing.crs.GeographicCRS)6 SingleOperation (org.opengis.referencing.operation.SingleOperation)6 AbstractCoordinateOperation (org.apache.sis.referencing.operation.AbstractCoordinateOperation)5 DefaultCompoundCRS (org.apache.sis.referencing.crs.DefaultCompoundCRS)4 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)4 ConcatenatedOperation (org.opengis.referencing.operation.ConcatenatedOperation)4 MathTransform (org.opengis.referencing.operation.MathTransform)3 Extent (org.opengis.metadata.extent.Extent)2 ReferenceSystem (org.opengis.referencing.ReferenceSystem)2 OperationMethod (org.opengis.referencing.operation.OperationMethod)2 TransformException (org.opengis.referencing.operation.TransformException)2 Transformation (org.opengis.referencing.operation.Transformation)2 FactoryException (org.opengis.util.FactoryException)2 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1