Source Code Cross Referenced for AbstractCoordinateOperationFactory.java in  » GIS » GeoTools-2.4.1 » org » geotools » referencing » operation » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.referencing.operation 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005:         *    (C) 2001, 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 and extensions
023:        import java.util.Map;
024:        import java.util.HashMap;
025:        import java.util.Iterator;
026:        import java.util.LinkedList;
027:        import java.util.Collections;
028:        import javax.units.ConversionException;
029:
030:        // OpenGIS dependencies
031:        import org.opengis.metadata.quality.PositionalAccuracy;
032:        import org.opengis.parameter.ParameterDescriptorGroup;
033:        import org.opengis.parameter.ParameterValueGroup;
034:        import org.opengis.referencing.FactoryException;
035:        import org.opengis.referencing.IdentifiedObject;
036:        import org.opengis.referencing.ReferenceIdentifier;
037:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
038:        import org.opengis.referencing.cs.CoordinateSystem;
039:        import org.opengis.referencing.operation.Conversion;
040:        import org.opengis.referencing.operation.CoordinateOperation;
041:        import org.opengis.referencing.operation.CoordinateOperationFactory;
042:        import org.opengis.referencing.operation.ConcatenatedOperation;
043:        import org.opengis.referencing.operation.MathTransform;
044:        import org.opengis.referencing.operation.MathTransformFactory;
045:        import org.opengis.referencing.operation.Matrix;
046:        import org.opengis.referencing.operation.Operation;
047:        import org.opengis.referencing.operation.OperationMethod;
048:        import org.opengis.referencing.operation.OperationNotFoundException;
049:        import org.opengis.referencing.operation.NoninvertibleTransformException;
050:        import org.opengis.referencing.operation.Transformation;
051:
052:        // Geotools dependencies
053:        import org.geotools.factory.Hints;
054:        import org.geotools.metadata.iso.citation.Citations;
055:        import org.geotools.metadata.iso.quality.PositionalAccuracyImpl;
056:        import org.geotools.referencing.CRS;
057:        import org.geotools.referencing.AbstractIdentifiedObject;
058:        import org.geotools.referencing.NamedIdentifier;
059:        import org.geotools.referencing.factory.FactoryGroup;
060:        import org.geotools.referencing.factory.ReferencingFactory;
061:        import org.geotools.referencing.cs.AbstractCS;
062:        import org.geotools.referencing.operation.transform.ProjectiveTransform;
063:        import org.geotools.resources.Utilities;
064:        import org.geotools.resources.i18n.Errors;
065:        import org.geotools.resources.i18n.ErrorKeys;
066:        import org.geotools.resources.i18n.Vocabulary;
067:        import org.geotools.resources.i18n.VocabularyKeys;
068:        import org.geotools.util.CanonicalSet;
069:
070:        /**
071:         * Base class for coordinate operation factories. This class provides helper methods for the
072:         * construction of building blocks. It doesn't figure out any operation path by itself. This
073:         * more "intelligent" job is left to subclasses.
074:         *
075:         * @since 2.1
076:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/AbstractCoordinateOperationFactory.java $
077:         * @version $Id: AbstractCoordinateOperationFactory.java 28264 2007-12-05 21:53:08Z desruisseaux $
078:         * @author Martin Desruisseaux
079:         */
080:        public abstract class AbstractCoordinateOperationFactory extends
081:                ReferencingFactory implements  CoordinateOperationFactory {
082:            /**
083:             * The identifier for an identity operation.
084:             */
085:            protected static final ReferenceIdentifier IDENTITY = new NamedIdentifier(
086:                    Citations.GEOTOOLS, Vocabulary
087:                            .formatInternational(VocabularyKeys.IDENTITY));
088:
089:            /**
090:             * The identifier for conversion using an affine transform for axis swapping and/or
091:             * unit conversions.
092:             */
093:            protected static final ReferenceIdentifier AXIS_CHANGES = new NamedIdentifier(
094:                    Citations.GEOTOOLS, Vocabulary
095:                            .formatInternational(VocabularyKeys.AXIS_CHANGES));
096:
097:            /**
098:             * The identifier for a transformation which is a datum shift.
099:             *
100:             * @see PositionalAccuracyImpl#DATUM_SHIFT_APPLIED
101:             */
102:            protected static final ReferenceIdentifier DATUM_SHIFT = new NamedIdentifier(
103:                    Citations.GEOTOOLS, Vocabulary
104:                            .formatInternational(VocabularyKeys.DATUM_SHIFT));
105:
106:            /**
107:             * The identifier for a transformation which is a datum shift without
108:             * {@linkplain org.geotools.referencing.datum.BursaWolfParameters Bursa Wolf parameters}.
109:             * Only the changes in ellipsoid axis-length are taken in account. Such ellipsoid shifts
110:             * are approximative and may have 1 kilometer error. This transformation is allowed
111:             * only if the factory was created with {@link Hints#LENIENT_DATUM_SHIFT} set to
112:             * {@link Boolean#TRUE}.
113:             *
114:             * @see PositionalAccuracyImpl#DATUM_SHIFT_OMITTED
115:             */
116:            protected static final ReferenceIdentifier ELLIPSOID_SHIFT = new NamedIdentifier(
117:                    Citations.GEOTOOLS,
118:                    Vocabulary
119:                            .formatInternational(VocabularyKeys.ELLIPSOID_SHIFT));
120:
121:            /**
122:             * The identifier for a geocentric conversion.
123:             */
124:            protected static final ReferenceIdentifier GEOCENTRIC_CONVERSION = new NamedIdentifier(
125:                    Citations.GEOTOOLS,
126:                    Vocabulary
127:                            .formatInternational(VocabularyKeys.GEOCENTRIC_TRANSFORM));
128:
129:            /**
130:             * The identifier for an inverse operation.
131:             */
132:            protected static final ReferenceIdentifier INVERSE_OPERATION = new NamedIdentifier(
133:                    Citations.GEOTOOLS,
134:                    Vocabulary
135:                            .formatInternational(VocabularyKeys.INVERSE_OPERATION));
136:
137:            /**
138:             * Shortcut to identified object constants.
139:             *
140:             * @todo Replace by a static import when we will be allowed to compile with J2SE 1.5.
141:             */
142:            private static final String NAME_KEY = IdentifiedObject.NAME_KEY;
143:
144:            /**
145:             * The set of helper methods on factories.
146:             *
147:             * @see #getFactoryGroup
148:             */
149:            private final FactoryGroup factories;
150:
151:            /**
152:             * The underlying math transform factory. This factory is used
153:             * for constructing {@link MathTransform} objects for all
154:             * {@linkplain CoordinateOperation coordinate operations}.
155:             *
156:             * @see #getMathTransformFactory
157:             */
158:            private final MathTransformFactory mtFactory;
159:
160:            /**
161:             * A pool of coordinate operation. This pool is used in order
162:             * to returns instance of existing operations when possible.
163:             */
164:            private final CanonicalSet pool = new CanonicalSet();
165:
166:            /**
167:             * Tells if {@link FactoryGroup#hints} has been invoked. It must be invoked exactly once,
168:             * but can't be invoked in the constructor because it causes a {@link StackOverflowError}
169:             * in some situations.
170:             */
171:            private boolean hintsInitialized;
172:
173:            /**
174:             * Constructs a coordinate operation factory using the specified hints.
175:             * This constructor recognizes the {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS},
176:             * {@link Hints#DATUM_FACTORY DATUM} and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM}
177:             * {@code FACTORY} hints.
178:             *
179:             * @param userHints The hints, or {@code null} if none.
180:             */
181:            public AbstractCoordinateOperationFactory(final Hints userHints) {
182:                this (userHints, NORMAL_PRIORITY);
183:            }
184:
185:            /**
186:             * Constructs a coordinate operation factory using the specified hints and priority.
187:             * This constructor recognizes the {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS},
188:             * {@link Hints#DATUM_FACTORY DATUM} and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM}
189:             * {@code FACTORY} hints.
190:             *
191:             * @param userHints The hints, or {@code null} if none.
192:             * @param priority The priority for this factory, as a number between
193:             *        {@link #MINIMUM_PRIORITY MINIMUM_PRIORITY} and
194:             *        {@link #MAXIMUM_PRIORITY MAXIMUM_PRIORITY} inclusive.
195:             *
196:             * @since 2.2
197:             */
198:            public AbstractCoordinateOperationFactory(final Hints userHints,
199:                    final int priority) {
200:                super (priority);
201:                factories = FactoryGroup.createInstance(userHints);
202:                mtFactory = factories.getMathTransformFactory();
203:            }
204:
205:            /**
206:             * If the specified factory is an instance of {@code AbstractCoordinateOperationFactory},
207:             * fetch the {@link FactoryGroup} from this instance instead of from the hints. This
208:             * constructor is strictly reserved for factory subclasses that are wrapper around an
209:             * other factory, like {@link BufferedCoordinateOperationFactory}.
210:             */
211:            AbstractCoordinateOperationFactory(
212:                    final CoordinateOperationFactory factory,
213:                    final Hints hints, final int priority) {
214:                super (priority);
215:                if (factory instanceof  AbstractCoordinateOperationFactory) {
216:                    factories = ((AbstractCoordinateOperationFactory) factory)
217:                            .getFactoryGroup();
218:                } else {
219:                    factories = FactoryGroup.createInstance(hints);
220:                }
221:                mtFactory = factories.getMathTransformFactory();
222:            }
223:
224:            /**
225:             * Returns the implementation hints for this factory. The returned map contains values for
226:             * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
227:             * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints. Other values
228:             * may be provided as well, at implementation choice.
229:             */
230:            public Map getImplementationHints() {
231:                synchronized (hints) { // Note: avoid lock on public object.
232:                    if (!hintsInitialized) {
233:                        initializeHints();
234:                        hintsInitialized = true; // Set only after success.
235:                    }
236:                }
237:                return super .getImplementationHints();
238:            }
239:
240:            /**
241:             * Invoked when the {@link #hints} map should be initialized. This method may
242:             * be overridden by subclasses like {@link BufferedCoordinateOperationFactory}.
243:             */
244:            void initializeHints() {
245:                assert Thread.holdsLock(hints);
246:                final FactoryGroup factories = getFactoryGroup();
247:                final Map factoryGroupHints = factories
248:                        .getImplementationHints();
249:                hints.putAll(factoryGroupHints);
250:            }
251:
252:            /**
253:             * Returns the underlying math transform factory. This factory
254:             * is used for constructing {@link MathTransform} objects for
255:             * all {@linkplain CoordinateOperation coordinate operations}.
256:             */
257:            public final MathTransformFactory getMathTransformFactory() {
258:                return mtFactory;
259:            }
260:
261:            /**
262:             * Returns the set of helper methods on factories.
263:             */
264:            final FactoryGroup getFactoryGroup() {
265:                return factories;
266:            }
267:
268:            /**
269:             * Returns an affine transform between two coordinate systems. Only units and
270:             * axis order (e.g. transforming from (NORTH,WEST) to (EAST,NORTH)) are taken
271:             * in account.
272:             * <p>
273:             * Example: If coordinates in {@code sourceCS} are (x,y) pairs in metres and
274:             * coordinates in {@code targetCS} are (-y,x) pairs in centimetres, then the
275:             * transformation can be performed as below:
276:             *
277:             * <pre><blockquote>
278:             *          [-y(cm)]   [ 0  -100    0 ] [x(m)]
279:             *          [ x(cm)] = [ 100   0    0 ] [y(m)]
280:             *          [ 1    ]   [ 0     0    1 ] [1   ]
281:             * </blockquote></pre>
282:             *
283:             * @param  sourceCS The source coordinate system.
284:             * @param  targetCS The target coordinate system.
285:             * @return The transformation from {@code sourceCS} to {@code targetCS} as
286:             *         an affine transform. Only axis orientation and units are taken in account.
287:             * @throws OperationNotFoundException If the affine transform can't be constructed.
288:             *
289:             * @see AbstractCS#swapAndScaleAxis
290:             */
291:            protected Matrix swapAndScaleAxis(final CoordinateSystem sourceCS,
292:                    final CoordinateSystem targetCS)
293:                    throws OperationNotFoundException {
294:                try {
295:                    return AbstractCS.swapAndScaleAxis(sourceCS, targetCS);
296:                } catch (IllegalArgumentException exception) {
297:                    throw new OperationNotFoundException(getErrorMessage(
298:                            sourceCS, targetCS), exception);
299:                } catch (ConversionException exception) {
300:                    throw new OperationNotFoundException(getErrorMessage(
301:                            sourceCS, targetCS), exception);
302:                }
303:                // No attempt to catch ClassCastException since such
304:                // exception would indicates a programming error.
305:            }
306:
307:            /**
308:             * Returns the specified identifier in a map to be given to coordinate operation constructors.
309:             * In the special case where the {@code name} identifier is {@link #DATUM_SHIFT} or
310:             * {@link #ELLIPSOID_SHIFT}, the map will contains extra informations like positional
311:             * accuracy.
312:             *
313:             * @todo In the datum shift case, an operation version is mandatory but unknow at this time.
314:             *       However, we noticed that the EPSG database do not always defines a version neither.
315:             *       Consequently, the Geotools implementation relax the rule requirying an operation
316:             *       version and we do not try to provide this information here for now.
317:             */
318:            private static Map getProperties(final ReferenceIdentifier name) {
319:                final Map properties;
320:                if (name == DATUM_SHIFT || name == ELLIPSOID_SHIFT) {
321:                    properties = new HashMap(4);
322:                    properties.put(NAME_KEY, name);
323:                    properties
324:                            .put(
325:                                    CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY,
326:                                    new org.opengis.metadata.quality.PositionalAccuracy[] { name == DATUM_SHIFT ? PositionalAccuracyImpl.DATUM_SHIFT_APPLIED
327:                                            : PositionalAccuracyImpl.DATUM_SHIFT_OMITTED });
328:                } else {
329:                    properties = Collections.singletonMap(NAME_KEY, name);
330:                }
331:                return properties;
332:            }
333:
334:            /**
335:             * Creates a coordinate operation from a matrix, which usually describes an affine tranform.
336:             * A default {@link OperationMethod} object is given to this transform. In the special case
337:             * where the {@code name} identifier is {@link #DATUM_SHIFT} or {@link #ELLIPSOID_SHIFT},
338:             * the operation will be an instance of {@link Transformation} instead of the usual
339:             * {@link Conversion}.
340:             *
341:             * @param  name      The identifier for the operation to be created.
342:             * @param  sourceCRS The source coordinate reference system.
343:             * @param  targetCRS The target coordinate reference system.
344:             * @param  matrix    The matrix which describe an affine transform operation.
345:             * @return The conversion or transformation.
346:             * @throws FactoryException if the operation can't be created.
347:             */
348:            protected CoordinateOperation createFromAffineTransform(
349:                    final ReferenceIdentifier name,
350:                    final CoordinateReferenceSystem sourceCRS,
351:                    final CoordinateReferenceSystem targetCRS,
352:                    final Matrix matrix) throws FactoryException {
353:                final MathTransform transform = mtFactory
354:                        .createAffineTransform(matrix);
355:                final Map properties = getProperties(name);
356:                final Class type = properties
357:                        .containsKey(CoordinateOperation.POSITIONAL_ACCURACY_KEY) ? Transformation.class
358:                        : Conversion.class;
359:                return createFromMathTransform(properties, sourceCRS,
360:                        targetCRS, transform,
361:                        ProjectiveTransform.ProviderAffine.getProvider(
362:                                transform.getSourceDimensions(), transform
363:                                        .getTargetDimensions()), type);
364:            }
365:
366:            /**
367:             * Creates a coordinate operation from a set of parameters.
368:             * The {@linkplain OperationMethod operation method} is inferred automatically,
369:             * if possible.
370:             *
371:             * @param  name       The identifier for the operation to be created.
372:             * @param  sourceCRS  The source coordinate reference system.
373:             * @param  targetCRS  The target coordinate reference system.
374:             * @param  parameters The parameters.
375:             * @return The conversion or transformation.
376:             * @throws FactoryException if the operation can't be created.
377:             */
378:            protected CoordinateOperation createFromParameters(
379:                    final ReferenceIdentifier name,
380:                    final CoordinateReferenceSystem sourceCRS,
381:                    final CoordinateReferenceSystem targetCRS,
382:                    final ParameterValueGroup parameters)
383:                    throws FactoryException {
384:                final Map properties = getProperties(name);
385:                final FactoryGroup factories = getFactoryGroup();
386:                final MathTransform transform = factories
387:                        .createParameterizedTransform(parameters);
388:                final OperationMethod method = factories.getLastUsedMethod();
389:                return createFromMathTransform(properties, sourceCRS,
390:                        targetCRS, transform, method, Operation.class);
391:            }
392:
393:            /**
394:             * Creates a coordinate operation from a math transform.
395:             *
396:             * @param  name       The identifier for the operation to be created.
397:             * @param  sourceCRS  The source coordinate reference system.
398:             * @param  targetCRS  The destination coordinate reference system.
399:             * @param  transform  The math transform.
400:             * @return A coordinate operation using the specified math transform.
401:             * @throws FactoryException if the operation can't be constructed.
402:             */
403:            protected CoordinateOperation createFromMathTransform(
404:                    final ReferenceIdentifier name,
405:                    final CoordinateReferenceSystem sourceCRS,
406:                    final CoordinateReferenceSystem targetCRS,
407:                    final MathTransform transform) throws FactoryException {
408:                return createFromMathTransform(Collections.singletonMap(
409:                        NAME_KEY, name), sourceCRS, targetCRS, transform, null,
410:                        CoordinateOperation.class);
411:            }
412:
413:            /**
414:             * Creates a coordinate operation from a math transform.
415:             * If the specified math transform is already a coordinate operation, and if source
416:             * and target CRS match, then {@code transform} is returned with no change.
417:             * Otherwise, a new coordinate operation is created.
418:             *
419:             * @param  properties The properties to give to the operation.
420:             * @param  sourceCRS  The source coordinate reference system.
421:             * @param  targetCRS  The destination coordinate reference system.
422:             * @param  transform  The math transform.
423:             * @param  method     The operation method, or {@code null}.
424:             * @param  type       The required super-class (e.g. <code>{@linkplain Transformation}.class</code>).
425:             * @return A coordinate operation using the specified math transform.
426:             * @throws FactoryException if the operation can't be constructed.
427:             */
428:            protected CoordinateOperation createFromMathTransform(
429:                    final Map properties,
430:                    final CoordinateReferenceSystem sourceCRS,
431:                    final CoordinateReferenceSystem targetCRS,
432:                    final MathTransform transform,
433:                    final OperationMethod method, final Class type)
434:                    throws FactoryException {
435:                CoordinateOperation operation;
436:                if (transform instanceof  CoordinateOperation) {
437:                    operation = (CoordinateOperation) transform;
438:                    if (Utilities.equals(operation.getSourceCRS(), sourceCRS)
439:                            && Utilities.equals(operation.getTargetCRS(),
440:                                    targetCRS)
441:                            && Utilities.equals(operation.getMathTransform(),
442:                                    transform)) {
443:                        if (operation instanceof  Operation) {
444:                            if (Utilities.equals(((Operation) operation)
445:                                    .getMethod(), method)) {
446:                                return operation;
447:                            }
448:                        } else {
449:                            return operation;
450:                        }
451:                    }
452:                }
453:                operation = DefaultOperation.create(properties, sourceCRS,
454:                        targetCRS, transform, method, type);
455:                operation = (CoordinateOperation) pool.unique(operation);
456:                return operation;
457:            }
458:
459:            /**
460:             * Constructs a defining conversion from a set of properties.
461:             *
462:             * @param  properties Set of properties. Should contains at least {@code "name"}.
463:             * @param  method The operation method.
464:             * @param  parameters The parameter values.
465:             * @return The defining conversion.
466:             * @throws FactoryException if the object creation failed.
467:             *
468:             * @see DefiningConversion
469:             *
470:             * @since 2.4
471:             */
472:            public Conversion createDefiningConversion(final Map properties,
473:                    final OperationMethod method,
474:                    final ParameterValueGroup parameters)
475:                    throws FactoryException {
476:                Conversion conversion = new DefiningConversion(properties,
477:                        method, parameters);
478:                conversion = (Conversion) pool.unique(conversion);
479:                return conversion;
480:            }
481:
482:            /**
483:             * Creates a concatenated operation from a sequence of operations.
484:             *
485:             * @param  properties Set of properties. Should contains at least {@code "name"}.
486:             * @param  operations The sequence of operations.
487:             * @return The concatenated operation.
488:             * @throws FactoryException if the object creation failed.
489:             */
490:            public CoordinateOperation createConcatenatedOperation(
491:                    Map properties, CoordinateOperation[] operations)
492:                    throws FactoryException {
493:                CoordinateOperation operation;
494:                operation = new DefaultConcatenatedOperation(properties,
495:                        operations, mtFactory);
496:                operation = (CoordinateOperation) pool.unique(operation);
497:                return operation;
498:            }
499:
500:            /**
501:             * Concatenate two operation steps. If an operation is an {@link #AXIS_CHANGES},
502:             * it will be included as part of the second operation instead of creating an
503:             * {@link ConcatenatedOperation}. If a concatenated operation is created, it
504:             * will get an automatically generated name.
505:             *
506:             * @param  step1 The first  step, or {@code null} for the identity operation.
507:             * @param  step2 The second step, or {@code null} for the identity operation.
508:             * @return A concatenated operation, or {@code null} if all arguments was nul.
509:             * @throws FactoryException if the operation can't be constructed.
510:             */
511:            protected CoordinateOperation concatenate(
512:                    final CoordinateOperation step1,
513:                    final CoordinateOperation step2) throws FactoryException {
514:                if (step1 == null)
515:                    return step2;
516:                if (step2 == null)
517:                    return step1;
518:                if (false) {
519:                    // Note: we sometime get this assertion failure if the user provided CRS with two
520:                    //       different ellipsoids but an identical TOWGS84 conversion infos (which is
521:                    //       usually wrong, but still happen).
522:                    assert equalsIgnoreMetadata(step1.getTargetCRS(), step2
523:                            .getSourceCRS()) : "CRS 1 =" + step1.getTargetCRS()
524:                            + '\n' + "CRS 2 =" + step2.getSourceCRS();
525:                }
526:                if (isIdentity(step1))
527:                    return step2;
528:                if (isIdentity(step2))
529:                    return step1;
530:                final MathTransform mt1 = step1.getMathTransform();
531:                final MathTransform mt2 = step2.getMathTransform();
532:                final CoordinateReferenceSystem sourceCRS = step1
533:                        .getSourceCRS();
534:                final CoordinateReferenceSystem targetCRS = step2
535:                        .getTargetCRS();
536:                CoordinateOperation step = null;
537:                if (step1.getName() == AXIS_CHANGES
538:                        && mt1.getSourceDimensions() == mt1
539:                                .getTargetDimensions())
540:                    step = step2;
541:                if (step2.getName() == AXIS_CHANGES
542:                        && mt2.getSourceDimensions() == mt2
543:                                .getTargetDimensions())
544:                    step = step1;
545:                if (step instanceof  Operation) {
546:                    /*
547:                     * Applies only on operation in order to avoid merging with PassThroughOperation.
548:                     * Also applies only if the transform to hide has identical source and target
549:                     * dimensions in order to avoid mismatch with the method's dimensions.
550:                     */
551:                    return createFromMathTransform(AbstractIdentifiedObject
552:                            .getProperties(step), sourceCRS, targetCRS,
553:                            mtFactory.createConcatenatedTransform(mt1, mt2),
554:                            ((Operation) step).getMethod(),
555:                            CoordinateOperation.class);
556:                }
557:                return createConcatenatedOperation(getTemporaryName(sourceCRS,
558:                        targetCRS), new CoordinateOperation[] { step1, step2 });
559:            }
560:
561:            /**
562:             * Concatenate three transformation steps. If the first and/or the last operation is an
563:             * {@link #AXIS_CHANGES}, it will be included as part of the second operation instead of
564:             * creating an {@link ConcatenatedOperation}. If a concatenated operation is created, it
565:             * will get an automatically generated name.
566:             *
567:             * @param  step1 The first  step, or {@code null} for the identity operation.
568:             * @param  step2 The second step, or {@code null} for the identity operation.
569:             * @param  step3 The third  step, or {@code null} for the identity operation.
570:             * @return A concatenated operation, or {@code null} if all arguments were null.
571:             * @throws FactoryException if the operation can't be constructed.
572:             */
573:            protected CoordinateOperation concatenate(
574:                    final CoordinateOperation step1,
575:                    final CoordinateOperation step2,
576:                    final CoordinateOperation step3) throws FactoryException {
577:                if (step1 == null)
578:                    return concatenate(step2, step3);
579:                if (step2 == null)
580:                    return concatenate(step1, step3);
581:                if (step3 == null)
582:                    return concatenate(step1, step2);
583:                assert equalsIgnoreMetadata(step1.getTargetCRS(), step2
584:                        .getSourceCRS()) : step1;
585:                assert equalsIgnoreMetadata(step2.getTargetCRS(), step3
586:                        .getSourceCRS()) : step3;
587:
588:                if (isIdentity(step1))
589:                    return concatenate(step2, step3);
590:                if (isIdentity(step2))
591:                    return concatenate(step1, step3);
592:                if (isIdentity(step3))
593:                    return concatenate(step1, step2);
594:                if (step1.getName() == AXIS_CHANGES)
595:                    return concatenate(concatenate(step1, step2), step3);
596:                if (step3.getName() == AXIS_CHANGES)
597:                    return concatenate(step1, concatenate(step2, step3));
598:                final CoordinateReferenceSystem sourceCRS = step1
599:                        .getSourceCRS();
600:                final CoordinateReferenceSystem targetCRS = step3
601:                        .getTargetCRS();
602:                return createConcatenatedOperation(getTemporaryName(sourceCRS,
603:                        targetCRS), new CoordinateOperation[] { step1, step2,
604:                        step3 });
605:            }
606:
607:            /**
608:             * Returns {@code true} if the specified operation is an identity conversion.
609:             * This method always returns {@code false} for transformations even if their
610:             * associated math transform is an identity one, because such transformations
611:             * are usually datum shift and must be visible.
612:             */
613:            private static boolean isIdentity(
614:                    final CoordinateOperation operation) {
615:                return (operation instanceof  Conversion)
616:                        && operation.getMathTransform().isIdentity();
617:            }
618:
619:            /**
620:             * Returns the inverse of the specified operation.
621:             *
622:             * @param  operation The operation to invert.
623:             * @return The inverse of {@code operation}.
624:             * @throws NoninvertibleTransformException if the operation is not invertible.
625:             * @throws FactoryException if the operation creation failed for an other reason.
626:             *
627:             * @since 2.3
628:             */
629:            protected CoordinateOperation inverse(
630:                    final CoordinateOperation operation)
631:                    throws NoninvertibleTransformException, FactoryException {
632:                final CoordinateReferenceSystem sourceCRS = operation
633:                        .getSourceCRS();
634:                final CoordinateReferenceSystem targetCRS = operation
635:                        .getTargetCRS();
636:                final Map properties = AbstractIdentifiedObject.getProperties(
637:                        operation, null);
638:                properties.putAll(getTemporaryName(targetCRS, sourceCRS));
639:                if (operation instanceof  ConcatenatedOperation) {
640:                    final LinkedList inverted = new LinkedList/*<CoordinateOperation>*/();
641:                    for (final Iterator it = ((ConcatenatedOperation) operation)
642:                            .getOperations().iterator(); it.hasNext();) {
643:                        inverted.addFirst(inverse((CoordinateOperation) it
644:                                .next()));
645:                    }
646:                    return createConcatenatedOperation(properties,
647:                            (CoordinateOperation[]) inverted
648:                                    .toArray(new CoordinateOperation[inverted
649:                                            .size()]));
650:                }
651:                final MathTransform transform = operation.getMathTransform()
652:                        .inverse();
653:                final Class type = AbstractCoordinateOperation
654:                        .getType(operation);
655:                final OperationMethod method = (operation instanceof  Operation) ? ((Operation) operation)
656:                        .getMethod()
657:                        : null;
658:                return createFromMathTransform(properties, targetCRS,
659:                        sourceCRS, transform, method, type);
660:            }
661:
662:            /////////////////////////////////////////////////////////////////////////////////
663:            /////////////////////////////////////////////////////////////////////////////////
664:            ////////////                                                         ////////////
665:            ////////////                M I S C E L L A N E O U S                ////////////
666:            ////////////                                                         ////////////
667:            /////////////////////////////////////////////////////////////////////////////////
668:            /////////////////////////////////////////////////////////////////////////////////
669:
670:            /**
671:             * Returns the dimension of the specified coordinate system,
672:             * or {@code 0} if the coordinate system is null.
673:             */
674:            static int getDimension(final CoordinateReferenceSystem crs) {
675:                return (crs != null) ? crs.getCoordinateSystem().getDimension()
676:                        : 0;
677:            }
678:
679:            /**
680:             * An identifier for temporary objects. This identifier manage a count of temporary
681:             * identifier. The count is appended to the identifier name (e.g. "WGS84 (step 1)").
682:             */
683:            private static final class TemporaryIdentifier extends
684:                    NamedIdentifier {
685:                /** The parent identifier. */
686:                private final ReferenceIdentifier parent;
687:
688:                /** The temporary object count. */
689:                private final int count;
690:
691:                /** Constructs an identifier derived from the specified one. */
692:                public TemporaryIdentifier(final ReferenceIdentifier parent) {
693:                    this (
694:                            parent,
695:                            ((parent instanceof  TemporaryIdentifier) ? ((TemporaryIdentifier) parent).count
696:                                    : 0) + 1);
697:                }
698:
699:                /** Work around for RFE #4093999 in Sun's bug database */
700:                private TemporaryIdentifier(final ReferenceIdentifier parent,
701:                        final int count) {
702:                    super (Citations.GEOTOOLS, unwrap(parent).getCode()
703:                            + " (step " + count + ')');
704:                    this .parent = parent;
705:                    this .count = count;
706:                }
707:
708:                /** Returns the parent identifier for the specified identifier, if any. */
709:                public static ReferenceIdentifier unwrap(
710:                        ReferenceIdentifier identifier) {
711:                    while (identifier instanceof  TemporaryIdentifier) {
712:                        identifier = ((TemporaryIdentifier) identifier).parent;
713:                    }
714:                    return identifier;
715:                }
716:            }
717:
718:            /**
719:             * Returns the name of the GeoAPI interface implemented by the specified object.
720:             * In addition, the name may be added between brackets.
721:             */
722:            private static String getClassName(final IdentifiedObject object) {
723:                if (object != null) {
724:                    Class type = object.getClass();
725:                    final Class[] interfaces = type.getInterfaces();
726:                    for (int i = 0; i < interfaces.length; i++) {
727:                        final Class candidate = interfaces[i];
728:                        if (candidate.getName().startsWith(
729:                                "org.opengis.referencing.")) {
730:                            type = candidate;
731:                            break;
732:                        }
733:                    }
734:                    String name = Utilities.getShortName(type);
735:                    final ReferenceIdentifier id = object.getName();
736:                    if (id != null) {
737:                        name = name + '[' + id.getCode() + ']';
738:                    }
739:                    return name;
740:                }
741:                return null;
742:            }
743:
744:            /**
745:             * Returns a temporary name for object derived from the specified one.
746:             *
747:             * @param source The CRS to base name on, or {@code null} if none.
748:             */
749:            static Map getTemporaryName(final IdentifiedObject source) {
750:                final Map properties = new HashMap(4);
751:                properties.put(NAME_KEY, new TemporaryIdentifier(source
752:                        .getName()));
753:                properties.put(IdentifiedObject.REMARKS_KEY, Vocabulary
754:                        .formatInternational(VocabularyKeys.DERIVED_FROM_$1,
755:                                getClassName(source)));
756:                return properties;
757:            }
758:
759:            /**
760:             * Returns a temporary name for object derived from a concatenation.
761:             *
762:             * @param source The CRS to base name on, or {@code null} if none.
763:             */
764:            static Map getTemporaryName(final CoordinateReferenceSystem source,
765:                    final CoordinateReferenceSystem target) {
766:                final String name = getClassName(source) + " \u21E8 "
767:                        + getClassName(target);
768:                return Collections.singletonMap(NAME_KEY, name);
769:            }
770:
771:            /**
772:             * Compares the specified objects for equality. If both objects are Geotools
773:             * implementations of {@linkplain AbstractIdentifiedObject},
774:             * then this method will ignore the metadata during the comparaison.
775:             *
776:             * @param  object1 The first object to compare (may be null).
777:             * @param  object2 The second object to compare (may be null).
778:             * @return {@code true} if both objects are equals.
779:             *
780:             * @todo This method may be insuffisient, since it will returns {@code false} for
781:             *       two different implementations, even if they encapsulate the same data values.
782:             */
783:            static boolean equalsIgnoreMetadata(final IdentifiedObject object1,
784:                    final IdentifiedObject object2) {
785:                return CRS.equalsIgnoreMetadata(object1, object2);
786:            }
787:
788:            /**
789:             * Returns an error message for "No path found from sourceCRS to targetCRS".
790:             * This is used for the construction of {@link OperationNotFoundException}.
791:             *
792:             * @param  source The source CRS.
793:             * @param  target The target CRS.
794:             * @return A default error message.
795:             */
796:            protected static String getErrorMessage(
797:                    final IdentifiedObject source, final IdentifiedObject target) {
798:                return Errors.format(ErrorKeys.NO_TRANSFORMATION_PATH_$2,
799:                        getClassName(source), getClassName(target));
800:            }
801:
802:            /**
803:             * Makes sure an argument is non-null.
804:             *
805:             * @param  name   Argument name.
806:             * @param  object User argument.
807:             * @throws IllegalArgumentException if {@code object} is null.
808:             */
809:            protected static void ensureNonNull(final String name,
810:                    final Object object) throws IllegalArgumentException {
811:                if (object == null) {
812:                    throw new IllegalArgumentException(Errors.format(
813:                            ErrorKeys.NULL_ARGUMENT_$1, name));
814:                }
815:            }
816:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.