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


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *
005:         *   (C) 2003-2006, Geotools Project Managment Committee (PMC)
006:         *   (C) 2001, Institut de Recherche pour le D�veloppement
007:         *
008:         *    This library is free software; you can redistribute it and/or
009:         *    modify it under the terms of the GNU Lesser General Public
010:         *    License as published by the Free Software Foundation; either
011:         *    version 2.1 of the License, or (at your option) any later version.
012:         *
013:         *    This library is distributed in the hope that it will be useful,
014:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
015:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
016:         *    Lesser General Public License for more details.
017:         */
018:        package org.geotools.referencing.operation.transform;
019:
020:        // J2SE dependencies
021:        import java.util.Arrays;
022:
023:        // OpenGIS dependencies
024:        import org.opengis.referencing.FactoryException;
025:        import org.opengis.referencing.operation.Matrix;
026:        import org.opengis.referencing.operation.MathTransform;
027:        import org.opengis.referencing.operation.MathTransformFactory;
028:
029:        // Geotools dependencies
030:        import org.geotools.factory.Hints;
031:        import org.geotools.referencing.ReferencingFactoryFinder;
032:        import org.geotools.referencing.operation.LinearTransform;
033:        import org.geotools.referencing.operation.matrix.XMatrix;
034:        import org.geotools.referencing.operation.matrix.MatrixFactory;
035:        import org.geotools.referencing.operation.matrix.GeneralMatrix;
036:        import org.geotools.resources.XArray;
037:        import org.geotools.resources.i18n.Errors;
038:        import org.geotools.resources.i18n.ErrorKeys;
039:
040:        /**
041:         * An utility class for the separation of {@linkplain ConcatenatedTransform concatenation} of
042:         * {@linkplain PassThroughTransform pass through transforms}. Given an arbitrary
043:         * {@linkplain MathTransform math transform}, this utility class will returns a new math transform
044:         * that operates only of a given set of source dimensions. For example if the supplied
045:         * {@code transform} has (<var>x</var>, <var>y</var>, <var>z</var>) inputs and
046:         * (<var>longitude</var>, <var>latitude</var>, <var>height</var>) outputs, then
047:         * the following code:
048:         *
049:         * <blockquote><pre>
050:         * {@linkplain #addSourceDimensionRange addSourceDimensionRange}(0, 2);
051:         * MathTransform mt = {@linkplain #separate separate}(transform);
052:         * </pre></blockquote>
053:         *
054:         * <P>will returns a transform with (<var>x</var>, <var>y</var>) inputs and (probably)
055:         * (<var>longitude</var>, <var>latitude</var>) outputs. The later can be verified with
056:         * a call to {@link #getTargetDimensions}.</P>
057:         *
058:         * @since 2.1
059:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/DimensionFilter.java $
060:         * @version $Id: DimensionFilter.java 29058 2008-02-03 17:47:07Z desruisseaux $
061:         * @author Martin Desruisseaux
062:         *
063:         * @todo This class is specific to Geotools implementation; it is better to avoid it if
064:         *       you can. It could be generalized a bit if we perform the same operations on
065:         *       {@link org.opengis.referencing.operation.CoordinateOperation} interfaces instead
066:         *       of math transforms. We should revisit this issue after grid coverage API has been
067:         *       revisited (since grid coverage is a user of this class).
068:         *
069:         * @todo This class contains a set of static methods that could be factored out in
070:         *       some kind of {@code org.geotools.util.SortedIntegerSet} implementation.
071:         */
072:        public class DimensionFilter {
073:            /**
074:             * The input dimensions to keep.
075:             * This sequence can contains any integers in the range 0 inclusive to
076:             * <code>transform.{@linkplain MathTransform#getSourceDimensions getSourceDimensions()}</code>
077:             * exclusive.
078:             */
079:            private int[] sourceDimensions;
080:
081:            /**
082:             * The output dimensions to keep.
083:             * This sequence can contains any integers in the range 0 inclusive to
084:             * <code>transform.{@linkplain MathTransform#getTargetDimensions getTargetDimensions()}</code>
085:             * exclusive.
086:             */
087:            private int[] targetDimensions;
088:
089:            /**
090:             * The factory for the creation of new math transforms.
091:             */
092:            private final MathTransformFactory factory;
093:
094:            /**
095:             * Constructs a dimension filter with the
096:             * {@linkplain ReferencingFactoryFinder#getMathTransformFactory default math transform factory}.
097:             */
098:            public DimensionFilter() {
099:                this (ReferencingFactoryFinder.getMathTransformFactory(null));
100:            }
101:
102:            /**
103:             * Constructs a dimension filter with a
104:             * {@linkplain ReferencingFactoryFinder#getMathTransformFactory math transform factory
105:             * built using the provided hints}.
106:             *
107:             * @param hints Hints to control the creation of the {@link MathTransformFactory}.
108:             */
109:            public DimensionFilter(Hints hints) {
110:                this (ReferencingFactoryFinder.getMathTransformFactory(hints));
111:            }
112:
113:            /**
114:             * Constructs a dimension filter with the specified factory.
115:             *
116:             * @param factory The factory for the creation of new math transforms.
117:             */
118:            public DimensionFilter(final MathTransformFactory factory) {
119:                this .factory = factory;
120:            }
121:
122:            /**
123:             * Clear any {@linkplain #getSourceDimensions source} and
124:             * {@linkplain #getTargetDimensions target dimension} setting.
125:             */
126:            public void clear() {
127:                sourceDimensions = null;
128:                targetDimensions = null;
129:            }
130:
131:            /**
132:             * Add an input dimension to keep. The {@code dimension} applies to the
133:             * source dimensions of the transform to be given to
134:             * <code>{@linkplain #separate separate}(transform)</code>.
135:             * The number must be in the range 0 inclusive to
136:             * <code>transform.{@linkplain MathTransform#getSourceDimensions getSourceDimensions()}</code>
137:             * exclusive.
138:             *
139:             * @param  dimension The dimension to add.
140:             * @throws IllegalArgumentException if {@code dimension} is negative.
141:             */
142:            public void addSourceDimension(final int dimension)
143:                    throws IllegalArgumentException {
144:                sourceDimensions = add(sourceDimensions, dimension);
145:            }
146:
147:            /**
148:             * Add input dimensions to keep. The {@code dimensions} apply to the
149:             * source dimensions of the transform to be given to
150:             * <code>{@linkplain #separate separate}(transform)</code>.
151:             * All numbers must be in the range 0 inclusive to
152:             * <code>transform.{@linkplain MathTransform#getSourceDimensions getSourceDimensions()}</code>
153:             * exclusive. The {@code dimensions} values must be in strictly increasing order.
154:             *
155:             * @param  dimensions The new sequence of dimensions.
156:             * @throws IllegalArgumentException if {@code dimensions} contains negative values or
157:             *         is not a strictly increasing sequence.
158:             */
159:            public void addSourceDimensions(final int[] dimensions)
160:                    throws IllegalArgumentException {
161:                sourceDimensions = add(sourceDimensions, dimensions);
162:            }
163:
164:            /**
165:             * Add a range of input dimensions to keep. The {@code lower} and {@code upper} values
166:             * apply to the source dimensions of the transform to be given to
167:             * <code>{@linkplain #separate separate}(transform)</code>.
168:             *
169:             * @param lower The lower dimension, inclusive. Must not be smaller than 0.
170:             * @param upper The upper dimension, exclusive. Must not be greater than
171:             * <code>transform.{@linkplain MathTransform#getSourceDimensions getSourceDimensions()}</code>.
172:             */
173:            public void addSourceDimensionRange(final int lower, final int upper)
174:                    throws IllegalArgumentException {
175:                sourceDimensions = add(sourceDimensions, lower, upper);
176:            }
177:
178:            /**
179:             * Returns the input dimensions. This information is available only if at least one
180:             * setter method has been explicitly invoked for source dimensions.
181:             *
182:             * @return The input dimension as a sequence of strictly increasing values.
183:             * @throws IllegalStateException if input dimensions have not been set.
184:             */
185:            public int[] getSourceDimensions() throws IllegalStateException {
186:                if (sourceDimensions != null) {
187:                    return (int[]) sourceDimensions.clone();
188:                }
189:                throw new IllegalStateException();
190:            }
191:
192:            /**
193:             * Add an output dimension to keep. The {@code dimension} applies to the
194:             * target dimensions of the transform to be given to
195:             * <code>{@linkplain #separate separate}(transform)</code>.
196:             * The number must be in the range 0 inclusive to
197:             * <code>transform.{@linkplain MathTransform#getTargetDimensions getTargetDimensions()}</code>
198:             * exclusive.
199:             *
200:             * @param  dimension The dimension to add.
201:             * @throws IllegalArgumentException if {@code dimension} is negative.
202:             */
203:            public void addTargetDimension(final int dimension)
204:                    throws IllegalArgumentException {
205:                targetDimensions = add(targetDimensions, dimension);
206:            }
207:
208:            /**
209:             * Add output dimensions to keep. The {@code dimensions} apply to the
210:             * target dimensions of the transform to be given to
211:             * <code>{@linkplain #separate separate}(transform)</code>.
212:             * All numbers must be in the range 0 inclusive to
213:             * <code>transform.{@linkplain MathTransform#getTargetDimensions getTargetDimensions()}</code>
214:             * exclusive. The {@code dimensions} values must be in strictly increasing order.
215:             *
216:             * @param  dimensions The new sequence of dimensions.
217:             * @throws IllegalArgumentException if {@code dimensions} contains negative values or
218:             *         is not a strictly increasing sequence.
219:             */
220:            public void addTargetDimensions(int[] dimensions)
221:                    throws IllegalArgumentException {
222:                targetDimensions = add(targetDimensions, dimensions);
223:            }
224:
225:            /**
226:             * Add a range of output dimensions to keep. The {@code lower} and {@code upper} values
227:             * apply to the target dimensions of the transform to be given to
228:             * <code>{@linkplain #separate separate}(transform)</code>.
229:             *
230:             * @param lower The lower dimension, inclusive. Must not be smaller than 0.
231:             * @param upper The upper dimension, exclusive. Must not be greater than
232:             * <code>transform.{@linkplain MathTransform#getTargetDimensions getTargetDimensions()}</code>.
233:             */
234:            public void addTargetDimensionRange(final int lower, final int upper)
235:                    throws IllegalArgumentException {
236:                targetDimensions = add(targetDimensions, lower, upper);
237:            }
238:
239:            /**
240:             * Returns the output dimensions. This information is available only if one of the following
241:             * conditions is meet:
242:             *
243:             * <ul>
244:             *   <li>Target dimensions has been explicitly set using setter methods.</li>
245:             *   <li>No target dimensions were set but <code>{@linkplain #separate separate}(transform)</code>
246:             *       has been invoked at least once, in which case the target dimensions are inferred
247:             *       automatically from the {@linkplain #getSourceDimensions source dimensions} and the
248:             *       {@code transform}.</li>
249:             * </ul>
250:             *
251:             * @return The output dimension as a sequence of strictly increasing values.
252:             * @throws IllegalStateException if this information is not available.
253:             */
254:            public int[] getTargetDimensions() throws IllegalStateException {
255:                if (targetDimensions != null) {
256:                    return (int[]) targetDimensions.clone();
257:                }
258:                throw new IllegalStateException();
259:            }
260:
261:            /**
262:             * Separates the specified math transform. This method returns a math transform that uses
263:             * only the specified {@linkplain #getSourceDimensions source dimensions} and returns only
264:             * the specified {@linkplain #getTargetDimensions target dimensions}. Special case:
265:             *
266:             * <ul>
267:             *   <li><p>If {@linkplain #getSourceDimensions source dimensions} are unspecified, then the
268:             *       returned transform will expects all source dimensions as input but will produces only
269:             *       the specified {@linkplain #getTargetDimensions target dimensions} as output.</p></li>
270:             *
271:             *   <li><p>If {@linkplain #getTargetDimensions target dimensions} are unspecified, then the
272:             *       returned transform will expects only the specified {@linkplain #getSourceDimensions
273:             *       source dimensions} as input, and the target dimensions will be inferred
274:             *       automatically.</p></li>
275:             * </ul>
276:             *
277:             * @param  transform The transform to separate.
278:             * @return The separated math transform.
279:             * @throws FactoryException if the transform can't be separated.
280:             */
281:            public MathTransform separate(MathTransform transform)
282:                    throws FactoryException {
283:                if (sourceDimensions == null) {
284:                    sourceDimensions = series(0, transform
285:                            .getSourceDimensions());
286:                    if (targetDimensions == null) {
287:                        targetDimensions = series(0, transform
288:                                .getTargetDimensions());
289:                        return transform;
290:                    }
291:                    return separateOutput(transform);
292:                }
293:                final int[] target = targetDimensions;
294:                transform = separateInput(transform);
295:                assert XArray.isStrictlySorted(targetDimensions);
296:                if (target != null) {
297:                    final int[] step = targetDimensions;
298:                    targetDimensions = new int[target.length];
299:                    for (int i = 0; i < target.length; i++) {
300:                        final int j = Arrays.binarySearch(step, target[i]);
301:                        if (j < 0) {
302:                            /*
303:                             * The user is asking for some target dimensions that we can't keep, probably
304:                             * because at least one of the requested target dimension as a dependency to
305:                             * an source dimension that do not appears in the list of source dimensions to
306:                             * kept.
307:                             *
308:                             * TODO: provide a more accurate error message.
309:                             */
310:                            throw new FactoryException(Errors
311:                                    .format(ErrorKeys.INSEPARABLE_TRANSFORM));
312:                        }
313:                        targetDimensions[i] = j;
314:                    }
315:                    transform = separateOutput(transform);
316:                    targetDimensions = target;
317:                }
318:                assert sourceDimensions.length == transform
319:                        .getSourceDimensions() : transform;
320:                assert targetDimensions.length == transform
321:                        .getTargetDimensions() : transform;
322:                return transform;
323:            }
324:
325:            /**
326:             * Separates the math transform on the basis of {@linkplain #sourceDimensions input dimensions}.
327:             * The remaining {@linkplain #targetDimensions output dimensions} will be selected automatically
328:             * according the specified input dimensions.
329:             *
330:             * @param  transform The transform to reduces.
331:             * @return A transform expecting only the specified input dimensions.
332:             * @throws FactoryException if the transform is not separable.
333:             */
334:            private MathTransform separateInput(final MathTransform transform)
335:                    throws FactoryException {
336:                final int dimSource = transform.getSourceDimensions();
337:                final int dimTarget = transform.getTargetDimensions();
338:                final int dimInput = sourceDimensions.length;
339:                final int lower = sourceDimensions[0];
340:                final int upper = sourceDimensions[dimInput - 1] + 1;
341:                assert XArray.isStrictlySorted(sourceDimensions);
342:                if (upper > dimSource) {
343:                    throw new IllegalArgumentException(Errors.format(
344:                            ErrorKeys.ILLEGAL_ARGUMENT_$2, "sourceDimensions",
345:                            new Integer(upper - 1)));
346:                }
347:                /*
348:                 * Check for easiest cases: same transform, identity transform or concatenated transforms.
349:                 */
350:                if (dimInput == dimSource) {
351:                    assert lower == 0 && upper == dimSource;
352:                    targetDimensions = series(0, dimTarget);
353:                    return transform;
354:                }
355:                if (transform.isIdentity()) {
356:                    targetDimensions = sourceDimensions;
357:                    return factory.createAffineTransform(MatrixFactory
358:                            .create(dimInput + 1));
359:                }
360:                if (transform instanceof  ConcatenatedTransform) {
361:                    final ConcatenatedTransform ctr = (ConcatenatedTransform) transform;
362:                    final int[] original = sourceDimensions;
363:                    final MathTransform step1, step2;
364:                    step1 = separateInput(ctr.transform1);
365:                    sourceDimensions = targetDimensions;
366:                    step2 = separateInput(ctr.transform2);
367:                    sourceDimensions = original;
368:                    return factory.createConcatenatedTransform(step1, step2);
369:                }
370:                /*
371:                 * Special case for the pass through transform:  if at least one input dimension
372:                 * belong to the passthrough's sub-transform, then delegates part of the work to
373:                 * {@code subTransform(passThrough.transform, ...)}
374:                 */
375:                if (transform instanceof  PassThroughTransform) {
376:                    final PassThroughTransform passThrough = (PassThroughTransform) transform;
377:                    final int dimPass = passThrough.subTransform
378:                            .getSourceDimensions();
379:                    final int dimDiff = passThrough.subTransform
380:                            .getTargetDimensions()
381:                            - dimPass;
382:                    final int subLower = passThrough.firstAffectedOrdinate;
383:                    final int subUpper = subLower + dimPass;
384:                    final DimensionFilter subFilter = new DimensionFilter(
385:                            factory);
386:                    for (int i = 0; i < sourceDimensions.length; i++) {
387:                        int n = sourceDimensions[i];
388:                        if (n >= subLower && n < subUpper) {
389:                            // Dimension n belong to the subtransform.
390:                            subFilter.addSourceDimension(n - subLower);
391:                        } else {
392:                            // Dimension n belong to heading or trailing dimensions.
393:                            // Passthrough, after adjustement for trailing dimensions.
394:                            if (n >= subUpper) {
395:                                n += dimDiff;
396:                            }
397:                            targetDimensions = add(targetDimensions, n);
398:                        }
399:                    }
400:                    if (subFilter.sourceDimensions == null) {
401:                        /*
402:                         * No source dimensions belong to the sub-transform. The only remaining
403:                         * sources are heading and trailing dimensions. A passthrough transform
404:                         * without its sub-transform is an identity transform...
405:                         */
406:                        return factory.createAffineTransform(MatrixFactory
407:                                .create(dimInput + 1));
408:                    }
409:                    /*
410:                     * There is at least one dimension to separate in the sub-transform. Performs this
411:                     * separation and gets the list of output dimensions. We need to offset the output
412:                     * dimensions by the amount of leading dimensions once the separation is done, in
413:                     * order to translate from the sub-transform's dimension numbering to the transform's
414:                     * numbering.
415:                     */
416:                    final MathTransform subTransform = subFilter
417:                            .separateInput(passThrough.subTransform);
418:                    for (int i = 0; i < subFilter.targetDimensions.length; i++) {
419:                        subFilter.targetDimensions[i] += subLower;
420:                    }
421:                    targetDimensions = add(targetDimensions,
422:                            subFilter.targetDimensions);
423:                    /*
424:                     * If all source dimensions not in the sub-transform are consecutive numbers, we can
425:                     * use our pass though transform implementation. The "consecutive numbers" requirement
426:                     * (expressed in the 'if' statement below) is a consequence of a limitation in our
427:                     * current implementation: our pass through transform doesn't accept arbitrary index
428:                     * for modified ordinates.
429:                     */
430:                    if (containsAll(sourceDimensions, lower, subLower)
431:                            && containsAll(sourceDimensions, subUpper, upper)) {
432:                        final int firstAffectedOrdinate = Math.max(0, subLower
433:                                - lower);
434:                        final int numTrailingOrdinates = Math.max(0, upper
435:                                - subUpper);
436:                        return factory.createPassThroughTransform(
437:                                firstAffectedOrdinate, subTransform,
438:                                numTrailingOrdinates);
439:                    }
440:                    // TODO: handle more general case here...
441:                    targetDimensions = null; // Clear before to fallback on the LinearTransform case.
442:                }
443:                /*
444:                 * If the transform is affine (or at least projective), express the transform as a matrix.
445:                 * Then, select output dimensions that depends only on selected input dimensions. If an
446:                 * output dimension depends on at least one discarted input dimension, then this output
447:                 * dimension will be discarted as well.
448:                 */
449:                if (transform instanceof  LinearTransform) {
450:                    int nRows = 0;
451:                    boolean hasLastRow = false;
452:                    final Matrix matrix = ((LinearTransform) transform)
453:                            .getMatrix();
454:                    assert dimSource + 1 == matrix.getNumCol()
455:                            && dimTarget + 1 == matrix.getNumRow() : matrix;
456:                    double[][] rows = new double[dimTarget + 1][];
457:                    reduce: for (int j = 0; j < rows.length; j++) {
458:                        final double[] row = new double[dimInput + 1];
459:                        /*
460:                         * For each output dimension (i.e. a matrix row), find the matrix elements for
461:                         * each input dimension to be kept. If a dependance to at least one discarted
462:                         * input dimension is found, then the whole output dimension is discarted.
463:                         *
464:                         * NOTE: The following loop stops at matrix.getNumCol()-1 because we don't
465:                         *       want to check the translation term.
466:                         */
467:                        int nCols = 0, scan = 0;
468:                        for (int i = 0; i < dimSource; i++) {
469:                            final double element = matrix.getElement(j, i);
470:                            if (scan < sourceDimensions.length
471:                                    && sourceDimensions[scan] == i) {
472:                                row[nCols++] = element;
473:                                scan++;
474:                            } else if (element != 0) {
475:                                // Output dimension 'j' depends on one of discarted input dimension 'i'.
476:                                // The whole row will be discarted.
477:                                continue reduce;
478:                            }
479:                        }
480:                        row[nCols++] = matrix.getElement(j, dimSource); // Copy the translation term.
481:                        assert nCols == row.length : nCols;
482:                        if (j == dimTarget) {
483:                            hasLastRow = true;
484:                        } else {
485:                            targetDimensions = add(targetDimensions, j);
486:                        }
487:                        rows[nRows++] = row;
488:                    }
489:                    rows = (double[][]) XArray.resize(rows, nRows);
490:                    if (hasLastRow) {
491:                        return factory.createAffineTransform(new GeneralMatrix(
492:                                rows));
493:                    }
494:                    // In an affine transform,  the last row is not supposed to have dependency
495:                    // to any input dimension. But in this particuler case, our matrix has such
496:                    // dependencies. TODO: is there anything we could do about that?
497:                }
498:                throw new FactoryException(Errors
499:                        .format(ErrorKeys.INSEPARABLE_TRANSFORM));
500:            }
501:
502:            /**
503:             * Creates a transform which retains only a subset of an other transform's outputs. The number
504:             * and nature of inputs stay unchanged. For example if the supplied {@code transform} has
505:             * (<var>longitude</var>, <var>latitude</var>, <var>height</var>) outputs, then a sub-transform
506:             * may be used to keep only the (<var>longitude</var>, <var>latitude</var>) part. In most cases,
507:             * the created sub-transform is non-invertible since it loose informations.
508:             * <br><br>
509:             * This transform may be see as a non-square matrix transform with less rows
510:             * than columns, concatenated with {@code transform}. However, invoking
511:             * {@code createFilterTransfom(...)} allows the optimization of some common cases.
512:             *
513:             * @param  transform The transform to reduces.
514:             * @return The {@code transform} keeping only the output dimensions.
515:             * @throws FactoryException if the transform can't be created.
516:             */
517:            private MathTransform separateOutput(MathTransform transform)
518:                    throws FactoryException {
519:                final int dimSource = transform.getSourceDimensions();
520:                final int dimTarget = transform.getTargetDimensions();
521:                final int dimOutput = targetDimensions.length;
522:                final int lower = targetDimensions[0];
523:                final int upper = targetDimensions[dimOutput - 1];
524:                assert XArray.isStrictlySorted(targetDimensions);
525:                if (upper > dimTarget) {
526:                    throw new IllegalArgumentException(Errors.format(
527:                            ErrorKeys.ILLEGAL_ARGUMENT_$2, "targetDimensions",
528:                            new Integer(upper)));
529:                }
530:                if (dimOutput == dimTarget) {
531:                    assert lower == 0 && upper == dimTarget;
532:                    return transform;
533:                }
534:                /*
535:                 * If the transform is an instance of "pass through" transform but no dimension from its
536:                 * subtransform is requested, then ignore the subtransform (i.e. treat the whole transform
537:                 * as identity, except for the number of output dimension which may be different from the
538:                 * number of input dimension).
539:                 */
540:                int dimPass = 0;
541:                int dimDiff = 0;
542:                int dimStep = dimTarget;
543:                if (transform instanceof  PassThroughTransform) {
544:                    final PassThroughTransform passThrough = (PassThroughTransform) transform;
545:                    final int subLower = passThrough.firstAffectedOrdinate;
546:                    final int subUpper = subLower
547:                            + passThrough.subTransform.getTargetDimensions();
548:                    if (!containsAny(targetDimensions, subLower, subUpper)) {
549:                        transform = null;
550:                        dimStep = dimSource;
551:                        dimPass = subLower;
552:                        dimDiff = (subLower + passThrough.subTransform
553:                                .getSourceDimensions())
554:                                - subUpper;
555:                    }
556:                }
557:                /*
558:                 * Creates the matrix to be used as a filter,       [x']     [1  0  0  0] [x]
559:                 * and concatenates it to the transform. The        [z']  =  [0  0  1  0] [y]
560:                 * matrix will contains only a 1 for the output     [1 ]     [0  0  0  1] [z]
561:                 * dimension to keep, as in the following example:                        [1]
562:                 */
563:                final XMatrix matrix = MatrixFactory.create(dimOutput + 1,
564:                        dimStep + 1);
565:                matrix.setZero();
566:                for (int j = 0; j < dimOutput; j++) {
567:                    int i = targetDimensions[j];
568:                    if (i >= dimPass) {
569:                        i += dimDiff;
570:                    }
571:                    matrix.setElement(j, i, 1);
572:                }
573:                // Affine transform has one more row/column than dimension.
574:                matrix.setElement(dimOutput, dimStep, 1);
575:                MathTransform filtered = factory.createAffineTransform(matrix);
576:                if (transform != null) {
577:                    filtered = factory.createConcatenatedTransform(transform,
578:                            filtered);
579:                }
580:                return filtered;
581:            }
582:
583:            /**
584:             * Returns {@code true} if the given sequence contains all index in the range {@code lower}
585:             * inclusive to {@code upper} exclusive.
586:             *
587:             * @param  sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to test.
588:             * @param  lower The lower value, inclusive.
589:             * @param  upper The upper value, exclusive.
590:             * @return {@code true} if the full range was found in the sequence.
591:             */
592:            private static boolean containsAll(final int[] sequence,
593:                    final int lower, int upper) {
594:                if (lower == upper) {
595:                    return true;
596:                }
597:                if (sequence != null) {
598:                    assert XArray.isStrictlySorted(sequence);
599:                    int index = Arrays.binarySearch(sequence, lower);
600:                    if (index >= 0) {
601:                        index += --upper - lower;
602:                        if (index >= 0 && index < sequence.length) {
603:                            return sequence[index] == upper;
604:                        }
605:                    }
606:                }
607:                return false;
608:            }
609:
610:            /**
611:             * Returns {@code true} if the given sequence contains any value in the given range.
612:             *
613:             * @param  sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to test.
614:             * @param  lower The lower value, inclusive.
615:             * @param  upper The upper value, exclusive.
616:             * @return {@code true} if the sequence contains at least one value in the given range.
617:             */
618:            private static boolean containsAny(final int[] sequence,
619:                    final int lower, final int upper) {
620:                if (upper == lower) {
621:                    return true;
622:                }
623:                if (sequence != null) {
624:                    assert XArray.isStrictlySorted(sequence);
625:                    int index = Arrays.binarySearch(sequence, lower);
626:                    if (index >= 0) {
627:                        return true;
628:                    }
629:                    index = ~index; // Tild, not minus sign.
630:                    return index < sequence.length && sequence[index] < upper;
631:                }
632:                return false;
633:            }
634:
635:            /**
636:             * Add the specified {@code dimension} to the specified sequence. Values are added
637:             * in increasing order. Duplicated values are not added.
638:             *
639:             * @param sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
640:             */
641:            private static int[] add(int[] sequence, int dimension)
642:                    throws IllegalArgumentException {
643:                if (dimension < 0) {
644:                    throw new IllegalArgumentException(Errors.format(
645:                            ErrorKeys.ILLEGAL_ARGUMENT_$2, "dimension",
646:                            new Integer(dimension)));
647:                }
648:                if (sequence == null) {
649:                    return new int[] { dimension };
650:                }
651:                assert XArray.isStrictlySorted(sequence);
652:                int i = Arrays.binarySearch(sequence, dimension);
653:                if (i < 0) {
654:                    i = ~i; // Tild, not the minus sign.
655:                    sequence = XArray.insert(sequence, i, 1);
656:                    sequence[i] = dimension;
657:                }
658:                assert Arrays.binarySearch(sequence, dimension) == i;
659:                return sequence;
660:            }
661:
662:            /**
663:             * Add the specified {@code dimensions} to the specified sequence. Values are added
664:             * in increasing order. Duplicated values are not added.
665:             *
666:             * @param sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
667:             */
668:            private static int[] add(int[] sequence, final int[] dimensions)
669:                    throws IllegalArgumentException {
670:                if (dimensions.length != 0) {
671:                    ensureValidSeries(dimensions);
672:                    if (sequence == null) {
673:                        sequence = (int[]) dimensions.clone();
674:                    } else {
675:                        // Note: the following loop is unefficient, but should suffise since this
676:                        //       case should not occurs often and arrays should be small anyway.
677:                        for (int i = 0; i < dimensions.length; i++) {
678:                            sequence = add(sequence, dimensions[i]);
679:                        }
680:                    }
681:                }
682:                return sequence;
683:            }
684:
685:            /**
686:             * Add the specified range to the specified sequence. Values are added
687:             * in increasing order. Duplicated values are not added.
688:             *
689:             * @param sequence The {@link #sourceDimensions} or {@link #targetDimensions} sequence to update.
690:             * @throws IllegalArgumentException if {@code lower} is not smaller than {@code upper}.
691:             */
692:            private static int[] add(int[] sequence, int lower, final int upper)
693:                    throws IllegalArgumentException {
694:                if (lower < 0 || lower >= upper) {
695:                    throw new IllegalArgumentException(Errors.format(
696:                            ErrorKeys.ILLEGAL_ARGUMENT_$2, "lower",
697:                            new Integer(lower)));
698:                }
699:                if (sequence == null) {
700:                    sequence = series(lower, upper);
701:                } else {
702:                    // Note: the following loop is unefficient, but should suffise since this
703:                    //       case should not occurs often and arrays should be small anyway.
704:                    while (lower < upper) {
705:                        sequence = add(sequence, lower++);
706:                    }
707:                }
708:                assert containsAll(sequence, lower, upper);
709:                return sequence;
710:            }
711:
712:            /**
713:             * Returns a series of increasing values starting at {@code lower}.
714:             */
715:            private static int[] series(final int lower, final int upper)
716:                    throws IllegalArgumentException {
717:                final int[] sequence = new int[upper - lower];
718:                for (int i = 0; i < sequence.length; i++) {
719:                    sequence[i] = i + lower;
720:                }
721:                return sequence;
722:            }
723:
724:            /**
725:             * Ensures that the specified array contains strictly increasing non-negative values.
726:             *
727:             * @param  dimensions The sequence to check.
728:             * @throws IllegalArgumentException if the specified sequence is not a valid series.
729:             */
730:            private static void ensureValidSeries(final int[] dimensions)
731:                    throws IllegalArgumentException {
732:                int last = -1;
733:                for (int i = 0; i < dimensions.length; i++) {
734:                    final int value = dimensions[i];
735:                    if (value <= last) {
736:                        throw new IllegalArgumentException(Errors.format(
737:                                ErrorKeys.ILLEGAL_ARGUMENT_$2, "dimensions["
738:                                        + i + ']', new Integer(value)));
739:                    }
740:                    last = value;
741:                }
742:            }
743:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.