source: osm/applications/editors/josm/plugins/opendata/includes/org/geotools/referencing/operation/AbstractCoordinateOperation.java@ 28000

Last change on this file since 28000 was 28000, checked in by donvip, 12 years ago

Import new "opendata" JOSM plugin

File size: 18.3 KB
Line 
1/*
2 * GeoTools - The Open Source Java GIS Toolkit
3 * http://geotools.org
4 *
5 * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation;
10 * version 2.1 of the License.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * This package contains documentation from OpenGIS specifications.
18 * OpenGIS consortium's work is fully acknowledged here.
19 */
20package org.geotools.referencing.operation;
21
22import java.util.Collection;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.Map;
26
27import org.geotools.referencing.AbstractIdentifiedObject;
28import org.geotools.referencing.crs.AbstractDerivedCRS;
29import org.geotools.resources.i18n.ErrorKeys;
30import org.geotools.resources.i18n.Errors;
31import org.geotools.util.Utilities;
32import org.opengis.metadata.extent.Extent;
33import org.opengis.metadata.quality.PositionalAccuracy;
34import org.opengis.referencing.crs.CoordinateReferenceSystem;
35import org.opengis.referencing.operation.ConicProjection;
36import org.opengis.referencing.operation.Conversion;
37import org.opengis.referencing.operation.CoordinateOperation;
38import org.opengis.referencing.operation.CylindricalProjection;
39import org.opengis.referencing.operation.MathTransform;
40import org.opengis.referencing.operation.Operation;
41import org.opengis.referencing.operation.PlanarProjection;
42import org.opengis.referencing.operation.Projection;
43import org.opengis.referencing.operation.Transformation;
44import org.opengis.util.InternationalString;
45
46
47/**
48 * Establishes an association between a source and a target coordinate reference system,
49 * and provides a {@linkplain MathTransform transform} for transforming coordinates in
50 * the source CRS to coordinates in the target CRS. Many but not all coordinate operations (from
51 * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>A</VAR> to
52 * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>B</VAR>)
53 * also uniquely define the inverse operation (from
54 * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>B</VAR> to
55 * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>A</VAR>).
56 * In some cases, the operation method algorithm for the inverse operation is the same
57 * as for the forward algorithm, but the signs of some operation parameter values must
58 * be reversed. In other cases, different algorithms are required for the forward and
59 * inverse operations, but the same operation parameter values are used. If (some)
60 * entirely different parameter values are needed, a different coordinate operation
61 * shall be defined.
62 * <p>
63 * This class is conceptually <cite>abstract</cite>, even if it is technically possible to
64 * instantiate it. Typical applications should create instances of the most specific subclass with
65 * {@code Default} prefix instead. An exception to this rule may occurs when it is not possible to
66 * identify the exact type.
67 *
68 * @since 2.1
69 *
70 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/referencing/src/main/java/org/geotools/referencing/operation/AbstractCoordinateOperation.java $
71 * @version $Id: AbstractCoordinateOperation.java 37299 2011-05-25 05:21:24Z mbedward $
72 * @author Martin Desruisseaux (IRD)
73 */
74public class AbstractCoordinateOperation extends AbstractIdentifiedObject
75 implements CoordinateOperation
76{
77 /**
78 * Serial number for interoperability with different versions.
79 */
80 private static final long serialVersionUID = 1237358357729193885L;
81
82 /**
83 * An empty array of positional accuracy. This is usefull for fetching accuracies as an array,
84 * using the following idiom:
85 * <blockquote><pre>
86 * {@linkplain #getPositionalAccuracy()}.toArray(EMPTY_ACCURACY_ARRAY);
87 * </pre></blockquote>
88 */
89 public static final PositionalAccuracy[] EMPTY_ACCURACY_ARRAY = new PositionalAccuracy[0];
90
91 /**
92 * List of localizable properties. To be given to {@link AbstractIdentifiedObject} constructor.
93 */
94 private static final String[] LOCALIZABLES = {SCOPE_KEY};
95
96 /**
97 * The source CRS, or {@code null} if not available.
98 */
99 protected final CoordinateReferenceSystem sourceCRS;
100
101 /**
102 * The target CRS, or {@code null} if not available.
103 */
104 protected final CoordinateReferenceSystem targetCRS;
105
106 /**
107 * Version of the coordinate transformation
108 * (i.e., instantiation due to the stochastic nature of the parameters).
109 */
110 final String operationVersion;
111
112 /**
113 * Estimate(s) of the impact of this operation on point accuracy, or {@code null}
114 * if none.
115 */
116 private final Collection<PositionalAccuracy> coordinateOperationAccuracy;
117
118 /**
119 * Area in which this operation is valid, or {@code null} if not available.
120 */
121 protected final Extent domainOfValidity;
122
123 /**
124 * Description of domain of usage, or limitations of usage, for which this operation is valid.
125 */
126 private final InternationalString scope;
127
128 /**
129 * Transform from positions in the {@linkplain #getSourceCRS source coordinate reference system}
130 * to positions in the {@linkplain #getTargetCRS target coordinate reference system}.
131 */
132 protected final MathTransform transform;
133
134 /**
135 * Constructs a new coordinate operation with the same values than the specified
136 * defining conversion, together with the specified source and target CRS. This
137 * constructor is used by {@link DefaultConversion} only.
138 */
139 AbstractCoordinateOperation(final Conversion definition,
140 final CoordinateReferenceSystem sourceCRS,
141 final CoordinateReferenceSystem targetCRS,
142 final MathTransform transform)
143 {
144 super(definition);
145 this.sourceCRS = sourceCRS;
146 this.targetCRS = targetCRS;
147 this.operationVersion = definition.getOperationVersion();
148 this.coordinateOperationAccuracy = definition.getCoordinateOperationAccuracy();
149 this.domainOfValidity = definition.getDomainOfValidity();
150 this.scope = definition.getScope();
151 this.transform = transform;
152 }
153
154 /**
155 * Constructs a coordinate operation from a set of properties.
156 * The properties given in argument follow the same rules than for the
157 * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
158 * Additionally, the following properties are understood by this construtor:
159 * <p>
160 * <table border='1'>
161 * <tr bgcolor="#CCCCFF" class="TableHeadingColor">
162 * <th nowrap>Property name</th>
163 * <th nowrap>Value type</th>
164 * <th nowrap>Value given to</th>
165 * </tr>
166 * <tr>
167 * <td nowrap>&nbsp;{@value org.opengis.referencing.operation.CoordinateOperation#OPERATION_VERSION_KEY}&nbsp;</td>
168 * <td nowrap>&nbsp;{@link String}&nbsp;</td>
169 * <td nowrap>&nbsp;{@link #getOperationVersion}</td>
170 * </tr>
171 * <tr>
172 * <td nowrap>&nbsp;{@value org.opengis.referencing.operation.CoordinateOperation#COORDINATE_OPERATION_ACCURACY_KEY}&nbsp;</td>
173 * <td nowrap>&nbsp;<code>{@linkplain PositionalAccuracy}[]</code>&nbsp;</td>
174 * <td nowrap>&nbsp;{@link #getCoordinateOperationAccuracy}</td>
175 * </tr>
176 * <tr>
177 * <td nowrap>&nbsp;{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}&nbsp;</td>
178 * <td nowrap>&nbsp;{@link Extent}&nbsp;</td>
179 * <td nowrap>&nbsp;{@link #getDomainOfValidity}</td>
180 * </tr>
181 * <tr>
182 * <td nowrap>&nbsp;{@value org.opengis.referencing.operation.CoordinateOperation#SCOPE_KEY}&nbsp;</td>
183 * <td nowrap>&nbsp;{@link String} or {@link InternationalString}&nbsp;</td>
184 * <td nowrap>&nbsp;{@link #getScope}</td>
185 * </tr>
186 * </table>
187 *
188 * @param properties Set of properties. Should contains at least {@code "name"}.
189 * @param sourceCRS The source CRS.
190 * @param targetCRS The target CRS.
191 * @param transform Transform from positions in the {@linkplain #getSourceCRS source CRS}
192 * to positions in the {@linkplain #getTargetCRS target CRS}.
193 */
194 public AbstractCoordinateOperation(final Map<String,?> properties,
195 final CoordinateReferenceSystem sourceCRS,
196 final CoordinateReferenceSystem targetCRS,
197 final MathTransform transform)
198 {
199 this(properties, new HashMap<String,Object>(), sourceCRS, targetCRS, transform);
200 }
201
202 /**
203 * Work around for RFE #4093999 in Sun's bug database
204 * ("Relax constraint on placement of this()/super() call in constructors").
205 */
206 private AbstractCoordinateOperation(final Map<String,?> properties,
207 final Map<String,Object> subProperties,
208 final CoordinateReferenceSystem sourceCRS,
209 final CoordinateReferenceSystem targetCRS,
210 final MathTransform transform)
211 {
212 super(properties, subProperties, LOCALIZABLES);
213 PositionalAccuracy[] positionalAccuracy;
214 domainOfValidity = (Extent) subProperties.get(DOMAIN_OF_VALIDITY_KEY);
215 scope = (InternationalString) subProperties.get(SCOPE_KEY);
216 operationVersion = (String) subProperties.get(OPERATION_VERSION_KEY);
217 positionalAccuracy = (PositionalAccuracy[]) subProperties.get(COORDINATE_OPERATION_ACCURACY_KEY);
218 if (positionalAccuracy==null || positionalAccuracy.length==0) {
219 positionalAccuracy = null;
220 } else {
221 positionalAccuracy = positionalAccuracy.clone();
222 for (int i=0; i<positionalAccuracy.length; i++) {
223 ensureNonNull(COORDINATE_OPERATION_ACCURACY_KEY, positionalAccuracy, i);
224 }
225 }
226 this.coordinateOperationAccuracy = asSet(positionalAccuracy);
227 this.sourceCRS = sourceCRS;
228 this.targetCRS = targetCRS;
229 this.transform = transform;
230 validate();
231 }
232
233 /**
234 * Checks the validity of this operation. This method is invoked by the constructor after
235 * every fields have been assigned. It can be overriden by subclasses if different rules
236 * should be applied.
237 * <p>
238 * {@link DefaultConversion} overrides this method in order to allow null values, providing
239 * that all of {@code transform}, {@code sourceCRS} and {@code targetCRS} are null together.
240 * Note that null values are not allowed for transformations, so {@link DefaultTransformation}
241 * does not override this method.
242 *
243 * @throws IllegalArgumentException if at least one of {@code transform}, {@code sourceCRS}
244 * or {@code targetCRS} is invalid. We throw this kind of exception rather than
245 * {@link IllegalStateException} because this method is invoked by the constructor
246 * for checking argument validity.
247 */
248 void validate() throws IllegalArgumentException {
249 ensureNonNull ("sourceCRS", transform);
250 ensureNonNull ("targetCRS", transform);
251 ensureNonNull ("transform", transform);
252 checkDimension("sourceCRS", sourceCRS, transform.getSourceDimensions());
253 checkDimension("targetCRS", targetCRS, transform.getTargetDimensions());
254 }
255
256 /**
257 * Checks if a reference coordinate system has the expected number of dimensions.
258 *
259 * @param name The argument name.
260 * @param crs The coordinate reference system to check.
261 * @param expected The expected number of dimensions.
262 */
263 private static void checkDimension(final String name,
264 final CoordinateReferenceSystem crs,
265 final int expected)
266 {
267 final int actual = crs.getCoordinateSystem().getDimension();
268 if (actual != expected) {
269 throw new IllegalArgumentException(Errors.format(
270 ErrorKeys.MISMATCHED_DIMENSION_$3, name, actual, expected));
271 }
272 }
273
274 /**
275 * Returns the source CRS.
276 */
277 public CoordinateReferenceSystem getSourceCRS() {
278 return sourceCRS;
279 }
280
281 /**
282 * Returns the target CRS.
283 */
284 public CoordinateReferenceSystem getTargetCRS() {
285 return targetCRS;
286 }
287
288 /**
289 * Version of the coordinate transformation (i.e., instantiation due to the stochastic
290 * nature of the parameters). Mandatory when describing a transformation, and should not
291 * be supplied for a conversion.
292 *
293 * @return The coordinate operation version, or {@code null} in none.
294 */
295 public String getOperationVersion() {
296 return operationVersion;
297 }
298
299 /**
300 * Estimate(s) of the impact of this operation on point accuracy. Gives
301 * position error estimates for target coordinates of this coordinate
302 * operation, assuming no errors in source coordinates.
303 *
304 * @return The position error estimates, or an empty collection if not available.
305 *
306 * @see #getAccuracy()
307 *
308 * @since 2.4
309 */
310 public Collection<PositionalAccuracy> getCoordinateOperationAccuracy() {
311 if (coordinateOperationAccuracy == null) {
312 return Collections.emptySet();
313 }
314 return coordinateOperationAccuracy;
315 }
316
317 /**
318 * Area or region or timeframe in which this coordinate operation is valid.
319 * Returns {@code null} if not available.
320 *
321 * @since 2.4
322 */
323 public Extent getDomainOfValidity() {
324 return domainOfValidity;
325 }
326
327 /**
328 * Description of domain of usage, or limitations of usage, for which this operation is valid.
329 */
330 public InternationalString getScope() {
331 return scope;
332 }
333
334 /**
335 * Gets the math transform. The math transform will transform positions in the
336 * {@linkplain #getSourceCRS source coordinate reference system} into positions
337 * in the {@linkplain #getTargetCRS target coordinate reference system}.
338 */
339 public MathTransform getMathTransform() {
340 return transform;
341 }
342
343 /**
344 * Returns the most specific GeoAPI interface implemented by the specified operation.
345 *
346 * @param object A coordinate operation.
347 * @return The most specific GeoAPI interface
348 * (e.g. <code>{@linkplain Transformation}.class</code>).
349 */
350 public static Class<? extends CoordinateOperation> getType(final CoordinateOperation object) {
351 if (object instanceof Transformation) return Transformation.class;
352 if (object instanceof ConicProjection) return ConicProjection.class;
353 if (object instanceof CylindricalProjection) return CylindricalProjection.class;
354 if (object instanceof PlanarProjection) return PlanarProjection.class;
355 if (object instanceof Projection) return Projection.class;
356 if (object instanceof Conversion) return Conversion.class;
357 if (object instanceof Operation) return Operation.class;
358 return CoordinateOperation.class;
359 }
360
361 /**
362 * Compares this coordinate operation with the specified object for equality.
363 * If {@code compareMetadata} is {@code true}, then all available properties are
364 * compared including {@linkplain #getDomainOfValidity domain of validity} and
365 * {@linkplain #getScope scope}.
366 *
367 * @param object The object to compare to {@code this}.
368 * @param compareMetadata {@code true} for performing a strict comparaison, or
369 * {@code false} for comparing only properties relevant to transformations.
370 * @return {@code true} if both objects are equal.
371 */
372 @Override
373 public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
374 if (object == this) {
375 return true; // Slight optimization.
376 }
377 if (super.equals(object, compareMetadata)) {
378 final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
379 if (equals(this.sourceCRS, that.sourceCRS, compareMetadata) &&
380 Utilities.equals(this.transform, that.transform))
381 // See comment in DefaultOperation.equals(...) about why we compare MathTransform.
382 {
383 if (compareMetadata) {
384 if (!Utilities.equals(this.domainOfValidity, that.domainOfValidity) ||
385 !Utilities.equals(this.scope, that.scope) ||
386 !Utilities.equals(this.coordinateOperationAccuracy, that.coordinateOperationAccuracy))
387 {
388 return false;
389 }
390 }
391 /*
392 * Avoid never-ending recursivity: AbstractDerivedCRS has a 'conversionFromBase'
393 * field that is set to this AbstractCoordinateOperation.
394 */
395 final Boolean comparing = AbstractDerivedCRS._COMPARING.get();
396 if (comparing!=null && comparing.booleanValue()) {
397 return true;
398 }
399 try {
400 AbstractDerivedCRS._COMPARING.set(Boolean.TRUE);
401 return equals(this.targetCRS, that.targetCRS, compareMetadata);
402 } finally {
403 AbstractDerivedCRS._COMPARING.set(Boolean.FALSE);
404 }
405 }
406 }
407 return false;
408 }
409
410 /**
411 * Returns a hash code value for this coordinate operation.
412 */
413 @Override
414 public int hashCode() {
415 int code = (int)serialVersionUID;
416 if (sourceCRS != null) code ^= sourceCRS.hashCode();
417 if (targetCRS != null) code ^= targetCRS.hashCode();
418 if (transform != null) code ^= transform.hashCode();
419 return code;
420 }
421}
Note: See TracBrowser for help on using the repository browser.