Search in sources :

Example 1 with ConcatenatedOperation

use of org.opengis.referencing.operation.ConcatenatedOperation in project sis by apache.

the class CoordinateOperationRegistryTest method verifyNTF.

/**
 * Verifies a coordinate operation which is expected to be <cite>"NTF (Paris) to WGS 84 (1)"</cite> (EPSG:8094).
 *
 * @param  domain  either {@code "geog2D domain"} or either {@code "geog3D domain"}.
 * @param  isEPSG  {@code true} if the coordinate operation is expected to contain EPSG identifiers.
 */
private static void verifyNTF(final CoordinateOperation operation, final String domain, final boolean isEPSG) {
    assertInstanceOf("Operation should have two steps.", ConcatenatedOperation.class, operation);
    final List<? extends CoordinateOperation> steps = ((ConcatenatedOperation) operation).getOperations();
    assertEquals("Operation should have two steps.", 2, steps.size());
    final SingleOperation step1 = (SingleOperation) steps.get(0);
    final SingleOperation step2 = (SingleOperation) steps.get(1);
    if (isEPSG) {
        assertEpsgNameAndIdentifierEqual("NTF (Paris) to WGS 84 (1)", 8094, operation);
        assertEpsgNameAndIdentifierEqual("NTF (Paris)", 4807, operation.getSourceCRS());
        assertEpsgNameAndIdentifierEqual("WGS 84", 4326, operation.getTargetCRS());
        assertEpsgNameAndIdentifierEqual("NTF (Paris) to NTF (1)", 1763, step1);
        assertEpsgNameAndIdentifierEqual("NTF to WGS 84 (1)", 1193, step2);
    } else {
        assertEpsgNameWithoutIdentifierEqual("NTF (Paris) to WGS 84 (1)", operation);
        assertEpsgNameWithoutIdentifierEqual("NTF (Paris)", operation.getSourceCRS());
        assertEquals("name", "WGS 84", operation.getTargetCRS().getName().getCode());
        assertEpsgNameWithoutIdentifierEqual("NTF (Paris) to NTF (1)", step1);
        assertEpsgNameWithoutIdentifierEqual("NTF to WGS 84 (1)", step2);
    }
    assertSame("SourceCRS shall be the targetCRS of previous step.", step1.getTargetCRS(), step2.getSourceCRS());
    assertEquals("Method 1", "Longitude rotation", step1.getMethod().getName().getCode());
    assertEquals("Method 2", "Geocentric translations (" + domain + ')', step2.getMethod().getName().getCode());
    final ParameterValueGroup p1 = step1.getParameterValues();
    final ParameterValueGroup p2 = step2.getParameterValues();
    assertEquals("Longitude offset", 2.5969213, p1.parameter("Longitude offset").doubleValue(), STRICT);
    assertEquals("X-axis translation", -168, p2.parameter("X-axis translation").doubleValue(), STRICT);
    assertEquals("Y-axis translation", -60, p2.parameter("Y-axis translation").doubleValue(), STRICT);
    assertEquals("Z-axis translation", 320, p2.parameter("Z-axis translation").doubleValue(), STRICT);
    assertEquals("linearAccuracy", 2, CRS.getLinearAccuracy(operation), STRICT);
}
Also used : ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) SingleOperation(org.opengis.referencing.operation.SingleOperation)

Example 2 with ConcatenatedOperation

use of org.opengis.referencing.operation.ConcatenatedOperation in project sis by apache.

the class PositionalAccuracyConstant method getLinearAccuracy.

