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;
}
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);
}
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;
}
Aggregations