/*
* Geotools 2 - OpenSource mapping toolkit
* (C) 2003, Geotools Project Managment Committee (PMC)
* (C) 2001, Institut de Recherche pour le Développement
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotools.ct;
// OpenGIS dependencies
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Locale;
import org.geotools.cs.CoordinateSystem;
import org.geotools.cs.Info;
import org.geotools.resources.Utilities;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.resources.i18n.Vocabulary;
import org.opengis.cs.CS_CoordinateSystem;
import org.opengis.ct.CT_CoordinateTransformation;
import org.opengis.ct.CT_MathTransform;
import org.opengis.ct.CT_TransformType;
import org.opengis.referencing.operation.NoninvertibleTransformException;
/**
* Describes a coordinate transformation. A coordinate transformation class establishes
* an association between a source and a target coordinate reference system, and provides
* a {@link MathTransform} for transforming coordinates in the source coordinate reference
* system to coordinates in the target coordinate reference system. These coordinate
* systems can be ground or image coordinates. In general mathematics, "transformation"
* is the general term for mappings between coordinate systems (see tensor analysis).
*
* For a ground coordinate point, if the transformation depends only on mathematically
* derived parameters (as in a cartographic projection), then this is an ISO conversion.
* If the transformation depends on empirically derived parameters (as in datum
* transformations), then this is an ISO transformation.
*
* @source $URL$
* @version $Id$
* @author OpenGIS (www.opengis.org)
* @author Martin Desruisseaux
*
* @see org.opengis.ct.CT_CoordinateTransformation
*
* @deprecated Replaced by {@link org.geotools.referencing.operation.AbstractCoordinateOperation}.
*/
public class CoordinateTransformation extends Info {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = -1850470924499685544L;
/**
* OpenGIS object returned by {@link #cachedOpenGIS}.
* It may be a hard or a weak reference.
*/
private transient Object proxy;
/**
* The source coordinate system.
*/
private final CoordinateSystem sourceCS;
/**
* The destination coordinate system.
*/
private final CoordinateSystem targetCS;
/**
* The transform type.
*/
private final TransformType type;
/**
* The underlying math transform, or null
if it
* doesn't has been constructed yet. If null
,
* then subclass must initialize this field
* the first time {@link #getMathTransform} is invoked.
*/
protected MathTransform transform;
/**
* The inverse transform. This field
* will be computed only when needed.
*/
transient CoordinateTransformation inverse;
/**
* Construct a coordinate transformation.
*
* @param name The coordinate transformation name, or null
* for an automatically generated name.
* @param sourceCS The source coordinate system.
* @param targetCS The destination coordinate system.
* @param type The transform type.
* @param transform The math transform. This argument is allowed to
* be null
only if this constructor is
* invoked from within a subclass constructor. In
* this case, the subclass must
* construct a math transform no later than the first
* time {@link #getMathTransform} is invoked.
*/
public CoordinateTransformation(final String name,
final CoordinateSystem sourceCS,
final CoordinateSystem targetCS,
final TransformType type,
final MathTransform transform)
{
super((name!=null) ? name : "");
this.sourceCS = sourceCS;
this.targetCS = targetCS;
this.type = type;
this.transform = transform;
ensureNonNull("sourceCS", sourceCS);
ensureNonNull("targetCS", targetCS);
ensureNonNull("type", type);
if (getClass().equals(CoordinateTransformation.class)) {
ensureNonNull("transform", transform);
}
checkDimension("sourceCS", sourceCS.getDimension(), transform.getDimSource());
checkDimension("targetCS", targetCS.getDimension(), transform.getDimTarget());
}
/**
* Check if a coordinate system has the expected number of dimensions.
*/
private static void checkDimension(final String name, final int actual, final int expected) {
if (actual != expected) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.MISMATCHED_DIMENSION_$3,
name, new Integer(actual), new Integer(expected)));
}
}
/**
* Gets the name of this coordinate transformation.
*
* @param locale The desired locale, or null
for the default locale.
*
* @deprecated Replaced by {@link org.geotools.referencing.operation.AbstractCoordinateOperation#getName}.
*/
public String getName(final Locale locale) {
final String name = super.getName().getCode();
if (name.length()!=0) {
return name;
} else if (transform instanceof AbstractMathTransform) {
return ((AbstractMathTransform) transform).getName(locale);
} else {
return sourceCS.getName().getCode()+"\u00A0\u21E8\u00A0"+targetCS.getName().getCode();
}
}
/**
* Gets the source coordinate system.
*
* @see org.opengis.ct.CT_CoordinateTransformation#getSourceCS()
*
* @deprecated Replaced by {@link org.geotools.referencing.operation.AbstractCoordinateOperation#getSourceCRS}.
*/
public CoordinateSystem getSourceCS() {
return sourceCS;
}
/**
* Gets the target coordinate system.
*
* @see org.opengis.ct.CT_CoordinateTransformation#getTargetCS()
*
* @deprecated Replaced by {@link org.geotools.referencing.operation.AbstractCoordinateOperation#getTargetCRS}.
*/
public CoordinateSystem getTargetCS() {
return targetCS;
}
/**
* Gets the semantic type of transform.
* For example, a datum transformation or a coordinate conversion.
*
* @see org.opengis.ct.CT_CoordinateTransformation#getTransformType()
*
* @deprecated No direct replacement. Check for instance of
* {@link org.opengis.referencing.operation.Conversion} or
* {@link org.opengis.referencing.operation.Transformation} instead.
*/
public TransformType getTransformType() {
return type;
}
/**
* Gets the math transform. The math transform will transform positions in
* the source coordinate system into positions in the target coordinate system.
*
* @see org.opengis.ct.CT_CoordinateTransformation#getMathTransform()
*
* @deprecated Replaced by {@link org.geotools.referencing.operation.AbstractCoordinateOperation#getMathTransform}.
*/
public MathTransform getMathTransform() {
if (transform!=null) {
return transform;
} else {
throw new IllegalStateException();
}
}
/**
* Returns the inverse transform of this object.
*/
public synchronized CoordinateTransformation inverse()
throws NoninvertibleTransformException
{
if (inverse==null) {
inverse = new Inverse(this);
}
return inverse;
}
/**
* The inverse coordinate transformation. This class override
* {@link #getName} in order to delegate part of the call to
* the underlying direct transformation.
*
* @version 1.0
* @author Martin Desruisseaux
*/
private static final class Inverse extends CoordinateTransformation {
/**
* Construct a coordinate transformation.
*/
public Inverse(final CoordinateTransformation transform)
throws NoninvertibleTransformException
{
super(null, transform.getTargetCS(), transform.getSourceCS(),
transform.getTransformType(), transform.getMathTransform().inverse());
this.inverse = transform;
}
/**
* Gets the name of this coordinate transformation.
*/
public String getName(final Locale locale) {
return Vocabulary.getResources(locale).getString(
VocabularyKeys.INVERSE_$1, this.inverse.getName(locale));
}
}
/**
* Returns a hash value for this
* coordinate transformation.
*/
public int hashCode() {
int code = 7851236;
CoordinateSystem cs;
if ((cs=getSourceCS()) != null) code = code*37 + cs.hashCode();
if ((cs=getTargetCS()) != null) code = code*37 + cs.hashCode();
return code;
}
/**
* Compares the specified object with this coordinate transformation
* for equality. The default implementation compare name, transform
* type, source and target coordinate systems. It doesn't compare the
* math transform, since it should be equivalents if the above mentionned
* parameters are equal.
*
* @param object The object to compare to this
.
* @param compareNames true
to comparare the {@linkplain #getName name},
* {@linkplain #getAlias alias}, {@linkplain #getAuthorityCode authority
* code}, etc. as well, or false
to compare only properties
* relevant to transformations.
* @return true
if both objects are equal.
*/
public boolean equals(final Info object, final boolean compareNames) {
if (object == this) {
return true;
}
if (super.equals(object, compareNames)) {
final CoordinateTransformation that = (CoordinateTransformation) object;
return Utilities.equals(this.getTransformType(), that.getTransformType()) &&
equals(this.getSourceCS(), that.getSourceCS(), compareNames) &&
equals(this.getTargetCS(), that.getTargetCS(), compareNames);
}
return false;
}
/**
* Compare two objects for equality.
*
* @param object1 The first object to compare (may be null
).
* @param object2 The second object to compare (may be null
).
* @param compareNames true
for performing a strict comparaison, or
* false
for comparing only properties relevant to transformations.
* @return true
if both objects are equal.
*/
private static boolean equals(final Info object1, final Info object2, final boolean compareNames) {
return (object1==object2) || (object1!=null && object1.equals(object2, compareNames));
}
/**
* Returns an OpenGIS interface for this math transform.
* The returned object is suitable for RMI use.
*
* Note 1: The returned type is a generic {@link Object} in order
* to avoid too early class loading of OpenGIS interface.
*
* Note 2: We do NOT want this method to override Info.toOpenGIS(),
* since the returned object do not implements CS_Info. The
* package-private access do the trick.
*/
Object toOpenGIS(final Object adapters) throws RemoteException {
return new Export(adapters);
}
/**
* Returns an OpenGIS interface for this info. This method first look in the cache.
* If no interface was previously cached, then this method create a new adapter and
* cache the result.
*
* @param adapters The originating {@link Adapters}.
* @return The OpenGIS interface for this info.
* @throws RemoteException if the object can't be exported.
*/
final synchronized Object cachedOpenGIS(final Object adapters) throws RemoteException {
if (proxy!=null) {
if (proxy instanceof Reference) {
final Object ref = ((Reference) proxy).get();
if (ref!=null) {
return ref;
}
} else {
return proxy;
}
}
final Object opengis = toOpenGIS(adapters);
proxy = new WeakReference(opengis);
return opengis;
}
/////////////////////////////////////////////////////////////////////////
//////////////// ////////////////
//////////////// OPENGIS ADAPTER ////////////////
//////////////// ////////////////
/////////////////////////////////////////////////////////////////////////
/**
* Wrap a {@link CoordinateTransformation} for use with OpenGIS.
* This wrapper is a good place to check for non-implemented
* OpenGIS methods (just check for methods throwing
* {@link UnsupportedOperationException}). This class
* is suitable for RMI use.
*
* @version $Id$
* @author Martin Desruisseaux
*/
final class Export extends UnicastRemoteObject implements CT_CoordinateTransformation {
/**
* The originating adapter.
*/
protected final Adapters adapters;
/**
* Construct a remote object.
*/
protected Export(final Object adapters) throws RemoteException {
super(); // TODO: Fetch the port number from the adapter.
this.adapters = (Adapters)adapters;
}
/**
* Returns the underlying math transform.
*/
public final CoordinateTransformation unwrap() {
return CoordinateTransformation.this;
}
/**
* Name of transformation.
*/
public String getName() throws RemoteException {
return CoordinateTransformation.this.getName(null);
}
/**
* Authority which defined transformation and parameter values.
*/
public String getAuthority() throws RemoteException {
return CoordinateTransformation.this.getAuthority(null);
}
/**
* Code used by authority to identify transformation.
*/
public String getAuthorityCode() throws RemoteException {
return CoordinateTransformation.this.getAuthorityCode(null);
}
/**
* Gets the provider-supplied remarks.
*/
public String getRemarks() throws RemoteException {
return CoordinateTransformation.this.getRemarks().toString();
}
/**
* Human readable description of domain in source coordinate system.
*/
public String getAreaOfUse() throws RemoteException {
throw new UnsupportedOperationException("Area of use not yet implemented");
}
/**
* Semantic type of transform.
*/
public CT_TransformType getTransformType() throws RemoteException {
return adapters.export(CoordinateTransformation.this.getTransformType());
}
/**
* Source coordinate system.
*/
public CS_CoordinateSystem getSourceCS() throws RemoteException {
return adapters.export(CoordinateTransformation.this.getSourceCS());
}
/**
* Target coordinate system.
*/
public CS_CoordinateSystem getTargetCS() throws RemoteException {
return adapters.export(CoordinateTransformation.this.getTargetCS());
}
/**
* Gets math transform.
*/
public CT_MathTransform getMathTransform() throws RemoteException {
return adapters.export(CoordinateTransformation.this.getMathTransform());
}
}
}