/**
 * Convenience method returning the accuracy in meters for the specified operation.
 * This method tries each of the following procedures and returns the first successful one:
 *
 * <ul>
 *   <li>If at least one {@link QuantitativeResult} is found with a linear unit, then the largest
 *       accuracy estimate is converted to {@linkplain Units#METRE metres} and returned.</li>
 *   <li>Otherwise, if the operation is a {@link Conversion}, then returns 0 since a conversion
 *       is by definition accurate up to rounding errors.</li>
 *   <li>Otherwise, if the operation is a {@link Transformation}, then checks if the datum shift
 *       were applied with the help of Bursa-Wolf parameters. This procedure looks for SIS-specific
 *       {@link #DATUM_SHIFT_APPLIED} and {@link #DATUM_SHIFT_OMITTED DATUM_SHIFT_OMITTED} constants.</li>
 *   <li>Otherwise, if the operation is a {@link ConcatenatedOperation}, returns the sum of the accuracy
 *       of all components. This is a conservative scenario where we assume that errors cumulate linearly.
 *       Note that this is not necessarily the "worst case" scenario since the accuracy could be worst
 *       if the math transforms are highly non-linear.</li>
 * </ul>
 *
 * If the above is modified, please update {@code AbstractCoordinateOperation.getLinearAccuracy()} javadoc.
 *
 * @param  operation  the operation to inspect for accuracy.
 * @return the accuracy estimate (always in meters), or NaN if unknown.
 *
 * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getLinearAccuracy()
 */
