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


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2006, GeoTools Project Managment Committee (PMC)
005:         *    (C) 2006, 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;
010:         *    version 2.1 of the License.
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.netcdf;
018:
019:        import java.util.Set;
020:        import java.util.List;
021:        import java.util.Locale;
022:        import java.util.HashSet;
023:        import java.awt.Rectangle;
024:        import java.awt.image.BufferedImage;
025:        import java.awt.image.WritableRaster;
026:        import java.awt.image.DataBuffer;
027:        import java.net.URL;
028:        import java.net.URI;
029:        import java.io.File;
030:        import java.io.IOException;
031:        import java.io.FileNotFoundException;
032:        import javax.imageio.IIOException;
033:        import javax.imageio.ImageReader;
034:        import javax.imageio.ImageReadParam;
035:        import javax.imageio.metadata.IIOMetadata;
036:
037:        import ucar.ma2.Array;
038:        import ucar.ma2.Range;
039:        import ucar.ma2.DataType;
040:        import ucar.ma2.IndexIterator;
041:        import ucar.ma2.InvalidRangeException;
042:        import ucar.nc2.dataset.AxisType;
043:        import ucar.nc2.dataset.CoordinateAxis;
044:        import ucar.nc2.dataset.CoordinateSystem;
045:        import ucar.nc2.dataset.CoordSysBuilder;
046:        import ucar.nc2.dataset.NetcdfDataset;
047:        import ucar.nc2.dataset.VariableDS;
048:        import ucar.nc2.dataset.VariableEnhanced;
049:        import ucar.nc2.dods.DODSNetcdfFile;
050:        import ucar.nc2.util.CancelTask;
051:        import ucar.nc2.Dimension;
052:        import ucar.nc2.Variable;
053:        import ucar.nc2.VariableIF;
054:
055:        import org.geotools.image.io.FileImageReader;
056:        import org.geotools.image.io.SampleConverter;
057:        import org.geotools.image.io.metadata.GeographicMetadata;
058:        import org.geotools.math.Statistics;
059:        import org.geotools.resources.XArray;
060:        import org.geotools.resources.i18n.Errors;
061:        import org.geotools.resources.i18n.ErrorKeys;
062:
063:        /**
064:         * Base implementation for NetCDF image reader. Pixels are assumed organized according the COARDS
065:         * convention (a precursor of <A HREF="http://www.cfconventions.org/">CF Metadata conventions</A>),
066:         * i.e. in (<var>t</var>,<var>z</var>,<var>y</var>,<var>x</var>) order, where <var>x</var> varies
067:         * faster. The image is created from the two last dimensions (<var>x</var>,<var>y</var>).
068:         * Additional dimensions (if any) are handled as below in the default implementation:
069:         * <p>
070:         * <ul>
071:         *   <li>The third dimension (<var>z</var> in the above sequence) is assigned to bands. Users
072:         *       can change this behavior by invoking the {@link NetcdfReadParam#setBandDimensionTypes}
073:         *       method.</li>
074:         *   <li>Additional dimensions like <var>t</var> are ignored; only the first slice is selected.
075:         *       Users can change this behavior by invoking the {@link NetcdfReadParam#setSliceIndice}
076:         *       method.</li>
077:         * </ul>
078:         * <p>
079:         * <b>Example:</b><br>
080:         * Assuming that:
081:         * <ul>
082:         *   <li>None of the above methods has been invoked</li>
083:         *   <li>Axis are (<var>t</var>,<var>z</var>,<var>y</var>,<var>x</var>)</li>
084:         * </ul>
085:         * Then the users can select the <var>z</var> value using {@link ImageReadParam#setSourceBands}.
086:         * If no band is selected, then the default selection is the first band (0) only. Note that this
087:         * is different than the usual Image I/O default, which is all bands.
088:         * <p>
089:         * <b>Connection to DODS servers</b>
090:         * This image reader accepts {@link File} and {@link URL} inputs. In the later case, if and only
091:         * if the URL uses the DODS protocol (as in "{@code dods://opendap.aviso.oceanobs.com/}"), then
092:         * this image reader tries to connect to the DODS remote server. Otherwise the URL content is
093:         * copied in a temporary file.
094:         *
095:         * @since 2.4
096:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio-netcdf/src/main/java/org/geotools/image/io/netcdf/NetcdfImageReader.java $
097:         * @version $Id: NetcdfImageReader.java 27762 2007-11-05 21:11:12Z desruisseaux $
098:         * @author Antoine Hnawia
099:         * @author Martin Desruisseaux
100:         */
101:        public class NetcdfImageReader extends FileImageReader implements 
102:                CancelTask {
103:            /**
104:             * The URL protocol for connections to a DODS server. Any URL with this protocol will be
105:             * open using {@link DODSNetcdfFile} instead of the ordinary {@link NetcdfDataset}. The
106:             * later works only for local {@linkplain File files}, while the former connects to a
107:             * remote server.
108:             */
109:            private static final String DODS_PROTOCOL = "dods";
110:
111:            /**
112:             * The dimension <strong>relative to the rank</strong> in {@link #variable} to use as image
113:             * width. The actual dimension is {@code variable.getRank() - X_DIMENSION}. Is hard-coded
114:             * because the loop in the {@code read} method expects this order.
115:             */
116:            private static final int X_DIMENSION = 1;
117:
118:            /**
119:             * The dimension <strong>relative to the rank</strong> in {@link #variable} to use as image
120:             * height. The actual dimension is {@code variable.getRank() - Y_DIMENSION}. Is hard-coded
121:             * because the loop in the {@code read} method expects this order.
122:             */
123:            private static final int Y_DIMENSION = 2;
124:
125:            /**
126:             * The default dimension <strong>relative to the rank</strong> in {@link #variable} to use
127:             * as image bands. The actual dimension is {@code variable.getRank() - Z_DIMENSION}.
128:             * <p>
129:             * At the difference of {@link #X_DIMENSION} and {@link #Y_DIMENSION}, this dimension doesn't
130:             * need to be hard-coded. User can invoke {@link NetcdfReadParam#setBandDimensionTypes} in
131:             * order to provide a different value.
132:             */
133:            private static final int Z_DIMENSION = 3;
134:
135:            /**
136:             * The data type to accept in images. Used for automatic detection of which variables
137:             * to assign to images.
138:             */
139:            private static final Set/*<DataType>*/VALID_TYPES = new HashSet(12);
140:            static {
141:                VALID_TYPES.add(DataType.BOOLEAN);
142:                VALID_TYPES.add(DataType.BYTE);
143:                VALID_TYPES.add(DataType.SHORT);
144:                VALID_TYPES.add(DataType.INT);
145:                VALID_TYPES.add(DataType.LONG);
146:                VALID_TYPES.add(DataType.FLOAT);
147:                VALID_TYPES.add(DataType.DOUBLE);
148:            }
149:
150:            /**
151:             * The NetCDF dataset, or {@code null} if not yet open. The NetCDF file is open by
152:             * {@link #ensureOpen} when first needed.
153:             */
154:            private NetcdfDataset dataset;
155:
156:            /**
157:             * The name of the {@linkplain Variable variables} to be read in a NetCDF file.
158:             * The first name is assigned to image index 0, the second name to image index 1,
159:             * <cite>etc.</cite>.
160:             */
161:            private String[] variableNames;
162:
163:            /**
164:             * The image index of the current {@linkplain #variable variable}.
165:             */
166:            private int variableIndex;
167:
168:            /**
169:             * The data from the NetCDF file. The value for this field is set by {@link #prepareVariable}
170:             * when first needed. This is typically (but not necessarly) an instance of {@link VariableDS}.
171:             */
172:            protected Variable variable;
173:
174:            /**
175:             * The last error from the NetCDF library.
176:             */
177:            private String lastError;
178:
179:            /**
180:             * {@code true} if {@link CoordSysBuilder#addCoordinateSystems} has been invoked
181:             * for current file.
182:             */
183:            private boolean metadataLoaded;
184:
185:            /**
186:             * The stream metadata. Will be created only when first needed.
187:             */
188:            private IIOMetadata streamMetadata;
189:
190:            /**
191:             * The current image metadata. Will be created only when first needed.
192:             */
193:            private IIOMetadata imageMetadata;
194:
195:            /**
196:             * Constructs a new NetCDF reader.
197:             *
198:             * @param spi The service provider.
199:             */
200:            public NetcdfImageReader(final Spi spi) {
201:                super (spi);
202:            }
203:
204:            /**
205:             * Returns the {@linkplain #input input} as an URL to a DODS dataset, or {@code null} if
206:             * none. If this method returns a non-null value, then the input should be open using a
207:             * {@link DODSNetcdfFile}. Otherwise it should be open using an ordinary {@link NetcdfDataset}.
208:             * <p>
209:             * Note that we returns the URL as a String, not as a {@link URL} object, in order to avoid
210:             * an "unknown protocol" exception.
211:             */
212:            private String getInputDODS() {
213:                String protocol = null;
214:                if (input instanceof  URL) {
215:                    final URL url = (URL) input;
216:                    protocol = url.getProtocol();
217:                } else if (input instanceof  URI) {
218:                    final URI url = (URI) input;
219:                    protocol = url.getScheme();
220:                } else if (input instanceof  String) {
221:                    final String url = (String) input;
222:                    final int s = url.indexOf(':');
223:                    if (s > 0) {
224:                        protocol = url.substring(0, s);
225:                    }
226:                }
227:                if (protocol == null
228:                        || !protocol.equalsIgnoreCase(DODS_PROTOCOL)) {
229:                    return null;
230:                }
231:                return input.toString();
232:            }
233:
234:            /**
235:             * Returns the names of the variables to be read. The first name is assigned to image
236:             * index 0, the second name to image index 1, <cite>etc.</cite>. In other words a call
237:             * to <code>{@linkplain #read(int) read}(imageIndex)</code> will read the variable names
238:             * {@code variables[imageIndex]} where {@code variables} is the value returned by this
239:             * method.
240:             * <p>
241:             * The sequence of variable to be read can be changed by a call to {@link #setVariables}.
242:             *
243:             * @return The name of the variables to be read.
244:             * @throws IOException if the NetCDF file can not be read.
245:             */
246:            public String[] getVariables() throws IOException {
247:                if (variableNames == null) {
248:                    ensureFileOpen();
249:                }
250:                return (String[]) variableNames.clone();
251:            }
252:
253:            /**
254:             * Sets the name of the {@linkplain Variable variables} to be read in a NetCDF file.
255:             * The first name is assigned to image index 0, the second name to image index 1,
256:             * <cite>etc.</cite>.
257:             * <p>
258:             * If {@code variableNames} is set to {@code null} (which is the default), then the
259:             * variables will be inferred from the content of the NetCDF file.
260:             */
261:            public void setVariables(final String[] variableNames) {
262:                this .variableNames = (variableNames != null) ? (String[]) variableNames
263:                        .clone()
264:                        : null;
265:            }
266:
267:            /**
268:             * Returns the number of images available from the current input source.
269:             *
270:             * @throws IllegalStateException if the input source has not been set.
271:             * @throws IOException if an error occurs reading the information from the input source.
272:             */
273:            //@Override
274:            public int getNumImages(final boolean allowSearch)
275:                    throws IllegalStateException, IOException {
276:                ensureFileOpen();
277:                // TODO: consider returning the actual number of images in the file.
278:                return variableNames.length;
279:            }
280:
281:            /**
282:             * Convenience method returning the first (and only) sample converter in the specified
283:             * array, or a default converter if the specified array contains a null element.
284:             */
285:            private static SampleConverter first(
286:                    final SampleConverter[] converters) {
287:                SampleConverter converter = converters[0];
288:                if (converter == null) {
289:                    converter = SampleConverter.IDENTITY;
290:                }
291:                return converter;
292:            }
293:
294:            /**
295:             * Returns statistics about the sample values in the specified image. This is for informative
296:             * purpose only and may be used when the {@linkplain #getImageMetadata metadata} do not provides
297:             * useful information about valid minimum and maximum values. Note that this method requires a
298:             * full scan of image data and may be slow.
299:             *
300:             * @param  imageIndex The index of the image to analyze.
301:             * @return Statistics on the sample values in the given image.
302:             * @throws IOException if an I/O error occured while reading the sample values.
303:             */
304:            public Statistics getStatistics(final int imageIndex)
305:                    throws IOException {
306:                final double[] fillValues;
307:                final GeographicMetadata metadata = getGeographicMetadata(imageIndex);
308:                if (metadata != null && metadata.getNumBands() >= 1) {
309:                    fillValues = metadata.getBand(0).getNoDataValues();
310:                    // TODO: What should we do with other bands? For now we assume that
311:                    //       every bands have the same fill values.
312:                } else {
313:                    fillValues = null;
314:                }
315:                final Array array = variable.read();
316:                final IndexIterator it = array.getIndexIterator();
317:                final Statistics stats = new Statistics();
318:                if (fillValues == null || fillValues.length == 0) {
319:                    while (it.hasNext()) {
320:                        stats.add(it.getDoubleNext());
321:                    }
322:                } else if (fillValues.length == 1) {
323:                    final double fillValue = fillValues[0];
324:                    while (it.hasNext()) {
325:                        final double value = it.getDoubleNext();
326:                        if (value != fillValue) {
327:                            stats.add(value);
328:                        }
329:                    }
330:                } else {
331:                    scan: while (it.hasNext()) {
332:                        final double value = it.getDoubleNext();
333:                        if (fillValues != null) {
334:                            for (int i = 0; i < fillValues.length; i++) {
335:                                if (fillValues[i] == value) {
336:                                    continue scan;
337:                                }
338:                            }
339:                        }
340:                        stats.add(value);
341:                    }
342:                }
343:                return stats;
344:            }
345:
346:            /**
347:             * Returns the image width.
348:             */
349:            public int getWidth(final int imageIndex) throws IOException {
350:                prepareVariable(imageIndex);
351:                return variable.getDimension(variable.getRank() - X_DIMENSION)
352:                        .getLength();
353:            }
354:
355:            /**
356:             * Returns the image height.
357:             */
358:            public int getHeight(final int imageIndex) throws IOException {
359:                prepareVariable(imageIndex);
360:                return variable.getDimension(variable.getRank() - Y_DIMENSION)
361:                        .getLength();
362:            }
363:
364:            /**
365:             * Ensures that metadata are loaded.
366:             */
367:            private void ensureMetadataLoaded() throws IOException {
368:                if (!metadataLoaded) {
369:                    CoordSysBuilder.addCoordinateSystems(dataset, this );
370:                    metadataLoaded = true;
371:                }
372:            }
373:
374:            /**
375:             * Returns the metadata associated with the input source as a whole.
376:             */
377:            //@Override
378:            public IIOMetadata getStreamMetadata() throws IOException {
379:                if (streamMetadata == null && !ignoreMetadata) {
380:                    ensureFileOpen();
381:                    ensureMetadataLoaded();
382:                    streamMetadata = createMetadata(dataset);
383:                }
384:                return streamMetadata;
385:            }
386:
387:            /**
388:             * Returns the metadata associated with the image at the specified index.
389:             */
390:            //@Override
391:            public IIOMetadata getImageMetadata(final int imageIndex)
392:                    throws IOException {
393:                if (imageMetadata == null && !ignoreMetadata) {
394:                    prepareVariable(imageIndex);
395:                    if (variable instanceof  VariableDS) {
396:                        ensureMetadataLoaded();
397:                        imageMetadata = createMetadata((VariableDS) variable);
398:                    }
399:                }
400:                return imageMetadata;
401:            }
402:
403:            /**
404:             * Creates metadata for the specified NetCDF file. This method is invoked automatically
405:             * by {@link #getStreamMetadata} when first needed. The default implementation returns an
406:             * instance of {@link NetcdfMetadata}. Subclasses can override this method in order to create
407:             * a more specific set of metadata.
408:             */
409:            protected IIOMetadata createMetadata(final NetcdfDataset file)
410:                    throws IOException {
411:                return new NetcdfMetadata(this , file);
412:            }
413:
414:            /**
415:             * Creates metadata for the specified NetCDF variable. This method is invoked automatically
416:             * by {@link #getImageMetadata} when first needed. The default implementation returns an
417:             * instance of {@link NetcdfMetadata}. Subclasses can override this method in order to create
418:             * a more specific set of metadata.
419:             */
420:            protected IIOMetadata createMetadata(final VariableDS variable)
421:                    throws IOException {
422:                return new NetcdfMetadata(this , variable);
423:            }
424:
425:            /**
426:             * Returns the data type which most closely represents the "raw" internal data of the image.
427:             *
428:             * @param  imageIndex The index of the image to be queried.
429:             * @return The data type, or {@link DataBuffer#TYPE_UNDEFINED} if unknown.
430:             * @throws IOException If an error occurs reading the format information from the input source.
431:             */
432:            //@Override
433:            protected int getRawDataType(final int imageIndex)
434:                    throws IOException {
435:                prepareVariable(imageIndex);
436:                return VariableMetadata.getRawDataType(variable);
437:            }
438:
439:            /**
440:             * Returns the slice to select for the specified axis type.
441:             *
442:             * @return The axis type.
443:             * @return The indice as a value from 0 inclusive to {@link Dimension#getLength} exclusive.
444:             * @throws IOException If an error occured while reading the data.
445:             */
446:            private int getSliceIndice(final ImageReadParam param,
447:                    final int dimension) throws IOException {
448:                if (param instanceof  NetcdfReadParam) {
449:                    final NetcdfReadParam p = (NetcdfReadParam) param;
450:                    if (p.hasNonDefaultIndices()) {
451:                        final AxisType type = getAxisType(dimension);
452:                        if (type != null) {
453:                            return p.getSliceIndice(type);
454:                        }
455:                    }
456:                }
457:                return NetcdfReadParam.DEFAULT_INDICE;
458:            }
459:
460:            /**
461:             * Returns the axis type at the specified dimension. The {@link #prepareVariable} method
462:             * must be invoked prior this method (this is not verified).
463:             * <p>
464:             * This method is not public because it duplicates (in a different form) the informations
465:             * already provided in image metadata. We use it when we want only this specific information
466:             * without the the rest of metadata. In many cases this method will not be invoked at all,
467:             * thus avoiding the need to load metadata.
468:             *
469:             * @param  dimension The dimension.
470:             * @return The axis type, or {@code null} if unknown.
471:             * @throws IOException If an error occured while loading the axis informations.
472:             */
473:            private AxisType getAxisType(final int dimension)
474:                    throws IOException {
475:                if (variable instanceof  VariableDS) {
476:                    ensureMetadataLoaded();
477:                    final List sys = ((VariableDS) variable)
478:                            .getCoordinateSystems();
479:                    if (sys != null) {
480:                        final int count = sys.size();
481:                        for (int i = 0; i < count; i++) {
482:                            final CoordinateSystem cs = (CoordinateSystem) sys
483:                                    .get(i);
484:                            final List axes = cs.getCoordinateAxes();
485:                            if (axes != null && axes.size() > dimension) {
486:                                final CoordinateAxis axis = (CoordinateAxis) axes
487:                                        .get(dimension);
488:                                if (axis != null) {
489:                                    return axis.getAxisType();
490:                                }
491:                            }
492:                        }
493:                    }
494:                }
495:                return null;
496:            }
497:
498:            /**
499:             * Returns {@code true} if the specified variable is a dimension of an other variable.
500:             * Such dimensions will be excluded from the list returned by {@link #getVariables}.
501:             *
502:             * @param  candidate The variable to test.
503:             * @param  variables The list of variables.
504:             * @return {@code true} if the specified variable is a dimension of an other variable.
505:             */
506:            private static boolean isAxis(final VariableIF candidate,
507:                    final List variables) {
508:                final String name = candidate.getName();
509:                final int size = variables.size();
510:                for (int i = 0; i < size; i++) {
511:                    final VariableIF var = (VariableIF) variables.get(i);
512:                    if (var != candidate) {
513:                        Dimension dim;
514:                        for (int d = 0; (dim = var.getDimension(d)) != null; d++) {
515:                            if (dim.getName().equals(name)) {
516:                                return true;
517:                            }
518:                        }
519:                    }
520:                }
521:                return false;
522:            }
523:
524:            /**
525:             * Ensures that the NetCDF file is open, but do not load any variable yet.
526:             * The {@linkplain #variable} will be read by {@link #prepareVariable} only.
527:             */
528:            private void ensureFileOpen() throws IOException {
529:                if (dataset == null) {
530:                    /*
531:                     * Clears the 'abort' flag here (instead of in 'read' method only) because
532:                     * we pass this ImageReader instance to the NetCDF DataSet as a CancelTask.
533:                     */
534:                    lastError = null;
535:                    clearAbortRequest();
536:                    final String dodsURL = getInputDODS();
537:                    if (dodsURL != null) {
538:                        if (variableNames == null) {
539:                            final int s = dodsURL.indexOf('?');
540:                            if (s >= 0) {
541:                                variableNames = new String[] { dodsURL
542:                                        .substring(s + 1) };
543:                            }
544:                        }
545:                        dataset = new NetcdfDataset(new DODSNetcdfFile(dodsURL,
546:                                this ), false);
547:                    } else {
548:                        final File inputFile = getInputFile();
549:                        dataset = NetcdfDataset.openDataset(
550:                                inputFile.getPath(), false, this );
551:                        if (dataset == null) {
552:                            throw new FileNotFoundException(Errors
553:                                    .format(ErrorKeys.FILE_DOES_NOT_EXIST_$1,
554:                                            inputFile));
555:                        }
556:                    }
557:                    if (variableNames == null) {
558:                        /*
559:                         * Gets a list of every variables found in the NetcdfDataset and copies the names
560:                         * in a filtered list which exclude every variable that are dimension of an other
561:                         * variable. For example "longitude" may be a variable found in the NetcdfDataset,
562:                         * but is declared only because it is needed as a dimension for the "temperature"
563:                         * variable. The "longitude" variable is usually not of direct interest to the user
564:                         * (the interresting variable is "temperature"), so we exclude it.
565:                         */
566:                        final List variables = dataset.getVariables();
567:                        final String[] filtered = new String[variables.size()];
568:                        int count = 0;
569:                        for (int i = 0; i < filtered.length; i++) {
570:                            final VariableIF candidate = (VariableIF) variables
571:                                    .get(i);
572:                            /*
573:                             * - Images require at least 2 dimensions. They may have more dimensions,
574:                             *   in which case a slice will be taken later.
575:                             *
576:                             * - Excludes characters, strings and structures, which can not be easily
577:                             *   mapped to an image type. In addition, 2-dimensional character arrays
578:                             *   are often used for annotations and we don't wan't to confuse them
579:                             *   with images.
580:                             *
581:                             * - Excludes axis. They are often already excluded by the first condition
582:                             *   because axis are usually 1-dimensional, but some are 2-dimensional,
583:                             *   e.g. a localization grid.
584:                             */
585:                            if (candidate.getRank() >= 2
586:                                    && VALID_TYPES.contains(candidate
587:                                            .getDataType())
588:                                    && !isAxis(candidate, variables)) {
589:                                filtered[count++] = candidate.getName();
590:                            }
591:                        }
592:                        variableNames = (String[]) XArray.resize(filtered,
593:                                count);
594:                    }
595:                }
596:            }
597:
598:            /**
599:             * Ensures that data are loaded in the NetCDF {@linkplain #variable}. If data are already
600:             * loaded, then this method do nothing.
601:             * <p>
602:             * This method is invoked automatically before any operation that require the NetCDF
603:             * variable, including (but not limited to):
604:             * <ul>
605:             *   <li>{@link #getWidth}</li>
606:             *   <li>{@link #getHeight}</li>
607:             *   <li>{@link #getStatistics}</li>
608:             *   <li>{@link #getImageMetadata}</li>
609:             *   <li>{@link #getRawDataType}</li>
610:             *   <li>{@link #read(int,ImageReadParam)}</li>
611:             * </ul>
612:             *
613:             * @param   imageIndex The image index.
614:             * @return  {@code true} if the {@linkplain #variable} changed as a result of this call,
615:             *          or {@code false} if the current value is already appropriate.
616:             * @throws  IndexOutOfBoundsException if the specified index is outside the expected range.
617:             * @throws  IllegalStateException If {@link #input} is not set.
618:             * @throws  IOException If the operation failed because of an I/O error.
619:             */
620:            protected boolean prepareVariable(final int imageIndex)
621:                    throws IOException {
622:                checkImageIndex(imageIndex);
623:                if (variable == null || variableIndex != imageIndex) {
624:                    ensureFileOpen();
625:                    final String name = variableNames[imageIndex];
626:                    final Variable candidate = findVariable(name);
627:                    final int rank = candidate.getRank();
628:                    if (rank < Math.max(X_DIMENSION, Y_DIMENSION)) {
629:                        throw new IIOException(Errors.format(
630:                                ErrorKeys.NOT_TWO_DIMENSIONAL_$1, new Integer(
631:                                        rank)));
632:                    }
633:                    variable = candidate;
634:                    variableIndex = imageIndex;
635:                    imageMetadata = null;
636:                    return true;
637:                }
638:                return false;
639:            }
640:
641:            /**
642:             * Returns the variable of the given name. This method is similar to
643:             * {@link NetcdfDataset#findVariable(String)} except that the search
644:             * is case-insensitive and an exception is thrown if no variable has
645:             * been found for the given name.
646:             *
647:             * @param  name The name of the variable to search.
648:             * @return The variable for the given name.
649:             * @throws IIOException if no variable has been found for the given name.
650:             * @throws IOException If the operation failed because of an I/O error.
651:             */
652:            protected Variable findVariable(final String name)
653:                    throws IOException {
654:                ensureFileOpen();
655:                /*
656:                 * First tries a case-sensitive search. Case matter since the same letter in different
657:                 * case may represent different variables. For example "t" and "T" are typically "time"
658:                 * and "temperature" respectively.
659:                 */
660:                Variable candidate = dataset.findVariable(name);
661:                if (candidate != null) {
662:                    return candidate;
663:                }
664:                /*
665:                 * We tried a case-sensitive search without success. Now tries a case-insensitive search
666:                 * before to report a failure.
667:                 */
668:                final List/*<Variable>*/variables = dataset.getVariables();
669:                if (variables != null) {
670:                    for (final java.util.Iterator/*<Variable>*/it = variables
671:                            .iterator(); it.hasNext();) {
672:                        candidate = (Variable) it.next();
673:                        if (candidate != null
674:                                && name.equalsIgnoreCase(candidate.getName())) {
675:                            return candidate;
676:                        }
677:                    }
678:                }
679:                throw new IIOException(Errors.format(
680:                        ErrorKeys.VARIABLE_NOT_FOUND_IN_FILE_$2, name, dataset
681:                                .getLocation()));
682:            }
683:
684:            /**
685:             * Returns parameters initialized with default values appropriate for this format.
686:             *
687:             * @return Parameters which may be used to control the decoding process using a set
688:             *         of default settings.
689:             */
690:            //@Override
691:            public ImageReadParam getDefaultReadParam() {
692:                return new NetcdfReadParam(this );
693:            }
694:
695:            /**
696:             * Creates an image from the specified parameters.
697:             */
698:            public BufferedImage read(final int imageIndex,
699:                    final ImageReadParam param) throws IOException {
700:                clearAbortRequest();
701:                prepareVariable(imageIndex);
702:                /*
703:                 * Fetchs the parameters that are not already processed by utility
704:                 * methods like 'getDestination' or 'computeRegions' (invoked below).
705:                 */
706:                final int strideX, strideY;
707:                final int[] srcBands, dstBands;
708:                if (param != null) {
709:                    strideX = param.getSourceXSubsampling();
710:                    strideY = param.getSourceYSubsampling();
711:                    srcBands = param.getSourceBands();
712:                    dstBands = param.getDestinationBands();
713:                } else {
714:                    strideX = 1;
715:                    strideY = 1;
716:                    srcBands = null;
717:                    dstBands = null;
718:                }
719:                final int rank = variable.getRank();
720:                int bandDimension = rank - Z_DIMENSION;
721:                if (param instanceof  NetcdfReadParam) {
722:                    final NetcdfReadParam p = (NetcdfReadParam) param;
723:                    if (variable instanceof  VariableEnhanced) {
724:                        ensureMetadataLoaded(); // Build the CoordinateSystems
725:                        bandDimension = p
726:                                .getBandDimension((VariableEnhanced) variable);
727:                        final int relative = rank - bandDimension;
728:                        if (relative < 0 || relative == X_DIMENSION
729:                                || relative == Y_DIMENSION) {
730:                            throw new IllegalArgumentException(Errors
731:                                    .format(ErrorKeys.BAD_PARAMETER_$2,
732:                                            "bandDimension", new Integer(
733:                                                    bandDimension)));
734:                        }
735:                    }
736:                }
737:                /*
738:                 * Gets the destination image of appropriate size. We create it now
739:                 * since it is a convenient way to get the number of destination bands.
740:                 */
741:                final int width = variable.getDimension(rank - X_DIMENSION)
742:                        .getLength();
743:                final int height = variable.getDimension(rank - Y_DIMENSION)
744:                        .getLength();
745:                final SampleConverter[] converters = new SampleConverter[1];
746:                final BufferedImage image = getDestination(imageIndex, param,
747:                        width, height, converters);
748:                final WritableRaster raster = image.getRaster();
749:                final SampleConverter converter = first(converters);
750:                /*
751:                 * Checks the band setting. If the NetCDF file is at least 3D, the
752:                 * data along the 'z' dimension are considered as different bands.
753:                 */
754:                final boolean hasBands = (bandDimension >= 0 && bandDimension < rank);
755:                final int numSrcBands = hasBands ? variable.getDimension(
756:                        bandDimension).getLength() : 1;
757:                final int numDstBands = raster.getNumBands();
758:                if (param != null) {
759:                    // Do not test when 'param == null' since our default 'srcBands'
760:                    // value is not the same than the one documented in Image I/O.
761:                    checkReadParamBandSettings(param, numSrcBands, numDstBands);
762:                }
763:                /*
764:                 * Computes the source region (in the NetCDF file) and the destination region
765:                 * (in the buffered image). Copies those informations into UCAR Range structure.
766:                 */
767:                final Rectangle srcRegion = new Rectangle();
768:                final Rectangle destRegion = new Rectangle();
769:                computeRegions(param, width, height, image, srcRegion,
770:                        destRegion);
771:                flipVertically(param, height, srcRegion);
772:                final Range[] ranges = new Range[rank];
773:                for (int i = 0; i < ranges.length; i++) {
774:                    final int first, length, stride;
775:                    switch (rank - i) {
776:                    case X_DIMENSION: {
777:                        first = srcRegion.x;
778:                        length = srcRegion.width;
779:                        stride = strideX;
780:                        break;
781:                    }
782:                    case Y_DIMENSION: {
783:                        first = srcRegion.y;
784:                        length = srcRegion.height;
785:                        stride = strideY;
786:                        break;
787:                    }
788:                    default: {
789:                        if (i == bandDimension) {
790:                            first = NetcdfReadParam.DEFAULT_INDICE;
791:                        } else {
792:                            first = getSliceIndice(param, i);
793:                        }
794:                        length = 1;
795:                        stride = 1;
796:                        break;
797:                    }
798:                    }
799:                    try {
800:                        ranges[i] = new Range(first, first + length - 1, stride);
801:                    } catch (InvalidRangeException e) {
802:                        throw netcdfFailure(e);
803:                    }
804:                }
805:                final List sections = Range.toList(ranges);
806:                /*
807:                 * Reads the requested sub-region only.
808:                 */
809:                processImageStarted(imageIndex);
810:                final float toPercent = 100f / numDstBands;
811:                final int type = raster.getSampleModel().getDataType();
812:                final int xmin = destRegion.x;
813:                final int ymin = destRegion.y;
814:                final int xmax = destRegion.width + xmin;
815:                final int ymax = destRegion.height + ymin;
816:                for (int zi = 0; zi < numDstBands; zi++) {
817:                    final int srcBand = (srcBands == null) ? zi : srcBands[zi];
818:                    final int dstBand = (dstBands == null) ? zi : dstBands[zi];
819:                    final Array array;
820:                    try {
821:                        if (hasBands) {
822:                            ranges[bandDimension] = new Range(srcBand, srcBand,
823:                                    1);
824:                            // No need to update 'sections' since it wraps directly the 'ranges' array.
825:                        }
826:                        array = variable.read(sections);
827:                    } catch (InvalidRangeException e) {
828:                        throw netcdfFailure(e);
829:                    }
830:                    final IndexIterator it = array.getIndexIterator();
831:                    for (int y = ymax; --y >= ymin;) {
832:                        for (int x = xmin; x < xmax; x++) {
833:                            switch (type) {
834:                            case DataBuffer.TYPE_DOUBLE: {
835:                                raster.setSample(x, y, dstBand, converter
836:                                        .convert(it.getDoubleNext()));
837:                                break;
838:                            }
839:                            case DataBuffer.TYPE_FLOAT: {
840:                                raster.setSample(x, y, dstBand, converter
841:                                        .convert(it.getFloatNext()));
842:                                break;
843:                            }
844:                            default: {
845:                                raster.setSample(x, y, dstBand, converter
846:                                        .convert(it.getIntNext()));
847:                                break;
848:                            }
849:                            }
850:                        }
851:                    }
852:                    /*
853:                     * Checks for abort requests after reading. It would be a waste of a potentially
854:                     * good image (maybe the abort request occured after we just finished the reading)
855:                     * if we didn't implemented the 'isCancel()' method. But because of the later, which
856:                     * is checked by the NetCDF library, we can't assume that the image is complete.
857:                     */
858:                    if (abortRequested()) {
859:                        processReadAborted();
860:                        return image;
861:                    }
862:                    /*
863:                     * Reports progress here, not in the deeper loop, because the costly part is the
864:                     * call to 'variable.read(...)' which can't report progress.  The loop that copy
865:                     * pixel values is fast, so reporting progress there would be pointless.
866:                     */
867:                    processImageProgress(zi * toPercent);
868:                }
869:                if (lastError != null) {
870:                    throw new IIOException(lastError);
871:                }
872:                processImageComplete();
873:                return image;
874:            }
875:
876:            /**
877:             * Wraps a generic exception into a {@link IIOException}.
878:             */
879:            private IIOException netcdfFailure(final Exception e)
880:                    throws IOException {
881:                return new IIOException(Errors.format(ErrorKeys.CANT_READ_$1,
882:                        dataset.getLocation()), e);
883:            }
884:
885:            /**
886:             * Invoked by the NetCDF library during read operation in order to check if the task has
887:             * been canceled. Users should not invoke this method directly.
888:             */
889:            public boolean isCancel() {
890:                return abortRequested();
891:            }
892:
893:            /**
894:             * Invoked by the NetCDF library when an error occured during the read operation.
895:             * Users should not invoke this method directly.
896:             */
897:            public void setError(final String message) {
898:                lastError = message;
899:            }
900:
901:            /**
902:             * Closes the NetCDF file.
903:             */
904:            //@Override
905:            protected void close() throws IOException {
906:                metadataLoaded = false;
907:                streamMetadata = null;
908:                imageMetadata = null;
909:                lastError = null;
910:                variable = null;
911:                if (dataset != null) {
912:                    dataset.close();
913:                    dataset = null;
914:                }
915:                super .close();
916:            }
917:
918:            /**
919:             * The service provider for {@link NetcdfImageReader}.
920:             *
921:             * @version $Id: NetcdfImageReader.java 27762 2007-11-05 21:11:12Z desruisseaux $
922:             * @author Antoine Hnawia
923:             * @author Martin Desruisseaux
924:             */
925:            public static class Spi extends FileImageReader.Spi {
926:                /**
927:                 * List of legal names for NetCDF readers.
928:                 */
929:                private static final String[] NAMES = new String[] { "netcdf",
930:                        "NetCDF" };
931:
932:                /**
933:                 * The mime types for the default {@link NetcdfImageReader} configuration.
934:                 */
935:                private static final String[] MIME_TYPES = new String[] { "image/x-netcdf" };
936:
937:                /**
938:                 * Default list of file's extensions.
939:                 */
940:                private static final String[] SUFFIXES = new String[] { "nc",
941:                        "NC" };
942:
943:                /**
944:                 * Constructs a default {@code NetcdfImageReader.Spi}. This constructor
945:                 * provides the following defaults in addition to the defaults defined
946:                 * in the super-class constructor:
947:                 *
948:                 * <ul>
949:                 *   <li>{@link #names}           = {@code "NetCDF"}</li>
950:                 *   <li>{@link #MIMETypes}       = {@code "image/x-netcdf"}</li>
951:                 *   <li>{@link #pluginClassName} = {@code "org.geotools.image.io.netcdf.NetcdfImageReader"}</li>
952:                 *   <li>{@link #vendorName}      = {@code "Geotools"}</li>
953:                 *   <li>{@link #suffixes}        = {{@code "nc"}, {@code "NC"}}</li>
954:                 * </ul>
955:                 *
956:                 * For efficienty reasons, the above fields are initialized to shared arrays. Subclasses
957:                 * can assign new arrays, but should not modify the default array content.
958:                 */
959:                public Spi() {
960:                    names = NAMES;
961:                    MIMETypes = MIME_TYPES;
962:                    suffixes = SUFFIXES;
963:                    pluginClassName = "org.geotools.image.io.netcdf.NetcdfImageReader";
964:                    vendorName = "Geotools";
965:                    version = "2.4";
966:                }
967:
968:                /**
969:                 * Returns a description for this provider.
970:                 *
971:                 * @todo Localize
972:                 */
973:                public String getDescription(final Locale locale) {
974:                    return "NetCDF image decoder";
975:                }
976:
977:                /**
978:                 * Checks if the specified input seems to be a readeable NetCDF file.
979:                 * This method is only for indication purpose. Current implementation
980:                 * conservatively returns {@code false}.
981:                 *
982:                 * @todo Implements a more advanced check.
983:                 */
984:                public boolean canDecodeInput(final Object source)
985:                        throws IOException {
986:                    return false;
987:                }
988:
989:                /**
990:                 * Constructs a NetCDF image reader.
991:                 */
992:                public ImageReader createReaderInstance(final Object extension)
993:                        throws IOException {
994:                    return new NetcdfImageReader(this);
995:                }
996:            }
997:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.