use of org.opengis.parameter.InvalidParameterValueException in project sis by apache.
the class FranceGeocentricInterpolation method createMathTransform.
/**
* Creates a transform from the specified group of parameter values.
* This method creates the transform from <em>target</em> to <em>source</em>
* (which is the direction that use the interpolation grid directly without iteration),
* then inverts the transform.
*
* @param factory the factory to use if this constructor needs to create other math transforms.
* @param values the group of parameter values.
* @return the created math transform.
* @throws ParameterNotFoundException if a required parameter was not found.
* @throws FactoryException if an error occurred while loading the grid.
*/
@Override
public MathTransform createMathTransform(final MathTransformFactory factory, final ParameterValueGroup values) throws ParameterNotFoundException, FactoryException {
boolean withHeights = false;
final Parameters pg = Parameters.castOrWrap(values);
final Integer dim = pg.getValue(Molodensky.DIMENSION);
if (dim != null)
switch(dim) {
case 2:
break;
case 3:
withHeights = true;
break;
default:
throw new InvalidParameterValueException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "dim", dim), "dim", dim);
}
final Path file = pg.getMandatoryValue(FILE);
final DatumShiftGridFile<Angle, Length> grid = getOrLoad(file, isRecognized(file) ? new double[] { TX, TY, TZ } : null, PRECISION);
MathTransform tr = createGeodeticTransformation(factory, createEllipsoid(pg, Molodensky.TGT_SEMI_MAJOR, Molodensky.TGT_SEMI_MINOR, // GRS 1980 ellipsoid
CommonCRS.ETRS89.ellipsoid()), createEllipsoid(pg, Molodensky.SRC_SEMI_MAJOR, Molodensky.SRC_SEMI_MINOR, // Clarke 1880 (IGN) ellipsoid
null), withHeights, grid);
try {
tr = tr.inverse();
} catch (NoninvertibleTransformException e) {
// Should never happen.
throw new FactoryException(e);
}
return tr;
}
use of org.opengis.parameter.InvalidParameterValueException in project sis by apache.
the class Verifier method ensureValidValue.
/**
* Ensures that the given value is valid according the specified parameter descriptor.
* This method ensures that {@code value} is assignable to the
* {@linkplain ParameterDescriptor#getValueClass() expected class}, is between the
* {@linkplain ParameterDescriptor#getMinimumValue() minimum} and
* {@linkplain ParameterDescriptor#getMaximumValue() maximum} values and is one of the
* {@linkplain ParameterDescriptor#getValidValues() set of valid values}.
* If the value fails any of those tests, then an exception is thrown.
*
* @param <T> the type of parameter value. The given {@code value} should typically be an instance of this class.
* This is not required by this method signature but is checked by this method implementation.
* @param descriptor the parameter descriptor to check against.
* @param value the value to check, or {@code null}.
* @param unit the unit of the value to check, or {@code null}.
* @return the given value converted to the descriptor unit if any,
* then casted to the descriptor parameterized type.
* @throws InvalidParameterValueException if the parameter value is invalid.
*/
static <T> T ensureValidValue(final ParameterDescriptor<T> descriptor, final Object value, final Unit<?> unit) throws InvalidParameterValueException {
final Class<T> valueClass = descriptor.getValueClass();
/*
* Before to verify if the given value is inside the bounds, we need to convert the value
* to the units used by the parameter descriptor. The first part of this block verifies
* the validity of the unit argument, so we execute it even if 'value' is null.
*/
UnitConverter converter = null;
Object convertedValue = value;
if (unit != null) {
Unit<?> def = descriptor.getUnit();
if (def == null) {
def = getCompatibleUnit(Parameters.getValueDomain(descriptor), unit);
if (def == null) {
final String name = getDisplayName(descriptor);
throw new InvalidParameterValueException(Resources.format(Resources.Keys.UnitlessParameter_1, name), name, unit);
}
}
if (!unit.equals(def)) {
final short expectedID = getUnitMessageID(def);
if (getUnitMessageID(unit) != expectedID) {
throw new IllegalArgumentException(Errors.format(expectedID, unit));
}
/*
* Verify the type of the user's value before to perform the unit conversion,
* because the conversion will create a new object not necessarily of the same type.
*/
if (value != null) {
if (!valueClass.isInstance(value)) {
final String name = getDisplayName(descriptor);
throw new InvalidParameterValueException(Resources.format(Resources.Keys.IllegalParameterValueClass_3, name, valueClass, value.getClass()), name, value);
}
/*
* From this point we will perform the actual unit conversion. The value may be either
* a Number instance, or an array of numbers (typically an array of type double[]). In
* the array case, we will store the converted values in a new array of the same type.
*/
try {
converter = unit.getConverterToAny(def);
} catch (IncommensurableException e) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatibleUnits_2, unit, def), e);
}
Class<?> componentType = valueClass.getComponentType();
if (componentType == null) {
/*
* Usual case where the value is not an array. Convert the value directly.
* Note that the value can only be a number because the unit is associated
* to MeasurementRange, which accepts only numbers.
*/
Number n = converter.convert(((Number) value).doubleValue());
try {
convertedValue = Numbers.cast(n, valueClass.asSubclass(Number.class));
} catch (IllegalArgumentException e) {
throw new InvalidParameterValueException(e.getLocalizedMessage(), getDisplayName(descriptor), value);
}
} else {
/*
* The value is an array. Creates a new array and store the converted values
* using Array reflection.
*/
final int length = Array.getLength(value);
convertedValue = Array.newInstance(componentType, length);
componentType = Numbers.primitiveToWrapper(componentType);
for (int i = 0; i < length; i++) {
Number n = (Number) Array.get(value, i);
// Value in units that we can compare.
n = converter.convert(n.doubleValue());
try {
n = Numbers.cast(n, componentType.asSubclass(Number.class));
} catch (IllegalArgumentException e) {
throw new InvalidParameterValueException(e.getLocalizedMessage(), getDisplayName(descriptor) + '[' + i + ']', value);
}
Array.set(convertedValue, i, n);
}
}
}
}
}
/*
* At this point the user's value has been fully converted to the unit of measurement specified
* by the ParameterDescriptor. Now compare the converted value to the restriction given by the
* descriptor (set of valid values and range of value domain).
*/
if (convertedValue != null) {
final Verifier error;
final Set<T> validValues = descriptor.getValidValues();
if (descriptor instanceof DefaultParameterDescriptor<?>) {
error = ensureValidValue(valueClass, validValues, ((DefaultParameterDescriptor<?>) descriptor).getValueDomain(), convertedValue);
} else {
error = ensureValidValue(valueClass, validValues, descriptor.getMinimumValue(), descriptor.getMaximumValue(), convertedValue);
}
/*
* If we found an error, we will usually throw an exception. An exception to this rule is
* when EPSGDataAccess is creating a deprecated ProjectedCRS in which some parameters are
* known to be invalid (the CRS was deprecated precisely for that reason). In such cases,
* we will log a warning instead than throwing an exception.
*/
if (error != null) {
error.convertRange(converter);
final String name = getDisplayName(descriptor);
final String message = error.message(null, name, value);
if (!Semaphores.query(Semaphores.SUSPEND_PARAMETER_CHECK)) {
throw new InvalidParameterValueException(message, name, value);
} else {
final LogRecord record = new LogRecord(Level.WARNING, message);
record.setLoggerName(Loggers.COORDINATE_OPERATION);
Logging.log(DefaultParameterValue.class, "setValue", record);
}
}
}
return valueClass.cast(convertedValue);
}
use of org.opengis.parameter.InvalidParameterValueException in project sis by apache.
the class MathTransformParser method parseParameters.
/**
* Parses a sequence of {@code "PARAMETER"} elements.
*
* @param element the parent element containing the parameters to parse.
* @param parameters the group where to store the parameter values.
* @param defaultUnit the default unit (for arbitrary quantity, including angular), or {@code null}.
* @param defaultAngularUnit the default angular unit, or {@code null} if none. This is determined by the context,
* especially when {@link GeodeticObjectParser} parses a {@code ProjectedCRS} element.
* @throws ParseException if the {@code "PARAMETER"} element can not be parsed.
*/
final void parseParameters(final Element element, final ParameterValueGroup parameters, final Unit<?> defaultUnit, final Unit<Angle> defaultAngularUnit) throws ParseException {
final Unit<?> defaultSI = (defaultUnit != null) ? defaultUnit.getSystemUnit() : null;
Element param = element;
try {
while ((param = element.pullElement(OPTIONAL, WKTKeywords.Parameter)) != null) {
final String name = param.pullString("name");
Unit<?> unit = parseUnit(param);
param.pullElement(OPTIONAL, ID_KEYWORDS);
/*
* DEPARTURE FROM ISO 19162: the specification recommends that we use the identifier instead
* than the parameter name. However we do not yet have a "get parameter by ID" in Apache SIS
* or in GeoAPI interfaces. This was not considered necessary since SIS is lenient (hopefully
* without introducing ambiguity) regarding parameter names, but we may revisit in a future
* version if it become no longer the case. See https://issues.apache.org/jira/browse/SIS-210
*/
final ParameterValue<?> parameter = parameters.parameter(name);
final ParameterDescriptor<?> descriptor = parameter.getDescriptor();
final Class<?> valueClass = descriptor.getValueClass();
final boolean isNumeric = Number.class.isAssignableFrom(valueClass);
if (isNumeric && unit == null) {
unit = descriptor.getUnit();
if (unit != null) {
final Unit<?> si = unit.getSystemUnit();
if (si.equals(defaultSI)) {
unit = defaultUnit;
} else if (si.equals(Units.RADIAN)) {
unit = defaultAngularUnit;
}
}
}
if (unit != null) {
parameter.setValue(param.pullDouble("doubleValue"), unit);
} else if (isNumeric) {
if (Numbers.isInteger(valueClass)) {
parameter.setValue(param.pullInteger("intValue"));
} else {
parameter.setValue(param.pullDouble("doubleValue"));
}
} else if (valueClass == Boolean.class) {
parameter.setValue(param.pullBoolean("booleanValue"));
} else {
parameter.setValue(param.pullString("stringValue"));
}
param.close(ignoredElements);
}
} catch (ParameterNotFoundException e) {
throw new UnparsableObjectException(errorLocale, Errors.Keys.UnexpectedParameter_1, new String[] { e.getParameterName() }, param.offset).initCause(e);
} catch (InvalidParameterValueException e) {
throw (ParseException) new ParseException(e.getLocalizedMessage(), param.offset).initCause(e);
}
}
use of org.opengis.parameter.InvalidParameterValueException in project sis by apache.
the class Molodensky method createMathTransform.
/**
* Creates a (potentially abridged) Molodensky transform from the specified group of parameter values.
* The specified number of dimensions are <em>default</em> values; they may be overridden by user parameters.
*
* @param factory the factory to use for creating concatenated transforms.
* @param values the group of parameter values specified by the users.
* @param sourceDimensions number of source dimensions (2 or 3) of the operation method.
* @param targetDimensions number of target dimensions (2 or 3) of the operation method.
* @param isAbridged {@code true} for the abridged form.
* @return the created (abridged) Molodensky transform.
* @throws FactoryException if a transform can not be created.
*/
static MathTransform createMathTransform(final MathTransformFactory factory, final Parameters values, int sourceDimensions, int targetDimensions, final boolean isAbridged) throws FactoryException {
final Integer dim = values.getValue(DIMENSION);
if (dim != null) {
// Unboxing.
final int n = dim;
if (n != 2 && n != 3) {
throw new InvalidParameterValueException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "dim", dim), "dim", dim);
}
sourceDimensions = targetDimensions = n;
}
/*
* Following method calls implicitly convert parameter values to metres.
* We do not try to match ellipsoid axis units because:
*
* 1) It complicates the code.
* 2) We have no guarantees that ellipsoid unit match the coordinate system unit.
* 3) OGC 01-009 explicitly said that angles are in degrees and heights in metres.
* 4) The above is consistent with what we do for map projections.
*/
double sa = values.doubleValue(SRC_SEMI_MAJOR);
double sb = values.doubleValue(SRC_SEMI_MINOR);
double ta = optional(values, TGT_SEMI_MAJOR);
double tb = optional(values, TGT_SEMI_MINOR);
double Δa = optional(values, AXIS_LENGTH_DIFFERENCE);
double Δf = optional(values, FLATTENING_DIFFERENCE);
if (Double.isNaN(ta)) {
ta = sa + Δa;
}
if (Double.isNaN(tb)) {
tb = ta * (sb / sa - Δf);
}
final Map<String, ?> name = Collections.singletonMap(DefaultEllipsoid.NAME_KEY, NilReferencingObject.UNNAMED);
final Ellipsoid source = new Ellipsoid(name, sa, sb, Δa, Δf);
final Ellipsoid target = new Ellipsoid(name, ta, tb, -Δa, -Δf);
source.other = target;
target.other = source;
source.computeDifferences(values);
return MolodenskyTransform.createGeodeticTransformation(factory, source, sourceDimensions >= 3, target, targetDimensions >= 3, values.doubleValue(TX), values.doubleValue(TY), values.doubleValue(TZ), isAbridged);
}
Aggregations