use of org.opengis.util.FactoryException in project sis by apache.
the class DefaultMathTransformFactory method createParameterizedTransform.
/**
* Creates a transform from a group of parameters.
* The set of expected parameters varies for each operation.
* The easiest way to provide parameter values is to get an initially empty group for the desired
* operation by calling {@link #getDefaultParameters(String)}, then to fill the parameter values.
* Example:
*
* {@preformat java
* ParameterValueGroup group = factory.getDefaultParameters("Transverse_Mercator");
* group.parameter("semi_major").setValue(6378137.000);
* group.parameter("semi_minor").setValue(6356752.314);
* MathTransform mt = factory.createParameterizedTransform(group, null);
* }
*
* Sometime the {@code "semi_major"} and {@code "semi_minor"} parameter values are not explicitly provided,
* but rather inferred from the {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum geodetic
* datum} of the source Coordinate Reference System. If the given {@code context} argument is non-null,
* then this method will use those contextual information for:
*
* <ol>
* <li>Inferring the {@code "semi_major"}, {@code "semi_minor"}, {@code "src_semi_major"},
* {@code "src_semi_minor"}, {@code "tgt_semi_major"} or {@code "tgt_semi_minor"} parameters values
* from the {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid ellipsoids} associated to
* the source or target CRS, if those parameters are not explicitly given and if they are relevant
* for the coordinate operation method.</li>
* <li>{@linkplain #createConcatenatedTransform Concatenating} the parameterized transform
* with any other transforms required for performing units changes and ordinates swapping.</li>
* </ol>
*
* The complete group of parameters, including {@code "semi_major"}, {@code "semi_minor"} or other calculated values,
* can be obtained by a call to {@link Context#getCompletedParameters()} after {@code createParameterizedTransform(…)}
* returned. Note that the completed parameters may only have additional parameters compared to the given parameter
* group; existing parameter values should not be modified.
*
* <p>The {@code OperationMethod} instance used by this constructor can be obtained by a call to
* {@link #getLastMethodUsed()}.</p>
*
* @param parameters the parameter values. The {@linkplain ParameterDescriptorGroup#getName() parameter group name}
* shall be the name of the desired {@linkplain DefaultOperationMethod operation method}.
* @param context information about the context (for example source and target coordinate systems)
* in which the new transform is going to be used, or {@code null} if none.
* @return the transform created from the given parameters.
* @throws NoSuchIdentifierException if there is no method for the given parameter group name.
* @throws FactoryException if the object creation failed. This exception is thrown
* if some required parameter has not been supplied, or has illegal value.
*
* @see #getDefaultParameters(String)
* @see #getAvailableMethods(Class)
* @see #getLastMethodUsed()
* @see org.apache.sis.parameter.ParameterBuilder#createGroupForMapProjection(ParameterDescriptor...)
*/
public MathTransform createParameterizedTransform(ParameterValueGroup parameters, final Context context) throws NoSuchIdentifierException, FactoryException {
OperationMethod method = null;
RuntimeException failure = null;
MathTransform transform;
try {
ArgumentChecks.ensureNonNull("parameters", parameters);
final ParameterDescriptorGroup descriptor = parameters.getDescriptor();
final String methodName = descriptor.getName().getCode();
String methodIdentifier = IdentifiedObjects.toString(IdentifiedObjects.getIdentifier(descriptor, Citations.EPSG));
if (methodIdentifier == null) {
methodIdentifier = methodName;
}
/*
* Get the MathTransformProvider of the same name or identifier than the given parameter group.
* We give precedence to EPSG identifier because operation method names are sometime ambiguous
* (e.g. "Lambert Azimuthal Equal Area (Spherical)"). If we fail to find the method by its EPSG code,
* we will try searching by method name. As a side effect, this second attempt will produce a better
* error message if the method is really not found.
*/
try {
method = getOperationMethod(methodIdentifier);
} catch (NoSuchIdentifierException exception) {
if (methodIdentifier.equals(methodName)) {
throw exception;
}
method = getOperationMethod(methodName);
Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION), DefaultMathTransformFactory.class, "createParameterizedTransform", exception);
}
if (!(method instanceof MathTransformProvider)) {
throw new NoSuchIdentifierException(// For now, handle like an unknown operation.
Errors.format(Errors.Keys.UnsupportedImplementation_1, Classes.getClass(method)), methodName);
}
/*
* Will catch only exceptions that may be the result of improper parameter usage (e.g. a value out
* of range). Do not catch exceptions caused by programming errors (e.g. null pointer exception).
*/
try {
/*
* If the user's parameters do not contain semi-major and semi-minor axis lengths, infer
* them from the ellipsoid. We have to do that because those parameters are often omitted,
* since the standard place where to provide this information is in the ellipsoid object.
*/
if (context != null) {
failure = context.completeParameters(this, method, parameters);
parameters = context.parameters;
method = context.provider;
}
transform = ((MathTransformProvider) method).createMathTransform(this, parameters);
} catch (IllegalArgumentException | IllegalStateException exception) {
throw new InvalidGeodeticParameterException(exception.getLocalizedMessage(), exception);
}
/*
* Cache the transform that we just created and make sure that the number of dimensions
* is compatible with the OperationMethod instance. Then make final adjustment for axis
* directions and units of measurement.
*/
transform = unique(transform);
method = DefaultOperationMethod.redimension(method, transform.getSourceDimensions(), transform.getTargetDimensions());
if (context != null) {
transform = swapAndScaleAxes(transform, context);
}
} catch (FactoryException e) {
if (failure != null) {
e.addSuppressed(failure);
}
throw e;
} finally {
// May be null in case of failure, which is intended.
lastMethod.set(method);
if (context != null) {
context.provider = null;
/*
* For now we conservatively reset the provider information to null. But if we choose to
* make that information public in a future SIS version, then we would remove this code.
*/
}
}
return transform;
}
use of org.opengis.util.FactoryException in project sis by apache.
the class DefaultMathTransformFactory method createFromWKT.
/**
* Creates a math transform object from a
* <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
* Known Text</cite> (WKT)</a>.
* If the given text contains non-fatal anomalies (unknown or unsupported WKT elements,
* inconsistent unit definitions, <i>etc.</i>), warnings may be reported in a
* {@linkplain java.util.logging.Logger logger} named {@code "org.apache.sis.io.wkt"}.
*
* @param text math transform encoded in Well-Known Text format.
* @return the math transform (never {@code null}).
* @throws FactoryException if the Well-Known Text can not be parsed,
* or if the math transform creation failed from some other reason.
*/
@Override
public MathTransform createFromWKT(final String text) throws FactoryException {
lastMethod.remove();
Parser p = parser.getAndSet(null);
if (p == null)
try {
Constructor<? extends Parser> c = parserConstructor;
if (c == null) {
c = Class.forName("org.apache.sis.io.wkt.MathTransformParser").asSubclass(Parser.class).getConstructor(MathTransformFactory.class);
// For allowing use in inner class or lambda expression.
final Constructor<?> cp = c;
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
cp.setAccessible(true);
return null;
});
parserConstructor = c;
}
p = c.newInstance(this);
} catch (ReflectiveOperationException e) {
throw new FactoryException(e);
}
/*
* No need to check the type of the parsed object, because MathTransformParser
* should return only instance of MathTransform.
*/
final Object object;
try {
object = p.createFromWKT(text);
} catch (FactoryException e) {
/*
* The parsing may fail because a operation parameter is not known to SIS. If this happen, replace
* the generic exception thrown be the parser (which is FactoryException) by a more specific one.
* Note that InvalidGeodeticParameterException is defined only in this sis-referencing module,
* so we could not throw it from the sis-metadata module that contain the parser.
*/
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof ParameterNotFoundException) {
throw new InvalidGeodeticParameterException(e.getLocalizedMessage(), cause);
}
cause = cause.getCause();
}
throw e;
}
parser.set(p);
return (MathTransform) object;
}
use of org.opengis.util.FactoryException in project sis by apache.
the class Proj4 method definition.
/**
* Infers a {@literal Proj.4} definition from the given projected, geographic or geocentric coordinate reference system.
* This method does not need the Proj.4 native library; it can be used in a pure Java application.
* However the returned definition string may differ depending on whether the Proj.4 library is available or not.
*
* @param crs the coordinate reference system for which to create a Proj.4 definition.
* @return the definition of the given CRS in a Proj.4 format.
* @throws FactoryException if the Proj.4 definition string can not be created from the given CRS.
*/
public static String definition(final CoordinateReferenceSystem crs) throws FactoryException {
ArgumentChecks.ensureNonNull("crs", crs);
/*
* If the given CRS object is associated to a Proj.4 structure, let Proj.4 formats itself
* the definition string. Note that this operation may fail if there is no Proj.4 library
* in the current system, or no JNI bindings to that library.
*/
try {
for (final Identifier id : crs.getIdentifiers()) {
if (id instanceof PJ) {
return ((PJ) id).getCode();
}
}
} catch (UnsatisfiedLinkError e) {
// Thrown the first time that we try to use the library.
Logging.unexpectedException(Logging.getLogger(Modules.GDAL), Proj4.class, "definition", e);
} catch (NoClassDefFoundError e) {
// Thrown on all attempts after the first one.
Logging.recoverableException(Logging.getLogger(Modules.GDAL), Proj4.class, "definition", e);
}
/*
* If we found no Proj.4 structure, formats the definition string ourself. The string may differ from
* what Proj.4 would have given. In particular, we do not provide "+init=" or "+datum=" parameter.
* But the definition should still be semantically equivalent.
*/
final String method;
final GeodeticDatum datum;
final ParameterValueGroup parameters;
final CoordinateSystem cs = crs.getCoordinateSystem();
if (crs instanceof GeodeticCRS) {
if (cs instanceof EllipsoidalCS) {
method = "latlon";
} else if (cs instanceof CartesianCS) {
method = "geocent";
} else {
throw new FactoryException(Errors.format(Errors.Keys.UnsupportedCoordinateSystem_1, cs.getClass()));
}
datum = ((GeodeticCRS) crs).getDatum();
parameters = null;
} else if (crs instanceof ProjectedCRS) {
Projection c = ((ProjectedCRS) crs).getConversionFromBase();
datum = ((ProjectedCRS) crs).getDatum();
method = name(c.getMethod());
parameters = c.getParameterValues();
} else {
throw new FactoryException(Errors.format(Errors.Keys.UnsupportedType_1, crs.getClass()));
}
/*
* Append the map projection parameters. Those parameters may include axis lengths (a and b),
* but not necessarily. If axis lengths are specified, then we will ignore the Ellipsoid instance
* associated to the CRS.
*/
final StringBuilder definition = new StringBuilder(100);
definition.append(Proj4Factory.PROJ_PARAM).append(method);
boolean hasSemiMajor = false;
boolean hasSemiMinor = false;
if (parameters != null) {
definition.append(Proj4Factory.STANDARD_OPTIONS);
for (final GeneralParameterValue parameter : parameters.values()) {
if (parameter instanceof ParameterValue<?>) {
final ParameterValue<?> pv = (ParameterValue<?>) parameter;
final Object value;
Unit<?> unit = pv.getUnit();
if (unit != null) {
unit = Units.isAngular(unit) ? Units.DEGREE : unit.getSystemUnit();
// Always in metres or degrees.
value = pv.doubleValue(unit);
} else {
value = pv.getValue();
if (value == null) {
continue;
}
}
final String pn = name(parameter.getDescriptor());
hasSemiMajor |= pn.equals("a");
hasSemiMinor |= pn.equals("b");
definition.append(" +").append(pn).append('=').append(value);
}
}
}
/*
* Append datum information: axis lengths if they were not part of the parameters, then prime meridian.
*/
final Ellipsoid ellipsoid = datum.getEllipsoid();
if (!hasSemiMajor)
definition.append(" +a=").append(ellipsoid.getSemiMajorAxis());
if (!hasSemiMinor)
definition.append(" +b=").append(ellipsoid.getSemiMinorAxis());
final PrimeMeridian pm = datum.getPrimeMeridian();
if (pm != null) {
double lon = pm.getGreenwichLongitude();
final Unit<Angle> unit = pm.getAngularUnit();
if (unit != null) {
lon = unit.getConverterTo(Units.DEGREE).convert(lon);
}
definition.append(" +pm=").append(lon);
}
/*
* Appends axis directions. This method always format a vertical direction (up or down)
* even if the coordinate system is two-dimensional, because Proj.4 seems to require it.
* Also extract axis units in the process.
*/
// Horizontal at index 0, vertical at index 1.
final Unit<?>[] units = new Unit<?>[2];
boolean validCS = true;
definition.append(' ').append(Proj4Factory.AXIS_ORDER_PARAM);
final int dimension = Math.min(cs.getDimension(), 3);
boolean hasVertical = false;
for (int i = 0; i < dimension; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
final AxisDirection dir = axis.getDirection();
int unitIndex = 0;
if (!AxisDirections.isCardinal(dir)) {
if (!AxisDirections.isVertical(dir)) {
throw new FactoryException(Errors.format(Errors.Keys.UnsupportedAxisDirection_1, dir));
}
hasVertical = true;
unitIndex = 1;
}
final Unit<?> old = units[unitIndex];
units[unitIndex] = axis.getUnit();
validCS &= (old == null || old.equals(units[unitIndex]));
definition.appendCodePoint(Character.toLowerCase(dir.name().codePointAt(0)));
}
if (!hasVertical && dimension < 3) {
// Add a UP direction if not already present.
definition.append('u');
}
/*
* Append units of measurement, then verify the coordinate system validity.
*/
for (int i = 0; i < units.length; i++) {
final Unit<?> unit = units[i];
if (unit != null && !unit.equals(Units.DEGREE) && !unit.equals(Units.METRE)) {
validCS &= Units.isLinear(unit);
definition.append(" +");
// "+vto_meter" parameter.
if (i == 1)
definition.append('v');
definition.append("to_meter=").append(Units.toStandardUnit(unit));
}
}
/*
* Append the "+towgs84" element if any. This is the last piece of information.
* Note that the use of a "+towgs84" parameter is an "early binding" approach,
* which is usually not recommended. But Proj4 works that way.
*/
if (validCS) {
if (datum instanceof DefaultGeodeticDatum) {
for (final BursaWolfParameters bwp : ((DefaultGeodeticDatum) datum).getBursaWolfParameters()) {
if (Utilities.equalsIgnoreMetadata(CommonCRS.WGS84.datum(), bwp.getTargetDatum())) {
definition.append(" +towgs84=").append(bwp.tX).append(',').append(bwp.tY).append(',').append(bwp.tZ);
if (!bwp.isTranslation()) {
definition.append(',').append(bwp.rX).append(',').append(bwp.rY).append(',').append(bwp.rZ).append(',').append(bwp.dS);
}
break;
}
}
}
return definition.toString();
}
/*
* If we reach this point, we detected a coordinate system that we can not format as a
* Proj.4 definition string. Format an error message with axis directions and units.
*/
definition.setLength(0);
definition.append('(');
for (int i = 0; i < units.length; i++) {
final CoordinateSystemAxis axis = cs.getAxis(i);
if (i != 0)
definition.append(", ");
definition.append(axis.getUnit()).append(' ').append(Types.getCodeName(axis.getDirection()));
}
throw new FactoryException(Errors.format(Errors.Keys.IllegalCoordinateSystem_1, definition.append(')')));
}
use of org.opengis.util.FactoryException in project jena by apache.
the class SearchEnvelope method build.
/**
* @param geometryWrapper
* @param srsInfo
* @return Search envelope of the geometry in target SRS.
*/
public static SearchEnvelope build(GeometryWrapper geometryWrapper, SRSInfo srsInfo) {
try {
// Get the envelope of the target GeometryWrapper and convert that to SRS URI, in case it is a complex polygon.
GeometryWrapper envelopeGeometryWrapper = geometryWrapper.envelope();
// Convert to SRS URI.
GeometryWrapper srsGeometryWrapper = envelopeGeometryWrapper.convertSRS(srsInfo.getSrsURI());
Envelope envelope = srsGeometryWrapper.getEnvelope();
SearchEnvelope searchEnvelope = new SearchEnvelope(envelope, srsInfo);
return searchEnvelope;
} catch (FactoryException | MismatchedDimensionException | TransformException ex) {
throw new ExprEvalException(ex.getMessage() + ": " + geometryWrapper.asLiteral(), ex);
}
}
use of org.opengis.util.FactoryException in project jena by apache.
the class SearchEnvelope method build.
/**
* Build search envelope in the indicated cardinal direction.<br>
* Geographic SRS will wrap for half world in East/West directions.<br>
* Other SRS will extend to the valid domain.
*
* @param geometryWrapper
* @param srsInfo
* @param direction
* @return Search envelope in cardinal direction.
*/
public static SearchEnvelope build(GeometryWrapper geometryWrapper, SRSInfo srsInfo, CardinalDirection direction) {
try {
// Get the envelope of the target GeometryWrapper and convert to SRS URI, in case it is a complex polygon.
GeometryWrapper envelopeGeometryWrapper = geometryWrapper.envelope();
// Convert to SRS URI.
GeometryWrapper srsGeometryWrapper = envelopeGeometryWrapper.convertSRS(srsInfo.getSrsURI());
Envelope envelope = srsGeometryWrapper.getEnvelope();
Envelope domEnvelope = srsInfo.getDomainEnvelope();
double x1 = domEnvelope.getMinX();
double x2 = domEnvelope.getMaxX();
double y1 = domEnvelope.getMinY();
double y2 = domEnvelope.getMaxY();
// Exclusive search so anything within envelope of a LineString or Polygon is excluded.
switch(direction) {
case NORTH:
y1 = envelope.getMaxY();
break;
case SOUTH:
y2 = envelope.getMinY();
break;
case EAST:
x1 = envelope.getMaxX();
if (srsInfo.isGeographic()) {
// Extend to the Eastern half from the origin.
x2 = x1 + domEnvelope.getMaxX();
}
break;
case WEST:
x2 = envelope.getMinX();
if (srsInfo.isGeographic()) {
// Extend to the West half from the origin.
x1 = x2 + domEnvelope.getMinX();
}
break;
}
Envelope cardinalEnvelope = new Envelope(x1, x2, y1, y2);
return new SearchEnvelope(cardinalEnvelope, srsInfo);
} catch (FactoryException | MismatchedDimensionException | TransformException ex) {
throw new ExprEvalException(ex.getMessage() + ": " + geometryWrapper.asLiteral(), ex);
}
}
Aggregations