001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.referencing.operation;
021:
022: // J2SE dependencies
023: import java.util.Map;
024:
025: // OpenGIS dependencies
026: import org.opengis.parameter.ParameterDescriptorGroup;
027: import org.opengis.parameter.ParameterValueGroup;
028: import org.opengis.referencing.crs.CoordinateReferenceSystem;
029: import org.opengis.referencing.operation.CoordinateOperation;
030: import org.opengis.referencing.operation.OperationMethod;
031: import org.opengis.referencing.operation.Operation;
032: import org.opengis.referencing.operation.Transformation;
033: import org.opengis.referencing.operation.Conversion;
034: import org.opengis.referencing.operation.Projection;
035: import org.opengis.referencing.operation.PlanarProjection;
036: import org.opengis.referencing.operation.CylindricalProjection;
037: import org.opengis.referencing.operation.ConicProjection;
038: import org.opengis.referencing.operation.MathTransform;
039:
040: // Geotools dependencies
041: import org.geotools.referencing.wkt.Formatter;
042: import org.geotools.referencing.AbstractIdentifiedObject;
043: import org.geotools.referencing.operation.transform.AbstractMathTransform;
044: import org.geotools.referencing.operation.transform.ConcatenatedTransform;
045: import org.geotools.util.UnsupportedImplementationException;
046:
047: /**
048: * A parameterized mathematical operation on coordinates that transforms or converts
049: * coordinates to another coordinate reference system. This coordinate operation thus
050: * uses an operation method, usually with associated parameter values.
051: *
052: * <P>In the Geotools implementation, the {@linkplain #getParameterValues parameter values}
053: * are inferred from the {@linkplain #transform transform}. Other implementations may have
054: * to overrides the {@link #getParameterValues} method.</P>
055: *
056: * @since 2.1
057: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/DefaultOperation.java $
058: * @version $Id: DefaultOperation.java 24384 2007-02-14 00:23:05Z desruisseaux $
059: * @author Martin Desruisseaux
060: *
061: * @see DefaultOperationMethod
062: */
063: public class DefaultOperation extends DefaultSingleOperation implements
064: Operation {
065: /**
066: * Serial number for interoperability with different versions.
067: */
068: private static final long serialVersionUID = -8923365753849532179L;
069:
070: /**
071: * The operation method.
072: */
073: protected final OperationMethod method;
074:
075: /**
076: * Constructs a new operation with the same values than the specified defining
077: * conversion, together with the specified source and target CRS. This constructor
078: * is used by {@link ConversionImpl} only.
079: */
080: DefaultOperation(final Conversion definition,
081: final CoordinateReferenceSystem sourceCRS,
082: final CoordinateReferenceSystem targetCRS,
083: final MathTransform transform) {
084: super (definition, sourceCRS, targetCRS, transform);
085: method = definition.getMethod();
086: }
087:
088: /**
089: * Constructs an operation from a set of properties. The properties given in argument
090: * follow the same rules than for the {@link AbstractCoordinateOperation} constructor.
091: *
092: * @param properties Set of properties. Should contains at least <code>"name"</code>.
093: * @param sourceCRS The source CRS.
094: * @param targetCRS The target CRS.
095: * @param transform Transform from positions in the {@linkplain #getSourceCRS source CRS}
096: * to positions in the {@linkplain #getTargetCRS target CRS}.
097: * @param method The operation method.
098: */
099: public DefaultOperation(final Map properties,
100: final CoordinateReferenceSystem sourceCRS,
101: final CoordinateReferenceSystem targetCRS,
102: final MathTransform transform, final OperationMethod method) {
103: super (properties, sourceCRS, targetCRS, transform);
104: ensureNonNull("method", method);
105: DefaultOperationMethod.checkDimensions(method, transform);
106: this .method = method;
107: }
108:
109: /**
110: * Returns a coordinate operation of the specified class. This method may constructs instance of
111: * {@link Conversion} or {@link Transformation} among others.
112: *
113: * @param properties Set of properties. Should contains at least <code>"name"</code>.
114: * @param sourceCRS The source CRS.
115: * @param targetCRS The target CRS.
116: * @param transform Transform from positions in the {@linkplain #getSourceCRS source CRS}
117: * to positions in the {@linkplain #getTargetCRS target CRS}.
118: * @param method The operation method, or {@code null}.
119: * @param type The minimal type as <code>{@linkplain Conversion}.class</code>,
120: * <code>{@linkplain Projection}.class</code>, etc. This method may
121: * create an instance of a subclass of {@code type}.
122: *
123: * @see DefaultConversion#create
124: */
125: public static CoordinateOperation create(final Map properties,
126: final CoordinateReferenceSystem sourceCRS,
127: final CoordinateReferenceSystem targetCRS,
128: final MathTransform transform,
129: final OperationMethod method, Class type) {
130: if (method != null) {
131: if (method instanceof MathTransformProvider) {
132: final Class candidate = ((MathTransformProvider) method)
133: .getOperationType();
134: if (candidate != null) {
135: if (type == null
136: || type.isAssignableFrom(candidate)) {
137: type = candidate;
138: }
139: }
140: }
141: if (type != null) {
142: if (Transformation.class.isAssignableFrom(type)) {
143: return new DefaultTransformation(properties,
144: sourceCRS, targetCRS, transform, method);
145: }
146: if (ConicProjection.class.isAssignableFrom(type)) {
147: return new DefaultConicProjection(properties,
148: sourceCRS, targetCRS, transform, method);
149: }
150: if (CylindricalProjection.class.isAssignableFrom(type)) {
151: return new DefaultCylindricalProjection(properties,
152: sourceCRS, targetCRS, transform, method);
153: }
154: if (PlanarProjection.class.isAssignableFrom(type)) {
155: return new DefaultPlanarProjection(properties,
156: sourceCRS, targetCRS, transform, method);
157: }
158: if (Projection.class.isAssignableFrom(type)) {
159: return new DefaultProjection(properties, sourceCRS,
160: targetCRS, transform, method);
161: }
162: if (Conversion.class.isAssignableFrom(type)) {
163: return new DefaultConversion(properties, sourceCRS,
164: targetCRS, transform, method);
165: }
166: }
167: return new DefaultOperation(properties, sourceCRS,
168: targetCRS, transform, method);
169: }
170: return new DefaultSingleOperation(properties, sourceCRS,
171: targetCRS, transform);
172: }
173:
174: /**
175: * Returns the operation method.
176: */
177: public OperationMethod getMethod() {
178: return method;
179: }
180:
181: /**
182: * Returns the parameter values. The default implementation infer the parameter
183: * values from the {@link #transform transform}, if possible.
184: *
185: * @throws UnsupportedOperationException if the parameters values can't be determined
186: * for current math transform implementation.
187: *
188: * @see DefaultMathTransformFactory#createParameterizedTransform
189: * @see org.geotools.referencing.operation.transform.AbstractMathTransform#getParameterValues
190: */
191: public ParameterValueGroup getParameterValues()
192: throws UnsupportedOperationException {
193: return getParameterValues(transform, method.getParameters(),
194: true);
195: }
196:
197: /**
198: * Returns the parameter values for the math transform that use the specified descriptor.
199: *
200: * @param mt The math transform for which parameters are desired.
201: * @param descriptor The descriptor to search for.
202: * @param required {@code true} if an exception must be thrown if parameters are unknow.
203: * @return The parameter values, or null.
204: * @throws UnsupportedImplementationException if the math transform implementation do not
205: * provide information about parameters.
206: */
207: private static ParameterValueGroup getParameterValues(
208: final MathTransform mt,
209: final ParameterDescriptorGroup descriptor, boolean required) {
210: if (mt instanceof ConcatenatedTransform) {
211: final ConcatenatedTransform ct = (ConcatenatedTransform) mt;
212: final ParameterValueGroup param1 = getParameterValues(
213: ct.transform1, descriptor, false);
214: final ParameterValueGroup param2 = getParameterValues(
215: ct.transform2, descriptor, false);
216: if (param1 == null && param2 != null)
217: return param2;
218: if (param2 == null && param1 != null)
219: return param1;
220: required = true;
221: }
222: if (mt instanceof AbstractMathTransform) {
223: final ParameterValueGroup param = ((AbstractMathTransform) mt)
224: .getParameterValues();
225: if (param != null) {
226: return param;
227: }
228: }
229: if (required) {
230: throw new UnsupportedImplementationException(mt.getClass());
231: }
232: return null;
233: }
234:
235: /**
236: * Compare this operation method with the specified object for equality.
237: * If {@code compareMetadata} is {@code true}, then all available properties
238: * are compared including {@linkplain DefaultOperationMethod#getFormula formula}.
239: *
240: * @param object The object to compare to {@code this}.
241: * @param compareMetadata {@code true} for performing a strict comparaison, or
242: * {@code false} for comparing only properties relevant to transformations.
243: * @return {@code true} if both objects are equal.
244: */
245: public boolean equals(final AbstractIdentifiedObject object,
246: final boolean compareMetadata) {
247: if (super .equals(object, compareMetadata)) {
248: final DefaultOperation that = (DefaultOperation) object;
249: if (compareMetadata) {
250: return equals(this .method, that.method, compareMetadata);
251: }
252: /*
253: * We consider the operation method as metadata. We could argue that OperationMethod's
254: * 'sourceDimensions' and 'targetDimensions' are not metadata, but their values should
255: * be identical to the 'sourceCRS' and 'targetCRS' dimensions, already checked by the
256: * superclass. We could also argue that 'OperationMethod.parameters' are not metadata,
257: * but their values should have been taken in account for the MathTransform creation,
258: * which was compared by the superclass.
259: *
260: * Comparing the MathTransforms instead of parameters avoid the problem of implicit
261: * parameters. For example in a ProjectedCRS, the "semiMajor" and "semiMinor" axis
262: * lengths are sometime provided as explicit parameters, and sometime inferred from
263: * the geodetic datum. The two cases would be different set of parameters from the
264: * OperationMethod's point of view, but still result in the creation of identical
265: * MathTransform.
266: *
267: * An other rational for treating OperationMethod as metadata is that Geotools
268: * MathTransformProvider extends DefaultOperationMethod. Consequently there is
269: * a wide range of subclasses, which make the comparaisons more difficult. For
270: * example Mercator1SP.Provider and Mercator2SP.Provider are two different ways
271: * to describe the same projection. The SQL-backed EPSG factory uses yet an
272: * other implementation.
273: *
274: * As a safety, we still compare the name. But I'm not completly sure that it is
275: * necessary.
276: */
277: return nameMatches(this .method, that.method);
278: }
279: return false;
280: }
281:
282: /**
283: * Returns a hash code value for this operation method.
284: */
285: public int hashCode() {
286: return super .hashCode() ^ method.hashCode();
287: }
288:
289: /**
290: * {@inheritDoc}
291: */
292: protected String formatWKT(final Formatter formatter) {
293: final String name = super .formatWKT(formatter);
294: append(formatter, method, "METHOD");
295: return name;
296: }
297: }
|