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


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2003-2006, Geotools Project Managment Committee (PMC)
005:         *    (C) 2001, Institut de Recherche pour le Développement
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.text;
018:
019:        import java.awt.Point;
020:        import java.awt.Rectangle;
021:        import java.awt.geom.AffineTransform;
022:        import java.awt.image.BufferedImage;
023:        import java.awt.image.DataBuffer;
024:        import java.awt.image.WritableRaster;
025:        import java.io.BufferedReader;
026:        import java.io.IOException;
027:        import java.text.ParseException;
028:        import java.util.ArrayList;
029:        import java.util.Iterator;
030:        import java.util.List;
031:        import java.util.Locale;
032:        import javax.imageio.IIOException;
033:        import javax.imageio.ImageReadParam;
034:        import javax.imageio.ImageReader;
035:        import javax.imageio.ImageTypeSpecifier;
036:        import javax.imageio.metadata.IIOMetadata;
037:        import javax.imageio.spi.ImageReaderSpi;
038:
039:        import org.geotools.io.LineFormat;
040:        import org.geotools.resources.XArray;
041:        import org.geotools.resources.i18n.Errors;
042:        import org.geotools.resources.i18n.ErrorKeys;
043:        import org.geotools.resources.i18n.Descriptions;
044:        import org.geotools.resources.i18n.DescriptionKeys;
045:        import org.geotools.image.io.metadata.ImageGeometry;
046:        import org.geotools.image.io.metadata.GeographicMetadata;
047:
048:        /**
049:         * Image decoder for text files storing pixel values as records.
050:         * Such text files use one line (record) by pixel. Each line contains
051:         * at least 3 columns (in arbitrary order):
052:         *
053:         * <ul>
054:         *   <li>Pixel's <var>x</var> coordinate.</li>
055:         *   <li>Pixel's <var>y</var> coordinate.</li>
056:         *   <li>An arbitrary number of pixel values.</li>
057:         * </ul>
058:         *
059:         * For example, some Sea Level Anomaly (SLA) files contains rows of longitude
060:         * (degrees), latitude (degrees), SLA (cm), East/West current (cm/s) and
061:         * North/South current (cm/s), as below:
062:         *
063:         * <blockquote><pre>
064:         * 45.1250 -29.8750    -7.28     10.3483     -0.3164
065:         * 45.1250 -29.6250    -4.97     11.8847      3.6192
066:         * 45.1250 -29.3750    -2.91      3.7900      3.0858
067:         * 45.1250 -29.1250    -3.48     -5.1833     -5.0759
068:         * 45.1250 -28.8750    -4.36     -1.8129    -16.3689
069:         * 45.1250 -28.6250    -3.91      7.5577    -24.6801
070:         * </pre>(...etc...)
071:         * </blockquote>
072:         *
073:         * From this decoder point of view, the two first columns (longitude and latitude)
074:         * are pixel's logical coordinate (<var>x</var>,<var>y</var>), while the three last
075:         * columns are three image's bands. The whole file contains only one image (unless
076:         * {@link #getNumImages} has been overridden). All (<var>x</var>,<var>y</var>)
077:         * coordinates belong to pixel's center. This decoder will automatically translate
078:         * (<var>x</var>,<var>y</var>) coordinates from logical space to pixel space. The
079:         * {@link #getTransform} method provides a convenient {@link AffineTransform} for
080:         * performing coordinate transformations between pixel and logical spaces.
081:         * <p>
082:         * By default, {@code TextRecordImageReader} assumes that <var>x</var> and
083:         * <var>y</var> coordinates appear in column #0 and 1 respectively. It also assumes
084:         * that numeric values are encoded using current defaults {@link java.nio.charset.Charset}
085:         * and {@link java.util.Locale}, and that there is no pad value. The easiest way to change
086:         * the default setting is to create a {@link Spi} subclass. There is no need to subclass
087:         * {@code TextRecordImageReader}, unless you want more control on the decoding process.
088:         *
089:         * @since 2.1
090:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/text/TextRecordImageReader.java $
091:         * @version $Id: TextRecordImageReader.java 27629 2007-10-26 09:59:20Z desruisseaux $
092:         * @author Martin Desruisseaux
093:         */
094:        public class TextRecordImageReader extends TextImageReader {
095:            /**
096:             * Petit facteur de tolérance servant à tenir compte des erreurs d'arrondissement.
097:             */
098:            private static final float EPS = 1E-5f;
099:
100:            /**
101:             * Intervalle (en nombre d'octets) entre les rapports de progrès.
102:             */
103:            private static final int PROGRESS_INTERVAL = 4096;
104:
105:            /**
106:             * Lorsque la lecture se fait par-dessus une image {@link BufferedReader} existante,
107:             * indique s'il faut effacer la région dans laquelle sera placée l'image avant de la
108:             * lire. La valeur {@code false} permettra de conserver les anciens pixels dans
109:             * les régions ou le fichier ne définit pas de nouvelles valeurs.
110:             */
111:            private static final boolean CLEAR = true;
112:
113:            /**
114:             * Données des images, ou {@code null} si aucune lecture n'a encore été
115:             * faite. Chaque élément contient les données de l'image à l'index correspondant
116:             * (i.e. l'élément {@code data[0]} contient les données de l'image #0,
117:             * {@code data[1]} contient les données de l'image #1, etc.). Des éléments
118:             * de ce tableau peuvent être nuls si les données des images correspondantes
119:             * ne sont pas retenues après chaque lecture (c'est-à-dire si
120:             * <code>{@link #seekForwardOnly}==true</code>).
121:             */
122:            private RecordList[] data;
123:
124:            /**
125:             * Index de la prochaine image à lire. Cet index n'est pas nécessairement
126:             * égal à la longueur du tableau {@link #data}. Il peut être aussi bien
127:             * plus petit que plus grand.
128:             */
129:            private int nextImageIndex;
130:
131:            /**
132:             * Nombre moyen de caractères par données (incluant les espaces et les codes
133:             * de fin de ligne). Cette information n'est qu'à titre indicative, mais son
134:             * exactitude peut aider à accelerer la lecture et rendre les rapport des
135:             * progrès plus précis. Elle sera automatiquement mise à jour en fonction
136:             * des lignes lues.
137:             */
138:            private float expectedDatumLength = 10.4f;
139:
140:            /**
141:             * Constructs a new image reader.
142:             *
143:             * @param provider the provider that is invoking this constructor, or {@code null} if none.
144:             */
145:            public TextRecordImageReader(final ImageReaderSpi provider) {
146:                super (provider);
147:            }
148:
149:            /**
150:             * Returns the grid tolerance (epsilon) value.
151:             */
152:            private float getGridTolerance() {
153:                return (originatingProvider instanceof  Spi) ? ((Spi) originatingProvider).gridTolerance
154:                        : EPS;
155:            }
156:
157:            /**
158:             * Returns the column number for <var>x</var> values. The default implementation returns
159:             * {@link TextRecordImageReader.Spi#xColumn}. Subclasses should override this method if
160:             * this information should be obtained in an other way.
161:             *
162:             * @param  imageIndex The index of the image to be queried.
163:             * @throws IOException If an error occurs reading the from the input source.
164:             */
165:            protected int getColumnX(final int imageIndex) throws IOException {
166:                return (originatingProvider instanceof  Spi) ? ((Spi) originatingProvider).xColumn
167:                        : 0;
168:            }
169:
170:            /**
171:             * Invokes {@link #getColumnX} and checks the result.
172:             */
173:            private int getCheckedColumnX(final int imageIndex)
174:                    throws IOException {
175:                final int xColumn = getColumnX(imageIndex);
176:                if (xColumn < 0) {
177:                    throw new IllegalStateException(Errors.format(
178:                            ErrorKeys.NEGATIVE_COLUMN_$2, "x", new Integer(
179:                                    xColumn)));
180:                }
181:                return xColumn;
182:            }
183:
184:            /**
185:             * Returns the column number for <var>x</var> values. The default implementation returns
186:             * {@link TextRecordImageReader.Spi#yColumn}. Subclasses should override this method if
187:             * this information should be obtained in an other way.
188:             *
189:             * @param  imageIndex The index of the image to be queried.
190:             * @throws IOException If an error occurs reading the from the input source.
191:             */
192:            protected int getColumnY(final int imageIndex) throws IOException {
193:                return (originatingProvider instanceof  Spi) ? ((Spi) originatingProvider).yColumn
194:                        : 1;
195:            }
196:
197:            /**
198:             * Invokes {@link #getColumnY} and checks the result.
199:             */
200:            private int getCheckedColumnY(final int imageIndex)
201:                    throws IOException {
202:                final int yColumn = getColumnY(imageIndex);
203:                if (yColumn < 0) {
204:                    throw new IllegalStateException(Errors.format(
205:                            ErrorKeys.NEGATIVE_COLUMN_$2, "y", new Integer(
206:                                    yColumn)));
207:                }
208:                return yColumn;
209:            }
210:
211:            /**
212:             * Retourne le numéro de colonne dans laquelle se trouvent les données de la
213:             * bande spécifiée. L'implémentation par défaut retourne {@code band}+1
214:             * ou 2 si la bande est plus grand ou égal à {@link #getColumnX} et/ou
215:             * {@link #getColumnY}. Cette implémentation devrait convenir pour des données
216:             * se trouvant aussi bien avant qu'après les colonnes <var>x</var>
217:             * et <var>y</var>, même si ces dernières ne sont pas consécutives.
218:             *
219:             * @param  imageIndex Index de l'image à lire.
220:             * @param  band Bande de l'image à lire.
221:             * @return Numéro de colonne des données de l'image.
222:             * @throws IOException si l'opération nécessitait une lecture du fichier (par exemple
223:             *         des informations inscrites dans un en-tête) et que cette lecture a échouée.
224:             */
225:            private int getColumn(final int imageIndex, int band)
226:                    throws IOException {
227:                final int xColumn = getCheckedColumnX(imageIndex);
228:                final int yColumn = getCheckedColumnY(imageIndex);
229:                if (band >= Math.min(xColumn, yColumn))
230:                    band++;
231:                if (band >= Math.max(xColumn, yColumn))
232:                    band++;
233:                return band;
234:            }
235:
236:            /**
237:             * Set the input source. It should be one of the following object, in preference order:
238:             * {@link java.io.File}, {@link java.net.URL}, {@link java.io.BufferedReader}.
239:             * {@link java.io.Reader}, {@link java.io.InputStream} or
240:             * {@link javax.imageio.stream.ImageInputStream}.
241:             */
242:            //@Override
243:            public void setInput(final Object input,
244:                    final boolean seekForwardOnly, final boolean ignoreMetadata) {
245:                clear();
246:                super .setInput(input, seekForwardOnly, ignoreMetadata);
247:            }
248:
249:            /**
250:             * Returns the number of bands available for the specified image.
251:             *
252:             * @param  imageIndex  The image index.
253:             * @throws IOException if an error occurs reading the information from the input source.
254:             */
255:            //@Override
256:            public int getNumBands(final int imageIndex) throws IOException {
257:                return getRecords(imageIndex).getColumnCount()
258:                        - (getCheckedColumnX(imageIndex) == getCheckedColumnY(imageIndex) ? 1
259:                                : 2);
260:            }
261:
262:            /**
263:             * Returns the width in pixels of the given image within the input source.
264:             *
265:             * @param  imageIndex the index of the image to be queried.
266:             * @return Image width.
267:             * @throws IOException If an error occurs reading the width information from the input source.
268:             */
269:            public int getWidth(final int imageIndex) throws IOException {
270:                return getRecords(imageIndex).getPointCount(
271:                        getCheckedColumnX(imageIndex), getGridTolerance());
272:            }
273:
274:            /**
275:             * Returns the height in pixels of the given image within the input source.
276:             *
277:             * @param  imageIndex the index of the image to be queried.
278:             * @return Image height.
279:             * @throws IOException If an error occurs reading the height information from the input source.
280:             */
281:            public int getHeight(final int imageIndex) throws IOException {
282:                return getRecords(imageIndex).getPointCount(
283:                        getCheckedColumnY(imageIndex), getGridTolerance());
284:            }
285:
286:            /**
287:             * Returns metadata associated with the given image.
288:             * Calling this method may force loading of full image.
289:             *
290:             * @param  imageIndex The image index.
291:             * @return The metadata, or {@code null} if none.
292:             * @throws IOException If an error occurs reading the data information from the input source.
293:             */
294:            //@Override
295:            public IIOMetadata getImageMetadata(final int imageIndex)
296:                    throws IOException {
297:                checkImageIndex(imageIndex);
298:                if (ignoreMetadata) {
299:                    return null;
300:                }
301:                final GeographicMetadata metadata = new GeographicMetadata(this );
302:                final ImageGeometry geometry = metadata.getGeometry();
303:                /*
304:                 * Computes the smallest bounding box containing the full image in user coordinates.
305:                 * This implementation searchs for minimum and maximum values in x and y columns as
306:                 * returned by getColumnX() and getColumnY(). Reminder: xmax and ymax are INCLUSIVE
307:                 * in the code below, as well as (width-1) and (height-1).
308:                 */
309:                final float tolerance = getGridTolerance();
310:                final RecordList records = getRecords(imageIndex);
311:                final int xColumn = getCheckedColumnX(imageIndex);
312:                final int yColumn = getCheckedColumnY(imageIndex);
313:                final int width = records.getPointCount(xColumn, tolerance);
314:                final int height = records.getPointCount(yColumn, tolerance);
315:                final double xmin = records.getMinimum(xColumn);
316:                final double ymin = records.getMinimum(yColumn);
317:                final double xmax = records.getMaximum(xColumn);
318:                final double ymax = records.getMaximum(yColumn);
319:                geometry.addCoordinateRange(0, width - 1, xmin, xmax);
320:                geometry.addCoordinateRange(0, height - 1, ymin, ymax);
321:                geometry.setPixelOrientation("center");
322:                /*
323:                 * Now adds the valid range of sample values for each band.
324:                 */
325:                final int numBands = records.getColumnCount()
326:                        - (xColumn == yColumn ? 1 : 2);
327:                for (int band = 0; band < numBands; band++) {
328:                    final int column = getColumn(imageIndex, band);
329:                    metadata.getBand(band).setValidRange(
330:                            records.getMinimum(column),
331:                            records.getMaximum(column));
332:                }
333:                return metadata;
334:            }
335:
336:            /**
337:             * Rounds the specified values. This method is invoked automatically by the {@link #read read}
338:             * method while reading an image. It provides a place where to fix rounding errors in latitude
339:             * and longitude coordinates. For example if longitudes have a step 1/6° but are written with
340:             * only 3 decimal digits, then we get {@linkplain #getColumnX x} values like {@code 10.000},
341:             * {@code 10.167}, {@code 10.333}, <cite>etc.</cite>, which can leads to an error of 0.001°
342:             * in longitude. This error may cause {@code TextRecordImageReader} to fails validation tests
343:             * and throws an {@link javax.imageio.IIOException}: "<cite>Points dont seem to be distributed
344:             * on a regular grid</cite>". A work around is to multiply the <var>x</var> and <var>y</var>
345:             * coordinates by 6, round to the nearest integer and divide them by 6.
346:             * <p>
347:             * The default implementation do nothing.
348:             *
349:             * @param values The values to round in place.
350:             */
351:            protected void round(double[] values) {
352:            }
353:
354:            /**
355:             * Retourne les données de l'image à l'index spécifié. Si cette image avait déjà été lue, ses
356:             * données seront retournées immédiatement.  Sinon, cette image sera lue ainsi que toutes les
357:             * images qui précèdent {@code imageIndex} et qui n'avaient pas encore été lues. Que ces
358:             * images précédentes soient mémorisées ou oubliées dépend de {@link #seekForwardOnly}.
359:             *
360:             * @param  imageIndex Index de l'image à lire.
361:             * @return Les données de l'image. Cette méthode ne retourne jamais {@code null}.
362:             * @throws IOException si une erreur est survenue lors de la lecture du flot,
363:             *         ou si des nombres n'étaient pas correctement formatés dans le flot.
364:             * @throws IndexOutOfBoundsException si l'index spécifié est en dehors des
365:             *         limites permises ou si aucune image n'a été conservée à cet index.
366:             */
367:            private RecordList getRecords(final int imageIndex)
368:                    throws IOException {
369:                clearAbortRequest();
370:                checkImageIndex(imageIndex);
371:                if (imageIndex >= nextImageIndex) {
372:                    processImageStarted(imageIndex);
373:                    final BufferedReader reader = getReader();
374:                    final long origine = getStreamPosition(reader);
375:                    final long length = getStreamLength(nextImageIndex,
376:                            imageIndex + 1);
377:                    long nextProgressPosition = (origine >= 0 && length > 0) ? 0
378:                            : Long.MAX_VALUE;
379:                    for (; nextImageIndex <= imageIndex; nextImageIndex++) {
380:                        /*
381:                         * Réduit la consommation de mémoire des images précédentes. On ne réduit
382:                         * pas celle de l'image courante,  puisque la plupart du temps le tableau
383:                         * sera bientôt détruit de toute façon.
384:                         */
385:                        if (seekForwardOnly) {
386:                            minIndex = nextImageIndex;
387:                        }
388:                        if (nextImageIndex != 0 && data != null) {
389:                            final RecordList records = data[nextImageIndex - 1];
390:                            if (records != null) {
391:                                if (seekForwardOnly) {
392:                                    data[nextImageIndex - 1] = null;
393:                                } else {
394:                                    records.trimToSize();
395:                                }
396:                            }
397:                        }
398:                        /*
399:                         * Procède à la lecture de chacune des lignes de données. Que ces lignes
400:                         * soient mémorisées ou pas dépend de l'image que l'on est en train de
401:                         * décoder ainsi que de la valeur de {@link #seekForwardOnly}.
402:                         */
403:                        double[] values = null;
404:                        RecordList records = null;
405:                        final boolean keep = (nextImageIndex == imageIndex)
406:                                || !seekForwardOnly;
407:                        final int xColumn = getCheckedColumnX(nextImageIndex);
408:                        final int yColumn = getCheckedColumnY(nextImageIndex);
409:                        final double padValue = getPadValue(nextImageIndex);
410:                        final LineFormat lineFormat = getLineFormat(nextImageIndex);
411:                        try {
412:                            String line;
413:                            while ((line = reader.readLine()) != null) {
414:                                if (isComment(line)
415:                                        || lineFormat.setLine(line) == 0) {
416:                                    continue;
417:                                }
418:                                values = lineFormat.getValues(values);
419:                                for (int i = 0; i < values.length; i++) {
420:                                    if (i != xColumn && i != yColumn
421:                                            && values[i] == padValue) {
422:                                        values[i] = Double.NaN;
423:                                    }
424:                                }
425:                                round(values);
426:                                if (keep) {
427:                                    if (records == null) {
428:                                        final int expectedLineCount = Math
429:                                                .max(
430:                                                        8,
431:                                                        Math
432:                                                                .min(
433:                                                                        65536,
434:                                                                        Math
435:                                                                                .round(length
436:                                                                                        / (expectedDatumLength * values.length))));
437:                                        records = new RecordList(values.length,
438:                                                expectedLineCount);
439:                                    }
440:                                    records.add(values);
441:                                }
442:                                final long position = getStreamPosition(reader)
443:                                        - origine;
444:                                if (position >= nextProgressPosition) {
445:                                    processImageProgress(position
446:                                            * (100f / length));
447:                                    nextProgressPosition = position
448:                                            + PROGRESS_INTERVAL;
449:                                    if (abortRequested()) {
450:                                        processReadAborted();
451:                                        return records;
452:                                    }
453:                                }
454:                            }
455:                        } catch (ParseException exception) {
456:                            throw new IIOException(getPositionString(exception
457:                                    .getLocalizedMessage()), exception);
458:                        }
459:                        /*
460:                         * Après la lecture d'une image, vérifie s'il y avait un nombre suffisant de lignes.
461:                         * Une exception sera lancée si l'image ne contenait pas au moins deux lignes. On
462:                         * ajustera ensuite le nombre moyens de caractères par données.
463:                         */
464:                        if (records != null) {
465:                            final int lineCount = records.getLineCount();
466:                            if (lineCount < 2) {
467:                                throw new IIOException(
468:                                        getPositionString(Errors
469:                                                .format(ErrorKeys.FILE_HAS_TOO_FEW_DATA)));
470:                            }
471:                            if (data == null) {
472:                                data = new RecordList[imageIndex + 1];
473:                            } else if (data.length <= imageIndex) {
474:                                data = (RecordList[]) XArray.resize(data,
475:                                        imageIndex + 1);
476:                            }
477:                            data[nextImageIndex] = records;
478:                            final float meanDatumLength = (getStreamPosition(reader) - origine)
479:                                    / (float) records.getDataCount();
480:                            if (meanDatumLength > 0)
481:                                expectedDatumLength = meanDatumLength;
482:                        }
483:                    }
484:                    processImageComplete();
485:                }
486:                /*
487:                 * Une fois les lectures terminées, retourne les données de l'image
488:                 * demandée. Une exception sera lancée si ces données n'ont pas été
489:                 * conservées.
490:                 */
491:                if (data != null && imageIndex < data.length) {
492:                    final RecordList records = data[imageIndex];
493:                    if (records != null) {
494:                        return records;
495:                    }
496:                }
497:                throw new IndexOutOfBoundsException(String.valueOf(imageIndex));
498:            }
499:
500:            /**
501:             * Reads the image indexed by {@code imageIndex} and returns it as a complete buffered image.
502:             *
503:             * @param  imageIndex the index of the image to be retrieved.
504:             * @param  param Parameters used to control the reading process, or {@code null}.
505:             * @return the desired portion of the image.
506:             * @throws IOException if an error occurs during reading.
507:             */
508:            public BufferedImage read(final int imageIndex,
509:                    final ImageReadParam param) throws IOException {
510:                final float tolerance = getGridTolerance();
511:                final int xColumn = getCheckedColumnX(imageIndex);
512:                final int yColumn = getCheckedColumnY(imageIndex);
513:                final RecordList records = getRecords(imageIndex);
514:                final int width = records.getPointCount(xColumn, tolerance);
515:                final int height = records.getPointCount(yColumn, tolerance);
516:                final int numSrcBands = records.getColumnCount()
517:                        - (xColumn == yColumn ? 1 : 2);
518:                /*
519:                 * Extracts user's parameters
520:                 */
521:                final int[] srcBands;
522:                final int[] dstBands;
523:                final int sourceXSubsampling;
524:                final int sourceYSubsampling;
525:                final int subsamplingXOffset;
526:                final int subsamplingYOffset;
527:                final int destinationXOffset;
528:                final int destinationYOffset;
529:                if (param != null) {
530:                    srcBands = param.getSourceBands();
531:                    dstBands = param.getDestinationBands();
532:                    final Point offset = param.getDestinationOffset();
533:                    sourceXSubsampling = param.getSourceXSubsampling();
534:                    sourceYSubsampling = param.getSourceYSubsampling();
535:                    subsamplingXOffset = param.getSubsamplingXOffset();
536:                    subsamplingYOffset = param.getSubsamplingYOffset();
537:                    destinationXOffset = offset.x;
538:                    destinationYOffset = offset.y;
539:                } else {
540:                    srcBands = null;
541:                    dstBands = null;
542:                    sourceXSubsampling = 1;
543:                    sourceYSubsampling = 1;
544:                    subsamplingXOffset = 0;
545:                    subsamplingYOffset = 0;
546:                    destinationXOffset = 0;
547:                    destinationYOffset = 0;
548:                }
549:                /*
550:                 * Initializes...
551:                 */
552:                final int numDstBands = (dstBands != null) ? dstBands.length
553:                        : (srcBands != null) ? srcBands.length : numSrcBands;
554:                final BufferedImage image = getDestination(imageIndex, param,
555:                        width, height, null); // TODO
556:                checkReadParamBandSettings(param, numSrcBands, image
557:                        .getSampleModel().getNumBands());
558:
559:                final Rectangle srcRegion = new Rectangle();
560:                final Rectangle dstRegion = new Rectangle();
561:                computeRegions(param, width, height, image, srcRegion,
562:                        dstRegion);
563:                final int sourceXMin = srcRegion.x;
564:                final int sourceYMin = srcRegion.y;
565:                final int sourceXMax = srcRegion.width + sourceXMin;
566:                final int sourceYMax = srcRegion.height + sourceYMin;
567:
568:                final WritableRaster raster = image.getRaster();
569:                final int rasterWidth = raster.getWidth();
570:                final int rasterHeigth = raster.getHeight();
571:                final int columnCount = records.getColumnCount();
572:                final int dataCount = records.getDataCount();
573:                final float[] data = records.getData();
574:                final double xmin = records.getMinimum(xColumn);
575:                final double ymin = records.getMinimum(yColumn);
576:                final double xmax = records.getMaximum(xColumn);
577:                final double ymax = records.getMaximum(yColumn);
578:                final double scaleX = (width - 1) / (xmax - xmin);
579:                final double scaleY = (height - 1) / (ymax - ymin);
580:                /*
581:                 * Clears the image area. All values are set to NaN.
582:                 */
583:                if (CLEAR) {
584:                    final int minX = dstRegion.x;
585:                    final int minY = dstRegion.y;
586:                    final int maxX = dstRegion.width + minX;
587:                    final int maxY = dstRegion.height + minY;
588:                    for (int b = (dstBands != null) ? dstBands.length
589:                            : numDstBands; --b >= 0;) {
590:                        final int band = (dstBands != null) ? dstBands[b] : b;
591:                        for (int y = minY; y < maxY; y++) {
592:                            for (int x = minX; x < maxX; x++) {
593:                                raster.setSample(x, y, band, Float.NaN);
594:                            }
595:                        }
596:                    }
597:                }
598:                /*
599:                 * Computes column numbers corresponding to source bands,
600:                 * and start storing values into the image.
601:                 */
602:                final int[] columns = new int[(srcBands != null) ? srcBands.length
603:                        : numDstBands];
604:                for (int i = 0; i < columns.length; i++) {
605:                    columns[i] = getColumn(imageIndex,
606:                            srcBands != null ? srcBands[i] : i);
607:                }
608:                for (int i = 0; i < dataCount; i += columnCount) {
609:                    /*
610:                     * On convertit maintenant la coordonnée (x,y) logique en coordonnée pixel. Cette
611:                     * coordonnée pixel se réfère à l'image "source";  elle ne se réfère pas encore à
612:                     * l'image destination. Elle doit obligatoirement être entière. Plus loin, nous
613:                     * tiendrons compte du "subsampling".
614:                     */
615:                    final double fx = (data[i + xColumn] - xmin) * scaleX; // (fx,fy) may be NaN: Use
616:                    final double fy = (ymax - data[i + yColumn]) * scaleY; // "!(abs(...)<=tolerance)".
617:                    int x = (int) Math.round(fx); // This conversion is not the same than
618:                    int y = (int) Math.round(fy); // getTransform(), but it should be ok.
619:                    if (!(Math.abs(x - fx) <= tolerance)) {
620:                        fireBadCoordinate(data[i + xColumn]);
621:                        continue;
622:                    }
623:                    if (!(Math.abs(y - fy) <= tolerance)) {
624:                        fireBadCoordinate(data[i + yColumn]);
625:                        continue;
626:                    }
627:                    if (x >= sourceXMin && x < sourceXMax && y >= sourceYMin
628:                            && y < sourceYMax) {
629:                        x -= subsamplingXOffset;
630:                        y -= subsamplingYOffset;
631:                        if ((x % sourceXSubsampling) == 0
632:                                && (y % sourceYSubsampling) == 0) {
633:                            x = x / sourceXSubsampling
634:                                    + (destinationXOffset - sourceXMin);
635:                            y = y / sourceYSubsampling
636:                                    + (destinationYOffset - sourceYMin);
637:                            if (x < rasterWidth && y < rasterHeigth) {
638:                                for (int j = 0; j < columns.length; j++) {
639:                                    raster
640:                                            .setSample(
641:                                                    x,
642:                                                    y,
643:                                                    (dstBands != null ? dstBands[j]
644:                                                            : j), data[i
645:                                                            + columns[j]]);
646:                                }
647:                            }
648:                        }
649:                    }
650:                }
651:                return image;
652:            }
653:
654:            /**
655:             * Prévient qu'une coordonnée est mauvaise. Cette méthode est appelée lors de la lecture
656:             * s'il a été détecté qu'une coordonnée est en dehors des limites prévues, ou qu'elle ne
657:             * correspond pas à des coordonnées pixels entières.
658:             */
659:            private void fireBadCoordinate(final float coordinate) {
660:                processWarningOccurred(getPositionString(Errors.format(
661:                        ErrorKeys.BAD_COORDINATE_$1, new Float(coordinate))));
662:            }
663:
664:            /**
665:             * Supprime les données de toutes les images
666:             * qui avait été conservées en mémoire.
667:             */
668:            private void clear() {
669:                data = null;
670:                nextImageIndex = 0;
671:                expectedDatumLength = 10.4f;
672:            }
673:
674:            /**
675:             * Restores the {@code TextRecordImageReader} to its initial state.
676:             */
677:            //@Override
678:            public void reset() {
679:                clear();
680:                super .reset();
681:            }
682:
683:            /**
684:             * Service provider interface (SPI) for {@link TextRecordImageReader}s. This SPI provides
685:             * necessary implementation for creating default {@link TextRecordImageReader} using default
686:             * locale and character set. Subclasses can set some fields at construction time in order to
687:             * tune the reader to a particular environment, e.g.:
688:             *
689:             * <blockquote><pre>
690:             * public final class CLSImageReaderSpi extends TextRecordImageReader.Spi {
691:             *     public CLSImageReaderSpi() {
692:             *         {@link #names      names}      = new String[] {"CLS"};
693:             *         {@link #MIMETypes  MIMETypes}  = new String[] {"text/x-records-CLS"};
694:             *         {@link #vendorName vendorName} = "Institut de Recherche pour le Développement";
695:             *         {@link #version    version}    = "1.0";
696:             *         {@link #locale     locale}     = Locale.US;
697:             *         {@link #charset    charset}    = Charset.forName("ISO-LATIN-1");
698:             *         {@link #padValue   padValue}   = 9999;
699:             *     }
700:             * }
701:             * </pre></blockquote>
702:             *
703:             * (Note: fields {@code vendorName} and {@code version} are only informatives).
704:             * There is no need to override any method in this example. However, developers
705:             * can gain more control by creating subclasses of {@link TextRecordImageReader}
706:             * and {@code Spi}.
707:             *
708:             * @since 2.1
709:             * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/text/TextRecordImageReader.java $
710:             * @version $Id: TextRecordImageReader.java 27629 2007-10-26 09:59:20Z desruisseaux $
711:             * @author Martin Desruisseaux
712:             */
713:            public static class Spi extends TextImageReader.Spi {
714:                /**
715:                 * The format names for the default {@link TextRecordImageReader} configuration.
716:                 */
717:                private static final String[] NAMES = { "records" };
718:
719:                /**
720:                 * The mime types for the default {@link TextRecordImageReader} configuration.
721:                 */
722:                private static final String[] MIME_TYPES = { "text/x-records" };
723:
724:                /**
725:                 * 0-based column number for <var>x</var> values. The default value is 0.
726:                 *
727:                 * @see TextRecordImageReader#getColumnX
728:                 * @see TextRecordImageReader#parseLine
729:                 */
730:                protected int xColumn;
731:
732:                /**
733:                 * 0-based column number for <var>y</var> values. The default value is 1.
734:                 *
735:                 * @see TextRecordImageReader#getColumnY
736:                 * @see TextRecordImageReader#parseLine
737:                 */
738:                protected int yColumn;
739:
740:                /**
741:                 * A tolerance factor during decoding, between 0 and 1. During decoding,
742:                 * the image reader compute cell's width and height   (i.e. the smallest
743:                 * non-null difference between ordinates in a given column: <var>x</var>
744:                 * for cell's width and <var>y</var> for cell's height). Then, it checks
745:                 * if every coordinate points fall on a grid having this cell's size. If
746:                 * a point depart from more than {@code gridTolerance} percent of cell's
747:                 * width or height, an exception is thrown.
748:                 * <p>
749:                 * {@code gridTolerance} should be a small number like {@code 1E-5f}
750:                 * or {@code 1E-3f}. The later is more tolerant than the former.
751:                 */
752:                protected float gridTolerance = EPS;
753:
754:                /**
755:                 * Constructs a default {@code TextRecordImageReader.Spi}. This constructor
756:                 * provides the following defaults in addition to the defaults defined in the
757:                 * {@linkplain TextImageReader.Spi#Spi super-class constructor}:
758:                 *
759:                 * <ul>
760:                 *   <li>{@link #names}           = {@code "records"}</li>
761:                 *   <li>{@link #MIMETypes}       = {@code "text/x-records"}</li>
762:                 *   <li>{@link #pluginClassName} = {@code "org.geotools.image.io.text.TextRecordImageReader"}</li>
763:                 *   <li>{@link #vendorName}      = {@code "Geotools"}</li>
764:                 *   <li>{@link #xColumn}         = {@code 0}</li>
765:                 *   <li>{@link #yColumn}         = {@code 1}</li>
766:                 * </ul>
767:                 *
768:                 * For efficienty reasons, the above fields are initialized to shared arrays. Subclasses
769:                 * can assign new arrays, but should not modify the default array content.
770:                 */
771:                public Spi() {
772:                    names = NAMES;
773:                    MIMETypes = MIME_TYPES;
774:                    pluginClassName = "org.geotools.image.io.text.TextRecordImageReader";
775:                    vendorName = "Geotools";
776:                    version = "2.4";
777:                    xColumn = 0;
778:                    yColumn = 1;
779:                    gridTolerance = EPS;
780:                }
781:
782:                /**
783:                 * Returns a brief, human-readable description of this service provider
784:                 * and its associated implementation. The resulting string should be
785:                 * localized for the supplied locale, if possible.
786:                 *
787:                 * @param  locale A Locale for which the return value should be localized.
788:                 * @return A String containing a description of this service provider.
789:                 */
790:                public String getDescription(final Locale locale) {
791:                    return Descriptions.getResources(locale).getString(
792:                            DescriptionKeys.CODEC_GRID);
793:                }
794:
795:                /**
796:                 * Returns an instance of the ImageReader implementation associated
797:                 * with this service provider.
798:                 *
799:                 * @param  extension An optional extension object, which may be null.
800:                 * @return An image reader instance.
801:                 * @throws IOException if the attempt to instantiate the reader fails.
802:                 */
803:                public ImageReader createReaderInstance(final Object extension)
804:                        throws IOException {
805:                    return new TextRecordImageReader(this );
806:                }
807:
808:                /**
809:                 * Returns {@code true} if the specified row length is valid. The default implementation
810:                 * returns {@code true} if the row seems "short", where "short" is arbitrary fixed to 10
811:                 * columns. This is an arbitrary choice, which is why this method is not public. It may
812:                 * be changed in any future Geotools version.
813:                 */
814:                //@Override
815:                boolean isValidColumnCount(final int count) {
816:                    return count >= (xColumn == yColumn ? 2 : 3) && count <= 10;
817:                }
818:            }
819:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.