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


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2005-2006, Geotools Project Management Committee (PMC)
0005:         *    (C) 2003, Institut de Recherche pour le Développement
0006:         *
0007:         *    This library is free software; you can redistribute it and/or
0008:         *    modify it under the terms of the GNU Lesser General Public
0009:         *    License as published by the Free Software Foundation; either
0010:         *    version 2.1 of the License, or (at your option) any later version.
0011:         *
0012:         *    This library is distributed in the hope that it will be useful,
0013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *    Lesser General Public License for more details.
0016:         */
0017:        package org.geotools.coverage;
0018:
0019:        // J2SE and JAI dependencies
0020:        import java.util.ArrayList;
0021:        import java.util.Arrays;
0022:        import java.util.Collection;
0023:        import java.util.Collections;
0024:        import java.util.Comparator;
0025:        import java.util.HashSet;
0026:        import java.util.Iterator;
0027:        import java.util.List;
0028:        import java.util.Locale;
0029:        import java.util.logging.Level;
0030:        import java.util.logging.Logger;
0031:        import java.util.logging.LogRecord;
0032:        import java.io.IOException;
0033:        import java.io.ObjectInputStream;
0034:        import javax.imageio.ImageReader;
0035:        import javax.imageio.event.IIOReadWarningListener;
0036:        import javax.imageio.event.IIOReadProgressListener;
0037:        import javax.media.jai.InterpolationNearest;
0038:        import java.lang.reflect.Array;
0039:        import java.lang.reflect.UndeclaredThrowableException;
0040:
0041:        // OpenGIS dependencies
0042:        import org.opengis.coverage.Coverage;
0043:        import org.opengis.coverage.SampleDimension;
0044:        import org.opengis.coverage.CannotEvaluateException;
0045:        import org.opengis.coverage.grid.GridRange;
0046:        import org.opengis.coverage.grid.GridGeometry;
0047:        import org.opengis.coverage.grid.GridCoverage;
0048:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0049:        import org.opengis.referencing.crs.TemporalCRS;
0050:        import org.opengis.referencing.operation.MathTransform;
0051:        import org.opengis.referencing.operation.TransformException;
0052:        import org.opengis.geometry.MismatchedDimensionException;
0053:        import org.opengis.geometry.DirectPosition;
0054:        import org.opengis.geometry.Envelope;
0055:
0056:        // Geotools dependencies
0057:        import org.geotools.coverage.grid.GridCoverage2D;
0058:        import org.geotools.coverage.grid.Interpolator2D;
0059:        import org.geotools.geometry.GeneralDirectPosition;
0060:        import org.geotools.geometry.GeneralEnvelope;
0061:        import org.geotools.image.io.IIOListeners;
0062:        import org.geotools.image.io.IIOReadProgressAdapter;
0063:        import org.geotools.util.logging.Logging;
0064:        import org.geotools.resources.Utilities;
0065:        import org.geotools.resources.CRSUtilities;
0066:        import org.geotools.resources.i18n.Errors;
0067:        import org.geotools.resources.i18n.ErrorKeys;
0068:        import org.geotools.resources.i18n.Vocabulary;
0069:        import org.geotools.resources.i18n.VocabularyKeys;
0070:        import org.geotools.referencing.CRS;
0071:        import org.geotools.referencing.crs.DefaultTemporalCRS;
0072:        import org.geotools.util.SimpleInternationalString;
0073:        import org.geotools.util.NumberRange;
0074:
0075:        /**
0076:         * Wraps a stack of {@linkplain Coverage coverages} as an extra dimension. For example this class
0077:         * can wraps an array of {@link org.geotools.coverage.grid.GridCoverage2D} on the same geographic
0078:         * area, but where each {@code GridCoverage2D} is for a different date. This {@code CoverageStack}
0079:         * manages the two-dimensional coverages as if the whole set was a huge three-dimensional coverage.
0080:         * <p>
0081:         * Each {@linkplain Element coverage element} in the stack usually covers the same
0082:         * {@linkplain Coverage#getEnvelope geographic area}, but this is not a requirement. However,
0083:         * they must use the same {@linkplain CoordinateReferenceSystem coordinate reference system}.
0084:         * For performance reason, the later condition will not be checked except at construction time
0085:         * if the CRS is provided in the envelope, and at evaluation time if Java assertion are enabled.
0086:         * If the CRS of coverage elements is uncertain, consider wrapping them in a
0087:         * {@link TransformedCoverage} object.
0088:         * <p>
0089:         * Coverage elements are often two-dimensional, but this is not a requirement. This stack will
0090:         * simply append one more dimension to the coverage element's CRS dimensions. Coverage elements
0091:         * may be other {@code CoverateStack} objects, thus allowing construction of coverages with four
0092:         * or more dimensions.
0093:         * <p>
0094:         * {@code GridCoverage2D} objects tend to be big. In order to keep memory usage raisonable, this
0095:         * implementation doesn't requires all {@code GridCoverage} objects at once. Instead, it requires
0096:         * an array of {@link Element} objects, which will load the coverage content only when first
0097:         * needed. This {@code CoverageStack} implementation remember the last coverage elements used;
0098:         * it will not trig new data loading as long as consecutive calls to {@code evaluate(...)}
0099:         * methods require the same coverage elements. Apart from this very simple caching mechanism,
0100:         * caching is the responsability of {@link Element} implementations. Note that this simple
0101:         * caching mechanism is suffisient if {@code evaluate(...)} methods are invoked with increasing
0102:         * <var>z</var> values.
0103:         * <p>
0104:         * Each coverage element is expected to extends over a range of <var>z</var> values (the new
0105:         * dimensions appended by this {@code CoverageStack}). If an {@code evaluate(...)} method is
0106:         * invoked with a <var>z</var> value not falling in the middle of a coverage element, a linear
0107:         * interpolation is applied.
0108:         * <p>
0109:         * <strong>Note:</strong> This implementation is thread-safe.
0110:         *
0111:         * @since 2.1
0112:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/CoverageStack.java $
0113:         * @version $Id: CoverageStack.java 27862 2007-11-12 19:51:19Z desruisseaux $
0114:         * @author Martin Desruisseaux
0115:         */
0116:        public class CoverageStack extends AbstractCoverage {
0117:            /**
0118:             * An element in a {@linkplain CoverageStack coverage stack}. Each element is expected to
0119:             * extents over a range of <var>z</var> values (the new dimensions appended by the
0120:             * {@code CoverageStack} container). Implementations should be capable to returns coverage's
0121:             * {@linkplain #getZRange range of z-values} without loading the coverage's data. If an
0122:             * expensive loading is required, it should be delayed until the {@link #getCoverage} method
0123:             * is invoked. If {@code getCoverage} is invoked more than once, caching (if desirable) is
0124:             * implementor's responsability.
0125:             * <p>
0126:             * All methods declares {@link IOException} in their throws cause in case I/O operations are
0127:             * required. Subclasses of {@code IOException} include {@link javax.imageio.IIOException} for
0128:             * image I/O operations, or {@link java.rmi.RemoteOperation} for remote method invocation
0129:             * (which may be useful for large images database backed by a distant server).
0130:             *
0131:             * @since 2.1
0132:             * @version $Id: CoverageStack.java 27862 2007-11-12 19:51:19Z desruisseaux $
0133:             * @author Martin Desruisseaux
0134:             */
0135:            public static interface Element {
0136:                /**
0137:                 * Returns a name for the coverage. This method should not load a large amount of data,
0138:                 * since it may be invoked soon. This method is invoked just before {@link #getCoverage}
0139:                 * in order to log a "Loading data..." message.
0140:                 */
0141:                String getName() throws IOException;
0142:
0143:                /**
0144:                 * Returns the minimum and maximum <var>z</var> value for the coverage.
0145:                 * This information is mandatory. This method should not load a large
0146:                 * amount of data, since it may be invoked soon. Note that this method
0147:                 * may be invoked often, so it should be efficient.
0148:                 *
0149:                 * @throws IOException if an I/O operation was required but failed.
0150:                 */
0151:                NumberRange getZRange() throws IOException;
0152:
0153:                /**
0154:                 * Returns the coverage envelope, or {@code null} if this information is too expensive to
0155:                 * compute. The envelope may or may not contains an extra dimension for the
0156:                 * {@linkplain #getZRange range of z values}, since the {@link CoverageStack} class is
0157:                 * tolerant in this regard. This method should not load a large amount of data, since it
0158:                 * may be invoked soon.
0159:                 *
0160:                 * @throws IOException if an I/O operation was required but failed.
0161:                 */
0162:                Envelope getEnvelope() throws IOException;
0163:
0164:                /**
0165:                 * The coverage grid geometry, or {@code null} if this information do not applies or is too
0166:                 * expensive to compute. This method should not load a large amount of data, since it may be
0167:                 * invoked soon.
0168:                 *
0169:                 * @throws IOException if an I/O operation was required but failed.
0170:                 */
0171:                GridGeometry getGridGeometry() throws IOException;
0172:
0173:                /**
0174:                 * The sample dimension for the coverage, or {@code null} if this information is too
0175:                 * expensive to compute. This method should not load a large amount of data, since it
0176:                 * may be invoked soon.
0177:                 *
0178:                 * @throws IOException if an I/O operation was required but failed.
0179:                 */
0180:                SampleDimension[] getSampleDimensions() throws IOException;
0181:
0182:                /**
0183:                 * Returns the coverage, loading the data if needed. Implementations should invokes the
0184:                 * {@link IIOListeners#addListenersTo(ImageReader)} method if they use an image reader
0185:                 * for loading data. Caching (if desired) is implementor's responsability. The default
0186:                 * {@link CoverageStack} implementation caches only the last coverage used.
0187:                 *
0188:                 * @param  listeners Listeners to register to the {@linkplain ImageReader image I/O reader},
0189:                 *         if such a reader is going to be used.
0190:                 * @throws IOException if a data loading was required but failed.
0191:                 */
0192:                Coverage getCoverage(IIOListeners listeners) throws IOException;
0193:            }
0194:
0195:            /**
0196:             * A convenience adapter class for wrapping a pre-loaded {@link Coverage} into an
0197:             * {@link Element} object. This adapter provides basic implementation for all methods,
0198:             * but they a require a fully constructed {@link Coverage} object. Subclasses are strongly
0199:             * encouraged to provides alternative implementation loading only the minimum amount of data
0200:             * required for each method.
0201:             *
0202:             * @since 2.1
0203:             * @version $Id: CoverageStack.java 27862 2007-11-12 19:51:19Z desruisseaux $
0204:             * @author Martin Desruisseaux
0205:             */
0206:            public static class Adapter implements  Element {
0207:                /**
0208:                 * The wrapped coverage, or {@code null} if not yet loaded.
0209:                 * If null, the loading must be performed by the {@link #getCoverage} method.
0210:                 */
0211:                protected Coverage coverage;
0212:
0213:                /**
0214:                 * Minimum and maximum <var>z</var> values for this element, or {@code null} if not yet
0215:                 * determined. If {@code null}, the range must be computed by the {@link #getZRange} method.
0216:                 */
0217:                protected NumberRange range;
0218:
0219:                /**
0220:                 * Constructs a new adapter for the specified coverage and <var>z</var> values.
0221:                 *
0222:                 * @param coverage The coverage to wrap. Can be {@code null} only if this constructor
0223:                 *                 is invoked from a sub-class constructor.
0224:                 * @param range    The minimum and maximum <var>z</var> values for this element, or
0225:                 *                 {@code null} to infers it from the last dimension in the coverage's
0226:                 *                 envelope.
0227:                 */
0228:                public Adapter(final Coverage coverage, final NumberRange range) {
0229:                    this .coverage = coverage;
0230:                    this .range = range;
0231:                    if (getClass() == Adapter.class) {
0232:                        if (coverage == null) {
0233:                            // TODO: provides a localized message.
0234:                            throw new IllegalArgumentException("coverage");
0235:                        }
0236:                    }
0237:                }
0238:
0239:                /**
0240:                 * Returns the coverage name. The default implementation delegates to the
0241:                 * {@linkplain #getCoverage underlying coverage} if it is an instance of
0242:                 * {@link AbstractCoverage}.
0243:                 */
0244:                public String getName() throws IOException {
0245:                    Object coverage = getCoverage(null);
0246:                    if (coverage instanceof  AbstractCoverage) {
0247:                        coverage = ((AbstractCoverage) coverage).getName();
0248:                    }
0249:                    return coverage.toString();
0250:                }
0251:
0252:                /**
0253:                 * Returns the minimum and maximum <var>z</var> values for the coverage. If the range was
0254:                 * not explicitly specified to the constructor, then the default implementation infers it
0255:                 * from the last dimension in the coverage's envelope.
0256:                 */
0257:                public NumberRange getZRange() throws IOException {
0258:                    if (range == null) {
0259:                        final Envelope envelope = getEnvelope();
0260:                        final int zDimension = envelope.getDimension() - 1;
0261:                        range = new NumberRange(
0262:                                envelope.getMinimum(zDimension), envelope
0263:                                        .getMaximum(zDimension));
0264:                    }
0265:                    return range;
0266:                }
0267:
0268:                /**
0269:                 * Returns the coverage envelope. The default implementation delegates to the
0270:                 * {@linkplain #getCoverage underlying coverage}.
0271:                 */
0272:                public Envelope getEnvelope() throws IOException {
0273:                    return getCoverage(null).getEnvelope();
0274:                }
0275:
0276:                /**
0277:                 * Returns the coverage grid geometry. The default implementation delegates to the
0278:                 * {@linkplain #getCoverage underlying coverage} if it is an instance of
0279:                 * {@link GridCoverage}.
0280:                 */
0281:                public GridGeometry getGridGeometry() throws IOException {
0282:                    final Coverage coverage = getCoverage(null);
0283:                    return (coverage instanceof  GridCoverage) ? ((GridCoverage) coverage)
0284:                            .getGridGeometry()
0285:                            : null;
0286:                }
0287:
0288:                /**
0289:                 * Returns the sample dimension for the coverage. The default implementation delegates to the
0290:                 * {@linkplain #getCoverage underlying coverage}.
0291:                 */
0292:                public SampleDimension[] getSampleDimensions()
0293:                        throws IOException {
0294:                    final Coverage coverage = getCoverage(null);
0295:                    final SampleDimension[] sd = new SampleDimension[coverage
0296:                            .getNumSampleDimensions()];
0297:                    for (int i = 0; i < sd.length; i++) {
0298:                        sd[i] = coverage.getSampleDimension(i);
0299:                    }
0300:                    return sd;
0301:                }
0302:
0303:                /**
0304:                 * Returns the coverage. Implementors can overrides this method if they want to load
0305:                 * {@link #coverage} only when first needed. However, they are strongly encouraged to
0306:                 * override all other methods as well in order to load the minimum amount of data,
0307:                 * since all default implementations invoke {@code getCoverage(null)}.
0308:                 */
0309:                public Coverage getCoverage(final IIOListeners listeners)
0310:                        throws IOException {
0311:                    return coverage;
0312:                }
0313:            }
0314:
0315:            /**
0316:             * Coverage elements in this stack. Elements may be shared by more than one
0317:             * instances of {@code CoverageStack}.
0318:             */
0319:            private final Element[] elements;
0320:
0321:            /**
0322:             * The sample dimensions for this coverage, or {@code null} if unknown.
0323:             */
0324:            private final SampleDimension[] sampleDimensions;
0325:
0326:            /**
0327:             * The number of sample dimensions for this coverage, or 0 is unknow.
0328:             * Note: this attribute may be non-null even if {@link #sampleDimensions} is null.
0329:             */
0330:            private final int numSampleDimensions;
0331:
0332:            /**
0333:             * The envelope for this coverage. This is the union of all elements envelopes.
0334:             *
0335:             * @see #getEnvelope
0336:             */
0337:            private final GeneralEnvelope envelope;
0338:
0339:            /**
0340:             * A direct position with {@link #zDimension} dimensions. will be created only
0341:             * when first needed.
0342:             */
0343:            private transient GeneralDirectPosition reducedPosition;
0344:
0345:            /**
0346:             * The dimension of the <var>z</var> ordinate (the last value in coordinate points).
0347:             * This is always the {@linkplain #getCoordinateReferenceSystem() coordinate reference
0348:             * system} dimension minus 1.
0349:             *
0350:             * @since 2.3
0351:             */
0352:            public final int zDimension;
0353:
0354:            /**
0355:             * The coordinate reference system for the {@linkplain #zDimension z dimension},
0356:             * or {@code null} if unknown.
0357:             */
0358:            private final CoordinateReferenceSystem zCRS;
0359:
0360:            /**
0361:             * {@code true} if interpolations are allowed.
0362:             */
0363:            private boolean interpolationEnabled = true;
0364:
0365:            /**
0366:             * Maximal interval between the upper z-value of a coverage and the lower z-value of the next
0367:             * one. If a greater difference is found, we will consider that there is a hole in the data
0368:             * and {@code evaluate(...)} methods will returns NaN for <var>z</var> values in this hole.
0369:             */
0370:            private final double lagTolerance = 0;
0371:
0372:            /**
0373:             * List of objects to inform when image loading are trigged.
0374:             */
0375:            private final IIOListeners listeners = new IIOListeners();
0376:
0377:            /**
0378:             * Internal listener for logging image loading.
0379:             */
0380:            private transient Listeners readListener;
0381:
0382:            /**
0383:             * Coverage with a minimum z-value lower than or equals to the requested <var>z</var> value.
0384:             * If possible, this class will tries to select a coverage with a middle value (not just the
0385:             * minimum value) lower than the requested <var>z</var> value.
0386:             */
0387:            private transient Coverage lower;
0388:
0389:            /**
0390:             * Coverage with a maximum z-value higher than or equals to the requested <var>z</var> value.
0391:             * If possible, this class will tries to select a coverage with a middle value (not just the
0392:             * maximum value) higher than the requested <var>z</var> value.
0393:             */
0394:            private transient Coverage upper;
0395:
0396:            /**
0397:             * <var>Z</var> values in the middle of {@link #lower} and {@link #upper} envelope.
0398:             */
0399:            private transient double lowerZ = Double.POSITIVE_INFINITY,
0400:                    upperZ = Double.NEGATIVE_INFINITY;
0401:
0402:            /**
0403:             * Range for {@link #lower} and {@link #upper}.
0404:             */
0405:            private transient NumberRange lowerRange, upperRange;
0406:
0407:            /**
0408:             * Sample byte values. Allocated when first needed, in order to avoid allocating
0409:             * thel again everytime an {@code evaluate(...)} method is invoked.
0410:             */
0411:            private transient byte[] byteBuffer;
0412:
0413:            /**
0414:             * Sample integer values. Allocated when first needed, in order to avoid allocating
0415:             * thel again everytime an {@code evaluate(...)} method is invoked.
0416:             */
0417:            private transient int[] intBuffer;
0418:
0419:            /**
0420:             * Sample float values. Allocated when first needed, in order to avoid allocating
0421:             * thel again everytime an {@code evaluate(...)} method is invoked.
0422:             */
0423:            private transient float[] floatBuffer;
0424:
0425:            /**
0426:             * Sample double values. Allocated when first needed, in order to avoid allocating
0427:             * thel again everytime an {@code evaluate(...)} method is invoked.
0428:             */
0429:            private transient double[] doubleBuffer;
0430:
0431:            /**
0432:             * Initialize fields after deserialization.
0433:             */
0434:            private void readObject(final ObjectInputStream in)
0435:                    throws IOException, ClassNotFoundException {
0436:                in.defaultReadObject();
0437:                lowerZ = Double.POSITIVE_INFINITY;
0438:                upperZ = Double.NEGATIVE_INFINITY;
0439:            }
0440:
0441:            /**
0442:             * Constructs a new coverage stack with all the supplied elements. All coverages must uses the
0443:             * same coordinate reference system. Additionnaly, all coverages must specify their <var>z</var>
0444:             * value in the last dimension of their envelope. The example below constructs two dimensional
0445:             * grid coverages (to be given as the {@code coverages} argument) for the same area, but at
0446:             * different times:
0447:             *
0448:             * <blockquote><pre>
0449:             * GridCoverageFactory     factory = ...;
0450:             * CoordinateReferenceSystem crs2D = ...;  // Yours horizontal CRS.
0451:             * TemporalCRS             timeCRS = ...;  // Yours CRS for time measurement.
0452:             * CoordinateReferenceSystem crs3D = new CompoundCRS(crs3D, timeCRS);
0453:             *
0454:             * List&lt;Coverage&gt; coverages = new ArrayList&lt;Coverage&gt;();
0455:             * GeneralEnvelope envelope = new GeneralEnvelope(3); // A <strong>3-dimensional</strong> envelope.
0456:             * envelope.setRange(...);                            // Set the horizontal part.
0457:             * for (int i=0; i<...; i++) {
0458:             *     envelope.setRange(2, startTime, endTime);
0459:             *     coverages.add(factory.create(..., crs, envelope, ...);
0460:             * }
0461:             * </pre></blockquote>
0462:             * 
0463:             * This convenience constructor wraps all coverage intos a {@link Adapter Adapter} object.
0464:             * Users with a significant amount of data are encouraged to uses the constructor expecting
0465:             * {@link Element Element} objects instead, in order to provides their own implementation
0466:             * loading data only when needed.
0467:             *
0468:             * @param  name      The name for this coverage.
0469:             * @param  coverages All {@link Coverage} elements for this stack.
0470:             * @throws IOException if an I/O operation was required and failed.
0471:             */
0472:            public CoverageStack(final CharSequence name,
0473:                    final Collection/*<Coverage>*/coverages)
0474:                    throws IOException {
0475:                this (name, getCoordinateReferenceSystem(coverages),
0476:                        toElements(coverages));
0477:            }
0478:
0479:            /**
0480:             * Workaround for RFE #4093999 ("Relax constraint on placement of this()/super()
0481:             * call in constructors").
0482:             */
0483:            private static CoordinateReferenceSystem getCoordinateReferenceSystem(
0484:                    final Collection coverages) {
0485:                CoordinateReferenceSystem crs = null;
0486:                for (final Iterator it = coverages.iterator(); it.hasNext();) {
0487:                    final CoordinateReferenceSystem candidate = ((Coverage) it
0488:                            .next()).getCoordinateReferenceSystem();
0489:                    if (crs == null) {
0490:                        crs = candidate;
0491:                    } else if (!crs.equals(candidate)) {
0492:                        // TODO: localize
0493:                        throw new IllegalArgumentException(
0494:                                "Inconsistent coordinate reference system");
0495:                    }
0496:                }
0497:                return crs;
0498:            }
0499:
0500:            /**
0501:             * Workaround for RFE #4093999 ("Relax constraint on placement of this()/super()
0502:             * call in constructors").
0503:             */
0504:            private static Collection/*<Element>*/toElements(
0505:                    final Collection/*<Coverage>*/coverages) {
0506:                final List elements = new ArrayList(coverages.size());
0507:                for (final Iterator it = coverages.iterator(); it.hasNext();) {
0508:                    elements.add(new Adapter((Coverage) it.next(), null));
0509:                }
0510:                return elements;
0511:            }
0512:
0513:            /**
0514:             * Constructs a new coverage stack with all the supplied elements.
0515:             *
0516:             * @param  name     The name for this coverage.
0517:             * @param  crs      The coordinate reference system for this coverage.
0518:             * @param  elements All coverage {@link Element Element}s for this stack.
0519:             * @throws IOException if an I/O operation was required and failed.
0520:             */
0521:            public CoverageStack(final CharSequence name,
0522:                    final CoordinateReferenceSystem crs,
0523:                    final Collection/*<Element>*/elements) throws IOException {
0524:                super (name, crs, null, null);
0525:                this .elements = (Element[]) elements
0526:                        .toArray(new Element[elements.size()]);
0527:                try {
0528:                    Arrays.sort(this .elements, COMPARATOR);
0529:                } catch (UndeclaredThrowableException exception) {
0530:                    throw rethrow(exception);
0531:                }
0532:                zDimension = crs.getCoordinateSystem().getDimension() - 1;
0533:                boolean sampleDimensionMismatch = false;
0534:                SampleDimension[] sampleDimensions = null;
0535:                GeneralEnvelope envelope = null;
0536:                for (int j = 0; j < this .elements.length; j++) {
0537:                    final Element element = this .elements[j];
0538:                    if (true) {
0539:                        /*
0540:                         * Ensures that all coverages uses the same number of sample dimension.
0541:                         * To be strict, we should ensure that all sample dimensions are identical.
0542:                         * However, this is not needed for proper working of this class, so we will
0543:                         * ensure this condition only in 'getSampleDimension' method.
0544:                         */
0545:                        final SampleDimension[] candidate = element
0546:                                .getSampleDimensions();
0547:                        if (candidate != null) {
0548:                            if (sampleDimensions == null) {
0549:                                sampleDimensions = candidate;
0550:                            } else {
0551:                                if (sampleDimensions.length != candidate.length) {
0552:                                    throw new IllegalArgumentException( // TODO: localize
0553:                                            "Inconsistent number of sample dimensions.");
0554:                                }
0555:                                if (!Arrays.equals(sampleDimensions, candidate)) {
0556:                                    sampleDimensionMismatch = true;
0557:                                }
0558:                            }
0559:                        }
0560:                    }
0561:                    /*
0562:                     * Computes an envelope for all coverage elements. If a coordinate reference system
0563:                     * information is bundled with the envelope, it will be used in order to reproject
0564:                     * the envelope on the fly (if needed). Otherwise, CRS are assumed the same than the
0565:                     * one specified at construction time.
0566:                     */
0567:                    final Envelope candidate = element.getEnvelope();
0568:                    if (candidate == null) {
0569:                        continue;
0570:                    }
0571:                    final CoordinateReferenceSystem sourceCRS;
0572:                    sourceCRS = candidate.getCoordinateReferenceSystem();
0573:                    if (sourceCRS != null) {
0574:                        final int dim = sourceCRS.getCoordinateSystem()
0575:                                .getDimension();
0576:                        if (dim < zDimension || dim > zDimension + 1) {
0577:                            // TODO: localize
0578:                            throw new MismatchedDimensionException(
0579:                                    "An element uses an incompatible CRS");
0580:                        }
0581:                        final CoordinateReferenceSystem targetCRS = CRSUtilities
0582:                                .getSubCRS(crs, 0, dim);
0583:                        if (!CRS.equalsIgnoreMetadata(sourceCRS, targetCRS)) {
0584:                            // TODO: localize
0585:                            throw new IllegalArgumentException(
0586:                                    "An element uses an incompatible CRS");
0587:                        }
0588:                    }
0589:                    /*
0590:                     * Increase the envelope in order to contains 'candidate'.
0591:                     * The range of z-values will be included in the envelope.
0592:                     */
0593:                    final boolean set = (envelope == null);
0594:                    if (set) {
0595:                        envelope = new GeneralEnvelope(zDimension + 1);
0596:                    }
0597:                    final int dim = candidate.getDimension();
0598:                    for (int i = 0; i <= zDimension; i++) {
0599:                        double min = envelope.getMinimum(i);
0600:                        double max = envelope.getMaximum(i);
0601:                        final double minimum, maximum;
0602:                        if (i < dim) {
0603:                            minimum = candidate.getMinimum(i);
0604:                            maximum = candidate.getMaximum(i);
0605:                        } else if (i == zDimension) {
0606:                            final NumberRange range = element.getZRange();
0607:                            minimum = range.getMinimum();
0608:                            maximum = range.getMaximum();
0609:                        } else {
0610:                            minimum = Double.NEGATIVE_INFINITY;
0611:                            maximum = Double.POSITIVE_INFINITY;
0612:                        }
0613:                        if (set || minimum < min)
0614:                            min = minimum;
0615:                        if (set || maximum > max)
0616:                            max = maximum;
0617:                        envelope.setRange(i, min, max);
0618:                    }
0619:                }
0620:                this .numSampleDimensions = (sampleDimensions != null) ? sampleDimensions.length
0621:                        : 0;
0622:                this .sampleDimensions = sampleDimensionMismatch ? null
0623:                        : sampleDimensions;
0624:                if (envelope != null) {
0625:                    this .envelope = envelope;
0626:                    envelope.setCoordinateReferenceSystem(crs);
0627:                } else {
0628:                    assert this .elements.length == 0;
0629:                    this .envelope = new GeneralEnvelope(CRS.getEnvelope(crs));
0630:                }
0631:                zCRS = CRSUtilities.getSubCRS(crs, zDimension, zDimension + 1);
0632:            }
0633:
0634:            /**
0635:             * Constructs a new coverage using the same elements than the specified coverage stack.
0636:             */
0637:            protected CoverageStack(final CharSequence name,
0638:                    final CoverageStack source) {
0639:                super (name, source);
0640:                elements = source.elements;
0641:                sampleDimensions = source.sampleDimensions;
0642:                numSampleDimensions = source.numSampleDimensions;
0643:                envelope = source.envelope;
0644:                zDimension = source.zDimension;
0645:                zCRS = source.zCRS;
0646:                interpolationEnabled = source.interpolationEnabled;
0647:            }
0648:
0649:            /**
0650:             * Rethrows the exception in {@link #COMPARATOR} as a {@link RuntimeException}.
0651:             * It gives an opportunity for implementations of {@link Element} to uses some
0652:             * checked exception like {@link IOException}.
0653:             */
0654:            private static IOException rethrow(
0655:                    final UndeclaredThrowableException exception) {
0656:                final Throwable cause = exception.getCause();
0657:                if (cause instanceof  IOException) {
0658:                    return (IOException) cause;
0659:                }
0660:                if (cause instanceof  RuntimeException) {
0661:                    throw (RuntimeException) cause;
0662:                }
0663:                throw exception;
0664:            }
0665:
0666:            /**
0667:             * A comparator for {@link Element} sorting and binary search. This comparator uses the
0668:             * middle <var>z</var> value as criterion. It must accepts {@link Double} objects as well
0669:             * as {@link Element}, because binary search will mix those two kinds of object.
0670:             */
0671:            private static final Comparator COMPARATOR = new Comparator() {
0672:                public int compare(final Object entry1, final Object entry2) {
0673:                    try {
0674:                        return Double.compare(zFromObject(entry1),
0675:                                zFromObject(entry2));
0676:                    } catch (IOException exception) {
0677:                        throw new UndeclaredThrowableException(exception);
0678:                        // Will be catch and rethrown as IOException
0679:                        // by all methods using this comparator.
0680:                    }
0681:                }
0682:            };
0683:
0684:            /**
0685:             * Returns the <var>z</var> value of the specified object. The specified
0686:             * object may be a {@link Double} or an {@link Element} instance.
0687:             *
0688:             * @param  object The object to sort.
0689:             * @return The z-value of the specified object.
0690:             * @throws IOException if an I/O operation was required but failed.
0691:             * @throws ClassCastException if {@code object} is not an instance of {@link Double}
0692:             *         or {@link Element}.
0693:             */
0694:            private static double zFromObject(final Object object)
0695:                    throws IOException, ClassCastException {
0696:                if (object instanceof  Number) {
0697:                    return ((Number) object).doubleValue();
0698:                }
0699:                return getZ((Element) object);
0700:            }
0701:
0702:            /**
0703:             * Returns the middle <var>z</var> value. If the element has no <var>z</var> value
0704:             * (for example if the <var>z</var> value is the time and the coverage is constant
0705:             * over the time), then this method returns {@link Double#NaN}.
0706:             */
0707:            private static double getZ(final Element entry) throws IOException {
0708:                return getZ(entry.getZRange());
0709:            }
0710:
0711:            /**
0712:             * Returns the <var>z</var> value in the middle of the specified range.
0713:             * If the range is null, then this method returns {@link Double#NaN}.
0714:             */
0715:            private static double getZ(final NumberRange range) {
0716:                if (range != null) {
0717:                    final Number lower = (Number) range.getMinValue();
0718:                    final Number upper = (Number) range.getMaxValue();
0719:                    if (lower != null) {
0720:                        if (upper != null) {
0721:                            return 0.5 * (lower.doubleValue() + upper
0722:                                    .doubleValue());
0723:                        } else {
0724:                            return lower.doubleValue();
0725:                        }
0726:                    } else if (upper != null) {
0727:                        return upper.doubleValue();
0728:                    }
0729:                }
0730:                return Double.NaN;
0731:            }
0732:
0733:            /**
0734:             * Returns {@code true} if the specified z-value is inside the specified range.
0735:             */
0736:            private static boolean contains(final NumberRange range,
0737:                    final double z) {
0738:                return z >= range.getMinimum() && z <= range.getMaximum();
0739:            }
0740:
0741:            /**
0742:             * Returns the bounding box for the coverage domain in coordinate system coordinates.
0743:             */
0744:            public Envelope getEnvelope() {
0745:                return (Envelope) envelope.clone();
0746:            }
0747:
0748:            /**
0749:             * Returns the number of sample dimension in this coverage.
0750:             */
0751:            public int getNumSampleDimensions() {
0752:                if (numSampleDimensions != 0) {
0753:                    return numSampleDimensions;
0754:                } else {
0755:                    // TODO: provides a localized message.
0756:                    throw new IllegalStateException(
0757:                            "Sample dimensions are undetermined.");
0758:                }
0759:            }
0760:
0761:            /**
0762:             * Retrieve sample dimension information for the coverage.
0763:             * For a grid coverage, a sample dimension is a band. The sample dimension information
0764:             * include such things as description, data type of the value (bit, byte, integer...),
0765:             * the no data values, minimum and maximum values and a color table if one is associated
0766:             * with the dimension.
0767:             */
0768:            public SampleDimension getSampleDimension(final int index) {
0769:                if (sampleDimensions != null) {
0770:                    return sampleDimensions[index];
0771:                } else {
0772:                    // TODO: provides a localized message.
0773:                    throw new IllegalStateException(
0774:                            "Sample dimensions are undetermined.");
0775:                }
0776:            }
0777:
0778:            /**
0779:             * Snaps the specified coordinate point to the coordinate of the nearest voxel available in
0780:             * this coverage. First, this method locate the {@linkplain Element coverage element} at or
0781:             * near the last ordinate value (the <var>z</var> value). If no coverage is available at the
0782:             * specified <var>z</var> value, then the nearest one is selected. Next, this method locate
0783:             * the pixel under the {@code point} coordinate in the coverage element. The {@code point}
0784:             * is then set to the pixel center coordinate and to the <var>z</var> value of the selected
0785:             * coverage element. Consequently, calling any {@code evaluate(...)} method with snapped
0786:             * coordinates will returns non-interpolated values.
0787:             *
0788:             * @param  point The point to snap.
0789:             * @throws IOException if an I/O operation was required but failed.
0790:             */
0791:            public void snap(final DirectPosition point) throws IOException { // No synchronization needed.
0792:                double z = point.getOrdinate(zDimension);
0793:                int index;
0794:                try {
0795:                    index = Arrays.binarySearch(elements, new Double(z),
0796:                            COMPARATOR);
0797:                } catch (UndeclaredThrowableException exception) {
0798:                    throw rethrow(exception);
0799:                }
0800:                if (index < 0) {
0801:                    /*
0802:                     * There is no exact match for the z value.
0803:                     * Snap it to the closest coverage element.
0804:                     */
0805:                    index = ~index;
0806:                    if (index == elements.length) {
0807:                        if (index == 0) {
0808:                            return; // No elements in this coverage
0809:                        }
0810:                        z = getZ(elements[--index]);
0811:                    } else if (index == 0) {
0812:                        z = getZ(elements[index]);
0813:                    } else {
0814:                        final double lowerZ = getZ(elements[index - 1]);
0815:                        final double upperZ = getZ(elements[index]);
0816:                        assert !(z <= lowerZ || z >= upperZ) : z; // Use !(...) in order to accept NaN values.
0817:                        if (Double.isNaN(upperZ) || z - lowerZ < upperZ - z) {
0818:                            index--;
0819:                            z = lowerZ;
0820:                        } else {
0821:                            z = upperZ;
0822:                        }
0823:                    }
0824:                    point.setOrdinate(zDimension, z);
0825:                }
0826:                /*
0827:                 * Now that we know the coverage element,
0828:                 * snap the spatial coordinate point.
0829:                 */
0830:                final Element element = elements[index];
0831:                final GridGeometry geometry = element.getGridGeometry();
0832:                if (geometry != null) {
0833:                    final GridRange range = geometry.getGridRange();
0834:                    final MathTransform transform = geometry.getGridToCRS();
0835:                    final int dimension = transform.getSourceDimensions();
0836:                    DirectPosition position = new GeneralDirectPosition(
0837:                            dimension);
0838:                    for (int i = dimension; --i >= 0;) {
0839:                        // Copy only the first dimensions (may not be up to crs.dimension)
0840:                        position.setOrdinate(i, point.getOrdinate(i));
0841:                    }
0842:                    try {
0843:                        position = transform.inverse().transform(position,
0844:                                position);
0845:                        for (int i = dimension; --i >= 0;) {
0846:                            position.setOrdinate(i, Math.max(range.getLower(i),
0847:                                    Math.min(range.getUpper(i) - 1, (int) Math
0848:                                            .rint(position.getOrdinate(i)))));
0849:                        }
0850:                        position = transform.transform(position, position);
0851:                        for (int i = Math.min(dimension, zDimension); --i >= 0;) {
0852:                            // Do not touch the z-value, copy the other ordinates.
0853:                            point.setOrdinate(i, position.getOrdinate(i));
0854:                        }
0855:                    } catch (TransformException exception) {
0856:                        throw new CannotEvaluateException(
0857:                                cannotEvaluate(point), exception);
0858:                    }
0859:                }
0860:            }
0861:
0862:            /**
0863:             * Returns a message for exception.
0864:             *
0865:             * @todo provides a better formatting of the point coordinate.
0866:             */
0867:            private static String cannotEvaluate(final DirectPosition point) {
0868:                return Errors.format(ErrorKeys.CANT_EVALUATE_$1, point);
0869:            }
0870:
0871:            /**
0872:             * Loads a single coverage for the specified element. All {@code evaluate(...)} methods
0873:             * ultimately loads their coverages through this method. It provides a single place where
0874:             * to add post-loading processing, if needed.
0875:             *
0876:             * @param  element The coverage to load.
0877:             * @return The loaded coverage.
0878:             * @throws IOException if an error occured while loading image.
0879:             */
0880:            private Coverage load(final Element element) throws IOException {
0881:                Coverage coverage = element.getCoverage(listeners);
0882:                if (coverage instanceof  GridCoverage2D) {
0883:                    final GridCoverage2D coverage2D = (GridCoverage2D) coverage;
0884:                    if (interpolationEnabled) {
0885:                        if (coverage2D.getInterpolation() instanceof  InterpolationNearest) {
0886:                            coverage = Interpolator2D.create(coverage2D);
0887:                        }
0888:                    }
0889:                }
0890:                /*
0891:                 * CRS assertions (for debugging purpose).
0892:                 */
0893:                final CoordinateReferenceSystem sourceCRS;
0894:                assert CRS.equalsIgnoreMetadata((sourceCRS = coverage
0895:                        .getCoordinateReferenceSystem()), CRSUtilities
0896:                        .getSubCRS(crs, 0, sourceCRS.getCoordinateSystem()
0897:                                .getDimension())) : sourceCRS + "\n\n" + crs;
0898:                assert coverage.getNumSampleDimensions() == numSampleDimensions : coverage;
0899:                return coverage;
0900:            }
0901:
0902:            /**
0903:             * Loads a single image at the given index.
0904:             *
0905:             * @param  index Index in {@link #elements} for the image to load.
0906:             * @throws IOException if an error occured while loading image.
0907:             */
0908:            private void load(final int index) throws IOException {
0909:                final Element element = elements[index];
0910:                final NumberRange zRange = element.getZRange();
0911:                logLoading(VocabularyKeys.LOADING_IMAGE_$1,
0912:                        new String[] { element.getName() });
0913:                lower = upper = load(element);
0914:                lowerZ = upperZ = getZ(zRange);
0915:                lowerRange = upperRange = zRange;
0916:            }
0917:
0918:            /**
0919:             * Loads images for the given elements.
0920:             *
0921:             * @throws IOException if an error occured while loading images.
0922:             */
0923:            private void load(final Element lowerElement,
0924:                    final Element upperElement) throws IOException {
0925:                logLoading(VocabularyKeys.LOADING_IMAGES_$2, new String[] {
0926:                        lowerElement.getName(), upperElement.getName() });
0927:                final NumberRange lowerRange = lowerElement.getZRange();
0928:                final NumberRange upperRange = upperElement.getZRange();
0929:                final Coverage lower = load(lowerElement);
0930:                final Coverage upper = load(upperElement);
0931:
0932:                this .lower = lower; // Set only when BOTH images are OK.
0933:                this .upper = upper;
0934:                this .lowerZ = getZ(lowerRange);
0935:                this .upperZ = getZ(upperRange);
0936:                this .lowerRange = lowerRange;
0937:                this .upperRange = upperRange;
0938:            }
0939:
0940:            /**
0941:             * Loads coverages required for a linear interpolation at the specified <var>z</var> value.
0942:             * The loaded coverages will be stored in {@link #lower} and {@link #upper} fields. It is
0943:             * possible that the same coverage is given to those two fields, if this method determine
0944:             * that no interpolation is necessary.
0945:             *
0946:             * @param  z The z value.
0947:             * @return {@code true} if data were found.
0948:             * @throws PointOutsideCoverageException if the <var>z</var> value is outside the allowed range.
0949:             * @throws CannotEvaluateException if the operation failed for some other reason.
0950:             */
0951:            private boolean seek(final double z) throws CannotEvaluateException {
0952:                assert Thread.holdsLock(this );
0953:                /*
0954:                 * Check if currently loaded coverages
0955:                 * are valid for the requested z value.
0956:                 */
0957:                if ((z >= lowerZ && z <= upperZ)
0958:                        || (Double.isNaN(z) && Double.isNaN(lowerZ) && Double
0959:                                .isNaN(upperZ))) {
0960:                    return true;
0961:                }
0962:                /*
0963:                 * Currently loaded coverages are not valid for the requested z value.
0964:                 * Search for the coverage to use as upper bounds ({@link #upper}).
0965:                 */
0966:                final Number Z = new Double(z);
0967:                int index;
0968:                try {
0969:                    index = Arrays.binarySearch(elements, Z, COMPARATOR);
0970:                } catch (UndeclaredThrowableException exception) {
0971:                    // TODO: localize
0972:                    throw new CannotEvaluateException(
0973:                            "Can't fetch coverage properties.",
0974:                            rethrow(exception));
0975:                }
0976:                try {
0977:                    if (index >= 0) {
0978:                        /*
0979:                         * An exact match has been found.
0980:                         * Load only this coverage and exit.
0981:                         */
0982:                        load(index);
0983:                        return true;
0984:                    }
0985:                    index = ~index; // Insertion point (note: ~ is NOT the minus sign).
0986:                    if (index == elements.length) {
0987:                        if (--index >= 0) { // Does this stack has at least 1 coverage?
0988:                            /*
0989:                             * The requested z is after the last coverage's central z.
0990:                             * Maybe it is not after the last coverage's upper z. Check...
0991:                             */
0992:                            if (elements[index].getZRange().contains(Z)) {
0993:                                load(index);
0994:                                return true;
0995:                            }
0996:                        }
0997:                        // fall through the exception at this method's end.
0998:                    } else if (index == 0) {
0999:                        /*
1000:                         * The requested z is before the first coverage's central z.
1001:                         * Maybe it is not before the first coverage's lower z. Check...
1002:                         */
1003:                        if (elements[index].getZRange().contains(Z)) {
1004:                            load(index);
1005:                            return true;
1006:                        }
1007:                        // fall through the exception at this method's end.
1008:                    } else {
1009:                        /*
1010:                         * An interpolation between two coverages seems possible.
1011:                         * Checks if there is not a z lag between both.
1012:                         */
1013:                        final Element lowerElement = elements[index - 1];
1014:                        final Element upperElement = elements[index];
1015:                        final NumberRange lowerRange = lowerElement.getZRange();
1016:                        final NumberRange upperRange = upperElement.getZRange();
1017:                        final double lowerEnd = lowerRange.getMaximum();
1018:                        final double upperStart = upperRange.getMinimum();
1019:                        if (lowerEnd + lagTolerance >= upperStart) {
1020:                            if (interpolationEnabled) {
1021:                                load(lowerElement, upperElement);
1022:                            } else {
1023:                                if (Math.abs(getZ(upperRange) - z) > Math.abs(z
1024:                                        - getZ(lowerRange))) {
1025:                                    index--;
1026:                                }
1027:                                load(index);
1028:                            }
1029:                            return true;
1030:                        }
1031:                        if (lowerRange.contains(Z)) {
1032:                            load(index - 1);
1033:                            return true;
1034:                        }
1035:                        if (upperRange.contains(Z)) {
1036:                            load(index);
1037:                            return true;
1038:                        }
1039:                        return false; // Missing data.
1040:                    }
1041:                } catch (IOException exception) {
1042:                    String message = exception.getLocalizedMessage();
1043:                    if (message == null) {
1044:                        message = Utilities.getShortClassName(exception);
1045:                    }
1046:                    throw new CannotEvaluateException(message, exception);
1047:                }
1048:                final Object Zp;
1049:                if (zCRS instanceof  TemporalCRS) {
1050:                    Zp = DefaultTemporalCRS.wrap((TemporalCRS) zCRS).toDate(z);
1051:                } else {
1052:                    Zp = Z;
1053:                }
1054:                throw new OrdinateOutsideCoverageException(Errors.format(
1055:                        ErrorKeys.ZVALUE_OUTSIDE_COVERAGE_$2, getName(), Zp),
1056:                        zDimension, getEnvelope());
1057:            }
1058:
1059:            /**
1060:             * Returns a point with the same number of dimensions than the specified coverage.
1061:             * The number of dimensions must be {@link #zDimensions} or {@code zDimensions+1}.
1062:             */
1063:            private final DirectPosition reduce(DirectPosition coord,
1064:                    final Coverage coverage) {
1065:                final CoordinateReferenceSystem targetCRS = coverage
1066:                        .getCoordinateReferenceSystem();
1067:                final int dimension = targetCRS.getCoordinateSystem()
1068:                        .getDimension();
1069:                if (dimension == zDimension) {
1070:                    if (reducedPosition == null) {
1071:                        reducedPosition = new GeneralDirectPosition(zDimension);
1072:                    }
1073:                    for (int i = 0; i < dimension; i++) {
1074:                        reducedPosition.ordinates[i] = coord.getOrdinate(i);
1075:                    }
1076:                    coord = reducedPosition;
1077:                } else {
1078:                    assert CRS.equalsIgnoreMetadata(crs, targetCRS) : targetCRS;
1079:                }
1080:                return coord;
1081:            }
1082:
1083:            /**
1084:             * Returns a sequence of values for a given point in the coverage. The default implementation
1085:             * delegates to the {@link #evaluate(DirectPosition, double[])} method.
1086:             *
1087:             * @param  coord The coordinate point where to evaluate.
1088:             * @return The value at the specified point.
1089:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1090:             * @throws CannotEvaluateException if the computation failed for some other reason.
1091:             */
1092:            public Object evaluate(final DirectPosition coord)
1093:                    throws CannotEvaluateException {
1094:                return evaluate(coord, (double[]) null);
1095:            }
1096:
1097:            /**
1098:             * Returns a sequence of boolean values for a given point in the coverage.
1099:             *
1100:             * @param  coord The coordinate point where to evaluate.
1101:             * @param  dest  An array in which to store values, or {@code null} to create a new array.
1102:             * @return The {@code dest} array, or a newly created array if {@code dest} was null.
1103:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1104:             * @throws CannotEvaluateException if the computation failed for some other reason.
1105:             */
1106:            public synchronized boolean[] evaluate(final DirectPosition coord,
1107:                    boolean[] dest) throws CannotEvaluateException {
1108:                final double z = coord.getOrdinate(zDimension);
1109:                if (!seek(z)) {
1110:                    // Missing data
1111:                    if (dest == null) {
1112:                        dest = new boolean[numSampleDimensions];
1113:                    } else {
1114:                        Arrays.fill(dest, 0, numSampleDimensions, false);
1115:                    }
1116:                    return dest;
1117:                }
1118:                if (lower == upper) {
1119:                    return lower.evaluate(reduce(coord, lower), dest);
1120:                }
1121:                assert !(z < lowerZ || z > upperZ) : z; // Uses !(...) in order to accepts NaN.
1122:                final Coverage coverage = (z >= 0.5 * (lowerZ + upperZ)) ? upper
1123:                        : lower;
1124:                return coverage.evaluate(reduce(coord, coverage), dest);
1125:            }
1126:
1127:            /**
1128:             * Returns a sequence of byte values for a given point in the coverage.
1129:             *
1130:             * @param  coord The coordinate point where to evaluate.
1131:             * @param  dest  An array in which to store values, or {@code null} to create a new array.
1132:             * @return The {@code dest} array, or a newly created array if {@code dest} was null.
1133:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1134:             * @throws CannotEvaluateException if the computation failed for some other reason.
1135:             */
1136:            public synchronized byte[] evaluate(final DirectPosition coord,
1137:                    byte[] dest) throws CannotEvaluateException {
1138:                final double z = coord.getOrdinate(zDimension);
1139:                if (!seek(z)) {
1140:                    // Missing data
1141:                    if (dest == null) {
1142:                        dest = new byte[numSampleDimensions];
1143:                    } else {
1144:                        Arrays.fill(dest, 0, numSampleDimensions, (byte) 0);
1145:                    }
1146:                    return dest;
1147:                }
1148:                if (lower == upper) {
1149:                    return lower.evaluate(reduce(coord, lower), dest);
1150:                }
1151:                byteBuffer = upper.evaluate(reduce(coord, upper), byteBuffer);
1152:                dest = lower.evaluate(reduce(coord, lower), dest);
1153:                assert !(z < lowerZ || z > upperZ) : z; // Uses !(...) in order to accepts NaN.
1154:                final double ratio = (z - lowerZ) / (upperZ - lowerZ);
1155:                for (int i = 0; i < byteBuffer.length; i++) {
1156:                    dest[i] = (byte) Math.round(dest[i] + ratio
1157:                            * (byteBuffer[i] - dest[i]));
1158:                }
1159:                return dest;
1160:            }
1161:
1162:            /**
1163:             * Returns a sequence of integer values for a given point in the coverage.
1164:             *
1165:             * @param  coord The coordinate point where to evaluate.
1166:             * @param  dest  An array in which to store values, or {@code null} to create a new array.
1167:             * @return The {@code dest} array, or a newly created array if {@code dest} was null.
1168:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1169:             * @throws CannotEvaluateException if the computation failed for some other reason.
1170:             */
1171:            public synchronized int[] evaluate(final DirectPosition coord,
1172:                    int[] dest) throws CannotEvaluateException {
1173:                final double z = coord.getOrdinate(zDimension);
1174:                if (!seek(z)) {
1175:                    // Missing data
1176:                    if (dest == null) {
1177:                        dest = new int[numSampleDimensions];
1178:                    } else {
1179:                        Arrays.fill(dest, 0, numSampleDimensions, 0);
1180:                    }
1181:                    return dest;
1182:                }
1183:                if (lower == upper) {
1184:                    return lower.evaluate(reduce(coord, lower), dest);
1185:                }
1186:                intBuffer = upper.evaluate(reduce(coord, upper), intBuffer);
1187:                dest = lower.evaluate(reduce(coord, lower), dest);
1188:                assert !(z < lowerZ || z > upperZ) : z; // Uses !(...) in order to accepts NaN.
1189:                final double ratio = (z - lowerZ) / (upperZ - lowerZ);
1190:                for (int i = 0; i < intBuffer.length; i++) {
1191:                    dest[i] = (int) Math.round(dest[i] + ratio
1192:                            * (intBuffer[i] - dest[i]));
1193:                }
1194:                return dest;
1195:            }
1196:
1197:            /**
1198:             * Returns a sequence of float values for a given point in the coverage.
1199:             *
1200:             * @param  coord The coordinate point where to evaluate.
1201:             * @param  dest  An array in which to store values, or {@code null} to create a new array.
1202:             * @return The {@code dest} array, or a newly created array if {@code dest} was null.
1203:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1204:             * @throws CannotEvaluateException if the computation failed for some other reason.
1205:             */
1206:            public synchronized float[] evaluate(final DirectPosition coord,
1207:                    float[] dest) throws CannotEvaluateException {
1208:                final double z = coord.getOrdinate(zDimension);
1209:                if (!seek(z)) {
1210:                    // Missing data
1211:                    if (dest == null) {
1212:                        dest = new float[numSampleDimensions];
1213:                    }
1214:                    Arrays.fill(dest, 0, numSampleDimensions, Float.NaN);
1215:                    return dest;
1216:                }
1217:                if (lower == upper) {
1218:                    return lower.evaluate(reduce(coord, lower), dest);
1219:                }
1220:                floatBuffer = upper.evaluate(reduce(coord, upper), floatBuffer);
1221:                dest = lower.evaluate(reduce(coord, lower), dest);
1222:                assert !(z < lowerZ || z > upperZ) : z; // Uses !(...) in order to accepts NaN.
1223:                final double ratio = (z - lowerZ) / (upperZ - lowerZ);
1224:                for (int i = 0; i < floatBuffer.length; i++) {
1225:                    final float lower = dest[i];
1226:                    final float upper = floatBuffer[i];
1227:                    float value = (float) (lower + ratio * (upper - lower));
1228:                    if (Float.isNaN(value)) {
1229:                        if (!Float.isNaN(lower)) {
1230:                            assert Float.isNaN(upper) : upper;
1231:                            if (contains(lowerRange, z)) {
1232:                                value = lower;
1233:                            }
1234:                        } else if (!Float.isNaN(upper)) {
1235:                            assert Float.isNaN(lower) : lower;
1236:                            if (contains(upperRange, z)) {
1237:                                value = upper;
1238:                            }
1239:                        }
1240:                    }
1241:                    dest[i] = value;
1242:                }
1243:                return dest;
1244:            }
1245:
1246:            /**
1247:             * Returns a sequence of double values for a given point in the coverage.
1248:             *
1249:             * @param  coord The coordinate point where to evaluate.
1250:             * @param  dest  An array in which to store values, or {@code null} to create a new array.
1251:             * @return The {@code dest} array, or a newly created array if {@code dest} was null.
1252:             * @throws PointOutsideCoverageException if {@code coord} is outside coverage.
1253:             * @throws CannotEvaluateException if the computation failed for some other reason.
1254:             */
1255:            public synchronized double[] evaluate(final DirectPosition coord,
1256:                    double[] dest) throws CannotEvaluateException {
1257:                final double z = coord.getOrdinate(zDimension);
1258:                if (!seek(z)) {
1259:                    // Missing data
1260:                    if (dest == null) {
1261:                        dest = new double[numSampleDimensions];
1262:                    }
1263:                    Arrays.fill(dest, 0, numSampleDimensions, Double.NaN);
1264:                    return dest;
1265:                }
1266:                if (lower == upper) {
1267:                    return lower.evaluate(reduce(coord, lower), dest);
1268:                }
1269:                doubleBuffer = upper.evaluate(reduce(coord, upper),
1270:                        doubleBuffer);
1271:                dest = lower.evaluate(reduce(coord, lower), dest);
1272:                assert !(z < lowerZ || z > upperZ) : z; // Uses !(...) in order to accepts NaN.
1273:                final double ratio = (z - lowerZ) / (upperZ - lowerZ);
1274:                for (int i = 0; i < doubleBuffer.length; i++) {
1275:                    final double lower = dest[i];
1276:                    final double upper = doubleBuffer[i];
1277:                    double value = lower + ratio * (upper - lower);
1278:                    if (Double.isNaN(value)) {
1279:                        if (!Double.isNaN(lower)) {
1280:                            assert Double.isNaN(upper) : upper;
1281:                            if (contains(lowerRange, z)) {
1282:                                value = lower;
1283:                            }
1284:                        } else if (!Double.isNaN(upper)) {
1285:                            assert Double.isNaN(lower) : lower;
1286:                            if (contains(upperRange, z)) {
1287:                                value = upper;
1288:                            }
1289:                        }
1290:                    }
1291:                    dest[i] = value;
1292:                }
1293:                return dest;
1294:            }
1295:
1296:            /**
1297:             * Returns the coverages to be used for the specified <var>z</var> value. Special cases:
1298:             * <p>
1299:             * <ul>
1300:             *   <li>If there is no coverage available for the specified <var>z</var> value, returns
1301:             *       an {@linkplain Collections#EMPTY_LIST empty list}.</li>
1302:             *   <li>If there is only one coverage available, or if the specified <var>z</var> value
1303:             *       falls exactly in the middle of the {@linkplain Element#getZRange range value}
1304:             *       (i.e. no interpolation are needed), or if {@linkplain #setInterpolationEnabled
1305:             *       interpolations are disabled}, then this method returns a
1306:             *       {@linkplain Collections#singletonList singleton}.</li>
1307:             *   <li>Otherwise, this method returns a list containing at least 2 coverages, one before
1308:             *       and one after the specified <var>z</var> value.</li>
1309:             * </ul>
1310:             *
1311:             * @param z The z value for the coverages to be returned.
1312:             * @return  The coverages for the specified values. May contains 0, 1 or 2 elements.
1313:             *
1314:             * @since 2.3
1315:             */
1316:            public synchronized List/*<Coverage>*/coveragesAt(final double z) {
1317:                if (!seek(z)) {
1318:                    return Collections.EMPTY_LIST;
1319:                }
1320:                if (lower == upper) {
1321:                    return Collections.singletonList(lower);
1322:                }
1323:                return Arrays.asList(new Coverage[] { lower, upper });
1324:            }
1325:
1326:            /**
1327:             * Returns {@code true} if interpolation are enabled in the <var>z</var> value dimension.
1328:             * Interpolations are enabled by default.
1329:             */
1330:            public boolean isInterpolationEnabled() {
1331:                return interpolationEnabled;
1332:            }
1333:
1334:            /**
1335:             * Enable or disable interpolations in the <var>z</var> value dimension.
1336:             */
1337:            public synchronized void setInterpolationEnabled(final boolean flag) {
1338:                lower = null;
1339:                upper = null;
1340:                lowerZ = Double.POSITIVE_INFINITY;
1341:                upperZ = Double.NEGATIVE_INFINITY;
1342:                interpolationEnabled = flag;
1343:            }
1344:
1345:            /**
1346:             * Adds an {@link IIOReadWarningListener} to the list of registered warning listeners.
1347:             */
1348:            public void addIIOReadWarningListener(
1349:                    final IIOReadWarningListener listener) {
1350:                listeners.addIIOReadWarningListener(listener);
1351:            }
1352:
1353:            /**
1354:             * Removes an {@link IIOReadWarningListener} from the list of registered warning listeners.
1355:             */
1356:            public void removeIIOReadWarningListener(
1357:                    final IIOReadWarningListener listener) {
1358:                listeners.removeIIOReadWarningListener(listener);
1359:            }
1360:
1361:            /**
1362:             * Adds an {@link IIOReadProgressListener} to the list of registered progress listeners.
1363:             */
1364:            public void addIIOReadProgressListener(
1365:                    final IIOReadProgressListener listener) {
1366:                listeners.addIIOReadProgressListener(listener);
1367:            }
1368:
1369:            /**
1370:             * Removes an {@link IIOReadProgressListener} from the list of registered progress listeners.
1371:             */
1372:            public void removeIIOReadProgressListener(
1373:                    final IIOReadProgressListener listener) {
1374:                listeners.removeIIOReadProgressListener(listener);
1375:            }
1376:
1377:            /**
1378:             * Invoked automatically when an image is about to be loaded. The default implementation
1379:             * logs the message in the {@code "org.geotools.coverage"} logger. Subclasses can override
1380:             * this method if they wants a different logging.
1381:             *
1382:             * @param record The log record. The message contains information about the images to load.
1383:             */
1384:            protected void logLoading(final LogRecord record) {
1385:                Logging.getLogger("org.geotools.coverage").log(record);
1386:            }
1387:
1388:            /**
1389:             * Prepares a log record about an image to be loaded, and put the log record in a stack.
1390:             * The record will be effectively logged only when image loading really beging.
1391:             */
1392:            private void logLoading(final int key, final Object[] parameters) {
1393:                final Locale locale = null;
1394:                final LogRecord record = Vocabulary.getResources(locale)
1395:                        .getLogRecord(Level.INFO, key);
1396:                record.setSourceClassName(CoverageStack.class.getName());
1397:                record.setSourceMethodName("evaluate");
1398:                record.setParameters(parameters);
1399:                if (readListener == null) {
1400:                    readListener = new Listeners();
1401:                    addIIOReadProgressListener(readListener);
1402:                }
1403:                readListener.record = record;
1404:            }
1405:
1406:            /**
1407:             * A listener for monitoring image loading. The purpose for this listener is to
1408:             * log a message when an image is about to be loaded.
1409:             *
1410:             * @version $Id: CoverageStack.java 27862 2007-11-12 19:51:19Z desruisseaux $
1411:             * @author Martin Desruisseaux
1412:             */
1413:            private final class Listeners extends IIOReadProgressAdapter {
1414:                /**
1415:                 * The record to log.
1416:                 */
1417:                public LogRecord record;
1418:
1419:                /**
1420:                 * Reports that an image read operation is beginning.
1421:                 */
1422:                public void imageStarted(ImageReader source, int imageIndex) {
1423:                    if (record != null) {
1424:                        logLoading(record);
1425:                        source.removeIIOReadProgressListener(this);
1426:                        record = null;
1427:                    }
1428:                }
1429:            }
1430:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.