org.geotoolkit.geometry.TransformedDirectPosition Maven / Gradle / Ivy
Show all versions of geotk-referencing Show documentation
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2005-2011, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2011, 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.geometry;
import org.opengis.util.FactoryException;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.geotoolkit.factory.Hints;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.factory.FactoryRegistryException;
import org.geotoolkit.referencing.CRS;
import org.geotoolkit.referencing.crs.DefaultGeographicCRS;
import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;
/**
* A direct position capable to {@linkplain #transform transform} a point between an arbitrary CRS
* and {@linkplain #getCoordinateReferenceSystem its own CRS}. This class caches the last transform
* used in order to improve the performances when the {@linkplain CoordinateOperation#getSourceCRS
* source} and {@linkplain CoordinateOperation#getTargetCRS target} CRS don't change often. Using
* this class is faster than invoking {@linkplain CoordinateOperationFactory#createOperation
* CoordinateOperationFactory.createOperation}(sourceCRS, targetCRS)
for every points.
*
*
* Note 1:
* This class is advantageous on a performance point of view only if the same instance of
* {@code TransformedDirectPosition} is used for transforming many points between arbitrary
* CRS and this {@linkplain #getCoordinateReferenceSystem position CRS}.
*
* Note 2:
* This convenience class is useful when the source and target CRS are not likely to
* change often. If you are sure that the source and target CRS will not change at all
* for a given set of positions, then using {@link CoordinateOperation} directly gives better
* performances. This is because {@code TransformedDirectPosition} checks if the CRS changed
* before every transformations, which may be costly.
*
* Note 3:
* This class is called Transformed Direct Position because it is more commonly
* used for transforming many points from arbitrary CRS to a common CRS (using the
* {@link #transform(DirectPosition)} method) than the other way around.
*
*
* This class usually don't appears in a public API. It is more typicaly used as a helper private
* field in some more complex class. For example suppose that {@code MyClass} needs to perform its
* internal working in some particular CRS, but we want robust API that adjusts itself to whatever
* CRS the client happen to use. {@code MyClass} could be written as below:
*
* {@preformat java
* public class MyClass {
* private static final CoordinateReferenceSystem PUBLIC_CRS = ...
* private static final CoordinateReferenceSystem INTERNAL_CRS = ...
*
* private final TransformedDirectPosition myPosition =
* new TransformedDirectPosition(PUBLIC_CRS, INTERNAL_CRS, null);
*
* public void setPosition(DirectPosition position) throws TransformException {
* // The position CRS is usually PUBLIC_CRS, but code below will work even if it is not.
* myPosition.transform(position);
* }
*
* public DirectPosition getPosition() throws TransformException {
* return myPosition.inverseTransform(PUBLIC_CRS);
* }
* }
* }
*
* @author Martin Desruisseaux (IRD)
* @version 3.00
*
* @since 2.2
* @level advanced
* @module
*/
public class TransformedDirectPosition extends GeneralDirectPosition {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -3988283183934950437L;
/**
* The factory to use for creating new coordinate operation.
*/
private final CoordinateOperationFactory factory;
/**
* The default source CRS. To be used only when the user invoked {@link #transform} with
* a position without associated {@link CoordinateReferenceSystem}. May be {@code null}
* if the default CRS is assumed equal to {@linkplain #getCoordinateReferenceSystem this
* position CRS}.
*/
private final CoordinateReferenceSystem defaultCRS;
/**
* The last source CRS used, or {@code null}. The {@code targetCRS} is the
* {@linkplain #getCoordinateReferenceSystem CRS associated with this position}.
*/
private transient CoordinateReferenceSystem sourceCRS;
/**
* The forward and inverse transforms. Will be created only when first needed.
*/
private transient MathTransform forward, inverse;
/**
* Creates a new direct position initialized with the
* {@linkplain DefaultGeographicCRS#WGS84 WGS84} CRS.
*
* @since 2.3
*/
public TransformedDirectPosition() {
this(null, DefaultGeographicCRS.WGS84, null);
}
/**
* Creates a new position which will contains the result of coordinate transformations from
* {@code sourceCRS} to {@code targetCRS}. The {@linkplain #getCoordinateReferenceSystem CRS
* associated with this position} will be initially set to {@code targetCRS}.
*
* @param sourceCRS The default CRS to be used by the
* {@link #transform transform}(position)
method only
* when the user-supplied {@code position} has a null
* {@linkplain DirectPosition#getCoordinateReferenceSystem associated CRS}.
* This {@code sourceCRS} argument may be {@code null}, in which case it is assumed
* the same than {@code targetCRS}.
*
* @param targetCRS The {@linkplain #getCoordinateReferenceSystem CRS associated with this
* position}. Used for every {@linkplain #transform coordinate transformations} until
* the next call to {@link #setCoordinateReferenceSystem setCoordinateReferenceSystem}
* or {@link #setLocation(DirectPosition) setLocation}. This argument can not be null.
*
* @param hints The set of hints to use for fetching a {@link CoordinateOperationFactory},
* or {@code null} if none.
*
* @throws FactoryRegistryException if no {@linkplain CoordinateOperationFactory coordinate
* operation factory} can be found for the specified hints.
*
* @since 2.3
*/
public TransformedDirectPosition(final CoordinateReferenceSystem sourceCRS,
final CoordinateReferenceSystem targetCRS,
final Hints hints)
throws FactoryRegistryException
{
super(targetCRS);
ensureNonNull("targetCRS", targetCRS);
defaultCRS = CRS.equalsIgnoreMetadata(sourceCRS, targetCRS) ? null : sourceCRS;
factory = FactoryFinder.getCoordinateOperationFactory(hints);
}
/**
* Sets the coordinate reference system in which the coordinate is given.
* The given CRS will be used as:
*
*
* - the {@linkplain CoordinateOperation#getTargetCRS target CRS} for every call to
* {@link #transform(DirectPosition)}
* - the {@linkplain CoordinateOperation#getSourceCRS source CRS} for every call to
* {@link #inverseTransform(CoordinateReferenceSystem)}
*
*
* @param crs The new CRS for this direct position.
* @throws MismatchedDimensionException if the specified CRS doesn't have the expected
* number of dimensions.
*/
@Override
public void setCoordinateReferenceSystem(final CoordinateReferenceSystem crs)
throws MismatchedDimensionException
{
ensureNonNull("crs", crs);
super.setCoordinateReferenceSystem(crs);
forward = null;
inverse = null;
}
/**
* Sets the {@link #sourceCRS} field and create the associated {@link #forward} transform.
* This method do not create yet the {@link #inverse} transform, since it may not be needed.
*/
private void setSourceCRS(final CoordinateReferenceSystem crs) throws TransformException {
final CoordinateReferenceSystem targetCRS = getCoordinateReferenceSystem();
final CoordinateOperation operation;
try {
operation = factory.createOperation(crs, targetCRS);
} catch (FactoryException exception) {
throw new TransformException(exception.getLocalizedMessage(), exception);
}
/*
* Note: 'sourceCRS' must be set last, when we are sure that all other fields
* are set to their correct value. This is in order to keep this instance in
* a consistent state in case an exception is thrown.
*/
forward = operation.getMathTransform();
inverse = null;
sourceCRS = crs;
}
/**
* Transforms a given position and stores the result in this object.
*
*
* The {@linkplain CoordinateOperation#getSourceCRS source CRS} is the
* {@linkplain DirectPosition#getCoordinateReferenceSystem CRS associated with the given
* position}, or the {@code sourceCRS} argument given at
* {@linkplain #TransformedDirectPosition(CoordinateReferenceSystem,
* CoordinateReferenceSystem, Hints) construction time} if and only if
* the CRS associated with {@code position} is null.
*
* The {@linkplain CoordinateOperation#getTargetCRS target CRS} is the {@linkplain
* #getCoordinateReferenceSystem CRS associated with this position}. This is always the
* {@code targetCRS} argument given at {@linkplain
* #TransformedDirectPosition(CoordinateReferenceSystem, CoordinateReferenceSystem,
* Hints) construction time} or by the last call to {@link #setCoordinateReferenceSystem
* setCoordinateReferenceSystem}.
*
*
* @param position A position using an arbitrary CRS. This object will not be modified.
* @throws TransformException if a coordinate transformation was required and failed.
*/
public void transform(final DirectPosition position) throws TransformException {
CoordinateReferenceSystem userCRS = position.getCoordinateReferenceSystem();
if (userCRS == null) {
userCRS = defaultCRS;
if (userCRS == null) {
setLocation(position);
return;
}
}
/*
* A projection may be required. Checks if it is the same one than the one used
* last time this method has been invoked. If the specified position uses a new
* CRS, then gets the transformation and saves it in case the next call to this
* method would uses again the same transformation.
*/
if (forward == null || !CRS.equalsIgnoreMetadata(sourceCRS, userCRS)) {
setSourceCRS(userCRS);
}
if (forward.transform(position, this) != this) {
throw new AssertionError(forward); // Should never occurs.
}
}
/**
* Returns a new point with the same coordinates than this one, but transformed in the given
* CRS. This method never returns {@code this}, so the returned point usually doesn't need to
* be cloned.
*
* @param crs The CRS for the position to be returned.
* @return The same position than {@code this}, but transformed in the specified CRS.
* @throws TransformException if a coordinate transformation was required and failed.
*
* @since 2.3
*/
public DirectPosition inverseTransform(final CoordinateReferenceSystem crs)
throws TransformException
{
if (inverse == null || !CRS.equalsIgnoreMetadata(sourceCRS, crs)) {
ensureNonNull("crs", crs);
setSourceCRS(crs);
inverse = forward.inverse();
}
return inverse.transform(this, null);
}
/**
* Returns a new point with the same coordinates than this one, but transformed in the
* {@code sourceCRS} given at {@linkplain #TransformedDirectPosition(CoordinateReferenceSystem,
* CoordinateReferenceSystem, Hints) construction time}. This method never returns {@code this},
* so the returned point usually doesn't need to be cloned.
*
* @return The same position than {@code this}, but transformed in the source CRS.
* @throws TransformException if a coordinate transformation was required and failed.
*
* @since 2.3
*/
public DirectPosition inverseTransform() throws TransformException {
if (defaultCRS != null) {
return inverseTransform(defaultCRS);
} else {
return new GeneralDirectPosition(this);
}
}
}