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


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
0005:         *    (C) 2001, 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:         *    This package contains documentation from OpenGIS specifications.
0018:         *    OpenGIS consortium's work is fully acknowledged here.
0019:         */
0020:        package org.geotools.coverage.grid;
0021:
0022:        import java.awt.Point;
0023:        import java.awt.RenderingHints;
0024:        import java.awt.geom.Point2D;
0025:        import java.awt.geom.Rectangle2D;
0026:        import java.awt.image.*; // Numerous imports here.
0027:        import java.awt.image.renderable.ParameterBlock;
0028:        import java.awt.image.renderable.RenderableImage;
0029:        import java.io.IOException;
0030:        import java.io.InvalidClassException;
0031:        import java.io.InvalidObjectException;
0032:        import java.io.ObjectInputStream;
0033:        import java.io.ObjectOutputStream;
0034:        import java.lang.reflect.Field;
0035:        import java.util.Arrays;
0036:        import java.util.Collection;
0037:        import java.util.HashSet;
0038:        import java.util.Iterator;
0039:        import java.util.List;
0040:        import java.util.Locale;
0041:        import java.util.Map;
0042:        import java.util.logging.Level;
0043:        import java.util.logging.LogRecord;
0044:        import javax.units.Unit;
0045:        import javax.media.jai.ImageLayout;
0046:        import javax.media.jai.Interpolation;
0047:        import javax.media.jai.InterpolationNearest;
0048:        import javax.media.jai.JAI;
0049:        import javax.media.jai.LookupTableJAI;
0050:        import javax.media.jai.NullOpImage;
0051:        import javax.media.jai.PlanarImage;
0052:        import javax.media.jai.RenderedImageAdapter;
0053:        import javax.media.jai.RenderedOp;
0054:        import javax.media.jai.operator.LookupDescriptor;
0055:        import javax.media.jai.operator.PiecewiseDescriptor;
0056:        import javax.media.jai.operator.RescaleDescriptor;
0057:        import javax.media.jai.remote.SerializableRenderedImage;
0058:        import javax.media.jai.util.CaselessStringKey; // For javadoc
0059:
0060:        import org.opengis.coverage.CannotEvaluateException;
0061:        import org.opengis.coverage.PointOutsideCoverageException;
0062:        import org.opengis.coverage.SampleDimension;
0063:        import org.opengis.coverage.grid.GridCoverage;
0064:        import org.opengis.coverage.grid.GridGeometry;
0065:        import org.opengis.coverage.grid.GridRange;
0066:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0067:        import org.opengis.referencing.operation.MathTransform1D;
0068:        import org.opengis.referencing.operation.TransformException;
0069:        import org.opengis.geometry.DirectPosition;
0070:        import org.opengis.geometry.Envelope;
0071:        import org.opengis.geometry.MismatchedDimensionException;
0072:
0073:        import org.geotools.coverage.Category;
0074:        import org.geotools.coverage.GridSampleDimension;
0075:        import org.geotools.coverage.AbstractCoverage;
0076:        import org.geotools.coverage.processing.AbstractProcessor;
0077:        import org.geotools.geometry.Envelope2D;
0078:        import org.geotools.resources.XArray;
0079:        import org.geotools.resources.coverage.CoverageUtilities;
0080:        import org.geotools.resources.i18n.Errors;
0081:        import org.geotools.resources.i18n.ErrorKeys;
0082:        import org.geotools.resources.i18n.Logging;
0083:        import org.geotools.resources.i18n.LoggingKeys;
0084:        import org.geotools.resources.image.ImageUtilities;
0085:        import org.geotools.util.NumberRange;
0086:
0087:        /**
0088:         * Basic access to grid data values backed by a two-dimensional
0089:         * {@linkplain RenderedImage rendered image}. Each band in an image is represented as a
0090:         * {@linkplain GridSampleDimension sample dimension}.
0091:         * <p>
0092:         * Grid coverages are usually two-dimensional. However, {@linkplain #getEnvelope their envelope}
0093:         * may have more than two dimensions. For example, a remote sensing image may be valid only over
0094:         * some time range (the time of satellite pass over the observed area). Envelopes for such grid
0095:         * coverage can have three dimensions: the two usual ones (horizontal extent along <var>x</var>
0096:         * and <var>y</var>), and a third one for start time and end time (time extent along <var>t</var>).
0097:         * However, the {@linkplain GeneralGridRange grid range} for all extra-dimension <strong>must</strong>
0098:         * have a {@linkplain GeneralGridRange#getLength size} not greater than 1. In other words, a
0099:         * {@code GridCoverage2D} can be a slice in a 3 dimensional grid coverage. Each slice can have an
0100:         * arbitrary width and height (like any two-dimensional images), but only 1 voxel depth (a "voxel"
0101:         * is a three-dimensional pixel).
0102:         * <p>
0103:         * <strong>Serialization note:</strong><br>
0104:         * Because it is serializable, {@code GridCoverage2D} can be included as method argument or as
0105:         * return type in <cite>Remote Method Invocation</cite> (RMI). However, the pixel data are not
0106:         * sent during serialization. Instead, the image data are transmitted "on-demand" using socket
0107:         * communications. This mechanism is implemented using JAI {@link SerializableRenderedImage}
0108:         * class. While serialization (usually on server side) should work on J2SE 1.4 and above,
0109:         * deserialization (usually on client side) of {@code GridCoverage2D} instances requires J2SE 1.5.
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/grid/GridCoverage2D.java $
0113:         * @version $Id: GridCoverage2D.java 26655 2007-08-22 13:57:25Z desruisseaux $
0114:         * @author Martin Desruisseaux
0115:         */
0116:        public class GridCoverage2D extends AbstractGridCoverage implements 
0117:                RenderedCoverage {
0118:            /**
0119:             * For compatibility during cross-version serialization.
0120:             */
0121:            private static final long serialVersionUID = 667472989475027853L;
0122:
0123:            /**
0124:             * Slight number for rounding errors in floating point comparaison.
0125:             */
0126:            private static final float EPS = 1E-5f;
0127:
0128:            /**
0129:             * {@code true} if we should apply a conservative policy for the "piecewise" operation.
0130:             * The conservative policy is to apply "piecewise" only if there is no ambiguity about what
0131:             * the user wants.
0132:             */
0133:            private static final boolean CONSERVATIVE_PIECEWISE = true;
0134:
0135:            /**
0136:             * A grid coverage using the sample dimensions {@code GridSampleDimension.inverse}.
0137:             * This object is constructed and returned by {@link #geophysics}. Constructed when
0138:             * first needed. May appears also in the {@link #sources} list.
0139:             */
0140:            private transient GridCoverage2D inverse;
0141:
0142:            /**
0143:             * The raster data.
0144:             */
0145:            protected transient final PlanarImage image;
0146:
0147:            /**
0148:             * The serialized image, as an instance of {@link SerializableRenderedImage}.
0149:             * This image will be created only when first needed during serialization.
0150:             */
0151:            private RenderedImage serializedImage;
0152:
0153:            /**
0154:             * The grid geometry.
0155:             */
0156:            protected final GridGeometry2D gridGeometry;
0157:
0158:            /**
0159:             * List of sample dimension information for the grid coverage.
0160:             * For a grid coverage, a sample dimension is a band. The sample dimension information
0161:             * include such things as description, data type of the value (bit, byte, integer...),
0162:             * the no data values, minimum and maximum values and a color table if one is associated
0163:             * with the dimension. A coverage must have at least one sample dimension.
0164:             */
0165:            private final GridSampleDimension[] sampleDimensions;
0166:
0167:            /**
0168:             * {@code true} is all sample in the image are geophysics values.
0169:             */
0170:            private final boolean isGeophysics;
0171:
0172:            /**
0173:             * {@code true} if this coverage has been disposed.
0174:             *
0175:             * @see #dispose
0176:             */
0177:            private transient boolean disposed;
0178:
0179:            /**
0180:             * The preferred encoding to use for serialization using the {@code writeObject} method,
0181:             * or {@code null} for the default encoding. This value is set by {@link GridCoverageFactory}
0182:             * according the hints provided to the factory.
0183:             */
0184:            transient String tileEncoding;
0185:
0186:            /**
0187:             * Construct a new grid coverage with the same parameter than the specified
0188:             * coverage. This constructor is useful when creating a coverage with
0189:             * identical data, but in which some method has been overridden in order to
0190:             * process data differently (e.g. interpolating them).
0191:             *
0192:             * @param name The name for this coverage, or {@code null} for the same than {@code coverage}.
0193:             * @param coverage The source grid coverage.
0194:             */
0195:            protected GridCoverage2D(final CharSequence name,
0196:                    final GridCoverage2D coverage) {
0197:                super (name, coverage);
0198:                image = coverage.image;
0199:                gridGeometry = coverage.gridGeometry;
0200:                sampleDimensions = coverage.sampleDimensions;
0201:                isGeophysics = coverage.isGeophysics;
0202:                tileEncoding = coverage.tileEncoding;
0203:            }
0204:
0205:            /**
0206:             * Constructs a grid coverage with the specified {@linkplain GridGeometry2D grid geometry} and
0207:             * {@linkplain GridSampleDimension sample dimensions}. The {@linkplain Envelope envelope}
0208:             * (including the {@linkplain CoordinateReferenceSystem coordinate reference system}) is
0209:             * inferred from the grid geometry.
0210:             * <p>
0211:             * This constructor accepts an optional set of properties. "Properties" in <cite>Java Advanced
0212:             * Imaging</cite> is what OpenGIS calls "Metadata". Keys are {@link String} objects
0213:             * ({@link CaselessStringKey} are accepted as well), while values may be any {@link Object}.
0214:             *
0215:             * @param name         The grid coverage name.
0216:             * @param image        The image.
0217:             * @param gridGeometry The grid geometry (must contains an {@linkplain GridGeometry2D#getEnvelope
0218:             *                     envelope} with its {@linkplain GridGeometry2D#getCoordinateReferenceSystem
0219:             *                     coordinate reference system} and a "{@linkplain
0220:             *                     GridGeometry2D#getGridToCoordinateSystem grid to CRS}" transform).
0221:             * @param bands        Sample dimensions for each image band, or {@code null} for default sample
0222:             *                     dimensions. If non-null, then this array's length must matches the number
0223:             *                     of bands in {@code image}.
0224:             * @param sources      The sources for this grid coverage, or {@code null} if none.
0225:             * @param properties   The set of properties for this coverage, or {@code null} none.
0226:             *
0227:             * @throws IllegalArgumentException if the number of bands differs from the number of sample
0228:             *         dimensions.
0229:             *
0230:             * @since 2.2
0231:             */
0232:            protected GridCoverage2D(final CharSequence name,
0233:                    final PlanarImage image, GridGeometry2D gridGeometry,
0234:                    final GridSampleDimension[] bands,
0235:                    final GridCoverage[] sources, final Map properties)
0236:                    throws IllegalArgumentException {
0237:                super (name, gridGeometry.getCoordinateReferenceSystem(),
0238:                        sources, image, properties);
0239:                this .image = image;
0240:                /*
0241:                 * Wraps the user-suplied sample dimensions into instances of Grid2DSampleDimension. This
0242:                 * process will creates default sample dimensions if the user supplied null values. Those
0243:                 * default will be inferred from image type (integers, floats...) and range of values. If
0244:                 * an inconsistency is found in user-supplied sample dimensions, an IllegalArgumentException
0245:                 * is thrown.
0246:                 */
0247:                sampleDimensions = new GridSampleDimension[image.getNumBands()];
0248:                isGeophysics = Grid2DSampleDimension.create(name, image, bands,
0249:                        sampleDimensions);
0250:                /*
0251:                 * Computes the grid range if it was not explicitly provided. The range will be inferred
0252:                 * from the image size, if needed. The envelope computation (if needed) requires a valid
0253:                 * 'gridToCRS' transform in the GridGeometry object. In any case, the envelope must be
0254:                 * non-empty and its dimension must matches the coordinate reference system's dimension.
0255:                 */
0256:                final int dimension = crs.getCoordinateSystem().getDimension();
0257:                if (!gridGeometry.isDefined(GridGeometry2D.GRID_RANGE)) {
0258:                    final GridRange r = new GeneralGridRange(image, dimension);
0259:                    if (gridGeometry.isDefined(GridGeometry2D.GRID_TO_CRS)) {
0260:                        gridGeometry = new GridGeometry2D(r, gridGeometry
0261:                                .getGridToCRS(), crs);
0262:                    } else {
0263:                        /*
0264:                         * If the math transform was not explicitly specified by the user, then it will be
0265:                         * computed from the envelope. In this case, some heuristic rules are used in order
0266:                         * to decide if we should reverse some axis directions or swap axis. 
0267:                         */
0268:                        gridGeometry = new GridGeometry2D(r, gridGeometry
0269:                                .getEnvelope());
0270:                    }
0271:                } else {
0272:                    /*
0273:                     * Makes sure that the 'gridToCRS' transform is defined.
0274:                     * An exception will be thrown otherwise.
0275:                     */
0276:                    gridGeometry.getGridToCRS();
0277:                }
0278:                this .gridGeometry = gridGeometry;
0279:                assert gridGeometry.isDefined(GridGeometry2D.CRS
0280:                        | GridGeometry2D.ENVELOPE | GridGeometry2D.GRID_RANGE
0281:                        | GridGeometry2D.GRID_TO_CRS);
0282:                /*
0283:                 * Last argument checks. The image size must be consistent with the grid range
0284:                 * and the envelope must be non-empty.
0285:                 */
0286:                final String error = checkConsistency(image, gridGeometry);
0287:                if (error != null) {
0288:                    throw new IllegalArgumentException(error);
0289:                }
0290:                if (dimension <= Math.max(gridGeometry.axisDimensionX,
0291:                        gridGeometry.axisDimensionY)
0292:                        || !(gridGeometry.envelope
0293:                                .getLength(gridGeometry.axisDimensionX) > 0)
0294:                        || !(gridGeometry.envelope
0295:                                .getLength(gridGeometry.axisDimensionY) > 0)) {
0296:                    throw new IllegalArgumentException(Errors
0297:                            .format(ErrorKeys.EMPTY_ENVELOPE));
0298:                }
0299:            }
0300:
0301:            /**
0302:             * Checks if the bounding box of the specified image is consistents with the specified
0303:             * grid geometry. If an inconsistency has been found, then an error string is returned.
0304:             * This string will be typically used as a message in an exception to be thrown.
0305:             * <p>
0306:             * Note that a succesful check at construction time may fails later if the image is part
0307:             * of a JAI chain (i.e. is a {@link RenderedOp}) and its bounds has been edited (i.e the
0308:             * image node as been re-rendered). Since {@code GridCoverage} are immutable by design,
0309:             * we are not allowed to propagate the image change here. The {@link #getGridGeometry} method
0310:             * will thrown an {@link IllegalStateException} in this case.
0311:             */
0312:            private static String checkConsistency(final RenderedImage image,
0313:                    final GridGeometry2D grid) {
0314:                final GridRange range = grid.getGridRange();
0315:                final int dimension = range.getDimension();
0316:                for (int i = 0; i < dimension; i++) {
0317:                    final int min, length;
0318:                    final Object label;
0319:                    if (i == grid.gridDimensionX) {
0320:                        min = image.getMinX();
0321:                        length = image.getWidth();
0322:                        label = "\"X\"";
0323:                    } else if (i == grid.gridDimensionY) {
0324:                        min = image.getMinY();
0325:                        length = image.getHeight();
0326:                        label = "\"Y\"";
0327:                    } else {
0328:                        min = range.getLower(i);
0329:                        length = Math.min(Math.max(range.getUpper(i), 0), 1);
0330:                        label = new Integer(i);
0331:                    }
0332:                    if (range.getLower(i) != min
0333:                            || range.getLength(i) != length) {
0334:                        return Errors.format(ErrorKeys.BAD_GRID_RANGE_$3,
0335:                                label, new Integer(min), new Integer(min
0336:                                        + length));
0337:                    }
0338:                }
0339:                return null;
0340:            }
0341:
0342:            /**
0343:             * Returns {@code true} if grid data can be edited. The default
0344:             * implementation returns {@code true} if {@link #image} is an
0345:             * instance of {@link WritableRenderedImage}.
0346:             */
0347:            public boolean isDataEditable() {
0348:                return (image instanceof  WritableRenderedImage);
0349:            }
0350:
0351:            /**
0352:             * Returns information for the grid coverage geometry. Grid geometry
0353:             * includes the valid range of grid coordinates and the georeferencing.
0354:             *
0355:             * @todo Use covariant return type once we are allowed to compile for J2SE 1.5.
0356:             */
0357:            public GridGeometry getGridGeometry() {
0358:                final String error = checkConsistency(image, gridGeometry);
0359:                if (error != null) {
0360:                    throw new IllegalStateException(error);
0361:                }
0362:                return gridGeometry;
0363:            }
0364:
0365:            /**
0366:             * Returns the bounding box for the coverage domain in coordinate reference system coordinates.
0367:             * The returned envelope have at least two dimensions. It may have more dimensions if the
0368:             * coverage has some extent in other dimensions (for example a depth, or a start and end time).
0369:             */
0370:            public Envelope getEnvelope() {
0371:                return gridGeometry.getEnvelope();
0372:            }
0373:
0374:            /**
0375:             * Returns the two-dimensional bounding box for the coverage domain in coordinate reference
0376:             * system coordinates. If the coverage envelope has more than two dimensions, only the
0377:             * dimensions used in the underlying rendered image are returned.
0378:             */
0379:            public Envelope2D getEnvelope2D() {
0380:                return gridGeometry.getEnvelope2D();
0381:            }
0382:
0383:            /**
0384:             * Returns the two-dimensional part of this grid coverage CRS. This is usually (but not
0385:             * always) identical to the {@linkplain #getCoordinateReferenceSystem full CRS}.
0386:             *
0387:             * @see #getCoordinateReferenceSystem
0388:             */
0389:            public CoordinateReferenceSystem getCoordinateReferenceSystem2D() {
0390:                return gridGeometry.getCoordinateReferenceSystem2D();
0391:            }
0392:
0393:            /**
0394:             * Returns the number of bands in the grid coverage.
0395:             */
0396:            public int getNumSampleDimensions() {
0397:                return sampleDimensions.length;
0398:            }
0399:
0400:            /**
0401:             * Retrieve sample dimension information for the coverage.
0402:             * For a grid coverage, a sample dimension is a band. The sample dimension information
0403:             * include such things as description, data type of the value (bit, byte, integer...),
0404:             * the no data values, minimum and maximum values and a color table if one is associated
0405:             * with the dimension. A coverage must have at least one sample dimension.
0406:             */
0407:            public SampleDimension getSampleDimension(final int index) {
0408:                return sampleDimensions[index];
0409:            }
0410:
0411:            /**
0412:             * Returns all sample dimensions for this grid coverage.
0413:             */
0414:            public GridSampleDimension[] getSampleDimensions() {
0415:                return (GridSampleDimension[]) sampleDimensions.clone();
0416:            }
0417:
0418:            /**
0419:             * Returns the interpolation used for all {@code evaluate(...)} methods.
0420:             * The default implementation returns {@link InterpolationNearest}.
0421:             *
0422:             * @return The interpolation.
0423:             */
0424:            public Interpolation getInterpolation() {
0425:                return Interpolation.getInstance(Interpolation.INTERP_NEAREST);
0426:            }
0427:
0428:            /**
0429:             * Returns the value vector for a given point in the coverage.
0430:             * A value for each sample dimension is included in the vector.
0431:             */
0432:            public Object evaluate(final DirectPosition point)
0433:                    throws CannotEvaluateException {
0434:                final int dataType = image.getSampleModel().getDataType();
0435:                switch (dataType) {
0436:                case DataBuffer.TYPE_BYTE:
0437:                    return evaluate(point, (byte[]) null);
0438:                case DataBuffer.TYPE_SHORT: // Fall through
0439:                case DataBuffer.TYPE_USHORT: // Fall through
0440:                case DataBuffer.TYPE_INT:
0441:                    return evaluate(point, (int[]) null);
0442:                case DataBuffer.TYPE_FLOAT:
0443:                    return evaluate(point, (float[]) null);
0444:                case DataBuffer.TYPE_DOUBLE:
0445:                    return evaluate(point, (double[]) null);
0446:                default:
0447:                    throw new CannotEvaluateException();
0448:                }
0449:            }
0450:
0451:            /**
0452:             * Returns a sequence of byte values for a given point in the coverage.
0453:             *
0454:             * @param  coord The coordinate point where to evaluate.
0455:             * @param  dest  An array in which to store values, or {@code null}.
0456:             * @return An array containing values.
0457:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0458:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0459:             *         failed because the input point has invalid coordinates.
0460:             */
0461:            public byte[] evaluate(final DirectPosition coord, byte[] dest)
0462:                    throws CannotEvaluateException {
0463:                final int[] array = evaluate(coord, (int[]) null);
0464:                if (dest == null) {
0465:                    dest = new byte[array.length];
0466:                }
0467:                for (int i = 0; i < array.length; i++) {
0468:                    dest[i] = (byte) array[i];
0469:                }
0470:                return dest;
0471:            }
0472:
0473:            /**
0474:             * Returns a sequence of integer values for a given point in the coverage.
0475:             *
0476:             * @param  coord The coordinate point where to evaluate.
0477:             * @param  dest  An array in which to store values, or {@code null}.
0478:             * @return An array containing values.
0479:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0480:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0481:             *         failed because the input point has invalid coordinates.
0482:             */
0483:            public int[] evaluate(final DirectPosition coord, final int[] dest)
0484:                    throws CannotEvaluateException {
0485:                return evaluate(toPoint2D(coord), dest);
0486:            }
0487:
0488:            /**
0489:             * Returns a sequence of float values for a given point in the coverage.
0490:             *
0491:             * @param  coord The coordinate point where to evaluate.
0492:             * @param  dest  An array in which to store values, or {@code null}.
0493:             * @return An array containing values.
0494:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0495:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0496:             *         failed because the input point has invalid coordinates.
0497:             */
0498:            public float[] evaluate(final DirectPosition coord,
0499:                    final float[] dest) throws CannotEvaluateException {
0500:                return evaluate(toPoint2D(coord), dest);
0501:            }
0502:
0503:            /**
0504:             * Returns a sequence of double values for a given point in the coverage.
0505:             *
0506:             * @param  coord The coordinate point where to evaluate.
0507:             * @param  dest  An array in which to store values, or {@code null}.
0508:             * @return An array containing values.
0509:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0510:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0511:             *         failed because the input point has invalid coordinates.
0512:             */
0513:            public double[] evaluate(final DirectPosition coord,
0514:                    final double[] dest) throws CannotEvaluateException {
0515:                return evaluate(toPoint2D(coord), dest);
0516:            }
0517:
0518:            /**
0519:             * Converts the specified point into a two-dimensional one.
0520:             *
0521:             * @param  point The point to transform into a {@link Point2D} object.
0522:             * @return The specified point as a {@link Point2D} object.
0523:             * @throws MismatchedDimensionException if the point doesn't have the expected dimension.
0524:             */
0525:            private Point2D toPoint2D(final DirectPosition point)
0526:                    throws MismatchedDimensionException {
0527:                final int actual = point.getDimension();
0528:                final int expected = crs.getCoordinateSystem().getDimension();
0529:                if (actual != expected) {
0530:                    throw new MismatchedDimensionException(Errors.format(
0531:                            ErrorKeys.MISMATCHED_DIMENSION_$2, new Integer(
0532:                                    actual), new Integer(expected)));
0533:                }
0534:                if (point instanceof  Point2D) {
0535:                    return (Point2D) point;
0536:                }
0537:                return new Point2D.Double(point
0538:                        .getOrdinate(gridGeometry.axisDimensionX), point
0539:                        .getOrdinate(gridGeometry.axisDimensionY));
0540:            }
0541:
0542:            /**
0543:             * Returns a sequence of integer values for a given two-dimensional point in the coverage.
0544:             *
0545:             * @param  coord The coordinate point where to evaluate.
0546:             * @param  dest  An array in which to store values, or {@code null}.
0547:             * @return An array containing values.
0548:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0549:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0550:             *         failed because the input point has invalid coordinates.
0551:             */
0552:            public int[] evaluate(final Point2D coord, final int[] dest)
0553:                    throws CannotEvaluateException {
0554:                final Point2D pixel = gridGeometry.inverseTransform(coord);
0555:                final double fx = pixel.getX();
0556:                final double fy = pixel.getY();
0557:                if (!Double.isNaN(fx) && !Double.isNaN(fy)) {
0558:                    final int x = (int) Math.round(fx);
0559:                    final int y = (int) Math.round(fy);
0560:                    if (image.getBounds().contains(x, y)) { // getBounds() returns a cached instance.
0561:                        return image.getTile(image.XToTileX(x),
0562:                                image.YToTileY(y)).getPixel(x, y, dest);
0563:                    }
0564:                }
0565:                throw new PointOutsideCoverageException(
0566:                        pointOutsideCoverage(coord));
0567:            }
0568:
0569:            /**
0570:             * Returns a sequence of float values for a given two-dimensional point in the coverage.
0571:             *
0572:             * @param  coord The coordinate point where to evaluate.
0573:             * @param  dest  An array in which to store values, or {@code null}.
0574:             * @return An array containing values.
0575:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0576:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0577:             *         failed because the input point has invalid coordinates.
0578:             */
0579:            public float[] evaluate(final Point2D coord, final float[] dest)
0580:                    throws CannotEvaluateException {
0581:                final Point2D pixel = gridGeometry.inverseTransform(coord);
0582:                final double fx = pixel.getX();
0583:                final double fy = pixel.getY();
0584:                if (!Double.isNaN(fx) && !Double.isNaN(fy)) {
0585:                    final int x = (int) Math.round(fx);
0586:                    final int y = (int) Math.round(fy);
0587:                    if (image.getBounds().contains(x, y)) { // getBounds() returns a cached instance.
0588:                        return image.getTile(image.XToTileX(x),
0589:                                image.YToTileY(y)).getPixel(x, y, dest);
0590:                    }
0591:                }
0592:                throw new PointOutsideCoverageException(
0593:                        pointOutsideCoverage(coord));
0594:            }
0595:
0596:            /**
0597:             * Returns a sequence of double values for a given two-dimensional point in the coverage.
0598:             *
0599:             * @param  coord The coordinate point where to evaluate.
0600:             * @param  dest  An array in which to store values, or {@code null}.
0601:             * @return An array containing values.
0602:             * @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
0603:             *         More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
0604:             *         failed because the input point has invalid coordinates.
0605:             */
0606:            public double[] evaluate(final Point2D coord, final double[] dest)
0607:                    throws CannotEvaluateException {
0608:                final Point2D pixel = gridGeometry.inverseTransform(coord);
0609:                final double fx = pixel.getX();
0610:                final double fy = pixel.getY();
0611:                if (!Double.isNaN(fx) && !Double.isNaN(fy)) {
0612:                    final int x = (int) Math.round(fx);
0613:                    final int y = (int) Math.round(fy);
0614:                    if (image.getBounds().contains(x, y)) { // getBounds() returns a cached instance.
0615:                        return image.getTile(image.XToTileX(x),
0616:                                image.YToTileY(y)).getPixel(x, y, dest);
0617:                    }
0618:                }
0619:                throw new PointOutsideCoverageException(
0620:                        pointOutsideCoverage(coord));
0621:            }
0622:
0623:            /**
0624:             * Returns a debug string for the specified coordinate.   This method produces a
0625:             * string with pixel coordinates and pixel values for all bands (with geophysics
0626:             * values or category name in parenthesis). Example for a 1-banded image:
0627:             *
0628:             * <blockquote><pre>(1171,1566)=[196 (29.6 °C)]</pre></blockquote>
0629:             *
0630:             * @param  coord The coordinate point where to evaluate.
0631:             * @return A string with pixel coordinates and pixel values at the specified location,
0632:             *         or {@code null} if {@code coord} is outside coverage.
0633:             */
0634:            public synchronized String getDebugString(final DirectPosition coord) {
0635:                Point2D pixel = toPoint2D(coord);
0636:                pixel = gridGeometry.inverseTransform(pixel);
0637:                final int x = (int) Math.round(pixel.getX());
0638:                final int y = (int) Math.round(pixel.getY());
0639:                if (image.getBounds().contains(x, y)) { // getBounds() returns a cached instance.
0640:                    final int numBands = image.getNumBands();
0641:                    final Raster raster = image.getTile(image.XToTileX(x),
0642:                            image.YToTileY(y));
0643:                    final int datatype = image.getSampleModel().getDataType();
0644:                    final StringBuffer buffer = new StringBuffer();
0645:                    buffer.append('(');
0646:                    buffer.append(x);
0647:                    buffer.append(',');
0648:                    buffer.append(y);
0649:                    buffer.append(")=[");
0650:                    for (int band = 0; band < numBands; band++) {
0651:                        if (band != 0) {
0652:                            buffer.append(";\u00A0");
0653:                        }
0654:                        final double sample = raster
0655:                                .getSampleDouble(x, y, band);
0656:                        switch (datatype) {
0657:                        case DataBuffer.TYPE_DOUBLE:
0658:                            buffer.append((double) sample);
0659:                            break;
0660:                        case DataBuffer.TYPE_FLOAT:
0661:                            buffer.append((float) sample);
0662:                            break;
0663:                        default:
0664:                            buffer.append((int) sample);
0665:                            break;
0666:                        }
0667:                        final String formatted = sampleDimensions[band]
0668:                                .getLabel(sample, null);
0669:                        if (formatted != null) {
0670:                            buffer.append("\u00A0(");
0671:                            buffer.append(formatted);
0672:                            buffer.append(')');
0673:                        }
0674:                    }
0675:                    buffer.append(']');
0676:                    return buffer.toString();
0677:                }
0678:                return null;
0679:            }
0680:
0681:            /**
0682:             * Returns the optimal size to use for each dimension when accessing grid values.
0683:             * The default implementation returns the image's tiles size.
0684:             */
0685:            public int[] getOptimalDataBlockSizes() {
0686:                final int[] size = new int[getDimension()];
0687:                Arrays.fill(size, 1);
0688:                size[gridGeometry.gridDimensionX] = image.getTileWidth();
0689:                size[gridGeometry.gridDimensionY] = image.getTileHeight();
0690:                return size;
0691:            }
0692:
0693:            /**
0694:             * Returns grid data as a rendered image.
0695:             */
0696:            public RenderedImage getRenderedImage() {
0697:                ensureValid();
0698:                return image;
0699:            }
0700:
0701:            /**
0702:             * Returns 2D view of this grid coverage as a renderable image.
0703:             * This method allows interoperability with Java2D.
0704:             *
0705:             * @param  xAxis Dimension to use for <var>x</var> axis.
0706:             * @param  yAxis Dimension to use for <var>y</var> axis.
0707:             * @return A 2D view of this grid coverage as a renderable image.
0708:             */
0709:            public RenderableImage getRenderableImage(final int xAxis,
0710:                    final int yAxis) {
0711:                ensureValid();
0712:                if (xAxis == gridGeometry.axisDimensionX
0713:                        && yAxis == gridGeometry.axisDimensionY) {
0714:                    return new Renderable();
0715:                } else {
0716:                    return super .getRenderableImage(xAxis, yAxis);
0717:                }
0718:            }
0719:
0720:            /**
0721:             * {inheritDoc}
0722:             */
0723:            //@Override
0724:            public void show(String title, final int xAxis, final int yAxis) {
0725:                ensureValid();
0726:                final GridCoverage2D displayable = geophysics(false);
0727:                if (displayable != this ) {
0728:                    displayable.show(title, xAxis, yAxis);
0729:                    return;
0730:                }
0731:                if (title == null || (title = title.trim()).length() == 0) {
0732:                    final StringBuffer buffer = new StringBuffer(String
0733:                            .valueOf(getName()));
0734:                    final int visibleBandIndex = CoverageUtilities
0735:                            .getVisibleBand(this );
0736:                    final SampleDimension visibleBand = getSampleDimension(visibleBandIndex);
0737:                    final Unit unit = visibleBand.getUnits();
0738:                    buffer.append(" - ").append(
0739:                            String.valueOf(visibleBand.getDescription()));
0740:                    if (unit != null) {
0741:                        buffer.append(" (").append(unit).append(')');
0742:                    }
0743:                    title = buffer.toString();
0744:                }
0745:                super .show(title, xAxis, yAxis);
0746:            }
0747:
0748:            /**
0749:             * {inheritDoc}
0750:             */
0751:            //@Override
0752:            public void show(final String title) {
0753:                show(title, gridGeometry.axisDimensionX,
0754:                        gridGeometry.axisDimensionY);
0755:            }
0756:
0757:            /**
0758:             * A view of a {@linkplain GridCoverage2D grid coverage} as a renderable image. Renderable images
0759:             * allow interoperability with <A HREF="http://java.sun.com/products/java-media/2D/">Java2D</A>
0760:             * for a two-dimensional slice of a grid coverage.
0761:             *
0762:             * @version $Id: GridCoverage2D.java 26655 2007-08-22 13:57:25Z desruisseaux $
0763:             * @author Martin Desruisseaux
0764:             *
0765:             * @see AbstractCoverage#getRenderableImage
0766:             *
0767:             * @todo Override {@link #createRendering} and use the affine transform operation.
0768:             *       Also uses the JAI's "Transpose" operation is x and y axis are interchanged.
0769:             */
0770:            protected class Renderable extends AbstractCoverage.Renderable {
0771:                /**
0772:                 * Constructs a renderable image.
0773:                 */
0774:                public Renderable() {
0775:                    super (gridGeometry.axisDimensionX,
0776:                            gridGeometry.axisDimensionY);
0777:                }
0778:
0779:                /**
0780:                 * Returns a rendered image with a default width and height in pixels.
0781:                 *
0782:                 * @return A rendered image containing the rendered data
0783:                 */
0784:                public RenderedImage createDefaultRendering() {
0785:                    if (xAxis == gridGeometry.axisDimensionX
0786:                            && yAxis == gridGeometry.axisDimensionY) {
0787:                        return getRenderedImage();
0788:                    }
0789:                    return super .createDefaultRendering();
0790:                }
0791:            }
0792:
0793:            /**
0794:             * Hints that the given area may be needed in the near future. Some implementations
0795:             * may spawn a thread or threads to compute the tiles while others may ignore the hint.
0796:             *
0797:             * @param area A rectangle indicating which geographic area to prefetch.
0798:             *             This area's coordinates must be expressed according the
0799:             *             grid coverage's coordinate reference system, as given by
0800:             *             {@link #getCoordinateReferenceSystem}.
0801:             */
0802:            public void prefetch(final Rectangle2D area) {
0803:                final Point[] tileIndices = image.getTileIndices(gridGeometry
0804:                        .inverseTransform(area));
0805:                if (tileIndices != null) {
0806:                    image.prefetchTiles(tileIndices);
0807:                }
0808:            }
0809:
0810:            /**
0811:             * If {@code true}, returns the geophysics companion of this grid coverage. In a
0812:             * <cite>geophysics grid coverage</cite>, all sample values are equals to geophysics
0813:             * ("real world") values without the need for any transformation. In such geophysics
0814:             * coverage, the {@linkplain SampleDimension#getSampleToGeophysics sample to geophysics}
0815:             * transform is the identity transform for all sample dimensions. "No data" values are
0816:             * expressed by {@linkplain Float#NaN NaN} numbers.
0817:             * <p>
0818:             * This method may be understood as applying the JAI's {@linkplain PiecewiseDescriptor
0819:             * piecewise} operation with breakpoints specified by the {@link Category} objects in
0820:             * each sample dimension. However, it is more general in that the transformation specified
0821:             * with each breakpoint doesn't need to be linear. On an implementation note, this method
0822:             * will really try to use the first of the following operations which is found applicable:
0823:             * <cite>identity</cite>, {@linkplain LookupDescriptor lookup}, {@linkplain RescaleDescriptor
0824:             * rescale}, {@linkplain PiecewiseDescriptor piecewise} and in last ressort a more general
0825:             * (but slower) <cite>sample transcoding</cite> algorithm.
0826:             * <p>
0827:             * {@code GridCoverage} objects live by pair: a <cite>geophysics</cite> one (used for
0828:             * computation) and a <cite>non-geophysics</cite> one (used for packing data, usually as
0829:             * integers). The {@code geo} argument specifies which object from the pair is wanted,
0830:             * regardless if this method is invoked on the geophysics or non-geophysics instance of the
0831:             * pair. In other words, the result of {@code geophysics(b1).geophysics(b2).geophysics(b3)}
0832:             * depends only on the value in the last call ({@code b3}).
0833:             *
0834:             * @param  geo {@code true} to get a grid coverage with sample values equals to geophysics
0835:             *         values, or {@code false} to get the packed version.
0836:             * @return The grid coverage. Never {@code null}, but may be {@code this}.
0837:             *
0838:             * @see GridSampleDimension#geophysics
0839:             * @see Category#geophysics
0840:             * @see LookupDescriptor
0841:             * @see RescaleDescriptor
0842:             * @see PiecewiseDescriptor
0843:             */
0844:            public GridCoverage2D geophysics(final boolean geo) {
0845:                if (geo == isGeophysics) {
0846:                    return this ;
0847:                }
0848:                if (inverse != null) {
0849:                    return inverse;
0850:                }
0851:                if (!CoverageUtilities.hasTransform(sampleDimensions)) {
0852:                    return inverse = this ;
0853:                }
0854:                synchronized (this ) {
0855:                    inverse = createGeophysics(geo);
0856:                    if (inverse.inverse == null) {
0857:                        inverse.inverse = this ;
0858:                    } else if (inverse.inverse != this ) {
0859:                        final Locale locale = getLocale();
0860:                        throw new RasterFormatException(Errors.getResources(
0861:                                locale).getString(
0862:                                ErrorKeys.COVERAGE_ALREADY_BOUND_$2,
0863:                                "geophysics",
0864:                                inverse.inverse.getName().toString(locale)));
0865:                    }
0866:                    return inverse;
0867:                }
0868:            }
0869:
0870:            /**
0871:             * Invoked by {@link #geophysics(boolean)} when the packed or geophysics companion of this
0872:             * grid coverage need to be created. Subclasses may override this method in order to modify
0873:             * the object to be created.
0874:             *
0875:             * @param  geo {@code true} to get a grid coverage with sample values equals to
0876:             *         geophysics values, or {@code false} to get the packed version.
0877:             * @return The newly created grid coverage.
0878:             *
0879:             * @todo IndexColorModel seems to badly choose its sample model. As of JDK 1.4-rc1, it
0880:             *       construct a ComponentSampleModel, which is drawn very slowly to the screen. A
0881:             *       much faster sample model is PixelInterleavedSampleModel,  which is the sample
0882:             *       model used by BufferedImage for TYPE_BYTE_INDEXED. We should check if this is
0883:             *       fixed in future J2SE release.
0884:             *
0885:             * @todo The "Piecewise" operation is disabled because javac 1.4.1_01 generate illegal
0886:             *       bytecode. This bug is fixed in javac 1.4.2-beta. However, we still have an
0887:             *       ArrayIndexOutOfBoundsException in JAI code...
0888:             *
0889:             * @todo A special case (exactly one linear relationship with one NaN value mapping
0890:             *       exactly to the index value 0) was optimized to the "Rescale" operation in
0891:             *       previous version. This case is very common, which make this optimization a
0892:             *       usefull one. Unfortunatly, it had to be disabled because there is nothing
0893:             *       in the "Rescale" preventing some real number (not NaN) to maps to 0 through
0894:             *       the normal linear relationship. Note that the optimization worked well in
0895:             *       previous version except for the above-cited problem. We can very easily re-
0896:             *       enable it later if we know the range of values really stored in the image
0897:             *       (as of JAI's "extrema" operation). If would suffice to add a check making
0898:             *       sure that the range of transformed values doesn't contains 0.
0899:             */
0900:            protected GridCoverage2D createGeophysics(final boolean geo) {
0901:                ensureValid();
0902:                /*
0903:                 * STEP 1 - Gets the source image and prepare the target sample dimensions.
0904:                 *          As a slight optimisation, we skip the "Null" operations since
0905:                 *          such image may be the result of some "Colormap" operation.
0906:                 */
0907:                PlanarImage image = this .image;
0908:                while (image instanceof  NullOpImage) {
0909:                    final NullOpImage op = (NullOpImage) image;
0910:                    if (op.getNumSources() != 1) {
0911:                        break;
0912:                    }
0913:                    image = op.getSourceImage(0);
0914:                }
0915:                final int numBands = image.getNumBands();
0916:                final int visibleBand = CoverageUtilities.getVisibleBand(image);
0917:                final GridSampleDimension[] targetBands = (GridSampleDimension[]) sampleDimensions
0918:                        .clone();
0919:                assert targetBands.length == numBands : targetBands.length;
0920:                for (int i = 0; i < targetBands.length; i++) {
0921:                    targetBands[i] = targetBands[i].geophysics(geo);
0922:                }
0923:                /*
0924:                 * STEP 2 - Computes the layout for the destination RenderedImage. We will use the same
0925:                 *          layout than the parent image, except for tile size if the parent image had
0926:                 *          only one big tile, and for the color model and sample model  (since we are
0927:                 *          reformating data in the process of this operation).
0928:                 */
0929:                ImageLayout layout = ImageUtilities.getImageLayout(image);
0930:                ColorModel colors = targetBands[visibleBand].getColorModel(
0931:                        visibleBand, numBands);
0932:                SampleModel model = colors.createCompatibleSampleModel(layout
0933:                        .getTileWidth(image), layout.getTileHeight(image));
0934:                if (colors instanceof  IndexColorModel
0935:                        && model.getClass().equals(ComponentSampleModel.class)) {
0936:                    // There is the 'IndexColorModel' hack (see method description).
0937:                    final int w = model.getWidth();
0938:                    final int h = model.getHeight();
0939:                    model = new PixelInterleavedSampleModel(colors
0940:                            .getTransferType(), w, h, 1, w, new int[1]);
0941:                }
0942:                layout = layout.setSampleModel(model).setColorModel(colors);
0943:                ParameterBlock param = new ParameterBlock().addSource(image);
0944:                RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT,
0945:                        layout);
0946:                hints.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE);
0947:                String operation = null; // Will be set in step 3 or 4.
0948:                /*
0949:                 * STEP 3 - Checks if the transcoding could be done with the JAI's "Lookup" operation.
0950:                 *          This is probably the fatest operation available for 'geophysics(true)'.
0951:                 */
0952:                try {
0953:                    final int sourceType = image.getSampleModel().getDataType();
0954:                    final int targetType = model.getDataType();
0955:                    final MathTransform1D[] transforms = new MathTransform1D[numBands];
0956:                    for (int i = 0; i < numBands; i++) {
0957:                        transforms[i] = sampleDimensions[i].geophysics(false)
0958:                                .getSampleToGeophysics();
0959:                        if (transforms[i] != null && !geo) {
0960:                            // We are going to convert geophysics values to packed one.
0961:                            transforms[i] = (MathTransform1D) transforms[i]
0962:                                    .inverse();
0963:                        }
0964:                    }
0965:                    LookupTableJAI table = LookupTableFactory.create(
0966:                            sourceType, targetType, transforms);
0967:                    if (table != null) {
0968:                        operation = "Lookup";
0969:                        param = param.add(table);
0970:                    }
0971:                } catch (TransformException exception) {
0972:                    // A value can't be transformed. Fallback on a more general operation.
0973:                    // REVISIT: the more general operations are likely to fail too...
0974:                }
0975:                /*
0976:                 * STEP 4 - Check if the transcoding could be done with a JAI's "Rescale" or "Piecewise"
0977:                 *          operations. The "Rescale" operation requires a completly linear relationship
0978:                 *          between the source and the destination sample values. The "Piecewise" operation
0979:                 *          is less strict: piecewise breakpoints are very similar to categories, but the
0980:                 *          transformation for all categories still have to be linear.
0981:                 */
0982:                if (operation == null)
0983:                    try {
0984:                        boolean canRescale = true; // 'true' if the "Rescale"   operation can be applied.
0985:                        boolean canPiecewise = true; // 'true' if the "Piecewise" operation can be applied.
0986:                        double[] scales = null; // The first  argument for "Rescale".
0987:                        double[] offsets = null; // The second argument for "Rescale".
0988:                        float[][][] breakpoints = null; // The only   argument for "Piecewise".
0989:                        testLinear: for (int i = 0; i < numBands; i++) {
0990:                            final GridSampleDimension sd = sampleDimensions[i];
0991:                            final List categories = sd.getCategories();
0992:                            final int numCategories = categories.size();
0993:                            float[] sourceBreakpoints = null;
0994:                            float[] targetBreakpoints = null;
0995:                            double expectedSource = Double.NaN;
0996:                            double expectedTarget = Double.NaN;
0997:                            int jbp = 0; // Break point index (vary with j)
0998:                            for (int j = 0; j < numCategories; j++) {
0999:                                final Category category = (Category) categories
1000:                                        .get(j);
1001:                                MathTransform1D transform = category
1002:                                        .geophysics(false)
1003:                                        .getSampleToGeophysics();
1004:                                if (transform == null) {
1005:                                    // A "qualitative" category was found. Those categories maps NaN values,
1006:                                    // which need the special processing by our "SampleTranscode" operation.
1007:                                    canPiecewise = false;
1008:                                    if (false) {
1009:                                        // As a special case, the "Rescale" operation  could continue to work
1010:                                        // if the NaN value maps to 0. Unfortunatly, this optimization had to
1011:                                        // be disabled for now for the reason explained in the @todo tag in
1012:                                        // method's comments.
1013:                                        if (category.geophysics(geo).getRange()
1014:                                                .getMinimum(true) == 0) {
1015:                                            assert Double.isNaN(category
1016:                                                    .getRange().getMinimum()) : category;
1017:                                            continue;
1018:                                        }
1019:                                    }
1020:                                    canRescale = false;
1021:                                    break testLinear;
1022:                                }
1023:                                if (!geo) {
1024:                                    // We are going to convert geophysics values to packed one.
1025:                                    transform = (MathTransform1D) transform
1026:                                            .inverse();
1027:                                }
1028:                                final double offset = transform.transform(0);
1029:                                final double scale = transform
1030:                                        .derivative(Double.NaN);
1031:                                if (Double.isNaN(scale) || Double.isNaN(offset)) {
1032:                                    // One category doesn't use a linear transformation. We can't deal with
1033:                                    // that with "Rescale" or "Piecewise". Fallback on our "SampleTranscode".
1034:                                    canRescale = false;
1035:                                    canPiecewise = false;
1036:                                    break testLinear;
1037:                                }
1038:                                // Allocates arrays the first time the loop is run up to this point.
1039:                                // Store scale and offset, and check if they still the same.
1040:                                if (j == 0) {
1041:                                    if (i == 0) {
1042:                                        scales = new double[numBands];
1043:                                        offsets = new double[numBands];
1044:                                        breakpoints = new float[numBands][][];
1045:                                    }
1046:                                    sourceBreakpoints = new float[numCategories * 2];
1047:                                    targetBreakpoints = new float[numCategories * 2];
1048:                                    breakpoints[i] = new float[][] {
1049:                                            sourceBreakpoints,
1050:                                            targetBreakpoints };
1051:                                    offsets[i] = offset;
1052:                                    scales[i] = scale;
1053:                                }
1054:                                if (offset != offsets[i] || scale != scales[i]) {
1055:                                    canRescale = false;
1056:                                }
1057:                                // Compute breakpoints.
1058:                                final NumberRange range = category.getRange();
1059:                                final double minimum = range.getMinimum(true);
1060:                                final double maximum = range.getMaximum(true);
1061:                                final float sourceMin = (float) minimum;
1062:                                final float sourceMax = (float) maximum;
1063:                                final float targetMin = (float) (minimum
1064:                                        * scale + offset);
1065:                                final float targetMax = (float) (maximum
1066:                                        * scale + offset);
1067:                                assert sourceMin <= sourceMax : range;
1068:                                if (Math.abs(minimum - expectedSource) <= EPS) {
1069:                                    if (Math.abs(targetMin - expectedTarget) <= EPS) {
1070:                                        // This breakpoint is identical to the previous one. Do not
1071:                                        // duplicate; overwrites the previous one since this one is
1072:                                        // likely to be more accurate.
1073:                                        jbp--;
1074:                                    } else {
1075:                                        // Found a discontinuity!!! The "piecewise" operation is not really
1076:                                        // designed for such case. The behavior between the last breakpoint
1077:                                        // and the current one may not be what the user expected.
1078:                                        assert sourceBreakpoints[jbp - 1] < sourceMin : expectedSource;
1079:                                        if (CONSERVATIVE_PIECEWISE) {
1080:                                            canPiecewise = false;
1081:                                        }
1082:                                    }
1083:                                } else if (j != 0) {
1084:                                    // Found a gap between the last category and the current one. The
1085:                                    // "piecewise" operation may not behave as the user expected  for
1086:                                    // sample values falling in this gap.
1087:                                    assert !(expectedSource > sourceMin) : expectedSource;
1088:                                    if (CONSERVATIVE_PIECEWISE) {
1089:                                        canPiecewise = false;
1090:                                    }
1091:                                }
1092:                                sourceBreakpoints[jbp] = sourceMin;
1093:                                sourceBreakpoints[jbp + 1] = sourceMax;
1094:                                targetBreakpoints[jbp] = targetMin;
1095:                                targetBreakpoints[jbp + 1] = targetMax;
1096:                                jbp += 2;
1097:                                expectedSource = range.getMaximum(false);
1098:                                expectedTarget = expectedSource * scale
1099:                                        + offset;
1100:                            }
1101:                            if (false) {
1102:                                // HACK: temporarily disabled because 'javac' 1.4.1_02 produces invalid
1103:                                //       bytecode. This bug is fixed in 'java' 1.4.2-beta. Furthermore,
1104:                                //       the "piecewise" operation throws an ArrayIndexOutOfBoundsException
1105:                                //       in JAI code for an unknow reason...
1106:                                breakpoints[i][0] = sourceBreakpoints = XArray
1107:                                        .resize(sourceBreakpoints, jbp);
1108:                                breakpoints[i][1] = targetBreakpoints = XArray
1109:                                        .resize(targetBreakpoints, jbp);
1110:                                assert XArray.isSorted(sourceBreakpoints);
1111:                            } else {
1112:                                canPiecewise = false;
1113:                            }
1114:                        }
1115:                        if (canRescale && scales != null) {
1116:                            operation = "Rescale";
1117:                            param = param.add(scales).add(offsets);
1118:                        } else if (canPiecewise && breakpoints != null) {
1119:                            operation = "Piecewise";
1120:                            param = param.add(breakpoints);
1121:                        }
1122:                    } catch (TransformException exception) {
1123:                        // At least one category doesn't use a linear relation.
1124:                        // Ignore the exception and fallback on the next case.
1125:                    }
1126:                /*
1127:                 * STEP 5 - Transcode the image sample values. The "SampleTranscode" operation is
1128:                 *          registered in the org.geotools.coverage package in the GridSampleDimension
1129:                 *          class.
1130:                 */
1131:                if (operation == null) {
1132:                    param = param.add(sampleDimensions);
1133:                    operation = "org.geotools.SampleTranscode";
1134:                }
1135:                if (LOGGER.isLoggable(AbstractProcessor.OPERATION)) {
1136:                    // Log a message using the same level than grid coverage processor.
1137:                    final int index = operation.lastIndexOf('.');
1138:                    final String shortName = (index >= 0) ? operation
1139:                            .substring(index + 1) : operation;
1140:                    final Locale locale = getLocale();
1141:                    final LogRecord record = Logging
1142:                            .getResources(locale)
1143:                            .getLogRecord(
1144:                                    AbstractProcessor.OPERATION,
1145:                                    LoggingKeys.SAMPLE_TRANSCODE_$3,
1146:                                    new Object[] { getName().toString(locale),
1147:                                            new Integer(geo ? 1 : 0), shortName });
1148:                    record.setSourceClassName(GridCoverage2D.class.getName());
1149:                    record.setSourceMethodName("geophysics");
1150:                    LOGGER.log(record);
1151:                }
1152:                final PlanarImage view = JAI.create(operation, param, hints);
1153:                final GridCoverage[] sources = new GridCoverage[] { this  };
1154:                return new GridCoverage2D(getName(), view, gridGeometry,
1155:                        targetBands, sources, null);
1156:            }
1157:
1158:            /**
1159:             * Constructs the {@link PlanarImage} from the {@linkplain SerializableRenderedImage}
1160:             * after deserialization.
1161:             */
1162:            private void readObject(final ObjectInputStream in)
1163:                    throws IOException, ClassNotFoundException {
1164:                in.defaultReadObject();
1165:                try {
1166:                    /*
1167:                     * Set the 'image' field using reflection, because this field is final.
1168:                     * This is a legal usage for deserialization according Field.set(...)
1169:                     * documentation in J2SE 1.5.
1170:                     */
1171:                    final Field field = GridCoverage2D.class
1172:                            .getDeclaredField("image");
1173:                    field.setAccessible(true);
1174:                    field.set(this , PlanarImage
1175:                            .wrapRenderedImage(serializedImage));
1176:                } catch (NoSuchFieldException cause) {
1177:                    InvalidClassException e = new InvalidClassException(cause
1178:                            .getLocalizedMessage());
1179:                    e.initCause(cause);
1180:                    throw e;
1181:                } catch (IllegalAccessException cause) {
1182:                    InvalidObjectException e = new InvalidObjectException(cause
1183:                            .getLocalizedMessage());
1184:                    e.initCause(cause);
1185:                    throw e;
1186:                }
1187:            }
1188:
1189:            /**
1190:             * Serializes this grid coverage. Before serialization, a {@linkplain SerializableRenderedImage
1191:             * serializable rendered image} is created if it was not already done.
1192:             */
1193:            private void writeObject(final ObjectOutputStream out)
1194:                    throws IOException {
1195:                ensureValid();
1196:                if (serializedImage == null) {
1197:                    RenderedImage source = image;
1198:                    while (source instanceof  RenderedImageAdapter) {
1199:                        source = ((RenderedImageAdapter) source)
1200:                                .getWrappedImage();
1201:                    }
1202:                    if (source instanceof  SerializableRenderedImage) {
1203:                        serializedImage = (SerializableRenderedImage) source;
1204:                    } else {
1205:                        if (tileEncoding == null) {
1206:                            tileEncoding = "gzip";
1207:                        }
1208:                        serializedImage = new SerializableRenderedImage(source,
1209:                                false, null, tileEncoding, null, null);
1210:                        final LogRecord record = Logging.format(Level.FINE,
1211:                                LoggingKeys.CREATED_SERIALIZABLE_IMAGE_$2,
1212:                                getName(), tileEncoding);
1213:                        record.setSourceClassName(GridCoverage2D.class
1214:                                .getName());
1215:                        record.setSourceMethodName("writeObject");
1216:                        LOGGER.log(record);
1217:                    }
1218:                }
1219:                out.defaultWriteObject();
1220:            }
1221:
1222:            /**
1223:             * Ensures that this coverage has not be {@linkplain #dispose disposed}.
1224:             */
1225:            private void ensureValid() throws IllegalStateException {
1226:                if (disposed) {
1227:                    // TODO: localize.
1228:                    throw new IllegalStateException(
1229:                            "This coverage has been disposed.");
1230:                }
1231:            }
1232:
1233:            /**
1234:             * Provides a hint that a coverage will no longer be accessed from a reference in user space.
1235:             * This method {@linkplain PlanarImage#dispose disposes} the {@linkplain #image} only if at
1236:             * least one of the following conditions is true (otherwise this method do nothing):
1237:             * <p>
1238:             * <ul>
1239:             *   <li>{@code force} is {@code true}, <strong>or</strong></li>
1240:             *   <li>The underlying {@linkplain #image} has no {@linkplain PlanarImage#getSinks sinks}
1241:             *       other than the views (geophysics, display, <cite>etc.</cite>).</li>
1242:             * </ul>
1243:             * <p>
1244:             * This safety check helps to prevent the disposal of an {@linkplain #image} that still
1245:             * used in a JAI operation chain. It doesn't prevent the disposal in every cases however.
1246:             * When unsure about whatever a coverage is still in use or not, it is safer to not invoke
1247:             * this method and rely on the garbage collector instead.
1248:             * 
1249:             * @see PlanarImage#dispose
1250:             *
1251:             * @since 2.4
1252:             */
1253:            //@Override
1254:            public synchronized boolean dispose(final boolean force) {
1255:                if (disposed) {
1256:                    // Recursive invocation of this method.
1257:                    return true;
1258:                }
1259:                /*
1260:                 * Checks if this coverage can be disposed. First we get the set of every sinks, which
1261:                 * may or may not be RenderedImages. Then we remove every sinks that are geophysics or
1262:                 * other views of this coverage. If there is no remaining sinks, we can process.
1263:                 */
1264:                if (!force) {
1265:                    Collection/*<?>*/sinks = image.getSinks();
1266:                    if (sinks != null) {
1267:                        if (inverse != null) {
1268:                            sinks = new HashSet(sinks);
1269:                            sinks.remove(inverse.image);
1270:                            deepRemove(sinks, inverse.image);
1271:                        }
1272:                        if (!sinks.isEmpty()) {
1273:                            return false;
1274:                        }
1275:                    }
1276:                }
1277:                /*
1278:                 * No remaining sinks; we can process. First, applies the same procedure on the other
1279:                 * views (geophysics, display, etc.). If we were able to dispose the views, then we
1280:                 * can really dispose this coverage.
1281:                 */
1282:                disposed = true; // Must be set before to invoke inverse.dispose().
1283:                if (inverse != null && !inverse.dispose(force)) {
1284:                    disposed = false; // Reset only on success, otherwise the coverage may be invalid.
1285:                    return false;
1286:                }
1287:                image.dispose();
1288:                return super .dispose(force);
1289:            }
1290:
1291:            /**
1292:             * Removes from the specified collection the specified image and its dependencies.
1293:             * This method invokes itself recursively in order to scan down the sources tree.
1294:             */
1295:            private static void deepRemove(final Collection/*<?>*/sinks,
1296:                    final RenderedImage image) {
1297:                /*
1298:                 * The following must be unchecked, because PlanarImage.getSources()
1299:                 * violates RenderedImage.getSources() contract on parameterized type.
1300:                 */
1301:                //@SuppressWarning("unchecked")
1302:                final List sources = image.getSources();
1303:                if (sources != null) {
1304:                    for (final Iterator it = sources.iterator(); it.hasNext();) {
1305:                        final Object dependency = it.next();
1306:                        sinks.remove(dependency);
1307:                        if (dependency instanceof  RenderedImage) {
1308:                            deepRemove(sinks, (RenderedImage) dependency);
1309:                        }
1310:                    }
1311:                }
1312:            }
1313:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.