/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-2008, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.referencing.operation; import java.util.Map; import java.util.Set; import java.util.List; import java.util.Iterator; import java.util.logging.Level; import java.util.logging.LogRecord; import org.opengis.metadata.Identifier; import org.opengis.metadata.citation.Citation; import org.opengis.referencing.AuthorityFactory; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.*; import org.geotools.factory.Hints; import org.geotools.factory.OptionalFactory; import org.geotools.factory.FactoryRegistryException; import org.geotools.referencing.AbstractIdentifiedObject; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; import org.geotools.referencing.factory.BackingStoreException; import org.geotools.resources.i18n.Loggings; import org.geotools.resources.i18n.LoggingKeys; import static org.geotools.referencing.CRS.equalsIgnoreMetadata; /** * 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#getValidArea area of validity}, * {@linkplain CoordinateOperation#getScope scope} and * {@linkplain CoordinateOperation#getPositionalAccuracy positional accuracy}. *
* When
* Note that this method may be invoked recursively. For example no operation may be available
* from the {@linkplain #getAuthorityFactory underlying authority factory} between two
* {@linkplain org.opengis.referencing.crs.CompoundCRS compound CRS}, but an operation
* may be available between two components of those compound CRS.
*
* @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 the given source and target CRS
* are already under examination by a previous call to this method. Note: there is no
* need to synchronize since the Boolean is thread-local.
*/
if (Boolean.TRUE.equals(processing.get())) {
return null;
}
/*
* Now performs the real work.
*/
final CoordinateOperationAuthorityFactory authorityFactory = getAuthorityFactory();
final Citation authority = authorityFactory.getAuthority();
final Identifier sourceID = AbstractIdentifiedObject.getIdentifier(sourceCRS, authority);
if (sourceID == null) {
return null;
}
final Identifier targetID = AbstractIdentifiedObject.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 unfortunatly 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
* 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())) {
if(!CRS.equalsIgnoreMetadata(sourceCRS, operation.getSourceCRS()) ||
!CRS.equalsIgnoreMetadata(targetCRS, operation.getTargetCRS())) {
return new ForcedCRSOperation(operation, sourceCRS, targetCRS);
} else {
return operation;
}
}
final Map{@linkplain #createOperation createOperation}(sourceCRS, targetCRS)
is invoked,
* {@code AuthorityBackedFactory} fetch 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.
*
* @since 2.2
*
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*/
public class AuthorityBackedFactory extends DefaultCoordinateOperationFactory
implements OptionalFactory
{
/**
* The priority level for this factory.
*/
static final int PRIORITY = DefaultCoordinateOperationFactory.PRIORITY + 10;
/**
* 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 CoordinateOperationAuthorityFactory authorityFactory;
/**
* Used as a guard against infinite recursivity.
*/
private final ThreadLocal{@linkplain CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes
* createFromCoordinateReferenceSystemCodes}(sourceCode, targetCode)
methods.
* If no operation is found for those codes, then this method returns {@code null}.
* {@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.
*
* @since 2.3
*/
protected boolean accept(final CoordinateOperation operation) {
return true;
}
/**
* Returns {@code true} if this factory and its underlying
* {@linkplain #getAuthorityFactory authority factory} are available for use.
*/
public boolean isAvailable() {
try {
final CoordinateOperationAuthorityFactory authorityFactory = getAuthorityFactory();
if (authorityFactory instanceof OptionalFactory) {
return ((OptionalFactory) authorityFactory).isAvailable();
}
return true;
} catch (FactoryRegistryException exception) {
// No factory found. Ignore the exception since it is the
// purpose of this method to figure out this kind of case.
return false;
}
}
}