All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotoolkit.referencing.operation.AuthorityBackedFactory Maven / Gradle / Ivy

/*
 *    Geotoolkit.org - An Open Source Java GIS Toolkit
 *    http://www.geotoolkit.org
 *
 *    (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
 *    (C) 2009-2012, Geomatys
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 */
package org.geotoolkit.referencing.operation;

import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.Iterator;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import net.jcip.annotations.ThreadSafe;

import org.opengis.util.FactoryException;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.quality.ConformanceResult;
import org.opengis.referencing.AuthorityFactory;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.operation.*;

import org.geotoolkit.factory.Hints;
import org.geotoolkit.factory.Factory;
import org.geotoolkit.factory.FactoryRegistryException;
import org.geotoolkit.internal.referencing.Identifier3D;
import org.geotoolkit.referencing.IdentifiedObjects;
import org.geotoolkit.referencing.operation.matrix.Matrices;
import org.geotoolkit.referencing.operation.transform.EllipsoidalTransform;
import org.geotoolkit.referencing.operation.transform.ConcatenatedTransform;
import org.geotoolkit.referencing.factory.NoSuchIdentifiedResource;
import org.geotoolkit.util.collection.BackingStoreException;
import org.geotoolkit.util.Utilities;
import org.geotoolkit.resources.Loggings;
import org.geotoolkit.resources.Descriptions;

import static org.geotoolkit.referencing.CRS.equalsApproximatively;
import static org.geotoolkit.util.collection.XCollections.isNullOrEmpty;
import static org.geotoolkit.factory.AuthorityFactoryFinder.getCoordinateOperationAuthorityFactory;


