/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-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. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.referencing.operation; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.HashMap; import java.util.Map; import javax.measure.unit.SI; import javax.measure.unit.Unit; import org.opengis.metadata.extent.Extent; import org.opengis.metadata.quality.Result; import org.opengis.metadata.quality.QuantitativeResult; import org.opengis.metadata.quality.PositionalAccuracy; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.ConcatenatedOperation; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.Transformation; import org.opengis.referencing.operation.Operation; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.PlanarProjection; import org.opengis.referencing.operation.CylindricalProjection; import org.opengis.referencing.operation.ConicProjection; import org.opengis.referencing.IdentifiedObject; import org.opengis.util.InternationalString; import org.opengis.util.Record; import org.geotools.metadata.iso.quality.PositionalAccuracyImpl; import org.geotools.referencing.AbstractIdentifiedObject; import org.geotools.referencing.crs.AbstractDerivedCRS; import org.geotools.referencing.wkt.Formatter; import org.geotools.util.Utilities; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Establishes an association between a source and a target coordinate reference system, * and provides a {@linkplain MathTransform transform} for transforming coordinates in * the source CRS to coordinates in the target CRS. Many but not all coordinate operations (from * {@linkplain CoordinateReferenceSystem coordinate reference system} A to * {@linkplain CoordinateReferenceSystem coordinate reference system} B) * also uniquely define the inverse operation (from * {@linkplain CoordinateReferenceSystem coordinate reference system} B to * {@linkplain CoordinateReferenceSystem coordinate reference system} A). * In some cases, the operation method algorithm for the inverse operation is the same * as for the forward algorithm, but the signs of some operation parameter values must * be reversed. In other cases, different algorithms are required for the forward and * inverse operations, but the same operation parameter values are used. If (some) * entirely different parameter values are needed, a different coordinate operation * shall be defined. *
* This class is conceptually abstract, even if it is technically possible to * instantiate it. Typical applications should create instances of the most specific subclass with * {@code Default} prefix instead. An exception to this rule may occurs when it is not possible to * identify the exact type. * * @since 2.1 * * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) */ public class AbstractCoordinateOperation extends AbstractIdentifiedObject implements CoordinateOperation { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = 1237358357729193885L; /** * An empty array of positional accuracy. This is usefull for fetching accuracies as an array, * using the following idiom: *
*/ public static final PositionalAccuracy[] EMPTY_ACCURACY_ARRAY = new PositionalAccuracy[0]; /** * List of localizable properties. To be given to {@link AbstractIdentifiedObject} constructor. */ private static final String[] LOCALIZABLES = {SCOPE_KEY}; /** * The source CRS, or {@code null} if not available. */ protected final CoordinateReferenceSystem sourceCRS; /** * The target CRS, or {@code null} if not available. */ protected final CoordinateReferenceSystem targetCRS; /** * Version of the coordinate transformation * (i.e., instantiation due to the stochastic nature of the parameters). */ final String operationVersion; /** * Estimate(s) of the impact of this operation on point accuracy, or {@code null} * if none. */ private final Collection* {@linkplain #getPositionalAccuracy()}.toArray(EMPTY_ACCURACY_ARRAY); *
*
Property name | *Value type | *Value given to | *
---|---|---|
{@value org.opengis.referencing.operation.CoordinateOperation#OPERATION_VERSION_KEY} | *{@link String} | *{@link #getOperationVersion} | *
{@value org.opengis.referencing.operation.CoordinateOperation#COORDINATE_OPERATION_ACCURACY_KEY} | * {@linkplain PositionalAccuracy}[] |
* {@link #getCoordinateOperationAccuracy} | *
{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY} | *{@link Extent} | *{@link #getDomainOfValidity} | *
{@value org.opengis.referencing.operation.CoordinateOperation#SCOPE_KEY} | *{@link String} or {@link InternationalString} | *{@link #getScope} | *
{@linkplain #getAccuracy(CoordinateOperation) getAccuracy}(this)
. Subclasses
* should override this method if they can provide a more accurate algorithm.
*
* @return The accuracy in meters, or NaN if unknown.
*
* @since 2.2
*/
public double getAccuracy() {
return getAccuracy0(this);
}
/**
* Convenience method returning the accuracy in meters for the specified operation. This method
* try each of the following procedures and returns the first successful one:
*
* {@linkplain Transformation}.class
).
*/
public static Class extends CoordinateOperation> getType(final CoordinateOperation object) {
if (object instanceof Transformation) return Transformation.class;
if (object instanceof ConicProjection) return ConicProjection.class;
if (object instanceof CylindricalProjection) return CylindricalProjection.class;
if (object instanceof PlanarProjection) return PlanarProjection.class;
if (object instanceof Projection) return Projection.class;
if (object instanceof Conversion) return Conversion.class;
if (object instanceof Operation) return Operation.class;
return CoordinateOperation.class;
}
/**
* Compares this coordinate operation with the specified object for equality.
* If {@code compareMetadata} is {@code true}, then all available properties are
* compared including {@linkplain #getDomainOfValidity domain of validity} and
* {@linkplain #getScope scope}.
*
* @param object The object to compare to {@code this}.
* @param compareMetadata {@code true} for performing a strict comparaison, or
* {@code false} for comparing only properties relevant to transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
if (object == this) {
return true; // Slight optimization.
}
if (super.equals(object, compareMetadata)) {
final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
if (equals(this.sourceCRS, that.sourceCRS, compareMetadata) &&
Utilities.equals(this.transform, that.transform))
// See comment in DefaultOperation.equals(...) about why we compare MathTransform.
{
if (compareMetadata) {
if (!Utilities.equals(this.domainOfValidity, that.domainOfValidity) ||
!Utilities.equals(this.scope, that.scope) ||
!Utilities.equals(this.coordinateOperationAccuracy, that.coordinateOperationAccuracy))
{
return false;
}
}
/*
* Avoid never-ending recursivity: AbstractDerivedCRS has a 'conversionFromBase'
* field that is set to this AbstractCoordinateOperation.
*/
final Boolean comparing = AbstractDerivedCRS._COMPARING.get();
if (comparing!=null && comparing.booleanValue()) {
return true;
}
try {
AbstractDerivedCRS._COMPARING.set(Boolean.TRUE);
return equals(this.targetCRS, that.targetCRS, compareMetadata);
} finally {
AbstractDerivedCRS._COMPARING.set(Boolean.FALSE);
}
}
}
return false;
}
/**
* Returns a hash code value for this coordinate operation.
*/
@Override
public int hashCode() {
int code = (int)serialVersionUID;
if (sourceCRS != null) code ^= sourceCRS.hashCode();
if (targetCRS != null) code ^= targetCRS.hashCode();
if (transform != null) code ^= transform.hashCode();
return code;
}
/**
* Format this operation as a pseudo-WKT format. No WKT format were defined for coordinate
* operation at the time this method was written. This method may change in any future version
* until a standard format is found.
*
* @param formatter The formatter to use.
* @return The WKT element name.
*/
@Override
protected String formatWKT(final Formatter formatter) {
append(formatter, sourceCRS, "SOURCE");
append(formatter, targetCRS, "TARGET");
return super.formatWKT(formatter);
}
/**
* Append the identifier for the specified object name (possibly {@code null}) to the specified
* formatter.
*
* @param formatter The formatter where to append the object name.
* @param object The object to append, or {@code null} if none.
* @param type The label to put in front of the object name.
*/
@SuppressWarnings("serial")
static void append(final Formatter formatter, final IdentifiedObject object, final String type) {
if (object != null) {
final Map