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


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2007, Geotools Project Managment Committee (PMC)
005:         *    (C) 2007, Geomatys
006:         *
007:         *    This library is free software; you can redistribute it and/or
008:         *    modify it under the terms of the GNU Lesser General Public
009:         *    License as published by the Free Software Foundation; either
010:         *    version 2.1 of the License, or (at your option) any later version.
011:         *
012:         *    This library is distributed in the hope that it will be useful,
013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015:         *    Lesser General Public License for more details.
016:         */
017:        package org.geotools.image.io;
018:
019:        import java.util.Set;
020:        import java.util.Arrays;
021:        import java.util.Iterator;
022:        import java.util.Collections;
023:        import java.util.logging.Level;
024:        import java.util.logging.Logger;
025:        import java.util.logging.LogRecord;
026:
027:        import java.awt.Rectangle;
028:        import java.awt.image.ColorModel; // For javadoc
029:        import java.awt.image.DataBuffer;
030:        import java.awt.image.SampleModel; // For javadoc
031:        import java.awt.image.BufferedImage;
032:        import java.awt.image.IndexColorModel; // For javadoc
033:
034:        import java.io.IOException;
035:        import javax.imageio.ImageReader;
036:        import javax.imageio.ImageReadParam;
037:        import javax.imageio.ImageTypeSpecifier;
038:        import javax.imageio.spi.ImageReaderSpi;
039:        import javax.imageio.metadata.IIOMetadata;
040:        import javax.imageio.event.IIOReadWarningListener; // For javadoc
041:
042:        import org.geotools.util.NumberRange;
043:        import org.geotools.util.logging.Logging;
044:        import org.geotools.resources.XArray;
045:        import org.geotools.resources.i18n.Locales;
046:        import org.geotools.resources.i18n.Errors;
047:        import org.geotools.resources.i18n.ErrorKeys;
048:        import org.geotools.resources.IndexedResourceBundle;
049:        import org.geotools.image.io.metadata.GeographicMetadata;
050:        import org.geotools.image.io.metadata.Band;
051:
052:        /**
053:         * Base class for readers of geographic images. The default implementation assumes that only one
054:         * {@linkplain ImageTypeSpecifier image type} is supported (as opposed to the arbitrary number
055:         * allowed by the standard {@link ImageReader}). It also provides a default image type built
056:         * automatically from a color palette and a range of valid values.
057:         * <p>
058:         * More specifically, this class provides the following conveniences to implementors:
059:         *
060:         * <ul>
061:         *   <li><p>Provides default {@link #getNumImages} and {@link #getNumBands} implementations,
062:         *       which return 1. This default behavior matches simple image formats like flat binary
063:         *       files or ASCII files. Those methods need to be overrided for more complex image
064:         *       formats.</p></li>
065:         *
066:         *   <li><p>Provides {@link #checkImageIndex} and {@link #checkBandIndex} convenience methods.
067:         *       Those methods are invoked by most implementation of public methods. They perform their
068:         *       checks based on the informations provided by the above-cited {@link #getNumImages} and
069:         *       {@link #getNumBands} methods.</p></li>
070:         *
071:         *   <li><p>Provides default implementations of {@link #getImageTypes} and {@link #getRawImageType},
072:         *       which assume that only one {@linkplain ImageTypeSpecifier image type} is supported. The
073:         *       default image type is created from the informations provided by {@link #getRawDataType}
074:         *       and {@link #getImageMetadata}.</p></li>
075:         *
076:         *   <li><p>Provides {@link #getStreamMetadata} and {@link #getImageMetadata} default
077:         *       implementations, which return {@code null} as authorized by the specification.
078:         *       Note that subclasses should consider returning {@link GeographicMetadata} instances.</p></li>
079:         * </ul>
080:         *
081:         * Images may be flat binary or ASCII files with no meta-data and no color information.
082:         * Their pixel values may be floating point values instead of integers. The default
083:         * implementation assumes floating point values and uses a grayscale color space scaled
084:         * to fit the range of values. Displaying such an image may be very slow. Consequently,
085:         * users who want to display image are encouraged to change data type and color space with
086:         * <a href="http://java.sun.com/products/java-media/jai/">Java Advanced Imaging</a>
087:         * operators after reading.
088:         *
089:         * @since 2.4
090:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/GeographicImageReader.java $
091:         * @version $Id: GeographicImageReader.java 27862 2007-11-12 19:51:19Z desruisseaux $
092:         * @author Martin Desruisseaux
093:         */
094:        public abstract class GeographicImageReader extends ImageReader {
095:            /**
096:             * The logger to use for events related to this image reader.
097:             */
098:            static final Logger LOGGER = Logging
099:                    .getLogger("org.geotools.image.io");
100:
101:            /**
102:             * Metadata for each images, or {@code null} if not yet created.
103:             */
104:            private transient GeographicMetadata[] metadata;
105:
106:            /**
107:             * Constructs a new image reader.
108:             *
109:             * @param provider The {@link ImageReaderSpi} that is invoking this constructor,
110:             *        or {@code null} if none.
111:             */
112:            protected GeographicImageReader(final ImageReaderSpi provider) {
113:                super (provider);
114:                availableLocales = Locales.getAvailableLocales();
115:            }
116:
117:            /**
118:             * Sets the input source to use.
119:             *
120:             * @param input           The input object to use for future decoding.
121:             * @param seekForwardOnly If {@code true}, images and metadata may only be read
122:             *                        in ascending order from this input source.
123:             * @param ignoreMetadata  If {@code true}, metadata may be ignored during reads.
124:             */
125:            //@Override
126:            public void setInput(final Object input,
127:                    final boolean seekForwardOnly, final boolean ignoreMetadata) {
128:                metadata = null; // Clears the cache
129:                super .setInput(input, seekForwardOnly, ignoreMetadata);
130:            }
131:
132:            /**
133:             * Returns the resources for formatting error messages.
134:             */
135:            final IndexedResourceBundle getErrorResources() {
136:                return Errors.getResources(getLocale());
137:            }
138:
139:            /**
140:             * Ensures that the specified image index is inside the expected range.
141:             * The expected range is {@link #minIndex minIndex} inclusive (initially 0)
142:             * to <code>{@link #getNumImages getNumImages}(false)</code> exclusive.
143:             *
144:             * @param  imageIndex Index to check for validity.
145:             * @throws IndexOutOfBoundsException if the specified index is outside the expected range.
146:             * @throws IOException If the operation failed because of an I/O error.
147:             */
148:            protected void checkImageIndex(final int imageIndex)
149:                    throws IOException, IndexOutOfBoundsException {
150:                final int numImages = getNumImages(false);
151:                if (imageIndex < minIndex
152:                        || (imageIndex >= numImages && numImages >= 0)) {
153:                    throw new IndexOutOfBoundsException(indexOutOfBounds(
154:                            imageIndex, minIndex, numImages));
155:                }
156:            }
157:
158:            /**
159:             * Ensures that the specified band index is inside the expected range. The expected
160:             * range is 0 inclusive to <code>{@link #getNumBands getNumBands}(imageIndex)</code>
161:             * exclusive.
162:             *
163:             * @param  imageIndex The image index.
164:             * @param  bandIndex Index to check for validity.
165:             * @throws IndexOutOfBoundsException if the specified index is outside the expected range.
166:             * @throws IOException If the operation failed because of an I/O error.
167:             */
168:            protected void checkBandIndex(final int imageIndex,
169:                    final int bandIndex) throws IOException,
170:                    IndexOutOfBoundsException {
171:                // Call 'getNumBands' first in order to call 'checkImageIndex'.
172:                final int numBands = getNumBands(imageIndex);
173:                if (bandIndex >= numBands || bandIndex < 0) {
174:                    throw new IndexOutOfBoundsException(indexOutOfBounds(
175:                            bandIndex, 0, numBands));
176:                }
177:            }
178:
179:            /**
180:             * Formats an error message for an index out of bounds.
181:             *
182:             * @param index The index out of bounds.
183:             * @param lower The lower legal value, inclusive.
184:             * @param upper The upper legal value, exclusive.
185:             */
186:            private String indexOutOfBounds(final int index, final int lower,
187:                    final int upper) {
188:                return getErrorResources().getString(
189:                        ErrorKeys.VALUE_OUT_OF_BOUNDS_$3, new Integer(index),
190:                        new Integer(lower), new Integer(upper - 1));
191:            }
192:
193:            /**
194:             * Returns the number of images available from the current input source.
195:             * The default implementation returns 1.
196:             *
197:             * @param  allowSearch If true, the number of images will be returned
198:             *         even if a search is required.
199:             * @return The number of images, or -1 if {@code allowSearch}
200:             *         is false and a search would be required.
201:             *
202:             * @throws IllegalStateException if the input source has not been set.
203:             * @throws IOException if an error occurs reading the information from the input source.
204:             */
205:            public int getNumImages(final boolean allowSearch)
206:                    throws IllegalStateException, IOException {
207:                if (input != null) {
208:                    return 1;
209:                }
210:                throw new IllegalStateException(getErrorResources().getString(
211:                        ErrorKeys.NO_IMAGE_INPUT));
212:            }
213:
214:            /**
215:             * Returns the number of bands available for the specified image.
216:             * The default implementation returns 1.
217:             *
218:             * @param  imageIndex  The image index.
219:             * @throws IOException if an error occurs reading the information from the input source.
220:             */
221:            public int getNumBands(final int imageIndex) throws IOException {
222:                checkImageIndex(imageIndex);
223:                return 1;
224:            }
225:
226:            /**
227:             * Returns metadata associated with the input source as a whole. Since many raw images
228:             * can't store metadata, the default implementation returns {@code null}.
229:             *
230:             * @throws IOException if an error occurs during reading.
231:             */
232:            public IIOMetadata getStreamMetadata() throws IOException {
233:                return null;
234:            }
235:
236:            /**
237:             * Returns metadata associated with the given image. Since many raw images
238:             * can't store metadata, the default implementation returns {@code null}.
239:             *
240:             * @param  imageIndex The image index.
241:             * @return The metadata, or {@code null} if none.
242:             * @throws IOException if an error occurs during reading.
243:             */
244:            public IIOMetadata getImageMetadata(int imageIndex)
245:                    throws IOException {
246:                checkImageIndex(imageIndex);
247:                return null;
248:            }
249:
250:            /**
251:             * Returns a helper parser for metadata associated with the given image. This implementation
252:             * invokes  <code>{@linkplain #getImageMetadata getImageMetadata}(imageIndex)</code>,  wraps
253:             * the result in a {@link GeographicMetadata} object if non-null and caches the result.
254:             * <p>
255:             * Note that this method forces {@link #ignoreMetadata} to {@code false} for the time of
256:             * <code>{@linkplain #getImageMetadata getImageMetadata}(imageIndex)</code> execution,
257:             * because some image reader implementations need geographic metadata in order to infer
258:             * a valid {@linkplain ColorModel color model}.
259:             *
260:             * @param  imageIndex The image index.
261:             * @return The geographic metadata, or {@code null} if none.
262:             * @throws IOException if an error occurs during reading.
263:             */
264:            public GeographicMetadata getGeographicMetadata(final int imageIndex)
265:                    throws IOException {
266:                // Checks if a cached instance is available.
267:                if (metadata != null && imageIndex >= 0
268:                        && imageIndex < metadata.length) {
269:                    final GeographicMetadata parser = metadata[imageIndex];
270:                    if (parser != null) {
271:                        return parser;
272:                    }
273:                }
274:                // Checks if metadata are availables. If the user set 'ignoreMetadata' to 'true',
275:                // we override his setting since we really need metadata for creating a ColorModel.
276:                final IIOMetadata candidate;
277:                final boolean oldIgnore = ignoreMetadata;
278:                try {
279:                    ignoreMetadata = false;
280:                    candidate = getImageMetadata(imageIndex);
281:                } finally {
282:                    ignoreMetadata = oldIgnore;
283:                }
284:                if (candidate == null) {
285:                    return null;
286:                }
287:                // Wraps the IIOMetadata into a GeographicMetadata object,
288:                // if it was not already of the appropriate type.
289:                final GeographicMetadata parser;
290:                if (candidate instanceof  GeographicMetadata) {
291:                    parser = (GeographicMetadata) candidate;
292:                } else {
293:                    parser = new GeographicMetadata(this );
294:                    parser.mergeTree(candidate);
295:                }
296:                if (metadata == null) {
297:                    metadata = new GeographicMetadata[Math.max(imageIndex + 1,
298:                            4)];
299:                }
300:                if (imageIndex >= metadata.length) {
301:                    metadata = (GeographicMetadata[]) XArray.resize(metadata,
302:                            Math.max(imageIndex + 1, metadata.length * 2));
303:                }
304:                metadata[imageIndex] = parser;
305:                return parser;
306:            }
307:
308:            /**
309:             * Returns a collection of {@link ImageTypeSpecifier} containing possible image types to which
310:             * the given image may be decoded. The default implementation returns a singleton containing
311:             * <code>{@link #getRawImageType(int) getRawImageType}(imageIndex)</code>.
312:             *
313:             * @param  imageIndex The index of the image to be retrieved.
314:             * @return A set of suggested image types for decoding the current given image.
315:             * @throws IOException If an error occurs reading the format information from the input source.
316:             */
317:            public Iterator getImageTypes(final int imageIndex)
318:                    throws IOException {
319:                return Collections.singleton(getRawImageType(imageIndex))
320:                        .iterator();
321:            }
322:
323:            /**
324:             * Returns an image type specifier indicating the {@link SampleModel} and {@link ColorModel}
325:             * which most closely represents the "raw" internal format of the image. The default
326:             * implementation delegates to the following:
327:             *
328:             * <blockquote><code>{@linkplain #getRawImageType(int,ImageReadParam,SampleConverter[])
329:             * getRawImageType}(imageIndex, {@linkplain #getDefaultReadParam()}, null);</code></blockquote>
330:             *
331:             * If this method needs to be overriden, consider overriding the later instead.
332:             *
333:             * @param  imageIndex The index of the image to be queried.
334:             * @return The image type (never {@code null}).
335:             * @throws IOException If an error occurs reading the format information from the input source.
336:             */
337:            //@Override
338:            public ImageTypeSpecifier getRawImageType(final int imageIndex)
339:                    throws IOException {
340:                return getRawImageType(imageIndex, getDefaultReadParam(), null);
341:            }
342:
343:            /**
344:             * Returns an image type specifier indicating the {@link SampleModel} and {@link ColorModel}
345:             * which most closely represents the "raw" internal format of the image. The default
346:             * implementation applies the following rules:
347:             *
348:             * <ol>
349:             *   <li><p>The {@linkplain Band#getValidRange range of expected values} and the
350:             *       {@linkplain Band#getNoDataValues no-data values} are extracted from the
351:             *       {@linkplain #getGeographicMetadata geographic metadata}, if any.</p></li>
352:             *
353:             *   <li><p>If the given {@code parameters} argument is an instance of {@link GeographicImageReadParam},
354:             *       then the user-supplied {@linkplain GeographicImageReadParam#getPaletteName palette name}
355:             *       is fetched. Otherwise or if no palette name was explicitly set, then this method default
356:             *       to {@value org.geotools.image.io.GeographicImageReadParam#DEFAULT_PALETTE_NAME}. The
357:             *       palette name will be used in order to {@linkplain PaletteFactory#getColors read a
358:             *       predefined set of colors} (as RGB values) to be given to the
359:             *       {@linkplain IndexColorModel index color model}.</p></li>
360:             *
361:             *   <li><p>If the {@linkplain #getRawDataType raw data type} is {@link DataBuffer#TYPE_FLOAT
362:             *       TYPE_FLOAT} or {@link DataBuffer#TYPE_DOUBLE TYPE_DOUBLE}, then this method builds
363:             *       a {@linkplain PaletteFactory#getContinuousPalette continuous palette} suitable for
364:             *       the range fetched at step 1. The data are assumed <cite>geophysics</cite> values
365:             *       rather than some packed values. Consequently, the {@linkplain SampleConverter sample
366:             *       converters} will replace no-data values by {@linkplain Float#NaN NaN} with no other
367:             *       changes.</p></li>
368:             *
369:             *   <li><p>Otherwise, if the {@linkplain #getRawDataType raw data type} is a unsigned integer type
370:             *       like {@link DataBuffer#TYPE_BYTE TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT TYPE_USHORT},
371:             *       then this method builds an {@linkplain PaletteFactory#getPalette indexed palette} (i.e. a
372:             *       palette backed by an {@linkplain IndexColorModel index color model}) with just the minimal
373:             *       {@linkplain IndexColorModel#getMapSize size} needed for containing fully the range and the
374:             *       no-data values fetched at step 1. The data are assumed <cite>packed</cite> values rather
375:             *       than geophysics values. Consequently, the {@linkplain SampleConverter sample converters}
376:             *       will be the {@linkplain SampleConverter#IDENTITY identity converter} except in the
377:             *       following cases:
378:             *       <ul>
379:             *         <li>The {@linkplain Band#getValidRange range of valid values} is outside the range
380:             *             allowed by the {@linkplain #getRawDataType raw data type} (e.g. the range of
381:             *             valid values contains negative integers). In this case, the sample converter
382:             *             will shift the values to a strictly positive range and replace no-data values
383:             *             by 0.</li>
384:             *         <li>At least one {@linkplain Band#getNoDataValues no-data value} is outside the range
385:             *             of values allowed by the {@linkplain #getRawDataType raw data type}. In this case,
386:             *             this method will try to only replace the no-data values by 0, without shifting
387:             *             the valid values if this shift can be avoided.</li>
388:             *         <li>At least one {@linkplain Band#getNoDataValues no-data value} is far away from the
389:             *             {@linkplain Band#getValidRange range of valid values} (for example 9999 while
390:             *             the range of valid values is [0..255]). The meaning of "far away" is determined
391:             *             by the {@link #collapseNoDataValues collapseNoDataValues} method.</li>
392:             *       </ul>
393:             *       </p></li>
394:             *
395:             *   <li><p>Otherwise, if the {@linkplain #getRawDataType raw data type} is a signed integer
396:             *       type like {@link DataBuffer#TYPE_SHORT TYPE_SHORT}, then this method builds an
397:             *       {@linkplain PaletteFactory#getPalette indexed palette} with the maximal {@linkplain
398:             *       IndexColorModel#getMapSize size} supported by the raw data type (note that this is
399:             *       memory expensive - typically 256 kilobytes). Negative values will be stored in their
400:             *       two's complement binary form in order to fit in the range of positive integers
401:             *       supported by the {@linkplain IndexColorModel index color model}.</p></li>
402:             * </ol>
403:             *
404:             * <h3>Overriding this method</h3>
405:             * Subclasses may override this method when a constant color {@linkplain Palette palette} is
406:             * wanted for all images in a series, for example for all <cite>Sea Surface Temperature</cite>
407:             * (SST) from the same provider. A constant color palette facilitates the visual comparaison
408:             * of different images at different time. The example below creates hard-coded objects:
409:             *
410:             * <blockquote><code>
411:             * int minimum    = -2000; // </code>minimal expected value<code><br>
412:             * int maximum    = +2300; // </code>maximal expected value<code><br>
413:             * int fillValue  = -9999; // </code>Value for missing data<code><br>
414:             * String palette = "SST-Nasa";// </code>Named set of RGB colors<code><br>
415:             * converters[0] = {@linkplain SampleConverter#createOffset(double,double)
416:             * SampleConverter.createOffset}(1 - minimum, fillValue);<br>
417:             * return {@linkplain PaletteFactory#getDefault()}.{@linkplain PaletteFactory#getPalettePadValueFirst
418:             * getPalettePadValueFirst}(paletteName, maximum - minimum).{@linkplain Palette#getImageTypeSpecifier
419:             * getImageTypeSpecifier}();
420:             * </code></blockquote>
421:             *
422:             * @param imageIndex
423:             *              The index of the image to be queried.
424:             * @param parameters
425:             *              The user-supplied parameters, or {@code null}. Note: we recommand to supply
426:             *              {@link #getDefaultReadParam} instead of {@code null} since subclasses may
427:             *              override the later with default values suitable to a particular format.
428:             * @param  converters
429:             *              If non-null, an array where to store the converters created by this method.
430:             *              Those converters should be used by <code>{@linkplain #read(int,ImageReadParam)
431:             *              read}(imageIndex, parameters)</code> implementations for converting the values
432:             *              read in the datafile to values acceptable for the underling {@linkplain
433:             *              ColorModel color model}.
434:             * @return
435:             *              The image type (never {@code null}).
436:             * @throws IOException
437:             *              If an error occurs while reading the format information from the input source.
438:             *
439:             * @see #getRawDataType
440:             * @see #collapseNoDataValues
441:             * @see #getDestination(int, ImageReadParam, int, int, SampleConverter[])
442:             */
443:            protected ImageTypeSpecifier getRawImageType(final int imageIndex,
444:                    final ImageReadParam parameters,
445:                    final SampleConverter[] converters) throws IOException {
446:                /*
447:                 * Gets the minimal and maximal values allowed for the target image type.
448:                 * Note that this is meanless for floating point types, so the values in
449:                 * that case are arbitrary.
450:                 *
451:                 * The only integer types that are signed are SHORT (not to be confused with
452:                 * USHORT) and INT. Other types like BYTE and USHORT are treated as unsigned.
453:                 */
454:                final boolean isFloat;
455:                final long floor, ceil;
456:                final int dataType = getRawDataType(imageIndex);
457:                switch (dataType) {
458:                case DataBuffer.TYPE_UNDEFINED: // Actually we don't really know what to do for this case...
459:                case DataBuffer.TYPE_DOUBLE: // Fall through since we can treat this case as float.
460:                case DataBuffer.TYPE_FLOAT: {
461:                    isFloat = true;
462:                    floor = Long.MIN_VALUE;
463:                    ceil = Long.MAX_VALUE;
464:                    break;
465:                }
466:                case DataBuffer.TYPE_INT: {
467:                    isFloat = false;
468:                    floor = Integer.MIN_VALUE;
469:                    ceil = Integer.MAX_VALUE;
470:                    break;
471:                }
472:                case DataBuffer.TYPE_SHORT: {
473:                    isFloat = false;
474:                    floor = Short.MIN_VALUE;
475:                    ceil = Short.MAX_VALUE;
476:                    break;
477:                }
478:                default: {
479:                    isFloat = false;
480:                    floor = 0;
481:                    ceil = (1L << DataBuffer.getDataTypeSize(dataType)) - 1;
482:                    break;
483:                }
484:                }
485:                /*
486:                 * Extracts all informations we will need from the user-supplied parameters, if any.
487:                 */
488:                final String paletteName;
489:                final int[] sourceBands;
490:                final int[] targetBands;
491:                final int visibleBand;
492:                if (parameters != null) {
493:                    sourceBands = parameters.getSourceBands();
494:                    targetBands = parameters.getDestinationBands();
495:                } else {
496:                    sourceBands = null;
497:                    targetBands = null;
498:                }
499:                if (parameters instanceof  GeographicImageReadParam) {
500:                    final GeographicImageReadParam geoparam = (GeographicImageReadParam) parameters;
501:                    paletteName = geoparam.getNonNullPaletteName();
502:                    visibleBand = geoparam.getVisibleBand();
503:                } else {
504:                    paletteName = GeographicImageReadParam.DEFAULT_PALETTE_NAME;
505:                    visibleBand = 0;
506:                }
507:                final int numBands;
508:                if (sourceBands != null) {
509:                    numBands = sourceBands.length;
510:                } else if (targetBands != null) {
511:                    numBands = targetBands.length;
512:                } else {
513:                    numBands = getNumBands(imageIndex);
514:                }
515:                /*
516:                 * Computes a range of values for all bands, as the union in order to make sure that
517:                 * we can stores every sample values. Also creates SampleConverters in the process.
518:                 * The later is an opportunist action since we gather most of the needed information
519:                 * during the loop.
520:                 */
521:                NumberRange allRanges = null;
522:                NumberRange visibleRange = null;
523:                SampleConverter visibleConverter = SampleConverter.IDENTITY;
524:                double maximumFillValue = 0; // Only in the visible band, and must be positive.
525:                final GeographicMetadata metadata = getGeographicMetadata(imageIndex);
526:                if (metadata != null) {
527:                    final int maxBand = metadata.getNumBands();
528:                    for (int i = 0; i < numBands; i++) {
529:                        int bandIndex = (sourceBands != null) ? sourceBands[i]
530:                                : i;
531:                        if (bandIndex < 0 || bandIndex >= maxBand) {
532:                            warningOccurred("getRawImageType",
533:                                    indexOutOfBounds(bandIndex, 0, maxBand));
534:                            if (sourceBands != null) {
535:                                continue; // Next bands may be okay.
536:                            } else {
537:                                break; // We are sure that next bands will not be better.
538:                            }
539:                        }
540:                        final Band band = metadata.getBand(bandIndex);
541:                        final double[] nodataValues = band.getNoDataValues();
542:                        final NumberRange range = band.getValidRange();
543:                        double minimum, maximum;
544:                        if (range != null) {
545:                            minimum = range.getMinimum();
546:                            maximum = range.getMaximum();
547:                            if (!isFloat) {
548:                                // If the metadata do not contain any information about the range,
549:                                // treat as if we use the maximal range allowed by the data type.
550:                                if (minimum == Double.NEGATIVE_INFINITY)
551:                                    minimum = floor;
552:                                if (maximum == Double.POSITIVE_INFINITY)
553:                                    maximum = ceil;
554:                            }
555:                            final double extent = maximum - minimum;
556:                            if (extent >= 0
557:                                    && (isFloat || extent <= (ceil - floor))) {
558:                                allRanges = (allRanges != null) ? (NumberRange) allRanges
559:                                        .union(range)
560:                                        : range;
561:                            } else {
562:                                // Use range.getMin/MaxValue() because they may be integers rather than doubles.
563:                                warningOccurred("getRawImageType", Errors
564:                                        .format(ErrorKeys.BAD_RANGE_$2, range
565:                                                .getMinValue(), range
566:                                                .getMaxValue()));
567:                                continue;
568:                            }
569:                        } else {
570:                            minimum = Double.NaN;
571:                            maximum = Double.NaN;
572:                        }
573:                        // Converts the band index from 'source' space to 'destination' space.
574:                        if (targetBands != null
575:                                && bandIndex < targetBands.length) {
576:                            bandIndex = targetBands[bandIndex];
577:                        }
578:                        /*
579:                         * For floating point types, replaces no-data values by NaN because the floating
580:                         * point numbers are typically used for geophysics data, so the raster is likely
581:                         * to be a "geophysics" view for GridCoverage2D. All other values are stored "as
582:                         * is" without any offset.
583:                         *
584:                         * For integer types, if the range of values from the source data file fits into
585:                         * the range of values allowed by the destination raster, we will use an identity
586:                         * converter. If the only required conversion is a shift from negative to positive
587:                         * values, creates an offset converter with no-data values collapsed to 0.
588:                         */
589:                        final SampleConverter converter;
590:                        if (isFloat) {
591:                            converter = SampleConverter
592:                                    .createPadValuesMask(nodataValues);
593:                        } else {
594:                            final boolean isZeroValid = (minimum <= 0 && maximum >= 0);
595:                            boolean collapsePadValues = false;
596:                            if (nodataValues != null
597:                                    && nodataValues.length != 0) {
598:                                final double[] sorted = (double[]) nodataValues
599:                                        .clone();
600:                                Arrays.sort(sorted);
601:                                double minFill = sorted[0];
602:                                double maxFill = minFill;
603:                                int indexMax = sorted.length;
604:                                while (--indexMax != 0
605:                                        && Double
606:                                                .isNaN(maxFill = sorted[indexMax]))
607:                                    ;
608:                                assert minFill <= maxFill
609:                                        || Double.isNaN(minFill) : maxFill;
610:                                if (bandIndex == visibleBand
611:                                        && maxFill > maximumFillValue) {
612:                                    maximumFillValue = maxFill;
613:                                }
614:                                if (minFill < floor || maxFill > ceil) {
615:                                    // At least one fill value is outside the range of acceptable values.
616:                                    collapsePadValues = true;
617:                                } else if (minimum >= 0) {
618:                                    /*
619:                                     * Arbitrary optimization of memory usage:  if there is a "large" empty
620:                                     * space between the range of valid values and a no-data value, then we
621:                                     * may (at subclass implementors choice) collapse the no-data values to
622:                                     * zero in order to avoid wasting the empty space.  Note that we do not
623:                                     * perform this collapse if the valid range contains negative values
624:                                     * because it would not save any memory. We do not check the no-data
625:                                     * values between 0 and 'minimum' for the same reason.
626:                                     */
627:                                    int k = Arrays
628:                                            .binarySearch(sorted, maximum);
629:                                    if (k >= 0)
630:                                        k++; // We want the first element greater than maximum.
631:                                    else
632:                                        k = ~k; // Really ~ operator, not -
633:                                    if (k <= indexMax) {
634:                                        double unusedSpace = Math.max(sorted[k]
635:                                                - maximum - 1, 0);
636:                                        while (++k <= indexMax) {
637:                                            final double delta = sorted[k]
638:                                                    - sorted[k - 1] - 1;
639:                                            if (delta > 0) {
640:                                                unusedSpace += delta;
641:                                            }
642:                                        }
643:                                        final int unused = (int) Math.min(Math
644:                                                .round(unusedSpace),
645:                                                Integer.MAX_VALUE);
646:                                        collapsePadValues = collapseNoDataValues(
647:                                                isZeroValid, sorted, unused);
648:                                        // We invoked 'collapseNoDataValues' inconditionnaly even if
649:                                        // 'unused' is zero because the user may decide on the basis
650:                                        // of other criterions, like 'isZeroValid'.
651:                                    }
652:                                }
653:                            }
654:                            if (minimum < floor || maximum > ceil) {
655:                                // The range of valid values is outside the range allowed by raw data type.
656:                                converter = SampleConverter.createOffset(
657:                                        1 - minimum, nodataValues);
658:                            } else if (collapsePadValues) {
659:                                if (isZeroValid) {
660:                                    // We need to collapse the no-data values to 0, but it causes a clash
661:                                    // with the range of valid values. So we also shift the later.
662:                                    converter = SampleConverter.createOffset(
663:                                            1 - minimum, nodataValues);
664:                                } else {
665:                                    // We need to collapse the no-data values and there is no clash.
666:                                    converter = SampleConverter
667:                                            .createPadValuesMask(nodataValues);
668:                                }
669:                            } else {
670:                                /*
671:                                 * Do NOT take 'nodataValues' in account if there is no need to collapse
672:                                 * them. This is not the converter's job to transform "packed" values to
673:                                 * "geophysics" values. We just want them to fit in the IndexColorModel,
674:                                 * and they already fit. So the identity converter is appropriate even
675:                                 * in presence of pad values.
676:                                 */
677:                                converter = SampleConverter.IDENTITY;
678:                            }
679:                        }
680:                        if (converters != null && bandIndex >= 0
681:                                && bandIndex < converters.length) {
682:                            converters[bandIndex] = converter;
683:                        }
684:                        if (bandIndex == visibleBand) {
685:                            visibleConverter = converter;
686:                            visibleRange = range;
687:                        }
688:                    }
689:                }
690:                /*
691:                 * Creates a color palette suitable for the range of values in the visible band.
692:                 * The case for floating points is the simpliest: we should not have any offset,
693:                 * at most a replacement of no-data values. In the case of integer values, we
694:                 * must make sure that the indexed color map is large enough for containing both
695:                 * the highest data value and the highest no-data value.
696:                 */
697:                if (visibleRange == null) {
698:                    visibleRange = (allRanges != null) ? allRanges
699:                            : new NumberRange(floor, ceil);
700:                }
701:                final PaletteFactory factory = PaletteFactory.getDefault();
702:                factory.setWarningLocale(locale);
703:                final Palette palette;
704:                if (isFloat) {
705:                    assert visibleConverter.getOffset() == 0 : visibleConverter;
706:                    palette = factory.getContinuousPalette(paletteName,
707:                            (float) visibleRange.getMinimum(),
708:                            (float) visibleRange.getMaximum(), dataType,
709:                            numBands, visibleBand);
710:                } else {
711:                    final double offset = visibleConverter.getOffset();
712:                    final double minimum = visibleRange.getMinimum();
713:                    final double maximum = visibleRange.getMaximum();
714:                    long lower, upper;
715:                    if (minimum == Double.NEGATIVE_INFINITY) {
716:                        lower = floor;
717:                    } else {
718:                        lower = Math.round(minimum + offset);
719:                        if (!visibleRange.isMinIncluded()) {
720:                            lower++; // Must be inclusive
721:                        }
722:                    }
723:                    if (maximum == Double.POSITIVE_INFINITY) {
724:                        upper = ceil;
725:                    } else {
726:                        upper = Math.round(maximum + offset);
727:                        if (visibleRange.isMaxIncluded()) {
728:                            upper++; // Must be exclusive
729:                        }
730:                    }
731:                    final long size = Math.max(upper, Math
732:                            .round(maximumFillValue) + 1);
733:                    /*
734:                     * The target lower, upper and size parameters are usually in the range of SHORT
735:                     * or USHORT data type. The Palette class will performs the necessary checks and
736:                     * throws an exception if those variables are out of range.  However, because we
737:                     * need to cast to int before passing the parameter values,  we restrict them to
738:                     * the 'int' range as a safety in order to avoid results that accidently fall in
739:                     * the SHORT or USHORT range.  Because Integer.MIN_VALUE or MAX_VALUE are out of
740:                     * range,  it doesn't matter if those values are inaccurate since we will get an
741:                     * exception anyway.
742:                     */
743:                    palette = factory.getPalette(paletteName, (int) Math.max(
744:                            lower, Integer.MIN_VALUE), (int) Math.min(upper,
745:                            Integer.MAX_VALUE), (int) Math.min(size,
746:                            Integer.MAX_VALUE), numBands, visibleBand);
747:                }
748:                return palette.getImageTypeSpecifier();
749:            }
750:
751:            /**
752:             * Returns the data type which most closely represents the "raw" internal data of the image.
753:             * It should be one of {@link DataBuffer} constants. The default {@code GeographicImageReader}
754:             * implementation works better with the following types:
755:             *
756:             * {@link DataBuffer#TYPE_BYTE   TYPE_BYTE},
757:             * {@link DataBuffer#TYPE_SHORT  TYPE_SHORT},
758:             * {@link DataBuffer#TYPE_USHORT TYPE_USHORT} and
759:             * {@link DataBuffer#TYPE_FLOAT  TYPE_FLOAT}.
760:             *
761:             * The default implementation returns {@link DataBuffer#TYPE_FLOAT TYPE_FLOAT} in every cases.
762:             * <p>
763:             * <h3>Handling of negative integer values</h3>
764:             * If the raw internal data contains negative values but this method still declares a unsigned
765:             * integer type ({@link DataBuffer#TYPE_BYTE TYPE_BYTE} or {@link DataBuffer#TYPE_USHORT TYPE_USHORT}),
766:             * then the values will be translated in order to fit in the range of strictly positive values.
767:             * For example if the raw internal data range from -23000 to +23000, then there is a choice:
768:             *
769:             * <ul>
770:             *   <li><p>If this method returns {@link DataBuffer#TYPE_SHORT}, then the data will be
771:             *       stored "as is" without transformation. However the {@linkplain IndexColorModel
772:             *       index color model} will have the maximal length allowed by 16 bits integers, with
773:             *       positive values in the [0 .. {@value java.lang.Short#MAX_VALUE}] range and negative
774:             *       values wrapped in the [32768 .. 65535] range in two's complement binary form. The
775:             *       results is a color model consuming 256 kilobytes in every cases. The space not used
776:             *       by the [-23000 .. +23000] range (in the above example) is lost.</p></li>
777:             *
778:             *   <li><p>If this method returns {@link DataBuffer#TYPE_USHORT}, then the data will be
779:             *       translated to the smallest strictly positive range that can holds the data
780:             *       ([1..46000] for the above example). Value 0 is reserved for missing data. The
781:             *       result is a smaller {@linkplain IndexColorModel index color model} than the
782:             *       one used by untranslated data.</p></li>
783:             * </ul>
784:             *
785:             * @param  imageIndex The index of the image to be queried.
786:             * @return The data type ({@link DataBuffer#TYPE_FLOAT} by default).
787:             * @throws IOException If an error occurs reading the format information from the input source.
788:             *
789:             * @see #getRawImageType(int, ImageReadParam, SampleConverter[])
790:             */
791:            protected int getRawDataType(final int imageIndex)
792:                    throws IOException {
793:                checkImageIndex(imageIndex);
794:                return DataBuffer.TYPE_FLOAT;
795:            }
796:
797:            /**
798:             * Returns {@code true} if the no-data values should be collapsed to 0 in order to save memory.
799:             * This method is invoked automatically by the {@link #getRawImageType(int, ImageReadParam,
800:             * SampleConverter[]) getRawImageType} method when it detected some unused space between the
801:             * {@linkplain Band#getValidRange range of valid values} and at least one
802:             * {@linkplain Band#getNoDataValues no-data value}.
803:             * <p>
804:             * The default implementation returns {@code false} in all cases, thus avoiding arbitrary
805:             * choice. Subclasses can override this method with some arbitrary threashold, as in the
806:             * example below:
807:             *
808:             * <blockquote><pre>
809:             * return unusedSpace >= 1024;
810:             * </pre></blockquote>
811:             *
812:             * @param isZeroValid
813:             *          {@code true} if 0 is a valid value. If this method returns {@code true} while
814:             *          {@code isZeroValid} is {@code true}, then the {@linkplain SampleConverter sample
815:             *          converter} to be returned by {@link #getRawImageType(int, ImageReadParam,
816:             *          SampleConverter[]) getRawImageType} will offset all valid values by 1.
817:             * @param nodataValues
818:             *          The {@linkplain Arrays#sort(double[]) sorted}
819:             *          {@linkplain Band#getNoDataValues no-data values} (never null and never empty).
820:             * @param unusedSpace
821:             *          The largest amount of unused space outside the range of valid values.
822:             */
823:            protected boolean collapseNoDataValues(final boolean isZeroValid,
824:                    final double[] nodataValues, final int unusedSpace) {
825:                return false;
826:            }
827:
828:            /**
829:             * Returns the buffered image to which decoded pixel data should be written. The image
830:             * is determined by inspecting the supplied parameters if it is non-null, as described
831:             * in the {@linkplain #getDestination(ImageReadParam,Iterator,int,int) super-class method}.
832:             * In the default implementation, the {@linkplain ImageTypeSpecifier image type specifier}
833:             * set is a singleton containing only the {@linkplain #getRawImageType(int,ImageReadParam,
834:             * SampleConverter[]) raw image type}.
835:             * <p>
836:             * Implementations of the {@link #read(int,ImageReadParam)} method should invoke this
837:             * method instead of {@link #getDestination(ImageReadParam,Iterator,int,int)}.
838:             *
839:             * @param  imageIndex The index of the image to be retrieved.
840:             * @param  parameters The parameter given to the {@code read} method.
841:             * @param  width      The true width of the image or tile begin decoded.
842:             * @param  height     The true width of the image or tile being decoded.
843:             * @param  converters If non-null, an array where to store the converters required
844:             *                    for converting decoded pixel data into stored pixel data.
845:             * @return The buffered image to which decoded pixel data should be written.
846:             *
847:             * @throws IOException If an error occurs reading the format information from the input source.
848:             *
849:             * @see #getRawImageType(int, ImageReadParam, SampleConverter[])
850:             */
851:            protected BufferedImage getDestination(final int imageIndex,
852:                    final ImageReadParam parameters, final int width,
853:                    final int height, final SampleConverter[] converters)
854:                    throws IOException {
855:                final Set spi = Collections.singleton(getRawImageType(
856:                        imageIndex, parameters, converters));
857:                return getDestination(parameters, spi.iterator(), width, height);
858:            }
859:
860:            /**
861:             * Returns a default parameter object appropriate for this format. The default
862:             * implementation constructs and returns a new {@link GeographicImageReadParam}.
863:             *
864:             * @return An {@code ImageReadParam} object which may be used.
865:             *
866:             * @todo Replace the return type by {@link GeographicImageReadParam} when we will
867:             *       be allowed to compile for J2SE 1.5.
868:             */
869:            //@Override
870:            public ImageReadParam getDefaultReadParam() {
871:                return new GeographicImageReadParam(this );
872:            }
873:
874:            /**
875:             * Reads the image indexed by {@code imageIndex} using a default {@link ImageReadParam}.
876:             * This is a convenience method that calls <code>{@linkplain #read(int,ImageReadParam)
877:             * read}(imageIndex, {@linkplain #getDefaultReadParam})</code>.
878:             * <p>
879:             * The default Java implementation passed a {@code null} parameter. This implementation
880:             * passes the default parameter instead in order to improve consistency when a subclass
881:             * overrides {@link #getDefaultReadParam}.
882:             *
883:             * @param imageIndex the index of the image to be retrieved.
884:             * @return the desired portion of the image.
885:             *
886:             * @throws IllegalStateException if the input source has not been set.
887:             * @throws IndexOutOfBoundsException if the supplied index is out of bounds.
888:             * @throws IOException if an error occurs during reading.
889:             */
890:            //@Override
891:            public BufferedImage read(final int imageIndex) throws IOException {
892:                return read(imageIndex, getDefaultReadParam());
893:            }
894:
895:            /**
896:             * Flips the source region vertically. This method should be invoked straight after
897:             * {@link #computeRegions computeRegions} when the image to be read will be flipped
898:             * vertically, for example when the {@linkplain java.awt.image.Raster raster} sample
899:             * values are filled in a "{@code for (y=ymax-1; y>=ymin; y--)}" loop instead of
900:             * "{@code for (y=ymin; y<ymax; y++)}".
901:             * <p>
902:             * This method should be invoked as in the example below:
903:             *
904:             * <blockquote><pre>
905:             * computeRegions(param, srcWidth, srcHeight, image, srcRegion, destRegion);
906:             * flipVertically(param, srcHeight, srcRegion);
907:             * </pre></blockquote>
908:             *
909:             * @param param     The {@code param}     argument given to {@code computeRegions}.
910:             * @param srcHeight The {@code srcHeight} argument given to {@code computeRegions}.
911:             * @param srcRegion The {@code srcRegion} argument given to {@code computeRegions}.
912:             */
913:            protected static void flipVertically(final ImageReadParam param,
914:                    final int srcHeight, final Rectangle srcRegion) {
915:                final int spaceLeft = srcRegion.y;
916:                srcRegion.y = srcHeight - (srcRegion.y + srcRegion.height);
917:                /*
918:                 * After the flip performed by the above line, we still have 'spaceLeft' pixels left for
919:                 * a downward translation.  We usually don't need to care about if, except if the source
920:                 * region is very close to the bottom of the source image,  in which case the correction
921:                 * computed below may be greater than the space left.
922:                 *
923:                 * We are done if there is no vertical subsampling. But if there is subsampling, then we
924:                 * need an adjustment. The flipping performed above must be computed as if the source
925:                 * region had exactly the size needed for reading nothing more than the last line, i.e.
926:                 * 'srcRegion.height' must be a multiple of 'sourceYSubsampling' plus 1. The "offset"
927:                 * correction is computed below accordingly.
928:                 */
929:                if (param != null) {
930:                    int offset = (srcRegion.height - 1)
931:                            % param.getSourceYSubsampling();
932:                    srcRegion.y += offset;
933:                    offset -= spaceLeft;
934:                    if (offset > 0) {
935:                        // Happen only if we are very close to image border and
936:                        // the above translation bring us outside the image area.
937:                        srcRegion.height -= offset;
938:                    }
939:                }
940:            }
941:
942:            /**
943:             * Invoked when a warning occured. The default implementation make the following choice:
944:             * <p>
945:             * <ul>
946:             *   <li>If at least one {@linkplain IIOReadWarningListener warning listener}
947:             *       has been {@linkplain #addIIOReadWarningListener specified}, then the
948:             *       {@link IIOReadWarningListener#warningOccurred warningOccurred} method is
949:             *       invoked for each of them and the log record is <strong>not</strong> logged.</li>
950:             *
951:             *   <li>Otherwise, the log record is sent to the {@code "org.geotools.image.io"} logger.</li>
952:             * </ul>
953:             *
954:             * Subclasses may override this method if more processing is wanted, or for
955:             * throwing exception if some warnings should be considered as fatal errors.
956:             */
957:            public void warningOccurred(final LogRecord record) {
958:                if (warningListeners == null) {
959:                    LOGGER.log(record);
960:                } else {
961:                    processWarningOccurred(IndexedResourceBundle.format(record));
962:                }
963:            }
964:
965:            /**
966:             * Convenience method for logging a warning from the given method.
967:             */
968:            private void warningOccurred(final String method,
969:                    final String message) {
970:                final LogRecord record = new LogRecord(Level.WARNING, message);
971:                record
972:                        .setSourceClassName(GeographicImageReader.class
973:                                .getName());
974:                record.setSourceMethodName(method);
975:                warningOccurred(record);
976:            }
977:
978:            /**
979:             * To be overriden and made {@code protected} by {@link StreamImageReader} only.
980:             */
981:            void close() throws IOException {
982:                metadata = null;
983:            }
984:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.