Source Code Cross Referenced for MathTransformBuilder.java in  » GIS » GeoTools-2.4.1 » org » geotools » referencing » operation » builder » 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.builder 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *    Geotools2 - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2006, Geotools Project Managment Committee (PMC)
005:         *
006:         *    This library is free software; you can redistribute it and/or
007:         *    modify it under the terms of the GNU Lesser General Public
008:         *    License as published by the Free Software Foundation;
009:         *    version 2.1 of the License.
010:         *
011:         *    This library is distributed in the hope that it will be useful,
012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014:         *    Lesser General Public License for more details.
015:         */
016:        package org.geotools.referencing.operation.builder;
017:
018:        // J2SE and extensions
019:        import java.util.Map;
020:        import java.util.HashMap;
021:        import java.util.List;
022:        import java.util.ArrayList;
023:        import java.util.Collections;
024:        import java.util.Iterator;
025:        import java.util.Locale;
026:        import java.io.Writer;
027:        import java.io.IOException;
028:        import java.io.StringWriter;
029:        import java.text.NumberFormat;
030:        import javax.vecmath.MismatchedSizeException;
031:
032:        // OpenGIS dependencies
033:        import org.opengis.util.InternationalString;
034:        import org.opengis.referencing.FactoryException;
035:        import org.opengis.referencing.cs.CartesianCS; // For javadoc only
036:        import org.opengis.referencing.cs.CoordinateSystem;
037:        import org.opengis.referencing.crs.*; // Includes imports used only for javadoc.
038:        import org.opengis.referencing.operation.*; // Includes imports used only for javadoc.
039:        import org.opengis.referencing.datum.DatumFactory;
040:        import org.opengis.metadata.extent.GeographicExtent;
041:        import org.opengis.metadata.extent.GeographicBoundingBox;
042:        import org.opengis.metadata.quality.EvaluationMethodType;
043:        import org.opengis.geometry.DirectPosition;
044:        import org.opengis.geometry.MismatchedDimensionException;
045:        import org.opengis.geometry.MismatchedReferenceSystemException;
046:
047:        // Geotools dependencies
048:        import org.geotools.factory.Hints;
049:        import org.geotools.io.TableWriter;
050:        import org.geotools.math.Statistics;
051:        import org.geotools.resources.XMath;
052:        import org.geotools.referencing.CRS;
053:        import org.geotools.referencing.ReferencingFactoryFinder;
054:        import org.geotools.referencing.cs.DefaultCartesianCS;
055:        import org.geotools.referencing.operation.DefaultOperationMethod;
056:        import org.geotools.referencing.operation.DefaultTransformation;
057:        import org.geotools.geometry.GeneralEnvelope;
058:        import org.geotools.geometry.GeneralDirectPosition;
059:        import org.geotools.metadata.iso.extent.ExtentImpl;
060:        import org.geotools.metadata.iso.extent.GeographicBoundingBoxImpl;
061:        import org.geotools.metadata.iso.quality.PositionalAccuracyImpl;
062:        import org.geotools.metadata.iso.quality.QuantitativeResultImpl;
063:        import org.geotools.resources.i18n.VocabularyKeys;
064:        import org.geotools.resources.i18n.Vocabulary;
065:        import org.geotools.resources.i18n.ErrorKeys;
066:        import org.geotools.resources.i18n.Errors;
067:        import org.geotools.resources.CRSUtilities;
068:        import org.geotools.resources.Utilities;
069:
070:        /**
071:         * Provides a basic implementation for {@linkplain MathTransform math transform}
072:         * builders.
073:         * 
074:         * Math transform builders create {@link MathTransform} objects for transforming 
075:         * coordinates from a source CRS 
076:         * ({@linkplain CoordinateReferenceSystem Coordinate Reference System}) to
077:         * a target CRS using empirical parameters. Usually, one of those CRS is a
078:         * {@linkplain GeographicCRS geographic} or {@linkplain ProjectedCRS projected}
079:         * one with a well known relationship to the earth. The other CRS is often an
080:         * {@linkplain EngineeringCRS engineering} or {@linkplain ImageCRS image} one
081:         * tied to some ship. For example a remote sensing image <em>before</em>
082:         * georectification may be referenced by an {@linkplain ImageCRS image CRS}.
083:         *
084:         * <blockquote><p><font size=-1><strong>Design note:</strong>
085:         * It is technically possible to reference such remote sensing images with a
086:         * {@linkplain DerivedCRS CRS derived} from the geographic or projected CRS,
087:         * where the {@linkplain DerivedCRS#getConversionFromBase conversion from base}
088:         * is the math transform {@linkplain #getMathTransform computed by this builder}.
089:         * Such approach is advantageous for {@linkplain CoordinateOperationFactory
090:         * coordinate operation factory} implementations, since they can determine the
091:         * operation just by inspection of the {@link DerivedCRS} instance. However this
092:         * is conceptually incorrect since {@link DerivedCRS} can be related to an other
093:         * CRS only through {@linkplain Conversion conversions}, which by definition are
094:         * accurate up to rounding errors. The operations created by math transform
095:         * builders are rather {@linkplain Transformation transformations}, which can't
096:         * be used for {@link DerivedCRS} creation.
097:         * </font></p></blockquote>
098:         *
099:         * The math transform from {@linkplain #getSourceCRS source CRS} to {@linkplain
100:         * #getTargetCRS target CRS} is calculated by {@code MathTransformBuilder} from
101:         * a set of {@linkplain #getMappedPositions mapped positions} in both CRS.
102:         * <p>
103:         * Subclasses must implement at least the {@link #getMinimumPointCount()} and
104:         * {@link #computeMathTransform()} methods.
105:         *
106:         * @since 2.4
107:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/builder/MathTransformBuilder.java $
108:         * @version $Id: MathTransformBuilder.java 28982 2008-01-28 16:27:33Z acuster $
109:         * @author Jan Jezek
110:         * @author Martin Desruisseaux
111:         */
112:        public abstract class MathTransformBuilder {
113:            /**
114:             * The list of mapped positions.
115:             */
116:            private final List/*<MappedPosition>*/positions = new ArrayList();
117:
118:            /**
119:             * An unmodifiable view of mapped positions to be returned by {@link #getMappedPositions}.
120:             */
121:            private final List/*<MappedPosition>*/unmodifiablePositions = Collections
122:                    .unmodifiableList(positions);
123:
124:            /**
125:             * Coordinate Reference System of the source and target points,
126:             * or {@code null} if unknown.
127:             */
128:            private CoordinateReferenceSystem sourceCRS, targetCRS;
129:
130:            /**
131:             * The math transform. Will be computed only when first needed.
132:             */
133:            private transient MathTransform transform;
134:
135:            /**
136:             * The transformation. Will be computed only when first needed.
137:             */
138:            private transient Transformation transformation;
139:
140:            /**
141:             * The factory to use for creating {@link MathTransform math transform} instances.
142:             */
143:            protected final MathTransformFactory mtFactory;
144:
145:            /**
146:             * The CRS factory to use for creating {@link EngineeringCRS} instances.
147:             */
148:            private final CRSFactory crsFactory;
149:
150:            /**
151:             * The datum factory to use for creating {@link EngineeringCRS} instances.
152:             */
153:            private final DatumFactory datumFactory;
154:
155:            /**
156:             * Creates a builder with the default factories.
157:             */
158:            public MathTransformBuilder() {
159:                this (null);
160:            }
161:
162:            /**
163:             * Creates a builder from the specified hints.
164:             */
165:            public MathTransformBuilder(final Hints hints) {
166:                mtFactory = ReferencingFactoryFinder
167:                        .getMathTransformFactory(hints);
168:                crsFactory = ReferencingFactoryFinder.getCRSFactory(hints);
169:                datumFactory = ReferencingFactoryFinder.getDatumFactory(hints);
170:            }
171:
172:            /**
173:             * Returns the name for the {@linkplain #getTransformation transformation} to
174:             * be created by this builder.
175:             */
176:            public String getName() {
177:                return Utilities.getShortClassName(this ) + " fit";
178:            }
179:
180:            /**
181:             * Returns the minimum number of points required by this builder. This minimum depends on the
182:             * algorithm used. For example {@linkplain AffineTransformBuilder affine transform builders}
183:             * require at least 3 points, while {@linkplain SimilarTransformBuilder similar transform
184:             * builders} requires only 2 points.
185:             */
186:            public abstract int getMinimumPointCount();
187:
188:            /**
189:             * Returns the dimension for {@linkplain #getSourceCRS source} and
190:             * {@link #getTargetCRS target} CRS. The default value is 2.
191:             */
192:            public int getDimension() {
193:                return 2;
194:            }
195:
196:            /**
197:             * Returns the list of mapped positions.
198:             */
199:            public List/*<MappedPosition>*/getMappedPositions() {
200:                return unmodifiablePositions;
201:            }
202:
203:            /**
204:             * Set the list of mapped positions.
205:             *
206:             * @throws MismatchedSizeException if the list doesn't have the expected number of points.
207:             * @throws MismatchedDimensionException if some points doesn't have the
208:             *         {@linkplain #getDimension expected number of dimensions}.
209:             * @throws MismatchedReferenceSystemException if CRS is not the same for all points.
210:             */
211:            public void setMappedPositions(
212:                    final List/*<MappedPosition>*/positions)
213:                    throws MismatchedSizeException,
214:                    MismatchedDimensionException,
215:                    MismatchedReferenceSystemException {
216:                final CoordinateReferenceSystem source, target;
217:                source = ensureValid(getPoints(positions, false),
218:                        "sourcePoints");
219:                target = ensureValid(getPoints(positions, true), "targetPoints");
220:                /*
221:                 * Now stores the informations. Note that we set the source and target CRS
222:                 * only after 'ensureValid' succeed for both CRS.
223:                 */
224:                this .positions.clear();
225:                this .positions.addAll(positions);
226:                this .sourceCRS = source;
227:                this .targetCRS = target;
228:                this .transform = null;
229:            }
230:
231:            /**
232:             * Extracts the source or target points from the specified list.
233:             *
234:             * @param positions The array where to take points from.
235:             * @param target {@code false} for extracting source points,
236:             *        or {@code true} for extracting target points.
237:             */
238:            private static DirectPosition[] getPoints(
239:                    List/*<MappedPosition>*/positions, boolean target) {
240:                final DirectPosition[] points = new DirectPosition[positions
241:                        .size()];
242:                for (int i = 0; i < points.length; i++) {
243:                    final MappedPosition mp = (MappedPosition) positions.get(i);
244:                    points[i] = target ? mp.getTarget() : mp.getSource();
245:                }
246:                return points;
247:            }
248:
249:            /**
250:             * Set the source or target points. Note: {@link #sourceCRS} or {@link #targetCRS} must be
251:             * setup appropriately before this method is invoked.
252:             *
253:             * @param points The new points to use.
254:             * @param target {@code false} for setting the source points,
255:             *        or {@code true} for setting the target points.
256:             *
257:             * @throws MismatchedSizeException if the array doesn't have the expected number of points.
258:             */
259:            private void setPoints(final DirectPosition[] points,
260:                    final boolean target) throws MismatchedSizeException {
261:                transform = null;
262:                final boolean add = positions.isEmpty();
263:                if (!add && points.length != positions.size()) {
264:                    throw new MismatchedSizeException(Errors
265:                            .format(ErrorKeys.MISMATCHED_ARRAY_LENGTH));
266:                }
267:                final int dimension = getDimension();
268:                for (int i = 0; i < points.length; i++) {
269:                    final MappedPosition mp;
270:                    if (add) {
271:                        mp = new MappedPosition(dimension);
272:                        positions.add(mp);
273:                    } else {
274:                        mp = (MappedPosition) positions.get(i);
275:                    }
276:                    final DirectPosition point = points[i];
277:                    if (target) {
278:                        mp.setTarget(point);
279:                    } else {
280:                        mp.setSource(point);
281:                    }
282:                }
283:            }
284:
285:            /**
286:             * Returns the source points. This convenience method extracts those points from
287:             * the {@linkplain #getMappedPositions mapped positions}.
288:             */
289:            public DirectPosition[] getSourcePoints() {
290:                final DirectPosition[] points = getPoints(getMappedPositions(),
291:                        false);
292:                assert ensureValid(points, "sourcePoints", sourceCRS);
293:                return points;
294:            }
295:
296:            /**
297:             * Convenience method setting the {@linkplain MappedPosition#getSource source points}
298:             * in mapped positions.
299:             *
300:             * @param  points The source points.
301:             * @throws MismatchedSizeException if the list doesn't have the expected number of points.
302:             * @throws MismatchedDimensionException if some points doesn't have the
303:             *         {@linkplain #getDimension expected number of dimensions}.
304:             * @throws MismatchedReferenceSystemException if CRS is not the same for all points.
305:             */
306:            public void setSourcePoints(final DirectPosition[] points)
307:                    throws MismatchedSizeException,
308:                    MismatchedDimensionException,
309:                    MismatchedReferenceSystemException {
310:                // Set the points only after we checked them.
311:                sourceCRS = ensureValid(points, "sourcePoints");
312:                setPoints(points, false);
313:            }
314:
315:            /**
316:             * Returns the target points. This convenience method extracts those points from
317:             * the {@linkplain #getMappedPositions mapped positions}.
318:             */
319:            public DirectPosition[] getTargetPoints() {
320:                final DirectPosition[] points = getPoints(getMappedPositions(),
321:                        true);
322:                assert ensureValid(points, "targetPoints", targetCRS);
323:                return points;
324:            }
325:
326:            /**
327:             * Convenience method setting the {@linkplain MappedPosition#getTarget target points}
328:             * in mapped positions.
329:             *
330:             * @param  points The target points.
331:             * @throws MismatchedSizeException if the list doesn't have the expected number of points.
332:             * @throws MismatchedDimensionException if some points doesn't have the
333:             *         {@linkplain #getDimension expected number of dimensions}.
334:             * @throws MismatchedReferenceSystemException if CRS is not the same for all points.
335:             */
336:            public void setTargetPoints(final DirectPosition[] points)
337:                    throws MismatchedSizeException,
338:                    MismatchedDimensionException,
339:                    MismatchedReferenceSystemException {
340:                // Set the points only after we checked them.
341:                targetCRS = ensureValid(points, "targetPoints");
342:                setPoints(points, true);
343:            }
344:
345:            /**
346:             * Prints a table of all source and target points stored in this builder.
347:             *
348:             * @param  out The output device where to print all points.
349:             * @param  locale The locale, or {@code null} for the default.
350:             * @throws IOException if an error occured while printing.
351:             *
352:             * @todo Insert a double-line column separator between the source and target points.
353:             */
354:            public void printPoints(final Writer out, Locale locale)
355:                    throws IOException {
356:                if (locale == null) {
357:                    locale = Locale.getDefault();
358:                }
359:                final NumberFormat source = getNumberFormat(locale, false);
360:                final NumberFormat target = getNumberFormat(locale, true);
361:                final TableWriter table = new TableWriter(out, " \u2502 ");
362:                table.setAlignment(TableWriter.ALIGN_CENTER);
363:                table.writeHorizontalSeparator();
364:                try {
365:                    final CoordinateSystem sourceCS = getSourceCRS()
366:                            .getCoordinateSystem();
367:                    final CoordinateSystem targetCS = getTargetCRS()
368:                            .getCoordinateSystem();
369:                    int dimension = sourceCS.getDimension();
370:                    for (int i = 0; i < dimension; i++) {
371:                        table.write(sourceCS.getAxis(i).getName().getCode());
372:                        table.nextColumn();
373:                    }
374:                    dimension = targetCS.getDimension();
375:                    for (int i = 0; i < dimension; i++) {
376:                        table.write(targetCS.getAxis(i).getName().getCode());
377:                        table.nextColumn();
378:                    }
379:                    table.writeHorizontalSeparator();
380:                } catch (FactoryException e) {
381:                    /*
382:                     * Ignore. The only consequences is that the table will not
383:                     * contains a title line.
384:                     */
385:                }
386:                table.setAlignment(TableWriter.ALIGN_RIGHT);
387:                for (final Iterator it = getMappedPositions().iterator(); it
388:                        .hasNext();) {
389:                    final MappedPosition mp = (MappedPosition) it.next();
390:                    DirectPosition point = mp.getSource();
391:                    int dimension = point.getDimension();
392:                    for (int i = 0; i < dimension; i++) {
393:                        table.write(source.format(point.getOrdinate(i)));
394:                        table.nextColumn();
395:                    }
396:                    point = mp.getTarget();
397:                    dimension = point.getDimension();
398:                    for (int i = 0; i < dimension; i++) {
399:                        table.write(target.format(point.getOrdinate(i)));
400:                        table.nextColumn();
401:                    }
402:                    table.nextLine();
403:                }
404:                table.writeHorizontalSeparator();
405:                table.flush();
406:            }
407:
408:            /**
409:             * Returns the coordinate reference system for the {@link #getSourcePoints source points}.
410:             * This method determines the CRS as below:
411:             * <p>
412:             * <ul>
413:             *   <li>If at least one source points has a CRS, then this CRS is selected
414:             *       as the source one and returned.</li>
415:             *   <li>If no source point has a CRS, then this method creates an
416:             *       {@linkplain EngineeringCRS engineering CRS} using the same
417:             *       {@linkplain CoordinateSystem coordinate system} than the one used
418:             *       by the {@linkplain #getTargetCRS target CRS}.</li>
419:             * </ul>
420:             *
421:             * @throws FactoryException if the CRS can't be created.
422:             */
423:            public CoordinateReferenceSystem getSourceCRS()
424:                    throws FactoryException {
425:                if (sourceCRS == null) {
426:                    sourceCRS = createEngineeringCRS(false);
427:                }
428:                assert sourceCRS.getCoordinateSystem().getDimension() == getDimension();
429:                return sourceCRS;
430:            }
431:
432:            /**
433:             * Returns the coordinate reference system for the {@link #getTargetPoints target points}.
434:             * This method determines the CRS as below:
435:             * <p>
436:             * <ul>
437:             *   <li>If at least one target points has a CRS, then this CRS is selected
438:             *       as the target one and returned.</li>
439:             *   <li>If no target point has a CRS, then this method creates an
440:             *       {@linkplain EngineeringCRS engineering CRS} using the same
441:             *       {@linkplain CoordinateSystem coordinate system} than the one used
442:             *       by the {@linkplain #getSourceCRS source CRS}.</li>
443:             * </ul>
444:             *
445:             * @throws FactoryException if the CRS can't be created.
446:             */
447:            public CoordinateReferenceSystem getTargetCRS()
448:                    throws FactoryException {
449:                if (targetCRS == null) {
450:                    targetCRS = createEngineeringCRS(true);
451:                }
452:                assert targetCRS.getCoordinateSystem().getDimension() == getDimension();
453:                return targetCRS;
454:            }
455:
456:            /**
457:             * Creates an engineering CRS using the same {@linkplain CoordinateSystem
458:             * coordinate system} than the existing CRS, and an area of validity
459:             * determined from the specified points. This method is used for creating
460:             * a {@linkplain #getTargetCRS target CRS} from the
461:             * {@linkplain #getSourceCRS source CRS}, or conversely.
462:             *
463:             * @param target {@code false} for creating the source CRS, or
464:             *        or {@code true} for creating the target CRS.
465:             * @throws FactoryException if the CRS can't be created.
466:             */
467:            private EngineeringCRS createEngineeringCRS(final boolean target)
468:                    throws FactoryException {
469:                final Map properties = new HashMap(4);
470:                properties.put(CoordinateReferenceSystem.NAME_KEY, Vocabulary
471:                        .format(VocabularyKeys.UNKNOW));
472:                final GeographicExtent validArea = getValidArea(target);
473:                if (validArea != null) {
474:                    final ExtentImpl extent = new ExtentImpl();
475:                    extent.getGeographicElements().add(validArea);
476:                    properties.put(
477:                            CoordinateReferenceSystem.DOMAIN_OF_VALIDITY_KEY,
478:                            extent.unmodifiable());
479:                }
480:                final CoordinateReferenceSystem oppositeCRS = target ? sourceCRS
481:                        : targetCRS;
482:                final CoordinateSystem cs;
483:                if (oppositeCRS != null) {
484:                    cs = oppositeCRS.getCoordinateSystem();
485:                } else {
486:                    switch (getDimension()) {
487:                    case 2:
488:                        cs = DefaultCartesianCS.GENERIC_2D;
489:                        break;
490:                    case 3:
491:                        cs = DefaultCartesianCS.GENERIC_3D;
492:                        break;
493:                    default:
494:                        throw new FactoryException(Errors
495:                                .format(ErrorKeys.UNSPECIFIED_CRS));
496:                    }
497:                }
498:                return crsFactory.createEngineeringCRS(properties, datumFactory
499:                        .createEngineeringDatum(properties), cs);
500:            }
501:
502:            /**
503:             * Returns a default format for source or target points.
504:             * The precision is computed from the envelope.
505:             */
506:            private NumberFormat getNumberFormat(final Locale locale,
507:                    final boolean target) {
508:                final NumberFormat format = NumberFormat
509:                        .getNumberInstance(locale);
510:                final GeneralEnvelope envelope = getEnvelope(target);
511:                double length = 0;
512:                for (int i = envelope.getDimension(); --i >= 0;) {
513:                    final double candidate = envelope.getLength(i);
514:                    if (candidate > length) {
515:                        length = candidate;
516:                    }
517:                }
518:                if (length > 0) {
519:                    final int digits = Math.max(0, 3 - (int) Math.ceil(XMath
520:                            .log10(length)));
521:                    if (digits < 16) {
522:                        format.setMinimumFractionDigits(digits);
523:                        format.setMaximumFractionDigits(digits);
524:                    }
525:                }
526:                return format;
527:            }
528:
529:            /**
530:             * Returns an envelope that contains fully all the specified points.
531:             * If the envelope can't be calculated, then this method returns {@code null}.
532:             *
533:             * @param target {@code false} for the envelope of source points,
534:             *        or {@code true} for the envelope of target points.
535:             */
536:            private GeneralEnvelope getEnvelope(final boolean target) {
537:                GeneralEnvelope envelope = null;
538:                CoordinateReferenceSystem crs = null;
539:                for (final Iterator it = getMappedPositions().iterator(); it
540:                        .hasNext();) {
541:                    final MappedPosition mp = (MappedPosition) it.next();
542:                    final DirectPosition point = target ? mp.getTarget() : mp
543:                            .getSource();
544:                    if (point != null) {
545:                        if (envelope == null) {
546:                            final double[] coordinates = point.getCoordinates();
547:                            envelope = new GeneralEnvelope(coordinates,
548:                                    coordinates);
549:                        } else {
550:                            envelope.add(point);
551:                        }
552:                        crs = getCoordinateReferenceSystem(point, crs);
553:                    }
554:                }
555:                if (envelope != null) {
556:                    envelope.setCoordinateReferenceSystem(crs);
557:                }
558:                return envelope;
559:            }
560:
561:            /**
562:             * Returns a geographic extent that contains fully all the specified points.
563:             * If the envelope can't be calculated, then this method returns {@code null}.
564:             *
565:             * @param target {@code false} for the valid area of source points,
566:             *        or {@code true} for the valid area of target points.
567:             */
568:            private GeographicBoundingBox getValidArea(final boolean target) {
569:                GeneralEnvelope envelope = getEnvelope(target);
570:                if (envelope != null)
571:                    try {
572:                        return new GeographicBoundingBoxImpl(envelope);
573:                    } catch (TransformException exception) {
574:                        /*
575:                         * Can't transform the envelope. Do not rethrown this exception. We don't
576:                         * log it neither (at least not at the warning level) because this method
577:                         * is optional.
578:                         */
579:                    }
580:                return null;
581:            }
582:
583:            /**
584:             * Returns the CRS of the specified point. If the CRS of the previous point is known,
585:             * it can be specified. This method will then ensure that the two CRS are compatibles.
586:             */
587:            private static CoordinateReferenceSystem getCoordinateReferenceSystem(
588:                    final DirectPosition point,
589:                    CoordinateReferenceSystem previousCRS)
590:                    throws MismatchedReferenceSystemException {
591:                final CoordinateReferenceSystem candidate = point
592:                        .getCoordinateReferenceSystem();
593:                if (candidate != null) {
594:                    if (previousCRS == null) {
595:                        return candidate;
596:                    }
597:                    /*
598:                     * We use strict 'equals' instead of 'equalsIgnoreCase' because if the metadata
599:                     * are not identical, we have no easy way to choose which CRS is the "main" one.
600:                     */
601:                    if (!previousCRS.equals(candidate)) {
602:                        throw new MismatchedReferenceSystemException(
603:                                Errors
604:                                        .format(ErrorKeys.MISMATCHED_COORDINATE_REFERENCE_SYSTEM));
605:                    }
606:                }
607:                return previousCRS;
608:            }
609:
610:            /**
611:             * Returns the required coordinate system type. The default implementation returns
612:             * {@code CoordinateSystem.class}, which means that every kind of coordinate system
613:             * is legal. Some subclasses will restrict to {@linkplain CartesianCS cartesian CS}.
614:             */
615:            public Class/*<? extends CoordinateSystem>*/getCoordinateSystemType() {
616:                return CoordinateSystem.class;
617:            }
618:
619:            /**
620:             * Ensures that the specified list of points is valid, and returns their CRS.
621:             *
622:             * @param points The points to check.
623:             * @param label  The argument name, used for formatting error message only.
624:             *
625:             * @throws MismatchedSizeException if the list doesn't have the expected number of points.
626:             * @throws MismatchedDimensionException if some points doesn't have the
627:             *         {@linkplain #getDimension expected number of dimensions}.
628:             * @throws MismatchedReferenceSystemException if CRS is not the same for all points.
629:             * @return The CRS used for the specified points, or {@code null} if unknown.
630:             */
631:            private CoordinateReferenceSystem ensureValid(
632:                    final DirectPosition[] points, final String label)
633:                    throws MismatchedSizeException,
634:                    MismatchedDimensionException,
635:                    MismatchedReferenceSystemException {
636:                final int necessaryNumber = getMinimumPointCount();
637:                if (points.length < necessaryNumber) {
638:                    throw new MismatchedSizeException(Errors.format(
639:                            ErrorKeys.INSUFFICIENT_POINTS_$2, new Integer(
640:                                    points.length),
641:                            new Integer(necessaryNumber)));
642:                }
643:                CoordinateReferenceSystem crs = null;
644:                final int dimension = getDimension();
645:                for (int i = 0; i < points.length; i++) {
646:                    final DirectPosition point = points[i];
647:                    final int pointDim = point.getDimension();
648:                    if (pointDim != dimension) {
649:                        throw new MismatchedDimensionException(Errors.format(
650:                                ErrorKeys.MISMATCHED_DIMENSION_$3, label + '['
651:                                        + i + ']', new Integer(pointDim),
652:                                new Integer(dimension)));
653:                    }
654:                    crs = getCoordinateReferenceSystem(point, crs);
655:                }
656:                if (crs != null) {
657:                    final CoordinateSystem cs = crs.getCoordinateSystem();
658:                    if (!getCoordinateSystemType().isAssignableFrom(
659:                            cs.getClass())) {
660:                        throw new MismatchedReferenceSystemException(
661:                                Errors
662:                                        .format(
663:                                                ErrorKeys.UNSUPPORTED_COORDINATE_SYSTEM_$1,
664:                                                cs.getName()));
665:                    }
666:                }
667:                return crs;
668:            }
669:
670:            /**
671:             * Used for assertions only.
672:             */
673:            private boolean ensureValid(final DirectPosition[] points,
674:                    final String label, final CoordinateReferenceSystem expected) {
675:                final CoordinateReferenceSystem actual = ensureValid(points,
676:                        label);
677:                return actual == null || actual == expected;
678:            }
679:
680:            /**
681:             * Returns statistics about the errors. The errors are computed as the distance between
682:             * {@linkplain #getSourcePoints source points} transformed by the math transform computed
683:             * by this {@code MathTransformBuilder}, and the {@linkplain #getTargetPoints target points}.
684:             * Use {@link Statistics#rms} for the <cite>Root Mean Squared error</cite>.
685:             *
686:             * @throws FactoryException If the math transform can't be created or used.
687:             */
688:            public Statistics getErrorStatistics() throws FactoryException {
689:                final MathTransform mt = getMathTransform();
690:                final Statistics stats = new Statistics();
691:                final DirectPosition buffer = new GeneralDirectPosition(
692:                        getDimension());
693:                for (final Iterator it = getMappedPositions().iterator(); it
694:                        .hasNext();) {
695:                    final MappedPosition mp = (MappedPosition) it.next();
696:                    /*
697:                     * Transforms the source point using the math transform calculated by this class.
698:                     * If the transform can't be applied, then we consider this failure as if it was
699:                     * a factory error rather than a transformation error. This simplify the exception
700:                     * declaration, but also has some sense on a conceptual point of view. We are
701:                     * transforming the exact same points than the one used for creating the math
702:                     * transform. If one of those points can't be transformed, then there is probably
703:                     * something wrong with the transform we just created.
704:                     */
705:                    final double error;
706:                    try {
707:                        error = mp.getError(mt, buffer);
708:                    } catch (TransformException e) {
709:                        throw new FactoryException(Errors
710:                                .format(ErrorKeys.CANT_TRANSFORM_VALID_POINTS),
711:                                e);
712:                    }
713:                    stats.add(error);
714:                }
715:                return stats;
716:            }
717:
718:            /**
719:             * Calculates the math transform immediately.
720:             *
721:             * @return Math transform from {@link #setMappedPositions MappedPosition}. 
722:             * @throws FactoryException if the math transform can't be created.
723:             */
724:            protected abstract MathTransform computeMathTransform()
725:                    throws FactoryException;
726:
727:            /**
728:             * Returns the calculated math transform. This method {@linkplain #computeMathTransform the math
729:             * transform} the first time it is requested.
730:             *
731:             * @return Math transform from {@link #setMappedPositions MappedPosition}. 
732:             * @throws FactoryException if the math transform can't be created.
733:             */
734:            public final MathTransform getMathTransform()
735:                    throws FactoryException {
736:                if (transform == null) {
737:                    transform = computeMathTransform();
738:                }
739:                return transform;
740:            }
741:
742:            /**
743:             * Returns the coordinate operation wrapping the {@linkplain #getMathTransform() calculated
744:             * math transform}. The {@linkplain Transformation#getPositionalAccuracy positional
745:             * accuracy} will be set to the Root Mean Square (RMS) of the differences between the
746:             * source points transformed to the target CRS, and the expected target points.
747:             */
748:            public Transformation getTransformation() throws FactoryException {
749:                if (transformation == null) {
750:                    final Map properties = new HashMap();
751:                    properties.put(Transformation.NAME_KEY, getName());
752:                    /*
753:                     * Set the valid area as the intersection of source CRS and target CRS valid area.
754:                     */
755:                    final CoordinateReferenceSystem sourceCRS = getSourceCRS();
756:                    final CoordinateReferenceSystem targetCRS = getTargetCRS();
757:                    final GeographicBoundingBox sourceBox = CRS
758:                            .getGeographicBoundingBox(sourceCRS);
759:                    final GeographicBoundingBox targetBox = CRS
760:                            .getGeographicBoundingBox(targetCRS);
761:                    final GeographicBoundingBox validArea;
762:                    if (sourceBox == null) {
763:                        validArea = targetBox;
764:                    } else if (targetBox == null) {
765:                        validArea = sourceBox;
766:                    } else {
767:                        final GeneralEnvelope area = new GeneralEnvelope(
768:                                sourceBox);
769:                        area.intersect(new GeneralEnvelope(sourceBox));
770:                        try {
771:                            validArea = new GeographicBoundingBoxImpl(area);
772:                        } catch (TransformException e) {
773:                            // Should never happen, because we know that 'area' CRS is WGS84.
774:                            throw new AssertionError(e);
775:                        }
776:                    }
777:                    if (validArea != null) {
778:                        final ExtentImpl extent = new ExtentImpl();
779:                        extent.getGeographicElements().add(validArea);
780:                        properties.put(Transformation.DOMAIN_OF_VALIDITY_KEY,
781:                                extent.unmodifiable());
782:                    }
783:                    /*
784:                     * Computes the positional accuracy as the RMS value of differences
785:                     * between the computed target points and the supplied target points.
786:                     */
787:                    final double error = getErrorStatistics().rms();
788:                    if (!Double.isNaN(error)) {
789:                        final InternationalString description = Vocabulary
790:                                .formatInternational(VocabularyKeys.ROOT_MEAN_SQUARED_ERROR);
791:                        final QuantitativeResultImpl result = new QuantitativeResultImpl();
792:                        result.setValues(new double[] { error });
793:                        //result.setValueType(Double.TYPE);
794:                        result.setValueUnit(CRSUtilities.getUnit(targetCRS
795:                                .getCoordinateSystem()));
796:                        result.setErrorStatistic(description);
797:                        final PositionalAccuracyImpl accuracy = new PositionalAccuracyImpl(
798:                                result);
799:                        accuracy
800:                                .setEvaluationMethodType(EvaluationMethodType.DIRECT_INTERNAL);
801:                        accuracy.setEvaluationMethodDescription(description);
802:                        properties
803:                                .put(
804:                                        Transformation.COORDINATE_OPERATION_ACCURACY_KEY,
805:                                        accuracy.unmodifiable());
806:                    }
807:                    /*
808:                     * Now creates the transformation.
809:                     */
810:                    final MathTransform transform = getMathTransform();
811:                    transformation = new DefaultTransformation(properties,
812:                            sourceCRS, targetCRS, transform,
813:                            new DefaultOperationMethod(transform));
814:                }
815:                return transformation;
816:            }
817:
818:            /**
819:             * Returns a string representation of this builder. The default implementation
820:             * returns a table containing all source and target points.
821:             */
822:            public String toString() {
823:                final StringWriter out = new StringWriter();
824:                try {
825:                    printPoints(out, null);
826:                } catch (IOException e) {
827:                    // Should never happen, since we are printing to a StringWriter.
828:                    throw new AssertionError(e);
829:                }
830:                return out.toString();
831:            }
832:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.