Source Code Cross Referenced for ReferencingFactoryContainer.java in  » GIS » GeoTools-2.4.1 » org » geotools » referencing » factory » 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.factory 
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) 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:        package org.geotools.referencing.factory;
018:
019:        // J2SE dependencies
020:        import java.util.*;
021:
022:        import javax.units.Unit;
023:        import javax.units.ConversionException;
024:
025:        // OpenGIS dependencies
026:        import org.opengis.metadata.Identifier; // For javadoc
027:        import org.opengis.parameter.ParameterValueGroup;
028:        import org.opengis.referencing.IdentifiedObject;
029:        import org.opengis.referencing.FactoryException;
030:        import org.opengis.referencing.NoSuchIdentifierException;
031:        import org.opengis.referencing.cs.*;
032:        import org.opengis.referencing.crs.*;
033:        import org.opengis.referencing.datum.*;
034:        import org.opengis.referencing.operation.*;
035:
036:        // Geotools dependencies
037:        import org.geotools.factory.GeoTools;
038:        import org.geotools.factory.Hints;
039:        import org.geotools.factory.Factory;
040:        import org.geotools.factory.FactoryCreator;
041:        import org.geotools.factory.FactoryRegistry;
042:        import org.geotools.parameter.Parameters;
043:        import org.geotools.referencing.ReferencingFactoryFinder;
044:        import org.geotools.referencing.AbstractIdentifiedObject;
045:        import org.geotools.referencing.operation.DefiningConversion;
046:        import org.geotools.referencing.operation.MathTransformProvider;
047:        import org.geotools.referencing.operation.DefaultMathTransformFactory;
048:        import org.geotools.referencing.operation.matrix.MatrixFactory;
049:        import org.geotools.referencing.crs.DefaultProjectedCRS;
050:        import org.geotools.referencing.crs.DefaultCompoundCRS;
051:        import org.geotools.referencing.cs.AbstractCS;
052:        import org.geotools.resources.i18n.ErrorKeys;
053:        import org.geotools.resources.i18n.Errors;
054:        import org.geotools.resources.CRSUtilities;
055:        import org.geotools.resources.XArray;
056:
057:        /**
058:         * A set of utilities methods working on factories. Many of those methods requires more than
059:         * one factory. Consequently, they can't be a method in a single factory. Furthermore, since
060:         * they are helper methods and somewhat implementation-dependent, they are not part of GeoAPI.
061:         *
062:         * @since 2.4
063:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/ReferencingFactoryContainer.java $
064:         * @version $Id: ReferencingFactoryContainer.java 26090 2007-06-29 08:09:27Z desruisseaux $
065:         * @author Martin Desruisseaux
066:         */
067:        public class ReferencingFactoryContainer extends ReferencingFactory {
068:            /**
069:             * A factory registry used as a cache for factory groups created up to date.
070:             */
071:            private static FactoryRegistry cache;
072:
073:            /**
074:             * The {@linkplain Datum datum} factory.
075:             * If null, then a default factory will be created only when first needed.
076:             */
077:            private DatumFactory datumFactory;
078:
079:            /**
080:             * The {@linkplain CoordinateSystem coordinate system} factory.
081:             * If null, then a default factory will be created only when first needed.
082:             */
083:            private CSFactory csFactory;
084:
085:            /**
086:             * The {@linkplain CoordinateReferenceSystem coordinate reference system} factory.
087:             * If null, then a default factory will be created only when first needed.
088:             */
089:            private CRSFactory crsFactory;
090:
091:            /**
092:             * The {@linkplain MathTransform math transform} factory.
093:             * If null, then a default factory will be created only when first needed.
094:             */
095:            private MathTransformFactory mtFactory;
096:
097:            // WARNING: Do NOT put a CoordinateOperationFactory field in this class. We tried that in
098:            // Geotools 2.2, and removed it in Geotools 2.3 because it leads to very tricky recursivity
099:            // problems when we try to initialize it with FactoryFinder.getCoordinateOperationFactory.
100:            // The Datum, CS, CRS and MathTransform factories above are standalone, while the Geotools
101:            // implementation of CoordinateOperationFactory has complex dependencies with all of those,
102:            // and even with authority factories.
103:
104:            /**
105:             * The operation method for the last CRS created, or {@code null} if none. This field
106:             * may be the operation name as a {@link String} rather than a {@link OperationMethod}
107:             * if the math transform was not created by a Geotools implementation of factory.
108:             */
109:            private final ThreadLocal/*<Object>*/lastMethod = new ThreadLocal();
110:
111:            /**
112:             * Constructs an instance using the specified factories. If any factory is null,
113:             * a default instance will be created by {@link ReferencingFactoryFinder} when first needed.
114:             *
115:             * @param datumFactory The {@linkplain Datum datum} factory.
116:             * @param    csFactory The {@linkplain CoordinateSystem coordinate system} factory.
117:             * @param   crsFactory The {@linkplain CoordinateReferenceSystem coordinate reference system}
118:             *                     factory.
119:             * @param    mtFactory The {@linkplain MathTransform math transform} factory.
120:             *
121:             * @deprecated Use {@link #createInstance} instead. The fate of this constructor is
122:             *             incertain. It may be removed in Geotools 2.4, or refactored as a new
123:             *             {@code createInstance} convenience method.
124:             */
125:            public ReferencingFactoryContainer(final DatumFactory datumFactory,
126:                    final CSFactory csFactory, final CRSFactory crsFactory,
127:                    final MathTransformFactory mtFactory) {
128:                this .datumFactory = datumFactory;
129:                this .csFactory = csFactory;
130:                this .crsFactory = crsFactory;
131:                this .mtFactory = mtFactory;
132:            }
133:
134:            /**
135:             * Creates an instance from the specified hints. This constructor recognizes the
136:             * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
137:             * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints.
138:             * <p>
139:             * This constructor is public mainly for {@link org.geotools.factory.FactoryCreator} usage.
140:             * Consider invoking <code>{@linkplain #createInstance createInstance}(userHints)</code> instead.
141:             *
142:             * @param userHints The hints, or {@code null} if none.
143:             */
144:            public ReferencingFactoryContainer(final Hints userHints) {
145:                final Hints reduced = new Hints(userHints);
146:                /*
147:                 * If hints are provided, we will fetch factory immediately (instead of storing the hints
148:                 * in an inner field) because most factories will retain few hints, while the Hints map
149:                 * may contains big objects. If no hints were provided, we will construct factories only
150:                 * when first needed.
151:                 */
152:                datumFactory = (DatumFactory) extract(reduced,
153:                        Hints.DATUM_FACTORY);
154:                csFactory = (CSFactory) extract(reduced, Hints.CS_FACTORY);
155:                crsFactory = (CRSFactory) extract(reduced, Hints.CRS_FACTORY);
156:                mtFactory = (MathTransformFactory) extract(reduced,
157:                        Hints.MATH_TRANSFORM_FACTORY);
158:                /*
159:                 * Checks if we still have some hints that need to be taken in account. Since we can't guess
160:                 * which hints are relevant and which ones are not, we have to create all factories now.
161:                 */
162:                if (!reduced.isEmpty()) {
163:                    setHintsInto(reduced);
164:                    hints.putAll(reduced);
165:                    initialize();
166:                    hints.clear();
167:                }
168:            }
169:
170:            /**
171:             * Returns the factory for the specified hint, or {@code null} if the hint is not a factory
172:             * instance. It could be for example a {@link Class}.
173:             */
174:            private static Factory extract(final Map reduced,
175:                    final Hints.Key key) {
176:                if (reduced != null) {
177:                    final Object candidate = reduced.get(key);
178:                    if (candidate instanceof  Factory) {
179:                        reduced.remove(key);
180:                        return (Factory) candidate;
181:                    }
182:                }
183:                return null;
184:            }
185:
186:            /**
187:             * Creates an instance from the specified hints. This method recognizes the
188:             * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
189:             * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints.
190:             *
191:             * @param  hints The hints, or {@code null} if none.
192:             * @return A factory group created from the specified set of hints.
193:             */
194:            public static ReferencingFactoryContainer instance(final Hints hints) {
195:                final Hints completed = GeoTools.getDefaultHints();
196:                if (hints != null) {
197:                    completed.add(hints);
198:                }
199:                /*
200:                 * Use the same synchronization lock than ReferencingFactoryFinder (instead of this class)
201:                 * in order to reduce the risk of dead lock. This is because ReferencingFactoryContainer
202:                 * creation may queries ReferencingFactoryFinder, and some implementations managed by
203:                 * ReferencingFactoryFinder may ask for a ReferencingFactoryContainer in turn.
204:                 */
205:                synchronized (ReferencingFactoryFinder.class) {
206:                    if (cache == null) {
207:                        cache = new FactoryCreator(
208:                                Arrays
209:                                        .asList(new Class[] { ReferencingFactoryContainer.class }));
210:                        cache.registerServiceProvider(
211:                                new ReferencingFactoryContainer(null),
212:                                ReferencingFactoryContainer.class);
213:                    }
214:                    return (ReferencingFactoryContainer) cache
215:                            .getServiceProvider(
216:                                    ReferencingFactoryContainer.class, null,
217:                                    completed, null);
218:                }
219:            }
220:
221:            /**
222:             * Forces the initialisation of all factories. Implementation note: we try to create the
223:             * factories in typical dependency order (CRS all because it has the greatest chances to
224:             * depends on other factories).
225:             */
226:            private void initialize() {
227:                mtFactory = getMathTransformFactory();
228:                datumFactory = getDatumFactory();
229:                csFactory = getCSFactory();
230:                crsFactory = getCRSFactory();
231:            }
232:
233:            /**
234:             * Put all factories available in this group into the specified map of hints.
235:             */
236:            private void setHintsInto(final Map hints) {
237:                if (crsFactory != null)
238:                    hints.put(Hints.CRS_FACTORY, crsFactory);
239:                if (csFactory != null)
240:                    hints.put(Hints.CS_FACTORY, csFactory);
241:                if (datumFactory != null)
242:                    hints.put(Hints.DATUM_FACTORY, datumFactory);
243:                if (mtFactory != null)
244:                    hints.put(Hints.MATH_TRANSFORM_FACTORY, mtFactory);
245:            }
246:
247:            /**
248:             * Returns all factories in this group. The returned map contains values for the
249:             * {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, {@link Hints#DATUM_FACTORY DATUM}
250:             * and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} {@code FACTORY} hints.
251:             */
252:            public Map getImplementationHints() {
253:                synchronized (hints) {
254:                    if (hints.isEmpty()) {
255:                        initialize();
256:                        setHintsInto(hints);
257:                    }
258:                }
259:                return super .getImplementationHints();
260:            }
261:
262:            /**
263:             * Returns the hints to be used for lazy creation of <em>default</em> factories in various
264:             * {@code getFoo} methods. This is different from {@link #getImplementationHints} because
265:             * the later may returns non-default factories.
266:             */
267:            private Hints hints() {
268:                final Hints completed = new Hints(hints);
269:                setHintsInto(completed);
270:                return completed;
271:            }
272:
273:            /**
274:             * Returns the {@linkplain Datum datum} factory.
275:             */
276:            public DatumFactory getDatumFactory() {
277:                if (datumFactory == null) {
278:                    synchronized (hints) {
279:                        datumFactory = ReferencingFactoryFinder
280:                                .getDatumFactory(hints());
281:                    }
282:                }
283:                return datumFactory;
284:            }
285:
286:            /**
287:             * Returns the {@linkplain CoordinateSystem coordinate system} factory.
288:             */
289:            public CSFactory getCSFactory() {
290:                if (csFactory == null) {
291:                    synchronized (hints) {
292:                        csFactory = ReferencingFactoryFinder
293:                                .getCSFactory(hints());
294:                    }
295:                }
296:                return csFactory;
297:            }
298:
299:            /**
300:             * Returns the {@linkplain CoordinateReferenceSystem coordinate reference system} factory.
301:             */
302:            public CRSFactory getCRSFactory() {
303:                if (crsFactory == null) {
304:                    synchronized (hints) {
305:                        crsFactory = ReferencingFactoryFinder
306:                                .getCRSFactory(hints());
307:                    }
308:                }
309:                return crsFactory;
310:            }
311:
312:            /**
313:             * Returns the {@linkplain MathTransform math transform} factory.
314:             */
315:            public MathTransformFactory getMathTransformFactory() {
316:                if (mtFactory == null) {
317:                    synchronized (hints) {
318:                        mtFactory = ReferencingFactoryFinder
319:                                .getMathTransformFactory(hints());
320:                    }
321:                }
322:                return mtFactory;
323:            }
324:
325:            /**
326:             * Returns the operation method for the specified name.
327:             * If the {@linkplain #getMathTransformFactory underlying math transform factory} is the
328:             * {@linkplain DefaultMathTransformFactory Geotools implementation}, then this method just
329:             * delegates the call to it. Otherwise this method scans all operations registered in the
330:             * math transform factory until a match is found.
331:             *
332:             * @param  name The case insensitive {@linkplain Identifier#getCode identifier code}
333:             *         of the operation method to search for (e.g. {@code "Transverse_Mercator"}).
334:             * @return The operation method.
335:             * @throws NoSuchIdentifierException if there is no operation method registered for the
336:             *         specified name.
337:             *
338:             * @see DefaultMathTransformFactory#getOperationMethod
339:             */
340:            public OperationMethod getOperationMethod(final String name)
341:                    throws NoSuchIdentifierException {
342:                final MathTransformFactory mtFactory = getMathTransformFactory();
343:                if (mtFactory instanceof  DefaultMathTransformFactory) {
344:                    // Special processing for Geotools implementation.
345:                    return ((DefaultMathTransformFactory) mtFactory)
346:                            .getOperationMethod(name);
347:                }
348:                // Not a geotools implementation. Scan all methods.
349:                final Set operations = mtFactory
350:                        .getAvailableMethods(Operation.class);
351:                for (final Iterator it = operations.iterator(); it.hasNext();) {
352:                    final OperationMethod method = (OperationMethod) it.next();
353:                    if (AbstractIdentifiedObject.nameMatches(method, name)) {
354:                        return method;
355:                    }
356:                }
357:                throw new NoSuchIdentifierException(Errors.format(
358:                        ErrorKeys.NO_TRANSFORM_FOR_CLASSIFICATION_$1, name),
359:                        name);
360:            }
361:
362:            /**
363:             * Returns the operation method for the last call to a {@code create} method in the currently
364:             * running thread. This method may be invoked after any of the following methods:
365:             * <p>
366:             * <ul>
367:             *   <li>{@link #createParameterizedTransform}</li>
368:             *   <li>{@link #createBaseToDerived}</li>
369:             * </ul>
370:             *
371:             * @return The operation method for the last call to a {@code create} method, or
372:             *         {@code null} if none.
373:             *
374:             * @see DefaultMathTransformFactory#getLastUsedMethod
375:             */
376:            public OperationMethod getLastUsedMethod() {
377:                final Object candidate = lastMethod.get();
378:                if (candidate instanceof  OperationMethod) {
379:                    return (OperationMethod) candidate;
380:                }
381:                if (candidate instanceof  String) {
382:                    /*
383:                     * The last math transform was not created by a Geotools implementation
384:                     * of the factory. Scans all methods until a match is found.
385:                     */
386:                    final MathTransformFactory mtFactory = getMathTransformFactory();
387:                    final Set operations = mtFactory
388:                            .getAvailableMethods(Operation.class);
389:                    final String classification = (String) candidate;
390:                    for (final Iterator it = operations.iterator(); it
391:                            .hasNext();) {
392:                        final OperationMethod method = (OperationMethod) it
393:                                .next();
394:                        if (AbstractIdentifiedObject.nameMatches(method
395:                                .getParameters(), classification)) {
396:                            lastMethod.set(method);
397:                            return method;
398:                        }
399:                    }
400:                }
401:                return null;
402:            }
403:
404:            /**
405:             * Creates a transform from a group of parameters. This method delegates the work to the
406:             * {@linkplain #getMathTransformFactory underlying math transform factory} and keep trace
407:             * of the {@linkplain OperationMethod operation method} used. The later can be obtained
408:             * by a call to {@link #getLastUsedMethod}.
409:             *
410:             * @param  parameters The parameter values.
411:             * @return The parameterized transform.
412:             * @throws NoSuchIdentifierException if there is no transform registered for the method.
413:             * @throws FactoryException if the object creation failed. This exception is thrown
414:             *         if some required parameter has not been supplied, or has illegal value.
415:             *
416:             * @see MathTransformFactory#createParameterizedTransform
417:             */
418:            public MathTransform createParameterizedTransform(
419:                    ParameterValueGroup parameters)
420:                    throws NoSuchIdentifierException, FactoryException {
421:                //        lastMethod.remove(); // TODO: uncomment when we will be allowed to target J2SE 1.5.
422:                final MathTransformFactory mtFactory = getMathTransformFactory();
423:                final MathTransform transform = mtFactory
424:                        .createParameterizedTransform(parameters);
425:                if (mtFactory instanceof  DefaultMathTransformFactory) {
426:                    // Special processing for Geotools implementation.
427:                    lastMethod.set(((DefaultMathTransformFactory) mtFactory)
428:                            .getLastUsedMethod());
429:                } else {
430:                    // Not a geotools implementation. Will try to guess the method later.
431:                    lastMethod.set(parameters.getDescriptor().getName()
432:                            .getCode());
433:                }
434:                return transform;
435:            }
436:
437:            /**
438:             * Creates a transform from a group of parameters and add the method used to a list.
439:             * This variant of {@code createParameterizedTransform(...)} provides a way for
440:             * the client to keep trace of any {@linkplain OperationMethod operation method}
441:             * used by this factory. 
442:             *
443:             * @param  parameters The parameter values.
444:             * @param  methods A collection where to add the operation method that apply to the transform,
445:             *                 or {@code null} if none.
446:             * @return The parameterized transform.
447:             * @throws NoSuchIdentifierException if there is no transform registered for the method.
448:             * @throws FactoryException if the object creation failed. This exception is thrown
449:             *         if some required parameter has not been supplied, or has illegal value.
450:             *
451:             * @deprecated Replaced by {@link #createParameterizedTransform(ParameterValueGroup)}
452:             *             followed by a call to {@link #getLastUsedMethod}.
453:             */
454:            public MathTransform createParameterizedTransform(
455:                    ParameterValueGroup parameters, Collection methods)
456:                    throws NoSuchIdentifierException, FactoryException {
457:                final MathTransform transform = createParameterizedTransform(parameters);
458:                if (methods != null) {
459:                    methods.add(getLastUsedMethod());
460:                }
461:                return transform;
462:            }
463:
464:            /**
465:             * Creates a {@linkplain #createParameterizedTransform parameterized transform} from a base
466:             * CRS to a derived CS. If the {@code "semi_major"} and {@code "semi_minor"} parameters are
467:             * not explicitly specified, they will be inferred from the {@linkplain Ellipsoid ellipsoid}
468:             * and added to {@code parameters}. In addition, this method performs axis switch as needed.
469:             * <p>
470:             * The {@linkplain OperationMethod operation method} used can be obtained by a call to
471:             * {@link #getLastUsedMethod}.
472:             *
473:             * @param  baseCRS The source coordinate reference system.
474:             * @param  parameters The parameter values for the transform.
475:             * @param  derivedCS the target coordinate system.
476:             * @return The parameterized transform.
477:             * @throws NoSuchIdentifierException if there is no transform registered for the method.
478:             * @throws FactoryException if the object creation failed. This exception is thrown
479:             *         if some required parameter has not been supplied, or has illegal value.
480:             */
481:            public MathTransform createBaseToDerived(
482:                    final CoordinateReferenceSystem baseCRS,
483:                    final ParameterValueGroup parameters,
484:                    final CoordinateSystem derivedCS)
485:                    throws NoSuchIdentifierException, FactoryException {
486:                /*
487:                 * If the user's parameter do not contains semi-major and semi-minor axis length, infers
488:                 * them from the ellipsoid. This is a convenience service since the user often omit those
489:                 * parameters (because they duplicate datum information).
490:                 */
491:                final Ellipsoid ellipsoid = CRSUtilities
492:                        .getHeadGeoEllipsoid(baseCRS);
493:                if (ellipsoid != null) {
494:                    final Unit axisUnit = ellipsoid.getAxisUnit();
495:                    Parameters.ensureSet(parameters, "semi_major", ellipsoid
496:                            .getSemiMajorAxis(), axisUnit, false);
497:                    Parameters.ensureSet(parameters, "semi_minor", ellipsoid
498:                            .getSemiMinorAxis(), axisUnit, false);
499:                }
500:                /*
501:                 * Computes matrix for swapping axis and performing units conversion.
502:                 * There is one matrix to apply before projection on (longitude,latitude)
503:                 * coordinates, and one matrix to apply after projection on (easting,northing)
504:                 * coordinates.
505:                 */
506:                final CoordinateSystem sourceCS = baseCRS.getCoordinateSystem();
507:                final Matrix swap1, swap3;
508:                try {
509:                    swap1 = AbstractCS.swapAndScaleAxis(sourceCS, AbstractCS
510:                            .standard(sourceCS));
511:                    swap3 = AbstractCS.swapAndScaleAxis(AbstractCS
512:                            .standard(derivedCS), derivedCS);
513:                } catch (IllegalArgumentException cause) {
514:                    // User-specified axis don't match.
515:                    throw new FactoryException(cause);
516:                } catch (ConversionException cause) {
517:                    // A Unit conversion is non-linear.
518:                    throw new FactoryException(cause);
519:                }
520:                /*
521:                 * Prepares the concatenation of the matrix computed above and the projection.
522:                 * Note that at this stage, the dimensions between each step may not be compatible.
523:                 * For example the projection (step2) is usually two-dimensional while the source
524:                 * coordinate system (step1) may be three-dimensional if it has a height.
525:                 */
526:                MathTransformFactory mtFactory = getMathTransformFactory();
527:                MathTransform step1 = mtFactory.createAffineTransform(swap1);
528:                MathTransform step3 = mtFactory.createAffineTransform(swap3);
529:                MathTransform step2 = createParameterizedTransform(parameters);
530:                // IMPORTANT: From this point, 'createParameterizedTransform' should not be invoked
531:                //            anymore, directly or indirectly, in order to preserve the 'lastMethod'
532:                //            value. It will be checked by the last assert before return.
533:                /*
534:                 * If the target coordinate system has a height, instructs the projection to pass
535:                 * the height unchanged from the base CRS to the target CRS. After this block, the
536:                 * dimensions of 'step2' and 'step3' should match.
537:                 */
538:                final int numTrailingOrdinates = step3.getSourceDimensions()
539:                        - step2.getTargetDimensions();
540:                if (numTrailingOrdinates > 0) {
541:                    step2 = mtFactory.createPassThroughTransform(0, step2,
542:                            numTrailingOrdinates);
543:                }
544:                /*
545:                 * If the source CS has a height but the target CS doesn't, drops the extra coordinates.
546:                 * After this block, the dimensions of 'step1' and 'step2' should match.
547:                 */
548:                final int sourceDim = step1.getTargetDimensions();
549:                final int targetDim = step2.getSourceDimensions();
550:                if (sourceDim > targetDim) {
551:                    final Matrix drop = MatrixFactory.create(targetDim + 1,
552:                            sourceDim + 1);
553:                    drop.setElement(targetDim, sourceDim, 1);
554:                    step1 = mtFactory.createConcatenatedTransform(mtFactory
555:                            .createAffineTransform(drop), step1);
556:                }
557:                final MathTransform transform = mtFactory
558:                        .createConcatenatedTransform(mtFactory
559:                                .createConcatenatedTransform(step1, step2),
560:                                step3);
561:                assert AbstractIdentifiedObject.nameMatches(parameters
562:                        .getDescriptor(), getLastUsedMethod());
563:                return transform;
564:            }
565:
566:            /**
567:             * Creates a {@linkplain #createParameterizedTransform parameterized transform} from a base
568:             * CRS to a derived CS. If the <code>"semi_major"</code> and <code>"semi_minor"</code>
569:             * parameters are not explicitly specified, they will be inferred from the
570:             * {@linkplain Ellipsoid ellipsoid} and added to {@code parameters}.
571:             * In addition, this method performs axis switch as needed. 
572:             *
573:             * @param  baseCRS The source coordinate reference system.
574:             * @param  parameters The parameter values for the transform.
575:             * @param  derivedCS the target coordinate system.
576:             * @param  methods A collection where to add the operation method that apply to the transform,
577:             *                 or {@code null} if none.
578:             * @return The parameterized transform.
579:             * @throws NoSuchIdentifierException if there is no transform registered for the method.
580:             * @throws FactoryException if the object creation failed. This exception is thrown
581:             *         if some required parameter has not been supplied, or has illegal value.
582:             *
583:             * @deprecated Replaced by {@link #createBaseToDerived}
584:             *             followed by a call to {@link #getLastUsedMethod}.
585:             */
586:            public MathTransform createBaseToDerived(
587:                    final CoordinateReferenceSystem baseCRS,
588:                    final ParameterValueGroup parameters,
589:                    final CoordinateSystem derivedCS, final Collection methods)
590:                    throws NoSuchIdentifierException, FactoryException {
591:                final MathTransform transform = createBaseToDerived(baseCRS,
592:                        parameters, derivedCS);
593:                if (methods != null) {
594:                    methods.add(getLastUsedMethod());
595:                }
596:                return transform;
597:            }
598:
599:            /**
600:             * Creates a projected coordinate reference system from a conversion.
601:             *
602:             * @param  properties Name and other properties to give to the new object.
603:             * @param  baseCRS Geographic coordinate reference system to base projection on.
604:             * @param  conversionFromBase The {@linkplain DefiningConversion defining conversion}.
605:             * @param  derivedCS The coordinate system for the projected CRS.
606:             * @throws FactoryException if the object creation failed.
607:             *
608:             * @todo Current implementation creates directly a Geotools implementation, because there
609:             *       is not yet a suitable method in GeoAPI interfaces.
610:             */
611:            public ProjectedCRS createProjectedCRS(Map properties,
612:                    final GeographicCRS baseCRS,
613:                    final Conversion conversionFromBase,
614:                    final CartesianCS derivedCS) throws FactoryException {
615:                final ParameterValueGroup parameters = conversionFromBase
616:                        .getParameterValues();
617:                final MathTransform mt = createBaseToDerived(baseCRS,
618:                        parameters, derivedCS);
619:                OperationMethod method = conversionFromBase.getMethod();
620:                if (!(method instanceof  MathTransformProvider)) {
621:                    /*
622:                     * Our Geotools implementation of DefaultProjectedCRS may not be able to detect
623:                     * the conversion type (PlanarProjection, CylindricalProjection, etc.)  because
624:                     * we rely on the Geotools-specific MathTransformProvider for that. We will try
625:                     * to help it with the optional "conversionType" hint,  providing that the user
626:                     * do not already provides this hint.
627:                     */
628:                    if (!properties
629:                            .containsKey(DefaultProjectedCRS.CONVERSION_TYPE_KEY)) {
630:                        method = getLastUsedMethod();
631:                        if (method instanceof  MathTransformProvider) {
632:                            properties = new HashMap(properties);
633:                            properties.put(
634:                                    DefaultProjectedCRS.CONVERSION_TYPE_KEY,
635:                                    ((MathTransformProvider) method)
636:                                            .getOperationType());
637:                        }
638:                    }
639:                }
640:                return new DefaultProjectedCRS(properties, conversionFromBase,
641:                        baseCRS, mt, derivedCS);
642:            }
643:
644:            /**
645:             * Creates a projected coordinate reference system from a set of parameters. If the
646:             * {@code "semi_major"} and {@code "semi_minor"} parameters are not explicitly specified,
647:             * they will be inferred from the {@linkplain Ellipsoid ellipsoid} and added to the
648:             * {@code parameters}. This method also checks for axis order and unit conversions.
649:             *
650:             * @param  properties Name and other properties to give to the new object.
651:             * @param  baseCRS Geographic coordinate reference system to base projection on.
652:             * @param  method The operation method, or {@code null} for a default one.
653:             * @param  parameters The parameter values to give to the projection.
654:             * @param  derivedCS The coordinate system for the projected CRS.
655:             * @throws FactoryException if the object creation failed.
656:             */
657:            public ProjectedCRS createProjectedCRS(Map properties,
658:                    GeographicCRS baseCRS, OperationMethod method,
659:                    ParameterValueGroup parameters, CartesianCS derivedCS)
660:                    throws FactoryException {
661:                final MathTransform mt = createBaseToDerived(baseCRS,
662:                        parameters, derivedCS);
663:                if (method == null) {
664:                    method = getLastUsedMethod();
665:                }
666:                return getCRSFactory().createProjectedCRS(properties, method,
667:                        baseCRS, mt, derivedCS);
668:            }
669:
670:            /**
671:             * Converts a 2D&nbsp;+&nbsp;1D compound CRS into a 3D CRS, if possible. More specifically,
672:             * if the specified {@linkplain CompoundCRS compound CRS} is made of a
673:             * {@linkplain GeographicCRS geographic} (or {@linkplain ProjectedCRS projected}) and a
674:             * {@linkplain VerticalCRS vertical} CRS, and if the vertical CRS datum type is
675:             * {@linkplain VerticalDatumType#ELLIPSOIDAL height above the ellipsoid}, then this method
676:             * converts the compound CRS in a single 3D CRS. Otherwise, the {@code crs} argument is
677:             * returned unchanged.
678:             *
679:             * @param  crs The compound CRS to converts in a 3D geographic or projected CRS.
680:             * @return The 3D geographic or projected CRS, or {@code crs} if the change can't be applied.
681:             * @throws FactoryException if the object creation failed.
682:             */
683:            public CoordinateReferenceSystem toGeodetic3D(final CompoundCRS crs)
684:                    throws FactoryException {
685:                final SingleCRS[] components = DefaultCompoundCRS
686:                        .getSingleCRS(crs);
687:                SingleCRS horizontal = null;
688:                VerticalCRS vertical = null;
689:                int hi = 0, vi = 0;
690:                for (int i = 0; i < components.length; i++) {
691:                    final SingleCRS candidate = components[i];
692:                    if (candidate instanceof  VerticalCRS) {
693:                        if (vertical == null) {
694:                            vertical = (VerticalCRS) candidate;
695:                            if (VerticalDatumType.ELLIPSOIDAL.equals( // TODO: remove cast with J2SE 1.5.
696:                                    ((VerticalDatum) vertical.getDatum())
697:                                            .getVerticalDatumType())) {
698:                                vi = i;
699:                                continue;
700:                            }
701:                        }
702:                        return crs;
703:                    }
704:                    if (candidate instanceof  GeographicCRS
705:                            || candidate instanceof  ProjectedCRS) {
706:                        if (horizontal == null) {
707:                            horizontal = (SingleCRS) candidate;
708:                            if (horizontal.getCoordinateSystem().getDimension() == 2) {
709:                                hi = i;
710:                                continue;
711:                            }
712:                        }
713:                        return crs;
714:                    }
715:                }
716:                if (horizontal != null && vertical != null
717:                        && Math.abs(vi - hi) == 1) {
718:                    /*
719:                     * Exactly one horizontal and one vertical CRS has been found, and those two CRS are
720:                     * consecutives. Constructs the new 3D CS. If the two above-cited components are the
721:                     * only one, the result is returned directly. Otherwise, a new compound CRS is created.
722:                     */
723:                    final boolean classic = (hi < vi);
724:                    final SingleCRS single = toGeodetic3D(
725:                            components.length == 2 ? crs : null, horizontal,
726:                            vertical, classic);
727:                    if (components.length == 2) {
728:                        return single;
729:                    }
730:                    final CoordinateReferenceSystem[] c = new CoordinateReferenceSystem[components.length - 1];
731:                    final int i = classic ? hi : vi;
732:                    System.arraycopy(components, 0, c, 0, i);
733:                    c[i] = single;
734:                    System.arraycopy(components, i + 2, c, i + 1,
735:                            components.length - (i + 2));
736:                    return crsFactory.createCompoundCRS(
737:                            AbstractIdentifiedObject.getProperties(crs), c);
738:                }
739:                return crs;
740:            }
741:
742:            /**
743:             * Implementation of {@link #toGeodetic3D(CompoundCRS)} invoked after the horizontal and
744:             * vertical parts have been identified. This method may invokes itself recursively if the
745:             * horizontal CRS is a derived one.
746:             *
747:             * @param  crs        The compound CRS to converts in a 3D geographic CRS, or {@code null}.
748:             *                    Used only in order to infer the name properties of objects to create.
749:             * @param  horizontal The horizontal component of {@code crs}.
750:             * @param  vertical   The vertical   component of {@code crs}.
751:             * @param  classic    {@code true} if the horizontal component appears before the vertical
752:             *                    component, or {@code false} for the converse.
753:             * @return The 3D geographic or projected CRS.
754:             * @throws FactoryException if the object creation failed.
755:             */
756:            private SingleCRS toGeodetic3D(final CompoundCRS crs,
757:                    final SingleCRS horizontal, final VerticalCRS vertical,
758:                    final boolean classic) throws FactoryException {
759:                final CoordinateSystemAxis[] axis = new CoordinateSystemAxis[3];
760:                final CoordinateSystem cs = horizontal.getCoordinateSystem();
761:                axis[classic ? 0 : 1] = cs.getAxis(0);
762:                axis[classic ? 1 : 2] = cs.getAxis(1);
763:                axis[classic ? 2 : 0] = vertical.getCoordinateSystem().getAxis(
764:                        0);
765:                final Map csName, crsName;
766:                if (crs != null) {
767:                    csName = AbstractIdentifiedObject.getProperties(crs
768:                            .getCoordinateSystem());
769:                    crsName = AbstractIdentifiedObject.getProperties(crs);
770:                } else {
771:                    csName = getTemporaryName(cs);
772:                    crsName = getTemporaryName(horizontal);
773:                }
774:                final CSFactory csFactory = getCSFactory();
775:                final CRSFactory crsFactory = getCRSFactory();
776:                final SingleCRS single;
777:                if (horizontal instanceof  GeographicCRS) {
778:                    /*
779:                     * Merges a 2D geographic CRS with the vertical CRS.
780:                     */
781:                    single = crsFactory.createGeographicCRS(crsName,
782:                            (GeodeticDatum) horizontal.getDatum(), csFactory
783:                                    .createEllipsoidalCS(csName, axis[0],
784:                                            axis[1], axis[2]));
785:                } else if (horizontal instanceof  ProjectedCRS) {
786:                    /*
787:                     * Merges a 2D projected CRS with the vertical CRS.
788:                     */
789:                    final ProjectedCRS projected = (ProjectedCRS) horizontal;
790:                    GeographicCRS baseCRS = (GeographicCRS) projected
791:                            .getBaseCRS();
792:                    baseCRS = (GeographicCRS) toGeodetic3D(null, baseCRS,
793:                            vertical, classic);
794:                    final Conversion projection = projected
795:                            .getConversionFromBase();
796:                    single = createProjectedCRS(crsName, baseCRS, projection,
797:                            csFactory.createCartesianCS(csName, axis[0],
798:                                    axis[1], axis[2]));
799:                } else {
800:                    // Should never happen.
801:                    throw new AssertionError(horizontal);
802:                }
803:                return single;
804:            }
805:
806:            /**
807:             * Returns a new coordinate reference system with only the specified dimension.
808:             * This method is used for example in order to get a component of a
809:             * {@linkplain CompoundCRS compound CRS}.
810:             *
811:             * @param  crs The original (usually compound) CRS.
812:             * @param  dimensions The dimensions to keep.
813:             * @return The CRS with only the specified dimensions.
814:             */
815:            public CoordinateReferenceSystem separate(
816:                    final CoordinateReferenceSystem crs, final int[] dimensions)
817:                    throws FactoryException {
818:                final int length = dimensions.length;
819:                final int crsDimension = crs.getCoordinateSystem()
820:                        .getDimension();
821:                if (length == 0 || dimensions[0] < 0
822:                        || dimensions[length - 1] >= crsDimension
823:                        || !XArray.isStrictlySorted(dimensions)) {
824:                    throw new IllegalArgumentException(Errors.format(
825:                            ErrorKeys.ILLEGAL_ARGUMENT_$1, "dimension"));
826:                }
827:                if (length == crsDimension) {
828:                    return crs;
829:                }
830:                /*
831:                 * If the CRS is a compound one, separate each components independently.
832:                 * For each component, we search the sub-array of 'dimensions' that apply
833:                 * to this component and invoke 'separate' recursively.
834:                 */
835:                if (crs instanceof  CompoundCRS) {
836:                    int count = 0, lowerDimension = 0, lowerIndex = 0;
837:                    final List/*<CoordinateReferenceSystem>*/sources;
838:                    final CoordinateReferenceSystem[] targets;
839:                    sources = ((CompoundCRS) crs)
840:                            .getCoordinateReferenceSystems();
841:                    targets = new CoordinateReferenceSystem[sources.size()];
842:                    search: for (final Iterator it = sources.iterator(); it
843:                            .hasNext();) {
844:                        final CoordinateReferenceSystem source = (CoordinateReferenceSystem) it
845:                                .next();
846:                        final int upperDimension = lowerDimension
847:                                + source.getCoordinateSystem().getDimension();
848:                        /*
849:                         * 'source' CRS applies to dimension 'lowerDimension' inclusive to 'upperDimension'
850:                         * exclusive. Now search the smallest range in the user-specified 'dimensions' that
851:                         * cover the [lowerDimension .. upperDimension] range.
852:                         */
853:                        if (lowerIndex == dimensions.length) {
854:                            break search;
855:                        }
856:                        while (dimensions[lowerIndex] < lowerDimension) {
857:                            if (++lowerIndex == dimensions.length) {
858:                                break search;
859:                            }
860:                        }
861:                        int upperIndex = lowerIndex;
862:                        while (dimensions[upperIndex] < upperDimension) {
863:                            if (++upperIndex == dimensions.length) {
864:                                break;
865:                            }
866:                        }
867:                        if (lowerIndex != upperIndex) {
868:                            final int[] sub = new int[upperIndex - lowerIndex];
869:                            for (int j = 0; j < sub.length; j++) {
870:                                sub[j] = dimensions[j + lowerIndex]
871:                                        - lowerDimension;
872:                            }
873:                            targets[count++] = separate(source, sub);
874:                        }
875:                        lowerDimension = upperDimension;
876:                        lowerIndex = upperIndex;
877:                    }
878:                    if (count == 1) {
879:                        return targets[0];
880:                    }
881:                    return getCRSFactory().createCompoundCRS(
882:                            getTemporaryName(crs),
883:                            (CoordinateReferenceSystem[]) XArray.resize(
884:                                    targets, count));
885:                }
886:                /*
887:                 * TODO: Implement other cases here (3D-GeographicCRS, etc.).
888:                 *       It may requires the creation of new CoordinateSystem objects,
889:                 *       which is why this method live in ReferencingFactoryContainer.
890:                 */
891:                throw new FactoryException(Errors
892:                        .format(ErrorKeys.CANT_SEPARATE_CRS_$1, crs.getName()
893:                                .getCode()));
894:            }
895:
896:            /**
897:             * Returns a temporary name for object derived from the specified one.
898:             */
899:            private static Map getTemporaryName(final IdentifiedObject source) {
900:                return Collections.singletonMap(IdentifiedObject.NAME_KEY,
901:                        source.getName().getCode() + " (3D)");
902:            }
903:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.