Search in sources :

Example 1 with PositionalAccuracy

use of org.opengis.metadata.quality.PositionalAccuracy 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 2 with PositionalAccuracy

use of org.opengis.metadata.quality.PositionalAccuracy in project sis by apache.

the class InverseOperationMethod method properties.

/**
 * Infers the properties to give to an inverse coordinate operation.
 * The returned map will contains three kind of information:
 *
 * <ul>
 *   <li>Metadata (domain of validity, accuracy)</li>
 *   <li>Parameter values, if possible</li>
 * </ul>
 *
 * This method copies accuracy and domain of validity metadata from the given operation.
 * We presume that the inverse operation has the same accuracy than the direct operation.
 *
 * <div class="note"><b>Note:</b>
 * in many cases, the inverse operation is numerically less accurate than the direct operation because it
 * uses approximations like series expansions or iterative methods. However the numerical errors caused by
 * those approximations are not of interest here, because they are usually much smaller than the inaccuracy
 * due to the stochastic nature of coordinate transformations (not to be confused with coordinate conversions;
 * see ISO 19111 for more information).</div>
 *
 * If the inverse of the given operation can be represented by inverting the sign of all numerical
 * parameter values, then this method copies also those parameters in a {@code "parameters"} entry.
 *
 * @param source  the operation for which to get the inverse parameters.
 * @param target  where to store the inverse parameters.
 */
static void properties(final SingleOperation source, final Map<String, Object> target) {
    target.put(SingleOperation.DOMAIN_OF_VALIDITY_KEY, source.getDomainOfValidity());
    final Collection<PositionalAccuracy> accuracy = source.getCoordinateOperationAccuracy();
    if (!Containers.isNullOrEmpty(accuracy)) {
        target.put(SingleOperation.COORDINATE_OPERATION_ACCURACY_KEY, accuracy.toArray(new PositionalAccuracy[accuracy.size()]));
    }
    /*
         * If the inverse of the given operation can be represented by inverting the sign of all numerical
         * parameter values, copies those parameters in a "parameters" entry in the properties map.
         * Otherwise does nothing.
         */
    final ParameterValueGroup parameters = source.getParameterValues();
    final ParameterValueGroup copy = parameters.getDescriptor().createValue();
    for (final GeneralParameterValue gp : parameters.values()) {
        if (gp instanceof ParameterValue<?>) {
            final ParameterValue<?> src = (ParameterValue<?>) gp;
            final Object value = src.getValue();
            if (value instanceof Number) {
                final ParameterDescriptor<?> descriptor = src.getDescriptor();
                final InternationalString remarks = descriptor.getRemarks();
                if (remarks != SignReversalComment.SAME) {
                    if (remarks != SignReversalComment.OPPOSITE) {
                        /*
                             * The parameter descriptor does not specify whether the values for the inverse operation
                             * have the same sign or opposite sign. We could heuristically presume that we can invert
                             * the sign if the minimum value has the opposite sign than the maximum value  (as in the
                             * [-10 … 10] range), but such assumption is dangerous. For example the values in a matrix
                             * could be bounded to a range like [-1 … 1], which would mislead above heuristic rule.
                             *
                             * Note that abandoning here does not mean that we will never know the parameter values.
                             * As a fallback, AbstractCoordinateOperation will try to get the parameter values from
                             * the MathTransform. This is the appropriate thing to do at least for Affine operation.
                             */
                        return;
                    }
                    /*
                         * The parameter value of the inverse operation is (or is presumed to be) the negative of
                         * the parameter value of the source operation.  We need to preserve units of measurement
                         * if they were specified.
                         */
                    final ParameterValue<?> tgt = copy.parameter(descriptor.getName().getCode());
                    final Unit<?> unit = src.getUnit();
                    if (unit != null) {
                        tgt.setValue(-src.doubleValue(), unit);
                    } else if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
                        tgt.setValue(-src.intValue());
                    } else {
                        tgt.setValue(-src.doubleValue());
                    }
                    // No need to add 'tgt' to 'copy' since it was done by the call to copy.parameter(…).
                    continue;
                }
            }
        }
        copy.values().add(gp);
    }
    target.put(ReferencingServices.PARAMETERS_KEY, copy);
}
Also used : GeneralParameterValue(org.opengis.parameter.GeneralParameterValue) PositionalAccuracy(org.opengis.metadata.quality.PositionalAccuracy) GeneralParameterValue(org.opengis.parameter.GeneralParameterValue) ParameterValue(org.opengis.parameter.ParameterValue) ParameterValueGroup(org.opengis.parameter.ParameterValueGroup) InternationalString(org.opengis.util.InternationalString)

Example 3 with PositionalAccuracy

use of org.opengis.metadata.quality.PositionalAccuracy in project sis by apache.

the class TransformationAccuracy method create.

/**
 * Creates a positional accuracy for the given value, in metres.
 * This method may return a cached value.
 *
 * @param  accuracy  the accuracy in metres.
 * @return a positional accuracy with the given value.
 */
public static PositionalAccuracy create(final Double accuracy) {
    PositionalAccuracy p = CACHE.get(accuracy);
    if (p == null) {
        final DefaultRecord record = new DefaultRecord(TYPE);
        record.setAll(accuracy);
        final DefaultQuantitativeResult result = new DefaultQuantitativeResult();
        result.setValues(Collections.singletonList(record));
        // In metres by definition in the EPSG database.
        result.setValueUnit(Units.METRE);
        result.setValueType(TYPE);
        final DefaultAbsoluteExternalPositionalAccuracy element = new DefaultAbsoluteExternalPositionalAccuracy(result);
        element.setNamesOfMeasure(Collections.singleton(TRANSFORMATION_ACCURACY));
        element.setEvaluationMethodType(EvaluationMethodType.DIRECT_EXTERNAL);
        element.freeze();
        p = CACHE.putIfAbsent(accuracy, element);
        if (p == null) {
            p = element;
        }
    }
    return p;
}
Also used : PositionalAccuracy(org.opengis.metadata.quality.PositionalAccuracy) DefaultAbsoluteExternalPositionalAccuracy(org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy) DefaultAbsoluteExternalPositionalAccuracy(org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy) DefaultRecord(org.apache.sis.util.iso.DefaultRecord) DefaultQuantitativeResult(org.apache.sis.metadata.iso.quality.DefaultQuantitativeResult)

Aggregations

PositionalAccuracy (org.opengis.metadata.quality.PositionalAccuracy)3 DefaultAbsoluteExternalPositionalAccuracy (org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy)2 Length (javax.measure.quantity.Length)1 DefaultConformanceResult (org.apache.sis.metadata.iso.quality.DefaultConformanceResult)1 DefaultQuantitativeResult (org.apache.sis.metadata.iso.quality.DefaultQuantitativeResult)1 DefaultRecord (org.apache.sis.util.iso.DefaultRecord)1 QuantitativeResult (org.opengis.metadata.quality.QuantitativeResult)1 Result (org.opengis.metadata.quality.Result)1 GeneralParameterValue (org.opengis.parameter.GeneralParameterValue)1 ParameterValue (org.opengis.parameter.ParameterValue)1 ParameterValueGroup (org.opengis.parameter.ParameterValueGroup)1 ConcatenatedOperation (org.opengis.referencing.operation.ConcatenatedOperation)1 Conversion (org.opengis.referencing.operation.Conversion)1 CoordinateOperation (org.opengis.referencing.operation.CoordinateOperation)1 Transformation (org.opengis.referencing.operation.Transformation)1 InternationalString (org.opengis.util.InternationalString)1 Record (org.opengis.util.Record)1