/**
 * A {@linkplain CoordinateOperationFactory coordinate operation factory} extended with the extra
 * informations provided by an {@linkplain CoordinateOperationAuthorityFactory authority factory}.
 * Such authority factory may help to find transformation paths not available otherwise (often
 * determined from empirical parameters). Authority factories can also provide additional
 * informations like the
 * {@linkplain CoordinateOperation#getDomainOfValidity domain of validity},
 * {@linkplain CoordinateOperation#getScope scope} and
 * {@linkplain CoordinateOperation#getCoordinateOperationAccuracy accuracy}.
 * 

* When {@linkplain #createOperation createOperation}(sourceCRS, targetCRS) is invoked, * {@code AuthorityBackedFactory} fetches the authority codes for source and target CRS and submits * them to the {@linkplain #getAuthorityFactory underlying authority factory} through a call to its * {@linkplain CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes * createFromCoordinateReferenceSystemCodes}(sourceCode, targetCode) method. If the * authority factory doesn't know about the specified CRS, then the default (standalone) * process from the super-class is used as a fallback. * * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.16 * * @since 2.2 * @module */ @ThreadSafe public class AuthorityBackedFactory extends DefaultCoordinateOperationFactory { /** * The default authority factory to use. */ private static final String DEFAULT_AUTHORITY = "EPSG"; /** * The authority factory to use for creating new operations. * If {@code null}, a default factory will be fetched when first needed. */ private volatile CoordinateOperationAuthorityFactory authorityFactory; /** * Used as a guard against infinite recursivity. */ private final ThreadLocal processing = new ThreadLocal(); /** * Creates a new factory backed by a default EPSG authority factory. */ public AuthorityBackedFactory() { this(EMPTY_HINTS); } /** * Creates a new factory backed by an authority factory fetched using the specified hints. * This constructor recognizes the {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, * {@link Hints#DATUM_FACTORY DATUM} and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} * {@code FACTORY} hints. * * @param userHints The hints, or {@code null} if none. */ public AuthorityBackedFactory(Hints userHints) { super(userHints); /* * Removes the hint processed by the super-class. This include hints like * LENIENT_DATUM_SHIFT, which usually don't apply to authority factories. * An other way to see this is to said that this class "consumed" the hints. * By removing them, we increase the chances to get an empty map of remaining hints, * which in turn help to get the default CoordinateOperationAuthorityFactory * (instead of forcing a new instance). */ if (userHints == null) { userHints = EMPTY_HINTS; } userHints = userHints.clone(); userHints.keySet().removeAll(hints.keySet()); userHints.remove(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER); userHints.remove(Hints.FORCE_STANDARD_AXIS_DIRECTIONS); userHints.remove(Hints.FORCE_STANDARD_AXIS_UNITS); if (!userHints.isEmpty()) { noForce(userHints); authorityFactory = getCoordinateOperationAuthorityFactory(DEFAULT_AUTHORITY, userHints); } } /** * Makes sure that every {@code FORCE_*} hints are set to false. We do that because we want * {@link CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes} to * returns coordinate operations straight from the EPSG database; we don't want an instance * like {@link org.geotoolkit.referencing.factory.OrderedAxisAuthorityFactory}. Axis swapping * are performed by {@link #createFromDatabase} in this class after we invoked * {@link CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes}. An * {@code OrderedAxisAuthorityFactory} instance in this class would be in the way and cause * an infinite recursivity. * * @see GEOT-1161 */ private static void noForce(final Hints userHints) { userHints.put(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.FALSE); userHints.put(Hints.FORCE_STANDARD_AXIS_DIRECTIONS, Boolean.FALSE); userHints.put(Hints.FORCE_STANDARD_AXIS_UNITS, Boolean.FALSE); } /** * Invoked by {@link org.geotoolkit.factory.FactoryRegistry} in order to set the ordering * relative to other factories. The current implementation specifies that this factory * should have priority over a plain (not backed by an authority) * {@code DefaultCoordinateOperationFactory}. * * @since 3.00 */ @Override protected void setOrdering(final Organizer organizer) { super.setOrdering(organizer); // Defer to CachingCoordinateOperationFactory organizer.before(DefaultCoordinateOperationFactory.class, false); } /** * Returns the underlying coordinate operation authority factory. This is the factory * where this {@code AuthorityBackedFactory} will search for an explicitly specified * operation before to fallback to the super-class. * * @return The underlying coordinate operation authority factory. */ protected CoordinateOperationAuthorityFactory getAuthorityFactory() { /* * No need to synchronize. This is not a big deal if AuthorityFactoryFinder is invoked * twice since it is already synchronized. Actually, we should not synchronize at all. * Every methods from the super-class are thread-safe without synchronized statements, * and we should preserve this advantage in order to reduce the risk of contention. */ CoordinateOperationAuthorityFactory factory = authorityFactory; if (factory == null) { /* * Factory creation at this stage will happen only if null hints were specified at * construction time, which explain why it is correct to use {@link FactoryFinder} * with empty hints here. */ final Hints hints = EMPTY_HINTS.clone(); noForce(hints); authorityFactory = factory = getCoordinateOperationAuthorityFactory(DEFAULT_AUTHORITY, hints); } return factory; } /** * Invokes {@link #createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem)} * with a guard against infinite recursivity. The check against recursivity is required * because the {@code createOperation(...)} method implemented in the super-class may * invoke {@code createFromDatabase(...)} again. * * @param source The source CRS. * @param target The target CRS. * @return The transform from source CRS to target CRS. * @throws FactoryException If an error occurred while creating the math transform. */ private MathTransform getMathTransform(final CoordinateReferenceSystem source, final CoordinateReferenceSystem target) throws FactoryException { processing.set(Boolean.TRUE); try { return createOperation(source, target).getMathTransform(); } finally { processing.set(Boolean.FALSE); } } /** * Returns an operation for conversion or transformation between two coordinate reference * systems. The default implementation extracts the authority code from the supplied * {@code sourceCRS} and {@code targetCRS}, and submit them to the * {@linkplain CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes * createFromCoordinateReferenceSystemCodes}(sourceCode, targetCode) methods. * If no operation is found for those codes, then this method returns {@code null}. * * @param sourceCRS Input coordinate reference system. * @param targetCRS Output coordinate reference system. * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}, or {@code null} * if no such operation is explicitly defined in the underlying database. * * @since 2.3 */ @Override protected CoordinateOperation createFromDatabase(final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS) { /* * Safety check against recursivity: returns null if this method is invoked indirectly * by the above getMathTransform(...) method. Note: there is no need to synchronize * since the Boolean is thread-local. */ if (Boolean.TRUE.equals(processing.get())) { return null; } final CoordinateOperationAuthorityFactory authorityFactory = getAuthorityFactory(); CoordinateOperation operation = null; int combine = 0; do { /* * First, try directly the provided (sourceCRS, targetCRS) pair. If it doesn't * work, try to use different combinations of original CRS and two-dimensional * components of those CRS. */ final CoordinateReferenceSystem source, target; source = (combine & 2) == 0 ? sourceCRS : getHorizontalCRS(sourceCRS); target = (combine & 1) == 0 ? targetCRS : getHorizontalCRS(targetCRS); if (source != null && target != null) try { operation = createFromDatabase(source, target, authorityFactory); if (operation != null) { /* * Found an operation. If we had to extract the horizontal part of * some 3D CRS, then we need to modify the coordinate operation. */ if (combine != 0) { operation = propagateVertical(operation, source != sourceCRS, target != targetCRS); operation = complete(operation, sourceCRS, targetCRS); } break; } } catch (FactoryException exception) { /* * Some kind of error more serious than NoSuchAuthorityCodeException (which was caught * by the createFromDatabase(...) method invoked in the 'try' block). It may be serious, * but the super-class is capable to provide a reasonable default behavior. Log as a * warning and stop this method. */ log(exception, authorityFactory); return null; } } while (++combine != 4); return operation; } /** * Implementation of {@link #createFromDatabase(CoordinateReferenceSystem, CoordinateReferenceSystem)} * looking only for the specified CRS. This method does not try to get the 2D components of a 3D CRS. * * @param sourceCRS Input coordinate reference system. * @param targetCRS Output coordinate reference system. * @param authorityFactory The factory to query for getting operation from the CRS. * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}, or {@code null} * if no such operation is explicitly defined in the underlying database. * @throws FactoryException If an error occurred while creating the operation. */ private CoordinateOperation createFromDatabase( final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS, final CoordinateOperationAuthorityFactory authorityFactory) throws FactoryException { final Citation authority = authorityFactory.getAuthority(); final Identifier sourceID = IdentifiedObjects.getIdentifier(sourceCRS, authority); if (sourceID == null) { return null; } final Identifier targetID = IdentifiedObjects.getIdentifier(targetCRS, authority); if (targetID == null) { return null; } final String sourceCode = sourceID.getCode().trim(); final String targetCode = targetID.getCode().trim(); if (sourceCode.equals(targetCode)) { /* * NOTE: This check is mandatory because this method may be invoked in some situations * where (sourceCode == targetCode) but (sourceCRS != targetCRS). Such situation * should be illegal (or at least the MathTransform from sourceCRS to targetCRS * should be the identity transform), but unfortunately it still happen because * EPSG defines axis order as (latitude,longitude) for geographic CRS while most * softwares expect (longitude,latitude) no matter what the EPSG authority said. * We will need to computes a transform from sourceCRS to targetCRS ignoring the * source and target codes. The superclass can do that, providing that we prevent * the authority database to (legitimately) claims that the transformation from * sourceCode to targetCode is the identity transform. See GEOT-854. */ return null; } final boolean inverse; Set operations; try { operations = authorityFactory.createFromCoordinateReferenceSystemCodes(sourceCode, targetCode); inverse = isNullOrEmpty(operations); if (inverse) { /* * No operation from 'source' to 'target' available. But maybe there is an inverse * operation. This is typically the case when the user wants to convert from a * projected to a geographic CRS. The EPSG database usually contains transformation * paths for geographic to projected CRS only. */ operations = authorityFactory.createFromCoordinateReferenceSystemCodes(targetCode, sourceCode); if (operations == null) { return null; } } } catch (NoSuchAuthorityCodeException exception) { /* * sourceCode or targetCode is unknown to the underlying authority factory. * Ignores the exception and fallback on the generic algorithm provided by * the super-class. */ log(Level.FINE, exception, authorityFactory, true); return null; } for (final Iterator it=operations.iterator(); it.hasNext();) { CoordinateOperation candidate; try { // The call to it.next() must be inside the try..catch block, // which is why we don't use the Java 5 for loop syntax here. candidate = it.next(); if (candidate == null) { continue; } if (inverse) { candidate = inverse(candidate); } } catch (NoninvertibleTransformException exception) { // The transform is non invertible. Log only at the fine level, since it // may be a normal failure - the transform is not required to be invertible. log(Level.FINE, exception, authorityFactory, true); continue; } catch (FactoryException exception) { // Other kind of error. Log a warning and try the next coordinate operation. // Note that this exception can occur only during the call to 'inverse', not // during the iteration. log(exception, authorityFactory); continue; } catch (BackingStoreException exception) { // Exception during the iteration. It may be a failure to instantiate // the CoordinateOperation because of an unsupported operation method. final Throwable cause = exception.getCause(); log(cause != null ? cause : exception, authorityFactory); continue; } /* * It is possible that the Identifier in user's CRS is not quite right. For * example the user may have created his source and target CRS from WKT using * a different axis order than the official one and still call it "EPSG:xxxx" * as if it were the official CRS. Checks if the source and target CRS for the * operation just created are really the same (ignoring metadata) than the one * specified by the user. * * NOTE: * A FactoryException is thrown by the getMathTransform(...) methods if we have been * unable to create a transform from the user-provided CRS to the authority-provided * CRS. In theory, the two above-cited CRS should been the same and the transform is * the identity transform. In practice, it is not always the case because of axis * swapping issue (see GEOT-854). * * If the getMathTransform(...) methods failed to create what should merely be an * affine transform for swapping axes (if not the identity transform), then we are * likely to fail for all other transforms. Let the FactoryException propagate in * order to stop the loop and avoid logging the same warning many time. */ candidate = complete(candidate, sourceCRS, targetCRS); if (accept(candidate)) { return candidate; } } return null; } /** * Completes (if necessary) the given coordinate operation for making sure that the source CRS * is the given one and the target CRS is the given one. In principle, the given CRS shall be * equivalent to the operation source/target CRS. However discrepancies happen if the user CRS * have flipped axis order, or if we looked for 2D operation while the user provided 3D CRS. * * @param operation The coordinate operation to complete. * @param sourceCRS The source CRS requested by the user. * @param targetCRS The target CRS requested by the user. * @return A coordinate operation for the given source and target CRS. * @throws FactoryException if the operation can't be constructed. */ private CoordinateOperation complete( final CoordinateOperation operation, final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS) throws FactoryException { CoordinateReferenceSystem source = operation.getSourceCRS(); CoordinateReferenceSystem target = operation.getTargetCRS(); final MathTransform prepend, append; if (!equalsApproximatively(sourceCRS, source)) { prepend = getMathTransform(sourceCRS, source); source = sourceCRS; } else { prepend = null; } if (!equalsApproximatively(target, targetCRS)) { append = getMathTransform(target, targetCRS); target = targetCRS; } else { append = null; } return transform(source, prepend, operation, append, target); } /** * Appends or prepends the specified math transforms to the * {@linkplain CoordinateOperation#getMathTransform math transform of the given operation}. * The new coordinate operation (if any) will share the same metadata than the original * operation, including the authority code. *

* This method is used in order to change axis order when the user-specified CRS * disagree with the authority-supplied CRS. * * @param sourceCRS The source CRS to give to the new operation. * @param prepend The transform to prepend to the operation math transform. * @param operation The operation in which to prepend the math transforms. * @param append The transform to append to the operation math transform. * @param targetCRS The target CRS to give to the new operation. * @return A new operation, or {@code operation} if {@code prepend} and {@code append} were * nulls or identity transforms. * @throws FactoryException if the operation can't be constructed. */ private CoordinateOperation transform(final CoordinateReferenceSystem sourceCRS, final MathTransform prepend, final CoordinateOperation operation, final MathTransform append, final CoordinateReferenceSystem targetCRS) throws FactoryException { if ((prepend == null || prepend.isIdentity()) && (append == null || append.isIdentity())) { return operation; } final Map properties = IdentifiedObjects.getProperties(operation); /* * In the particular case of concatenated operations, we can not prepend or append a math * transform to the operation as a whole (the math transform for a concatenated operation * is computed automatically as the concatenation of the math transform from every single * operations, and we need to stay consistent with that). Instead, we prepend to the first * single operation and append to the last single operation. */ if (operation instanceof ConcatenatedOperation) { final List c = ((ConcatenatedOperation) operation).getOperations(); final CoordinateOperation[] op = c.toArray(new CoordinateOperation[c.size()]); if (op.length != 0) { final CoordinateOperation first = op[0]; if (op.length == 1) { op[0] = transform(sourceCRS, prepend, first, append, targetCRS); } else { final CoordinateOperation last = op[op.length - 1]; op[0] = transform(sourceCRS, prepend, first, null, first.getTargetCRS()); op[op.length-1] = transform(last.getSourceCRS(), null, last, append, targetCRS); } return createConcatenatedOperation(properties, op); } } /* * Single operation case. */ MathTransform transform = operation.getMathTransform(); final MathTransformFactory mtFactory = getMathTransformFactory(); if (prepend != null) { transform = mtFactory.createConcatenatedTransform(prepend, transform); } if (append != null) { transform = mtFactory.createConcatenatedTransform(transform, append); } assert !transform.equals(operation.getMathTransform()) : transform; final Class type = AbstractCoordinateOperation.getType(operation); OperationMethod method = null; if (operation instanceof SingleOperation) { method = ((SingleOperation) operation).getMethod(); if (method != null) { final Integer sourceDimensions = transform.getSourceDimensions(); final Integer targetDimensions = transform.getTargetDimensions(); if (!Utilities.equals(sourceDimensions, method.getSourceDimensions()) || !Utilities.equals(targetDimensions, method.getTargetDimensions())) { method = new DefaultOperationMethod(method, sourceDimensions, targetDimensions); } } } return createFromMathTransform(properties, sourceCRS, targetCRS, transform, method, type); } /** * Returns a new coordinate operation with the ellipsoidal height added either in the source * coordinates, in the target coordinates or both. If there is an ellipsoidal transform, then * this method updates the transforms in order to use the ellipsoidal height (it has an impact * on the transformed values). *

* This method is not guaranteed to succeed in adding the ellipsoidal height. It works on a * best effort basis. In any case, the {@link #complete} method should be invoked * after this one in order to ensure that the source and target CRS are the expected ones. * * @param operation The original (typically two-dimensional) coordinate operation. * @param source3D {@code true} for adding ellipsoidal height in source coordinates. * @param target3D {@code true} for adding ellipsoidal height in target coordinates. * @return A coordinate operation with the source and/or target coordinates made 3D. * @throws FactoryException If an error occurred while creating the coordinate operation. * * @since 3.16 */ private CoordinateOperation propagateVertical(CoordinateOperation operation, final boolean source3D, final boolean target3D) throws FactoryException { /* * Get the list of all single (non-concatenated) transformation steps. */ final MathTransform[] steps; MathTransform transform = operation.getMathTransform(); if (transform instanceof ConcatenatedTransform) { final List list = ((ConcatenatedTransform) transform).getSteps(); steps = list.toArray(new MathTransform[list.size()]); } else { steps = new MathTransform[] {transform}; } /* * Find the first and the last EllipsoidalTransform. In the case of MolodenskyTransform, * the first and last occurences are typically the same instance. In the Geocentric datum * shift case, they are two different instances with an affine transform between them. */ EllipsoidalTransform first=null, last=null; int indexFirst=0, indexLast=0; for (int i=0; i srcDim)) { srcDim = tgtDim; } } else { srcDim = newTr.getTargetDimensions(); tgtDim = step .getTargetDimensions(); if (addVertical &= (srcDim > tgtDim)) { tgtDim = srcDim; } } Matrix matrix = Matrices.getMatrix(step); if (matrix == null || !Matrices.isAffine(matrix)) { continue; // Non affine step: do not update the transform steps. } matrix = Matrices.resizeAffine(matrix, srcDim, tgtDim); step = mtFactory.createAffineTransform(matrix); } /* * Now update the ellipsoidal transform and the CRS. The CRS should be an * instance of SingleCRS. However we check for safety. If it is not, then * we can not update the transforms. */ if (addVertical) { if (!(crs instanceof SingleCRS)) { continue; // Can't update the CRS, so don't update the transforms. } crs = factories.toGeodetic3D((SingleCRS) crs); if (!isLast) sourceCRS = crs; else targetCRS = crs; } steps[index] = newTr; if (step != null) { steps[toAdjust] = step; } updated = true; } } while ((isLast = !isLast) == true); /* * If at least one step has been changed, rebuild the concatenated transform. */ if (updated) { transform = steps[0]; for (int i=1; i type = AbstractCoordinateOperation.getType(operation); OperationMethod method = null; if (operation instanceof SingleOperation) { /* * If the original operation was a SingleOperation, make an exact copy of * the original method except for the number of source/target dimensions. */ method = ((SingleOperation) operation).getMethod(); if (!Utilities.equals(srcDim, method.getSourceDimensions()) || !Utilities.equals(tgtDim, method.getTargetDimensions())) { method = new DefaultOperationMethod(method, srcDim, tgtDim); } } else if (operation instanceof ConcatenatedOperation) { /* * If the original operation was a ConcatenatedOperation, build a SingleOperation * which will represent the operation as a whole. In the current implementation, * we don't try to build a new ConcatenatedOperation because separating the steps * is hard (because of propagation of dimension changes accross different steps). */ type = SingleOperation.class; final StringBuilder buffer = new StringBuilder(); for (final SingleOperation step : ((ConcatenatedOperation) operation).getOperations()) { final String id = IdentifiedObjects.getIdentifier(step); if (id != null) { if (buffer.length() != 0) { buffer.append(" + "); } buffer.append(id); } if (step instanceof Transformation) { type = Transformation.class; } else if (step instanceof Conversion && type != Transformation.class) { type = Conversion.class; } // Don't check the Projection case; it is confusing // since users expect a well known projection name. } method = createOperationMethod(Collections.singletonMap(OperationMethod.NAME_KEY, Descriptions.format(Descriptions.Keys.CONCATENATED_OPERATION_ADAPTED_$1, buffer)), srcDim, tgtDim, null); } operation = createFromMathTransform(IdentifiedObjects.getProperties(operation), sourceCRS, targetCRS, transform, method, type); } return operation; } /** * If a horizontal CRS can be extracted from the given CRS, returns it. * Otherwise returns {@code null}. * * @param crs The CRS from which to extract the horizontal component. * @return The horizontal component, or {@code null} if none or unknown. * * @since 3.16 */ private static SingleCRS getHorizontalCRS(final CoordinateReferenceSystem crs) { final Identifier id = crs.getName(); if (id instanceof Identifier3D) { return ((Identifier3D) id).horizontalCRS; } return null; } /** * Logs a warning when an object can't be created from the specified factory. * * @param exception The exception which occurred. * @param factory The factory used in the attempt to create an operation. */ private static void log(final Throwable exception, final AuthorityFactory factory) { log(Level.WARNING, exception, factory, exception instanceof NoSuchIdentifiedResource); } /** * Logs an exception at the given level. * * @param level The level to use for logging. * @param exception The exception which occurred. * @param factory The factory used in the attempt to create an operation. * @param isOptional Whatever the operation we just attempted was optional. */ private static void log(final Level level, final Throwable exception, final AuthorityFactory factory, final boolean isOptional) { if (LOGGER.isLoggable(level)) { final LogRecord record = Loggings.format(level, Loggings.Keys.CANT_CREATE_COORDINATE_OPERATION_$1, factory.getAuthority().getTitle()); record.setSourceClassName(AuthorityBackedFactory.class.getName()); record.setSourceMethodName("createFromDatabase"); if (isOptional) { record.setMessage(Loggings.format(record) + ' ' + exception.getLocalizedMessage()); } else { record.setThrown(exception); } record.setLoggerName(LOGGER.getName()); LOGGER.log(record); } } /** * Returns {@code true} if the specified operation is acceptable. This method is invoked * automatically by {@linkplain #createFromDatabase createFromDatabase}(...) * for every operation candidates found. The default implementation returns always {@code * true}. Subclasses should override this method if they wish to filter the coordinate * operations to be returned. * * @param operation The operation that {@code createFromDatabase} wants to return. * @return {@code true} if the given operation is acceptable, or {@code false} if * {@code createFromDatabase} should look for an other one. * * @since 2.3 */ protected boolean accept(final CoordinateOperation operation) { return true; } /** * Returns whatever this factory and its underlying * {@linkplain #getAuthorityFactory authority factory} are available for use. * * @since 3.03 */ @Override public ConformanceResult availability() { try { final CoordinateOperationAuthorityFactory authorityFactory = getAuthorityFactory(); if (authorityFactory instanceof Factory) { return ((Factory) authorityFactory).availability(); } } catch (FactoryRegistryException exception) { // Declares as not available for the given raison. return new Availability(exception); } // Declare as available if not disposed. return super.availability(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy