use of org.opengis.util.FactoryException in project sis by apache.
the class IdentifiedObjectFinder method createFromNames.
/**
* Creates an object equals (optionally ignoring metadata), to the specified object using only the
* {@linkplain AbstractIdentifiedObject#getName name} and {@linkplain AbstractIdentifiedObject#getAlias aliases}.
* If no such object is found, returns {@code null}.
*
* <p>This method may be used with some {@linkplain GeodeticAuthorityFactory authority factory}
* implementations like the one backed by the EPSG database, which are capable to find an object
* from its name when the identifier is unknown.</p>
*
* @param object the object looked up.
* @return the identified object, or {@code null} if not found.
* @throws FactoryException if an error occurred while creating an object.
*
* @see #createFromCodes(IdentifiedObject)
* @see #createFromIdentifiers(IdentifiedObject)
*/
private IdentifiedObject createFromNames(final IdentifiedObject object) throws FactoryException {
String code = object.getName().getCode();
IdentifiedObject candidate;
try {
candidate = create(code);
} catch (FactoryException e) {
/*
* The identifier was not recognized. We will continue later with aliases.
* Note: we catch a more generic exception than NoSuchAuthorityCodeException because
* this attempt may fail for various reasons (character string not supported
* by the underlying database for primary key, duplicated name found, etc.).
*/
exceptionOccurred(e);
candidate = null;
}
if (match(candidate, object)) {
return candidate;
}
for (final GenericName id : object.getAlias()) {
code = id.toString();
try {
candidate = create(code);
} catch (FactoryException e) {
// The name was not recognized. No problem, let's go on.
exceptionOccurred(e);
continue;
}
if (match(candidate, object)) {
return candidate;
}
}
return null;
}
use of org.opengis.util.FactoryException in project sis by apache.
the class IdentifiedObjectSet method get.
/**
* Returns the identified object for the specified value, creating it if needed.
*
* @throws BackingStoreException if the object creation failed.
*
* @see #createObject(String)
*/
final T get(final String code) throws BackingStoreException {
T object;
boolean success;
synchronized (objects) {
object = objects.get(code);
success = (object != null || !objects.containsKey(code));
}
/*
* If we need to create the object, it should be done outside synchronized block.
* There is a risk that the same object is created twice in concurrent threads.
* If this happen, we will discard the duplicated value.
*/
if (!success) {
try {
object = createObject(code);
// Shall be set only after above line succeed.
success = true;
} catch (FactoryException exception) {
if (!isRecoverableFailure(exception)) {
throw new BackingStoreException(exception);
}
final LogRecord record = Messages.getResources(getLocale()).getLogRecord(Level.WARNING, Messages.Keys.CanNotInstantiateForIdentifier_3, type, code, getCause(exception));
record.setLoggerName(Loggers.CRS_FACTORY);
Logging.log(IdentifiedObjectSet.class, "createObject", record);
}
synchronized (objects) {
if (success) {
/*
* The check for 'containsKey' is a paranoiac check in case the element has been removed
* in another thread while we were creating the object. This is likely to be unnecessary
* in the vast majority of cases where the set of codes is never modified after this set
* has been published. However, if someone decided to do such concurrent modifications,
* not checking for concurrent removal could be a subtle and hard-to-find bug, so we are
* better to be safe. Note that if a concurrent removal happened, we still return the non-null
* object but we do not put it in this IdentifiedObjectSet. This behavior is as if this method
* has been invoked before the concurrent removal happened.
*/
if (objects.containsKey(code)) {
// Needed because code may be associated to null value.
final T c = objects.putIfAbsent(code, object);
if (c != null) {
// The object has been created concurrently.
object = c;
}
}
} else if (objects.remove(code, null)) {
// Do not remove if a concurrent thread succeeded.
codes = null;
}
}
}
return object;
}
use of org.opengis.util.FactoryException in project sis by apache.
the class DefaultCoordinateOperationFactory method createSingleOperation.
/**
* Creates a transformation or conversion from the given properties.
* This method infers by itself if the operation to create is a
* {@link Transformation}, a {@link Conversion} or a {@link Projection} sub-type
* ({@link CylindricalProjection}, {@link ConicProjection} or {@link PlanarProjection})
* using the {@linkplain DefaultOperationMethod#getOperationType() information provided by the given method}.
*
* <p>The properties given in argument follow the same rules than for the
* {@linkplain AbstractCoordinateOperation#AbstractCoordinateOperation(Map, CoordinateReferenceSystem,
* CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransform) coordinate operation} constructor.
* The following table is a reminder of main (not all) properties:</p>
*
* <table class="sis">
* <caption>Recognized properties (non exhaustive list)</caption>
* <tr>
* <th>Property name</th>
* <th>Value type</th>
* <th>Returned by</th>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} or {@link String}</td>
* <td>{@link DefaultConversion#getName()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td>
* <td>{@link DefaultConversion#getIdentifiers()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}</td>
* <td>{@link org.opengis.metadata.extent.Extent}</td>
* <td>{@link DefaultConversion#getDomainOfValidity()}</td>
* </tr>
* </table>
*
* @param properties the properties to be given to the identified object.
* @param sourceCRS the source CRS.
* @param targetCRS the target CRS.
* @param interpolationCRS the CRS of additional coordinates needed for the operation, or {@code null} if none.
* @param method the coordinate operation method (mandatory in all cases).
* @param transform transform from positions in the source CRS to positions in the target CRS.
* @return the coordinate operation created from the given arguments.
* @throws FactoryException if the object creation failed.
*
* @see DefaultOperationMethod#getOperationType()
* @see DefaultTransformation
* @see DefaultConversion
*/
public SingleOperation createSingleOperation(final Map<String, ?> properties, final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS, final CoordinateReferenceSystem interpolationCRS, final OperationMethod method, MathTransform transform) throws FactoryException {
ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS);
ArgumentChecks.ensureNonNull("targetCRS", targetCRS);
ArgumentChecks.ensureNonNull("method", method);
/*
* Undocumented (for now) feature: if the 'transform' argument is null but parameters are
* found in the given properties, create the MathTransform instance from those parameters.
* This is needed for WKT parsing of CoordinateOperation[…] among others.
*/
if (transform == null) {
final ParameterValueGroup parameters = Containers.property(properties, ReferencingServices.PARAMETERS_KEY, ParameterValueGroup.class);
if (parameters == null) {
throw new NullArgumentException(Errors.format(Errors.Keys.NullArgument_1, "transform"));
}
transform = getMathTransformFactory().createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
}
/*
* The "operationType" property is currently undocumented. The intent is to help this factory method in
* situations where the given operation method is not an Apache SIS implementation or does not override
* getOperationType(), or the method is ambiguous (e.g. "Affine" can be used for both a transformation
* or a conversion).
*
* If we have both a 'baseType' and a Method.getOperationType(), take the most specific type.
* An exception will be thrown if the two types are incompatible.
*/
Class<?> baseType = Containers.property(properties, ReferencingServices.OPERATION_TYPE_KEY, Class.class);
if (baseType == null) {
baseType = SingleOperation.class;
}
if (method instanceof DefaultOperationMethod) {
final Class<? extends SingleOperation> c = ((DefaultOperationMethod) method).getOperationType();
if (c != null) {
// Paranoiac check (above method should not return null).
if (baseType.isAssignableFrom(c)) {
baseType = c;
} else if (!c.isAssignableFrom(baseType)) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatiblePropertyValue_1, ReferencingServices.OPERATION_TYPE_KEY));
}
}
}
/*
* If the base type is still abstract (probably because it was not specified neither in the given OperationMethod
* or in the properties), then try to find a concrete type using the following rules derived from the definitions
* given in ISO 19111:
*
* - If the two CRS uses the same datum (ignoring metadata), assume that we have a Conversion.
* - Otherwise we have a datum change, which implies that we have a Transformation.
*
* In the case of Conversion, we can specialize one step more if the conversion is going from a geographic CRS
* to a projected CRS. It may seems that we should check if ProjectedCRS.getBaseCRS() is equals (ignoring meta
* data) to source CRS. But we already checked the datum, which is the important part. The axis order and unit
* could be different, which we want to allow.
*/
if (baseType == SingleOperation.class) {
if (isConversion(sourceCRS, targetCRS)) {
if (interpolationCRS == null && sourceCRS instanceof GeographicCRS && targetCRS instanceof ProjectedCRS) {
baseType = Projection.class;
} else {
baseType = Conversion.class;
}
} else {
baseType = Transformation.class;
}
}
/*
* Now create the coordinate operation of the requested type. If we can not find a concrete class for the
* requested type, we will instantiate a SingleOperation in last resort. The later action is a departure
* from ISO 19111 since 'SingleOperation' is conceptually abstract. But we do that as a way to said that
* we are missing this important piece of information but still go ahead.
*
* It is inconvenient to guarantee that the created operation is an instance of 'baseType' since the user
* could have specified an implementation class or a custom sub-interface. We will perform the type check
* only after object creation.
*/
final AbstractSingleOperation op;
if (Transformation.class.isAssignableFrom(baseType)) {
op = new DefaultTransformation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform);
} else if (Projection.class.isAssignableFrom(baseType)) {
ArgumentChecks.ensureCanCast("sourceCRS", GeographicCRS.class, sourceCRS);
ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS.class, targetCRS);
if (interpolationCRS != null) {
throw new IllegalArgumentException(Errors.format(Errors.Keys.ForbiddenAttribute_2, "interpolationCRS", baseType));
}
final GeographicCRS baseCRS = (GeographicCRS) sourceCRS;
final ProjectedCRS crs = (ProjectedCRS) targetCRS;
if (CylindricalProjection.class.isAssignableFrom(baseType)) {
op = new DefaultCylindricalProjection(properties, baseCRS, crs, method, transform);
} else if (ConicProjection.class.isAssignableFrom(baseType)) {
op = new DefaultConicProjection(properties, baseCRS, crs, method, transform);
} else if (PlanarProjection.class.isAssignableFrom(baseType)) {
op = new DefaultPlanarProjection(properties, baseCRS, crs, method, transform);
} else {
op = new DefaultProjection(properties, baseCRS, crs, method, transform);
}
} else if (Conversion.class.isAssignableFrom(baseType)) {
op = new DefaultConversion(properties, sourceCRS, targetCRS, interpolationCRS, method, transform);
} else {
// See above comment about this last-resort fallback.
op = new AbstractSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform);
}
if (!baseType.isInstance(op)) {
throw new FactoryException(Resources.format(Resources.Keys.CanNotCreateObjectAsInstanceOf_2, baseType, op.getName()));
}
return pool.unique(op);
}
use of org.opengis.util.FactoryException in project sis by apache.
the class MultiAuthoritiesFactory method getAuthorityCodes.
/**
* Returns the set of authority codes for objects of the given type.
* This method returns the union of codes returned by all factories specified at construction time.
*
* <p>The {@link Set#contains(Object)} method of the returned set is lenient:
* it accepts various ways to format a code even if the iterator returns only one form.
* For example the {@code contains(Object)} method may return {@code true} for {@code "EPSG:4326"},
* {@code "EPSG::4326"}, {@code "urn:ogc:def:crs:EPSG::4326"}, <i>etc.</i> even if
* the iterator returns only {@code "EPSG:4326"}.</p>
*
* <p><b>Warnings:</b></p>
* <ul>
* <li>Callers should not retain a reference to the returned collection for a long time,
* since it may be backed by database connections (depending on the factory implementations).</li>
* <li>The returned set is not thread-safe. Each thread should ask its own instance and let
* the garbage collector disposes it as soon as the collection is not needed anymore.</li>
* <li>Call to the {@link Set#size()} method on the returned collection should be avoided
* since it may be costly.</li>
* </ul>
*
* @param type the spatial reference objects type.
* @return the set of authority codes for spatial reference objects of the given type.
* @throws FactoryException if access to an underlying factory failed.
*/
@Override
public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException {
return new SetOfUnknownSize<String>() {
/**
* Returns an iterator over all authority codes.
* Codes are fetched on-the-fly.
*/
@Override
public Iterator<String> iterator() {
return new AbstractIterator<String>() {
/**
* An iterator over the factories for which to return codes.
*/
private final Iterator<AuthorityFactory> factories = getAllFactories();
/**
* An iterator over the codes of the current factory.
*/
private Iterator<String> codes = Collections.emptyIterator();
/**
* The prefix to prepend before codes, or {@code null} if none.
*/
private String prefix;
/**
* For filtering duplicated codes when there is many versions of the same authority.
*/
private final Set<String> done = new HashSet<>();
/**
* Tests if there is more codes to return.
*/
@Override
public boolean hasNext() {
while (next == null) {
while (!codes.hasNext()) {
do {
if (!factories.hasNext()) {
return false;
}
final AuthorityFactory factory = factories.next();
codes = getAuthorityCodes(factory).iterator();
prefix = getCodeSpace(factory);
} while (!done.add(prefix));
}
next = codes.next();
}
return true;
}
/**
* Returns the next element, with namespace inserted before the code if needed.
*/
@Override
public String next() {
String code = super.next();
if (prefix != null && code.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR) < 0) {
code = prefix + DefaultNameSpace.DEFAULT_SEPARATOR + code;
}
return code;
}
};
}
/**
* The cache of values returned by {@link #getAuthorityCodes(AuthorityFactory)}.
*/
private final Map<AuthorityFactory, Set<String>> cache = new IdentityHashMap<>();
/**
* Returns the authority codes for the given factory.
* This method invokes {@link AuthorityFactory#getAuthorityCodes(Class)}
* only once per factory and caches the returned {@code Set<String>}.
*/
final Set<String> getAuthorityCodes(final AuthorityFactory factory) {
Set<String> codes = cache.get(factory);
if (codes == null) {
try {
codes = factory.getAuthorityCodes(type);
} catch (FactoryException e) {
throw new BackingStoreException(e);
}
if (cache.put(factory, codes) != null) {
throw new ConcurrentModificationException();
}
}
return codes;
}
/**
* The collection size, or a negative value if we have not yet computed the size.
* A negative value different than -1 means that we have not counted all elements,
* but we have determined that the set is not empty.
*/
private int size = -1;
/**
* Returns {@code true} if the {@link #size()} method is cheap.
*/
@Override
protected boolean isSizeKnown() {
return size >= 0;
}
/**
* Returns the number of elements in this set (costly operation).
*/
@Override
public int size() {
if (size < 0) {
int n = 0;
final Set<String> done = new HashSet<>();
for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext(); ) {
final AuthorityFactory factory = it.next();
if (done.add(getCodeSpace(factory))) {
n += getAuthorityCodes(factory).size();
}
}
size = n;
}
return size;
}
/**
* Returns {@code true} if the set does not contain any element.
* This method is much more efficient than testing {@code size() != 0}
* since it will stop iteration as soon as an element is found.
*/
@Override
public boolean isEmpty() {
if (size == -1) {
for (final Iterator<AuthorityFactory> it = getAllFactories(); it.hasNext(); ) {
if (!getAuthorityCodes(it.next()).isEmpty()) {
// Size still unknown, but we know that the set is not empty.
size = -2;
return false;
}
}
size = 0;
}
return size == 0;
}
/**
* The proxy for the {@code GeodeticAuthorityFactory.getAuthorityCodes(type).contains(String)}.
* Used by {@link #contains(Object)} for delegating its work to the most appropriate factory.
*/
private final AuthorityFactoryProxy<Boolean> contains = new AuthorityFactoryProxy<Boolean>(Boolean.class, AuthorityFactoryIdentifier.ANY) {
@Override
Boolean createFromAPI(AuthorityFactory factory, String code) throws FactoryException {
return getAuthorityCodes(factory).contains(code);
}
@Override
AuthorityFactoryProxy<Boolean> specialize(String typeName) {
return this;
}
};
/**
* Returns {@code true} if the factory contains the given code.
*/
@Override
public boolean contains(final Object code) {
if (code instanceof String)
try {
return create(contains, (String) code);
} catch (NoSuchAuthorityCodeException e) {
// Ignore - will return false.
} catch (FactoryException e) {
throw new BackingStoreException(e);
}
return false;
}
/**
* Declared soon as unsupported operation for preventing a call to {@link #size()}.
*/
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
};
}
use of org.opengis.util.FactoryException in project sis by apache.
the class MultiAuthoritiesFactory method combine.
/**
* Invoked when a {@code createFoo(…)} method is given a combined URI.
* A combined URI is a URN or URL referencing other components. For example if the given URI
* is {@code "urn:ogc:def:crs, crs:EPSG::27700, crs:EPSG::5701"}, then the components are:
* <ol>
* <li>{@code "urn:ogc:def:crs:EPSG:9.1:27700"}</li>
* <li>{@code "urn:ogc:def:crs:EPSG:9.1:5701"}</li>
* </ol>
*
* We do not require the components to be instance of CRS, since the "Definition identifier URNs in
* OGC namespace" best practice paper allows other kinds of combination (e.g. of coordinate operations).
*
* @param <T> compile-time value of {@code type} argument.
* @param type type of object to create.
* @param references parsed URI of the components.
* @param isHTTP whether the user URI is an URL (i.e. {@code "http://something"}) instead than a URN.
* @return the combined object.
* @throws FactoryException if an error occurred while creating the combined object.
*/
private <T> T combine(final Class<T> type, final DefinitionURI[] references, final boolean isHTTP) throws FactoryException {
/*
* Identify the type requested by the user and create all components with the assumption that they will
* be of that type. This is the most common case. If during iteration we find an object of another kind,
* then the array type will be downgraded to IdentifiedObject[]. The 'componentType' variable will keep
* its non-null value only if the array stay of the expected sub-type.
*/
final byte requestedType;
IdentifiedObject[] components;
Class<? extends IdentifiedObject> componentType;
if (CoordinateReferenceSystem.class.isAssignableFrom(type)) {
requestedType = AuthorityFactoryIdentifier.CRS;
componentType = CoordinateReferenceSystem.class;
// Intentional covariance.
components = new CoordinateReferenceSystem[references.length];
} else if (CoordinateOperation.class.isAssignableFrom(type)) {
requestedType = AuthorityFactoryIdentifier.OPERATION;
componentType = CoordinateOperation.class;
// Intentional covariance.
components = new CoordinateOperation[references.length];
} else {
throw new FactoryException(Resources.format(Resources.Keys.CanNotCombineUriAsType_1, type));
}
// Note: "compound-crs" ⟶ "crs".
final String expected = NameMeaning.toObjectType(componentType);
for (int i = 0; i < references.length; i++) {
final DefinitionURI ref = references[i];
final IdentifiedObject component = createObject(ref.toString());
if (componentType != null && (!componentType.isInstance(component) || !expected.equalsIgnoreCase(ref.type))) {
componentType = null;
components = Arrays.copyOf(components, components.length, IdentifiedObject[].class);
}
components[i] = component;
}
/*
* At this point we have successfully created all components. The way to interpret those components
* depends mostly on the type of object requested by the user. For a given requested type, different
* rules apply depending on the type of components. Those rules are described in OGC 07-092r1 (2007):
* "Definition identifier URNs in OGC namespace".
*/
IdentifiedObject combined = null;
switch(requestedType) {
case AuthorityFactoryIdentifier.OPERATION:
{
if (componentType != null) {
/*
* URN combined references for concatenated operations. We build an operation name from
* the operation identifiers (rather than CRS identifiers) because this is what the user
* gave to us, and because source/target CRS are not guaranteed to be defined. We do not
* yet support swapping roles of source and target CRS if an implied-reverse coordinate
* operation is included.
*/
final CoordinateOperation[] ops = (CoordinateOperation[]) components;
String name = IdentifiedObjects.getIdentifierOrName(ops[0]) + " ⟶ " + IdentifiedObjects.getIdentifierOrName(ops[ops.length - 1]);
combined = DefaultFactories.forBuildin(CoordinateOperationFactory.class).createConcatenatedOperation(Collections.singletonMap(CoordinateOperation.NAME_KEY, name), ops);
}
break;
}
case AuthorityFactoryIdentifier.CRS:
{
if (componentType != null) {
/*
* URN combined references for compound coordinate reference systems.
* The URNs of the individual well-known CRSs are listed in the same order in which the
* individual coordinate tuples are combined to form the CompoundCRS coordinate tuple.
*/
combined = CRS.compound((CoordinateReferenceSystem[]) components);
} else if (!isHTTP) {
final CoordinateSystem cs = remove(references, components, CoordinateSystem.class);
if (cs != null) {
final Datum datum = remove(references, components, Datum.class);
if (datum != null) {
/*
* URN combined references for datum and coordinate system. In this case, the URN shall
* concatenate the URNs of one well-known datum and one well-known coordinate system.
*/
if (ArraysExt.allEquals(references, null)) {
combined = combine((GeodeticDatum) datum, cs);
}
} else {
/*
* URN combined references for projected or derived CRSs. In this case, the URN shall
* concatenate the URNs of the one well-known CRS, one well-known Conversion, and one
* well-known CartesianCS. Similar action can be taken for derived CRS.
*/
CoordinateReferenceSystem baseCRS = remove(references, components, CoordinateReferenceSystem.class);
CoordinateOperation op = remove(references, components, CoordinateOperation.class);
if (ArraysExt.allEquals(references, null) && op instanceof Conversion) {
combined = combine(baseCRS, (Conversion) op, cs);
}
}
}
}
break;
}
}
/*
* At this point the combined object has been created if we know how to create it.
* Maybe the result matches the definition of an existing object in the database,
* in which case we will use the existing definition for better metadata.
*/
if (combined == null) {
throw new FactoryException(Resources.format(Resources.Keys.UnexpectedComponentInURI));
}
final IdentifiedObject existing = newIdentifiedObjectFinder().findSingleton(combined);
return type.cast(existing != null ? existing : combined);
}
Aggregations