source: osm/applications/editors/josm/plugins/opendata/includes/org/geotools/referencing/operation/DefaultOperationMethod.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: 16.2 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.Collections;
23import java.util.HashMap;
24import java.util.Map;
25
26import org.geotools.parameter.Parameters;
27import org.geotools.referencing.AbstractIdentifiedObject;
28import org.geotools.referencing.operation.transform.AbstractMathTransform;
29import org.geotools.referencing.operation.transform.ConcatenatedTransform;
30import org.geotools.referencing.operation.transform.PassThroughTransform;
31import org.geotools.resources.i18n.ErrorKeys;
32import org.geotools.resources.i18n.Errors;
33import org.geotools.resources.i18n.Vocabulary;
34import org.geotools.resources.i18n.VocabularyKeys;
35import org.geotools.util.Utilities;
36import org.opengis.geometry.MismatchedDimensionException;
37import org.opengis.parameter.ParameterDescriptorGroup;
38import org.opengis.referencing.operation.MathTransform;
39import org.opengis.referencing.operation.Matrix;
40import org.opengis.referencing.operation.Operation;
41import org.opengis.referencing.operation.OperationMethod;
42import org.opengis.referencing.operation.Projection;
43import org.opengis.util.InternationalString;
44
45
46/**
47 * Definition of an algorithm used to perform a coordinate operation. Most operation
48 * methods use a number of operation parameters, although some coordinate conversions
49 * use none. Each coordinate operation using the method assigns values to these parameters.
50 *
51 * @since 2.1
52 *
53 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/referencing/src/main/java/org/geotools/referencing/operation/DefaultOperationMethod.java $
54 * @version $Id: DefaultOperationMethod.java 37299 2011-05-25 05:21:24Z mbedward $
55 * @author Martin Desruisseaux (IRD)
56 *
57 * @see DefaultOperation
58 */
59public class DefaultOperationMethod extends AbstractIdentifiedObject implements OperationMethod {
60 /**
61 * Serial number for interoperability with different versions.
62 */
63 private static final long serialVersionUID = -98032729598205972L;
64
65 /**
66 * List of localizable properties. To be given to {@link AbstractIdentifiedObject} constructor.
67 */
68 private static final String[] LOCALIZABLES = {FORMULA_KEY};
69
70 /**
71 * Formula(s) or procedure used by this operation method. This may be a reference to a
72 * publication. Note that the operation method may not be analytic, in which case this
73 * attribute references or contains the procedure, not an analytic formula.
74 */
75 private final InternationalString formula;
76
77 /**
78 * Number of dimensions in the source CRS of this operation method.
79 */
80 protected final int sourceDimensions;
81
82 /**
83 * Number of dimensions in the target CRS of this operation method.
84 */
85 protected final int targetDimensions;
86
87 /**
88 * The set of parameters, or {@code null} if none.
89 */
90 private final ParameterDescriptorGroup parameters;
91
92 /**
93 * Convenience constructor that creates an operation method from a math transform.
94 * The information provided in the newly created object are approximative, and
95 * usually acceptable only as a fallback when no other information are available.
96 *
97 * @param transform The math transform to describe.
98 */
99 public DefaultOperationMethod(final MathTransform transform) {
100 this(getProperties(transform),
101 transform.getSourceDimensions(),
102 transform.getTargetDimensions(),
103 getDescriptor(transform));
104 }
105
106 /**
107 * Work around for RFE #4093999 in Sun's bug database
108 * ("Relax constraint on placement of this()/super() call in constructors").
109 */
110 private static Map<String,?> getProperties(final MathTransform transform) {
111 ensureNonNull("transform", transform);
112 final Map<String,?> properties;
113 if (transform instanceof AbstractMathTransform) {
114 final AbstractMathTransform mt = (AbstractMathTransform) transform;
115 properties = getProperties(mt.getParameterDescriptors(), null);
116 } else {
117 properties = Collections.singletonMap(NAME_KEY, Vocabulary.format(VocabularyKeys.UNKNOW));
118 }
119 return properties;
120 }
121
122 /**
123 * Work around for RFE #4093999 in Sun's bug database
124 * ("Relax constraint on placement of this()/super() call in constructors").
125 * This code should have been merged with {@code getProperties} above.
126 */
127 private static ParameterDescriptorGroup getDescriptor(final MathTransform transform) {
128 ParameterDescriptorGroup descriptor = null;
129 if (transform instanceof AbstractMathTransform) {
130 descriptor = ((AbstractMathTransform) transform).getParameterDescriptors();
131 }
132 return descriptor;
133 }
134
135 /**
136 * Constructs a new operation method with the same values than the specified one
137 * except the dimensions.
138 *
139 * @param method The operation method to copy.
140 * @param sourceDimensions Number of dimensions in the source CRS of this operation method.
141 * @param targetDimensions Number of dimensions in the target CRS of this operation method.
142 */
143 public DefaultOperationMethod(final OperationMethod method,
144 final int sourceDimensions,
145 final int targetDimensions)
146 {
147 super(method);
148 this.formula = method.getFormula();
149 this.parameters = method.getParameters();
150 this.sourceDimensions = sourceDimensions;
151 this.targetDimensions = targetDimensions;
152 ensurePositive("sourceDimensions", sourceDimensions);
153 ensurePositive("targetDimensions", targetDimensions);
154 }
155
156 /**
157 * Constructs an operation method from a set of properties and a descriptor group.
158 * The properties given in argument follow the same rules than for the
159 * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
160 * Additionally, the following properties are understood by this construtor:
161 * <br><br>
162 * <table border='1'>
163 * <tr bgcolor="#CCCCFF" class="TableHeadingColor">
164 * <th nowrap>Property name</th>
165 * <th nowrap>Value type</th>
166 * <th nowrap>Value given to</th>
167 * </tr>
168 * <tr>
169 * <td nowrap>&nbsp;{@link #FORMULA_KEY "formula"}&nbsp;</td>
170 * <td nowrap>&nbsp;{@link String} or {@link InternationalString}&nbsp;</td>
171 * <td nowrap>&nbsp;{@link #getFormula}</td>
172 * </tr>
173 * </table>
174 *
175 * @param properties Set of properties. Should contains at least {@code "name"}.
176 * @param sourceDimensions Number of dimensions in the source CRS of this operation method.
177 * @param targetDimensions Number of dimensions in the target CRS of this operation method.
178 * @param parameters The set of parameters, or {@code null} if none.
179 */
180 public DefaultOperationMethod(final Map<String,?> properties,
181 final int sourceDimensions,
182 final int targetDimensions,
183 final ParameterDescriptorGroup parameters)
184 {
185 this(properties, new HashMap<String,Object>(), sourceDimensions, targetDimensions, parameters);
186 }
187
188 /**
189 * Work around for RFE #4093999 in Sun's bug database
190 * ("Relax constraint on placement of this()/super() call in constructors").
191 */
192 private DefaultOperationMethod(final Map<String,?> properties,
193 final Map<String,Object> subProperties,
194 final int sourceDimensions,
195 final int targetDimensions,
196 ParameterDescriptorGroup parameters)
197 {
198 super(properties, subProperties, LOCALIZABLES);
199 formula = (InternationalString) subProperties.get(FORMULA_KEY);
200 // 'parameters' may be null, which is okay. A null value will
201 // make serialization smaller and faster than an empty object.
202 this.parameters = parameters;
203 this.sourceDimensions = sourceDimensions;
204 this.targetDimensions = targetDimensions;
205 ensurePositive("sourceDimensions", sourceDimensions);
206 ensurePositive("targetDimensions", targetDimensions);
207 }
208
209 /**
210 * Ensure that the specified value is positive.
211 * An {@link IllegalArgumentException} is throws if it is not.
212 *
213 * @param name The parameter name.
214 * @param value The parameter value.
215 * @throws IllegalArgumentException if the specified value is not positive.
216 */
217 private static void ensurePositive(final String name, final int value)
218 throws IllegalArgumentException
219 {
220 if (value < 0) {
221 throw new IllegalArgumentException(Errors.format(
222 ErrorKeys.ILLEGAL_ARGUMENT_$2, name, value));
223 }
224 }
225
226 /**
227 * Formula(s) or procedure used by this operation method. This may be a reference to a
228 * publication. Note that the operation method may not be analytic, in which case this
229 * attribute references or contains the procedure, not an analytic formula.
230 */
231 public InternationalString getFormula() {
232 return formula;
233 }
234
235 /**
236 * Number of dimensions in the source CRS of this operation method.
237 *
238 * @return The dimension of source CRS.
239 */
240 public int getSourceDimensions() {
241 return sourceDimensions;
242 }
243
244 /**
245 * Number of dimensions in the target CRS of this operation method.
246 */
247 public int getTargetDimensions() {
248 return targetDimensions;
249 }
250
251 /**
252 * Returns the set of parameters.
253 */
254 public ParameterDescriptorGroup getParameters() {
255 return (parameters!=null) ? parameters : Parameters.EMPTY_GROUP;
256 }
257
258 /**
259 * Returns the operation type. Current implementation returns {@code Projection.class} for
260 * proper WKT formatting using an unknow implementation. But the {@link MathTransformProvider}
261 * subclass (with protected access) will overrides this method with a more conservative default
262 * value.
263 *
264 * @return The GeoAPI interface implemented by this operation.
265 */
266 Class<? extends Operation> getOperationType() {
267 return Projection.class;
268 }
269
270 /**
271 * Compare this operation method with the specified object for equality.
272 * If {@code compareMetadata} is {@code true}, then all available
273 * properties are compared including {@linkplain #getFormula formula}.
274 *
275 * @param object The object to compare to {@code this}.
276 * @param compareMetadata {@code true} for performing a strict comparaison, or
277 * {@code false} for comparing only properties relevant to transformations.
278 * @return {@code true} if both objects are equal.
279 */
280 @Override
281 public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
282 if (object == this) {
283 return true; // Slight optimization.
284 }
285 if (super.equals(object, compareMetadata)) {
286 final DefaultOperationMethod that = (DefaultOperationMethod) object;
287 if (this.sourceDimensions == that.sourceDimensions &&
288 this.targetDimensions == that.targetDimensions &&
289 equals(this.parameters, that.parameters, compareMetadata))
290 {
291 return !compareMetadata || Utilities.equals(this.formula, that.formula);
292 }
293 }
294 return false;
295 }
296
297 /**
298 * Returns a hash code value for this operation method.
299 */
300 @Override
301 public int hashCode() {
302 int code = (int)serialVersionUID + sourceDimensions + 37*targetDimensions;
303 if (parameters != null) {
304 code = code * 37 + parameters.hashCode();
305 }
306 return code;
307 }
308
309 /**
310 * Returns {@code true} if the specified transform is likely to exists only for axis switch
311 * and/or unit conversions. The heuristic rule checks if the transform is backed by a square
312 * matrix with exactly one non-null value in each row and each column. This method is used
313 * for implementation of the {@link #checkDimensions} method only.
314 */
315 private static boolean isTrivial(final MathTransform transform) {
316 if (transform instanceof LinearTransform) {
317 final Matrix matrix = ((LinearTransform) transform).getMatrix();
318 final int size = matrix.getNumRow();
319 if (matrix.getNumCol() == size) {
320 for (int j=0; j<size; j++) {
321 int n1=0, n2=0;
322 for (int i=0; i<size; i++) {
323 if (matrix.getElement(j,i) != 0) n1++;
324 if (matrix.getElement(i,j) != 0) n2++;
325 }
326 if (n1 != 1 || n2 != 1) {
327 return false;
328 }
329 }
330 return true;
331 }
332 }
333 return false;
334 }
335
336 /**
337 * Checks if an operation method and a math transform have a compatible number of source
338 * and target dimensions. In the particular case of a {@linkplain PassThroughTransform pass
339 * through transform} with more dimension than the expected number, the check will rather be
340 * performed against the {@linkplain PassThroughTransform#getSubTransform sub transform}.
341 * <p>
342 * This convenience method is provided for argument checking.
343 *
344 * @param method The operation method to compare to the math transform, or {@code null}.
345 * @param transform The math transform to compare to the operation method, or {@code null}.
346 * @throws MismatchedDimensionException if the number of dimensions are incompatibles.
347 *
348 * @todo The check for {@link ConcatenatedTransform} and {@link PassThroughTransform} works
349 * only for Geotools implementation.
350 */
351 public static void checkDimensions(final OperationMethod method, MathTransform transform)
352 throws MismatchedDimensionException
353 {
354 if (method!=null && transform!=null) {
355 int actual, expected=method.getSourceDimensions();
356 while ((actual=transform.getSourceDimensions()) > expected) {
357 if (transform instanceof ConcatenatedTransform) {
358 // Ignore axis switch and unit conversions.
359 final ConcatenatedTransform c = (ConcatenatedTransform) transform;
360 if (isTrivial(c.transform1)) {
361 transform = c.transform2;
362 } else if (isTrivial(c.transform2)) {
363 transform = c.transform1;
364 } else {
365 // The transform is something more complex than an axis switch.
366 // Stop the loop with the current illegal transform and let the
367 // exception be thrown after the loop.
368 break;
369 }
370 } else if (transform instanceof PassThroughTransform) {
371 transform = ((PassThroughTransform) transform).getSubTransform();
372 } else {
373 break;
374 }
375 }
376 final String name;
377 if (actual != expected) {
378 name = "sourceDimensions";
379 } else {
380 actual = transform.getTargetDimensions();
381 expected = method.getTargetDimensions();
382 if (actual != expected) {
383 name = "targetDimensions";
384 } else {
385 return;
386 }
387 }
388 throw new IllegalArgumentException(Errors.format(
389 ErrorKeys.MISMATCHED_DIMENSION_$3, name, actual, expected));
390 }
391 }
392}
Note: See TracBrowser for help on using the repository browser.