public static double getLinearAccuracy(final CoordinateOperation operation) {
    double accuracy = Double.NaN;
    final Collection<PositionalAccuracy> accuracies = operation.getCoordinateOperationAccuracy();
    for (final PositionalAccuracy metadata : accuracies) {
        for (final Result result : metadata.getResults()) {
            if (result instanceof QuantitativeResult) {
                final QuantitativeResult quantity = (QuantitativeResult) result;
                final Collection<? extends Record> records = quantity.getValues();
                if (records != null) {
                    final Unit<?> unit = quantity.getValueUnit();
                    if (Units.isLinear(unit)) {
                        final Unit<Length> unitOfLength = unit.asType(Length.class);
                        for (final Record record : records) {
                            for (final Object value : record.getAttributes().values()) {
                                if (value instanceof Number) {
                                    double v = ((Number) value).doubleValue();
                                    v = unitOfLength.getConverterTo(Units.METRE).convert(v);
                                    if (v >= 0 && !(v <= accuracy)) {
                                        // '!' is for replacing the NaN value.
                                        accuracy = v;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    if (Double.isNaN(accuracy)) {
        /*
             * No quantitative (linear) accuracy were found. If the coordinate operation is actually
             * a conversion, the accuracy is up to rounding error (i.e. conceptually 0) by definition.
             */
        if (operation instanceof Conversion) {
            return 0;
        }
        /*
             * If the coordinate operation is actually a transformation, checks if Bursa-Wolf parameters
             * were available for the datum shift. This is SIS-specific. See field javadoc for a rational
             * about the return values chosen.
             */
        if (operation instanceof Transformation) {
            if (accuracies.contains(DATUM_SHIFT_APPLIED)) {
                return DATUM_SHIFT_ACCURACY;
            }
            if (accuracies.contains(DATUM_SHIFT_OMITTED)) {
                return UNKNOWN_ACCURACY;
            }
        }
        /*
             * If the coordinate operation is a compound of other coordinate operations, returns the sum of their accuracy,
             * skipping unknown ones. Making the sum is a conservative approach (not exactly the "worst case" scenario,
             * since it could be worst if the transforms are highly non-linear).
             */
        if (operation instanceof ConcatenatedOperation) {
            for (final CoordinateOperation op : ((ConcatenatedOperation) operation).getOperations()) {
                final double candidate = Math.abs(getLinearAccuracy(op));
                if (!Double.isNaN(candidate)) {
                    if (Double.isNaN(accuracy)) {
                        accuracy = candidate;
                    } else {
                        accuracy += candidate;
                    }
                }
            }
        }
    }
    return accuracy;
}
Also used : Transformation(org.opengis.referencing.operation.Transformation) PositionalAccuracy(org.opengis.metadata.quality.PositionalAccuracy) DefaultAbsoluteExternalPositionalAccuracy(org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) Conversion(org.opengis.referencing.operation.Conversion) Result(org.opengis.metadata.quality.Result) QuantitativeResult(org.opengis.metadata.quality.QuantitativeResult) DefaultConformanceResult(org.apache.sis.metadata.iso.quality.DefaultConformanceResult) QuantitativeResult(org.opengis.metadata.quality.QuantitativeResult) Length(javax.measure.quantity.Length) Record(org.opengis.util.Record) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation)

Example 3 with ConcatenatedOperation

use of org.opengis.referencing.operation.ConcatenatedOperation in project sis by apache.

the class Formatter method appendComplement.

/**
 * Appends the optional complementary attributes common to many {@link IdentifiedObject} subtypes.
 * Those attributes are {@code ANCHOR}, {@code SCOPE}, {@code AREA}, {@code BBOX}, {@code VERTICALEXTENT},
 * {@code TIMEEXTENT}, {@code ID} (previously known as {@code AUTHORITY}) and {@code REMARKS},
 * and have a special treatment: they are written by {@link #append(FormattableObject)}
 * after the {@code formatTo(Formatter)} method returned.
 *
 * <p>The {@code ID[<name>,<code>,…]} element is normally written only for the root element
 * (unless the convention is {@code INTERNAL}), but there is various exceptions to this rule.
 * If formatted, the {@code ID} element will be by default on the same line than the enclosing
 * element (e.g. {@code SPHEROID["Clarke 1866", …, ID["EPSG", 7008]]}). Other example:</p>
 *
 * {@preformat text
 *   PROJCS["NAD27 / Idaho Central",
 *     GEOGCS[...etc...],
 *     ...etc...
 *     ID["EPSG", 26769]]
 * }
 *
 * For non-internal conventions, all elements other than {@code ID[…]} are formatted
 * only for {@link CoordinateOperation} and root {@link ReferenceSystem} instances,
 * with an exception for remarks of {@code ReferenceSystem} embedded inside {@code CoordinateOperation}.
 * Those restrictions are our interpretation of the following ISO 19162 requirement:
 *
 * <blockquote>(…snip…) {@code <scope extent identifier remark>} is a collection of four optional attributes
 * which may be applied to a coordinate reference system, a coordinate operation or a boundCRS. (…snip…)
 * Identifier (…snip…) may also be utilised for components of these objects although this is not recommended
 * except for coordinate operation methods (including map projections) and parameters. (…snip…)
 * A {@code <remark>} can be included within the descriptions of source and target CRS embedded within
 * a coordinate transformation as well as within the coordinate transformation itself.</blockquote>
 */
@SuppressWarnings("null")
private void appendComplement(final IdentifiedObject object, final FormattableObject parent, final FormattableObject gp) {
    isComplement = true;
    // Whether to format ID[…] elements.
    final boolean showIDs;
    // Whether we shall limit to a single ID[…] element.
    final boolean filterID;
    // Whether to format any element other than ID[…] and Remarks[…].
    final boolean showOthers;
    // Whether to format Remarks[…].
    final boolean showRemarks;
    if (convention == Convention.INTERNAL) {
        showIDs = true;
        filterID = false;
        showOthers = true;
        showRemarks = true;
    } else {
        /*
             * Except for the special cases of OperationMethod and Parameters, ISO 19162 recommends to format the
             * ID only for the root element.  But Apache SIS adds an other exception to this rule by handling the
             * components of CompoundCRS as if they were root elements. The reason is that users often create their
             * own CompoundCRS from standard components, for example by adding a time axis to some standard CRS like
             * "WGS84". The resulting CompoundCRS usually have no identifier. Then the users often need to extract a
             * particular component of a CompoundCRS, most often the horizontal part, and will need its identifier
             * for example in a Web Map Service (WMS). Those ID are lost if we do not format them here.
             */
        if (parent == null || parent instanceof CompoundCRS) {
            showIDs = true;
        } else if (gp instanceof CoordinateOperation && !(parent instanceof IdentifiedObject)) {
            // "SourceCRS[…]" and "TargetCRS[…]" sub-elements in CoordinateOperation.
            showIDs = true;
        } else if (convention == Convention.WKT2_SIMPLIFIED) {
            showIDs = false;
        } else {
            showIDs = (object instanceof OperationMethod) || (object instanceof GeneralParameterDescriptor);
        }
        if (convention.majorVersion() == 1) {
            filterID = true;
            showOthers = false;
            showRemarks = false;
        } else {
            filterID = (parent != null);
            if (object instanceof CoordinateOperation) {
                showOthers = !(parent instanceof ConcatenatedOperation);
                showRemarks = showOthers;
            } else if (object instanceof ReferenceSystem) {
                showOthers = (parent == null);
                showRemarks = (parent == null) || (gp instanceof CoordinateOperation);
            } else {
                // Mandated by ISO 19162.
                showOthers = false;
                showRemarks = false;
            }
        }
    }
    if (showOthers) {
        appendForSubtypes(object);
    }
    if (showIDs) {
        Collection<ReferenceIdentifier> identifiers = object.getIdentifiers();
        if (identifiers != null) {
            // Paranoiac check
            if (filterID) {
                for (final ReferenceIdentifier id : identifiers) {
                    if (Citations.identifierMatches(authority, id.getAuthority())) {
                        identifiers = Collections.singleton(id);
                        break;
                    }
                }
            }
            for (ReferenceIdentifier id : identifiers) {
                if (!(id instanceof FormattableObject)) {
                    id = ImmutableIdentifier.castOrCopy(id);
                }
                append((FormattableObject) id);
                if (filterID)
                    break;
            }
        }
    }
    if (showRemarks) {
        appendOnNewLine(WKTKeywords.Remark, object.getRemarks(), ElementKind.REMARKS);
    }
    isComplement = false;
}
Also used : ReferenceIdentifier(org.opengis.referencing.ReferenceIdentifier) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) IdentifiedObject(org.opengis.referencing.IdentifiedObject) ReferenceSystem(org.opengis.referencing.ReferenceSystem) OperationMethod(org.opengis.referencing.operation.OperationMethod)

Example 4 with ConcatenatedOperation

use of org.opengis.referencing.operation.ConcatenatedOperation in project sis by apache.

the class TransformCommand method printOperations.

/**
 * Prints a summary of the given coordinate operation as a sequence of steps.
 * If the operations is specified by EPSG, prints the EPSG name and code.
 * Otherwise prints only the operation method names, since the coordinate operation names
 * generated by SIS are not very meaningful.
 *
 * @param  step  the coordinate operation to print as a sequence of steps.
 */
private void printOperations(final CoordinateOperation step, boolean isNext) {
    if (isNext) {
        isNext = false;
        if (colors) {
            outHeader.append(X364.FOREGROUND_GREEN.sequence());
        }
        outHeader.append(" → ");
        if (colors) {
            outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
        }
    }
    if (!printNameAndIdentifier(step, true)) {
        if (step instanceof ConcatenatedOperation) {
            for (final CoordinateOperation op : ((ConcatenatedOperation) step).getOperations()) {
                printOperations(op, isNext);
                isNext = true;
            }
        } else if (step instanceof PassThroughOperation) {
            printOperations(((PassThroughOperation) step).getOperation(), false);
        } else if (step instanceof SingleOperation) {
            outHeader.append(((SingleOperation) step).getMethod().getName().getCode());
        }
    }
}
Also used : PassThroughOperation(org.opengis.referencing.operation.PassThroughOperation) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) SingleOperation(org.opengis.referencing.operation.SingleOperation)

Example 5 with ConcatenatedOperation

use of org.opengis.referencing.operation.ConcatenatedOperation in project sis by apache.

the class IdentifiedObjects method lookupURN.

/**
 * Looks up a URN, such as {@code "urn:ogc:def:crs:EPSG:9.1:4326"}, of the specified object.
 * This method searches in all {@linkplain org.apache.sis.referencing.factory.GeodeticAuthorityFactory geodetic
 * authority factories} known to SIS for an object {@linkplain org.apache.sis.util.ComparisonMode#APPROXIMATIVE
 * approximatively equals} to the specified object. Then there is a choice:
 *
 * <ul>
 *   <li>If a single matching object is found in the specified authority factory, then its URN is returned.</li>
 *   <li>Otherwise if the given object is a {@link CompoundCRS} or {@link ConcatenatedOperation}
 *       and all components have an URN, then this method returns a combined URN.</li>
 *   <li>Otherwise this method returns {@code null}.</li>
 * </ul>
 *
 * <p><strong>Note that this method checks the identifier validity.</strong>
 * If the given object declares explicitly an identifier, then this method will instantiate an object from the
 * authority factory using that identifier and compare it with the given object. If the comparison fails, then
 * this method returns {@code null}. Consequently this method may return {@code null} even if the given object
 * declares explicitly its identifier. If the declared identifier is wanted unconditionally,
 * one can use the following pattern instead:
 *
 * {@preformat java
 *     String urn = toURN(object.getClass(), getIdentifier(object, authority));
 * }
 *
 * This method can be seen as a converse of {@link CRS#forCode(String)}.
 *
 * @param  object  the object (usually a {@linkplain org.apache.sis.referencing.crs.AbstractCRS
 *         coordinate reference system}) whose identifier is to be found, or {@code null}.
 * @param  authority  the authority for the identifier to return, or {@code null} for
 *         the first identifier regardless its authority.
 * @return the identifier, or {@code null} if none was found without ambiguity or if the given object was null.
 * @throws FactoryException if an error occurred during the search.
 *
 * @see #newFinder(String)
 * @see #toURN(Class, Identifier)
 *
 * @since 0.7
 */
public static String lookupURN(final IdentifiedObject object, final Citation authority) throws FactoryException {
    if (object == null) {
        return null;
    }
    IdentifiedObjectFinder finder;
    try {
        finder = newFinder(Citations.getCodeSpace(authority));
    } catch (NoSuchAuthorityFactoryException e) {
        warning("lookupURN", e);
        finder = newFinder(null);
    }
    String urn = lookupURN(object, authority, finder);
    if (urn != null) {
        return urn;
    }
    /*
         * If we didn't found a URN but the given object is made of smaller components, build a combined URN.
         * Example: "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701" (without spaces actually).
         */
    final List<? extends IdentifiedObject> components;
    if (object instanceof CompoundCRS) {
        components = CRS.getSingleComponents((CompoundCRS) object);
    } else if (object instanceof ConcatenatedOperation) {
        components = ((ConcatenatedOperation) object).getOperations();
    } else {
        return null;
    }
    StringBuilder buffer = null;
    for (final IdentifiedObject component : components) {
        urn = lookupURN(component, authority, finder);
        if (urn == null) {
            return null;
        }
        assert urn.startsWith(DefinitionURI.PREFIX) : urn;
        if (buffer == null) {
            buffer = new StringBuilder(40).append(DefinitionURI.PREFIX).append(DefinitionURI.SEPARATOR).append(NameMeaning.toObjectType(object.getClass()));
        }
        buffer.append(DefinitionURI.COMPONENT_SEPARATOR).append(urn, DefinitionURI.PREFIX.length() + 1, urn.length());
    }
    return (buffer != null) ? buffer.toString() : null;
}
Also used : IdentifiedObjectFinder(org.apache.sis.referencing.factory.IdentifiedObjectFinder) NoSuchAuthorityFactoryException(org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Aggregations

ConcatenatedOperation (org.opengis.referencing.operation.ConcatenatedOperation)7 CoordinateOperation (org.opengis.referencing.operation.CoordinateOperation)4 IdentifiedObject (org.opengis.referencing.IdentifiedObject)3 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)2 CompoundCRS (org.opengis.referencing.crs.CompoundCRS)2 OperationMethod (org.opengis.referencing.operation.OperationMethod)2 PassThroughOperation (org.opengis.referencing.operation.PassThroughOperation)2 SingleOperation (org.opengis.referencing.operation.SingleOperation)2 Transformation (org.opengis.referencing.operation.Transformation)2 Length (javax.measure.quantity.Length)1 FormattableObject (org.apache.sis.io.wkt.FormattableObject)1 Formatter (org.apache.sis.io.wkt.Formatter)1 DefaultAbsoluteExternalPositionalAccuracy (org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy)1 DefaultConformanceResult (org.apache.sis.metadata.iso.quality.DefaultConformanceResult)1 AbstractIdentifiedObject (org.apache.sis.referencing.AbstractIdentifiedObject)1 IdentifiedObjectFinder (org.apache.sis.referencing.factory.IdentifiedObjectFinder)1 NoSuchAuthorityFactoryException (org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException)1 Extent (org.opengis.metadata.extent.Extent)1 PositionalAccuracy (org.opengis.metadata.quality.PositionalAccuracy)1 QuantitativeResult (org.opengis.metadata.quality.QuantitativeResult)1