Source Code Cross Referenced for CategoryList.java in  » GIS » GeoTools-2.4.1 » org » geotools » coverage » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.coverage 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2003-2006, Geotools Project Management Committee (PMC)
0005:         *    (C) 2001, Institut de Recherche pour le Développement
0006:         *
0007:         *    This library is free software; you can redistribute it and/or
0008:         *    modify it under the terms of the GNU Lesser General Public
0009:         *    License as published by the Free Software Foundation; either
0010:         *    version 2.1 of the License, or (at your option) any later version.
0011:         *
0012:         *    This library is distributed in the hope that it will be useful,
0013:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015:         *    Lesser General Public License for more details.
0016:         *
0017:         *    This package contains documentation from OpenGIS specifications.
0018:         *    OpenGIS consortium's work is fully acknowledged here.
0019:         */
0020:        package org.geotools.coverage;
0021:
0022:        // J2SE dependencies and extensions
0023:        import java.awt.image.ColorModel;
0024:        import java.awt.image.DataBuffer;
0025:        import java.awt.image.RasterFormatException;
0026:        import java.awt.image.RenderedImage;
0027:        import java.io.IOException;
0028:        import java.io.ObjectInputStream;
0029:        import java.io.Serializable;
0030:        import java.util.AbstractList;
0031:        import java.util.Arrays;
0032:        import java.util.Comparator;
0033:        import java.util.Locale;
0034:        import javax.units.Unit;
0035:
0036:        // JAI dependencies
0037:        import javax.media.jai.iterator.WritableRectIter;
0038:
0039:        // OpenGIS dependencies
0040:        import org.opengis.referencing.operation.MathTransform;
0041:        import org.opengis.referencing.operation.MathTransform1D;
0042:        import org.opengis.referencing.operation.Matrix;
0043:        import org.opengis.referencing.operation.TransformException;
0044:        import org.opengis.geometry.DirectPosition;
0045:        import org.opengis.geometry.MismatchedDimensionException;
0046:        import org.opengis.util.InternationalString;
0047:
0048:        // Geotools dependencies
0049:        import org.geotools.geometry.GeneralDirectPosition;
0050:        import org.geotools.referencing.operation.matrix.Matrix1;
0051:        import org.geotools.referencing.wkt.UnformattableObjectException;
0052:        import org.geotools.resources.Utilities;
0053:        import org.geotools.resources.i18n.Errors;
0054:        import org.geotools.resources.i18n.ErrorKeys;
0055:        import org.geotools.resources.i18n.Vocabulary;
0056:        import org.geotools.resources.i18n.VocabularyKeys;
0057:        import org.geotools.util.AbstractInternationalString;
0058:        import org.geotools.util.NumberRange;
0059:
0060:        /**
0061:         * An immutable list of categories. Categories are sorted by their sample values.
0062:         * Overlapping ranges of sample values are not allowed. A {@code CategoryList} can
0063:         * contains a mix of qualitative and quantitative categories.  The {@link #getCategory}
0064:         * method is responsible for finding the right category for an arbitrary sample value.
0065:         *
0066:         * Instances of {@link CategoryList} are immutable and thread-safe.
0067:         *
0068:         * @since 2.1
0069:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/CategoryList.java $
0070:         * @version $Id: CategoryList.java 25449 2007-05-07 12:21:16Z desruisseaux $
0071:         * @author Martin Desruisseaux
0072:         */
0073:        class CategoryList extends AbstractList implements  MathTransform1D,
0074:                Comparator, Serializable {
0075:            /**
0076:             * Serial number for interoperability with different versions.
0077:             */
0078:            private static final long serialVersionUID = 2647846361059903365L;
0079:
0080:            /**
0081:             * The inverse transform, never {@code null}.
0082:             * The following rule must hold:
0083:             *
0084:             * <ul>
0085:             *   <li>If {@code this} is an instance of {@link CategoryList}, then
0086:             *       {@code inverse} must be an instance of {@link GeophysicsCategoryList}.</li>
0087:             *   <li>If {@code this} is an instance of {@link GeophysicsCategoryList}, then
0088:             *       {@code inverse} must be an instance of {@link CategoryList}.</li>
0089:             * </ul>
0090:             */
0091:            final CategoryList inverse;
0092:
0093:            /**
0094:             * The range of values in this category list. This is the union of the range of values
0095:             * of every categories, excluding {@code NaN} values. This field will be computed
0096:             * only when first requested.
0097:             */
0098:            private transient NumberRange range;
0099:
0100:            /**
0101:             * List of {@link Category#minimum} values for each category in {@link #categories}.
0102:             * This array <strong>must</strong> be in increasing order. Actually, this is the
0103:             * need to sort this array that determines the element order in {@link #categories}.
0104:             */
0105:            private final double[] minimums;
0106:
0107:            /**
0108:             * The list of categories to use for decoding samples. This list most be sorted
0109:             * in increasing order of {@link Category#minimum}.   This {@link CategoryList}
0110:             * object may be used as a {@link Comparator} for that.  Qualitative categories
0111:             * (with NaN values) are last.
0112:             */
0113:            private final Category[] categories;
0114:
0115:            /**
0116:             * The "main" category, or {@code null} if there is none. The main category
0117:             * is the quantitative category with the widest range of sample values.
0118:             */
0119:            private final Category main;
0120:
0121:            /**
0122:             * The "nodata" category (never {@code null}). The "nodata" category is a
0123:             * category mapping the geophysics {@link Double#NaN} value.  If none has been
0124:             * found, a default "nodata" category is used. This category is used to transform
0125:             * geophysics values to sample values into rasters when no suitable category has
0126:             * been found for a given geophysics value.
0127:             */
0128:            final Category nodata;
0129:
0130:            /**
0131:             * The category to use if {@link #getCategory(double)} is invoked  with  a sample value
0132:             * greater than all sample ranges in this category list. This is usually a reference to
0133:             * the last category to have a range of real values. A {@code null} value means that no
0134:             * fallback should be used. By extension, a {@code null} value also means that
0135:             * {@link #getCategory} should not try to find any fallback at all if the requested
0136:             * sample value do not falls in a category range.
0137:             */
0138:            private final Category overflowFallback;
0139:
0140:            /**
0141:             * The last used category. We assume that this category is the most likely
0142:             * to be requested in the next {@code transform(...)} invocation.
0143:             */
0144:            private transient Category last;
0145:
0146:            /**
0147:             * {@code true} if there is gaps between categories, or {@code false} otherwise.
0148:             * A gap is found if for example the range of value is [-9999 .. -9999] for the first
0149:             * category and [0 .. 1000] for the second one.
0150:             */
0151:            private final boolean hasGaps;
0152:
0153:            /**
0154:             * The name for this category list. Will be constructed only when first needed.
0155:             *
0156:             * @see #getName
0157:             */
0158:            private transient InternationalString name;
0159:
0160:            /**
0161:             * Construct a category list using the specified array of categories.
0162:             *
0163:             * @param  categories The list of categories.
0164:             * @param  units The geophysics unit, or {@code null} if none.
0165:             * @throws IllegalArgumentException if two or more categories
0166:             *         have overlapping sample value range.
0167:             */
0168:            public CategoryList(final Category[] categories, final Unit units)
0169:                    throws IllegalArgumentException {
0170:                this (categories, units, false, null);
0171:                assert isScaled(false);
0172:            }
0173:
0174:            /**
0175:             * Construct a category list using the specified array of categories.
0176:             *
0177:             *         <STRONG>This constructor is for internal use only</STRONG>
0178:             *
0179:             * It is not private only because {@link GeophysicsCategoryList} need this constructor.
0180:             *
0181:             * @param  categories The list of categories.
0182:             * @param  units The geophysics unit, or {@code null} if none.
0183:             * @param  searchNearest The policy when {@link #getCategory} doesn't find an exact match
0184:             *         for a sample value. {@code true} means that it should search for the nearest
0185:             *         category, while {@code false} means that it should returns {@code null}.
0186:             * @param  inverse The inverse transform, or {@code null} to build it automatically.
0187:             *         <STRONG>This argument can be non-null only if invoked from
0188:             *         {@link GeophysicsCategoryList} constructor</STRONG>.
0189:             * @throws IllegalArgumentException if two or more categories have overlapping sample value
0190:             *         range.
0191:             */
0192:            CategoryList(Category[] categories, Unit units,
0193:                    boolean searchNearest, CategoryList inverse)
0194:                    throws IllegalArgumentException {
0195:                /*
0196:                 * Check if we are constructing a geophysics category list,  then rescale all cagegories
0197:                 * according. We may loose the user intend by doing so (he may have specified explicitly
0198:                 * a list of GeophysicsCategory), but this is the SampleDimension's job to keep trace of
0199:                 * it.
0200:                 */
0201:                final boolean isGeophysics = (this  instanceof  GeophysicsCategoryList);
0202:                assert (inverse != null) == isGeophysics;
0203:                this .categories = categories = (Category[]) categories.clone();
0204:                for (int i = 0; i < categories.length; i++) {
0205:                    categories[i] = categories[i].geophysics(isGeophysics);
0206:                }
0207:                Arrays.sort(categories, this );
0208:                assert isSorted(categories);
0209:                assert isScaled(isGeophysics);
0210:                /*
0211:                 * Construct the array of Category.minimum values. During
0212:                 * the loop, we make sure there is no overlapping ranges.
0213:                 */
0214:                boolean hasGaps = false;
0215:                minimums = new double[categories.length];
0216:                for (int i = 0; i < categories.length; i++) {
0217:                    final double minimum = minimums[i] = categories[i].minimum;
0218:                    if (i != 0) {
0219:                        assert !(minimum < minimums[i - 1]) : minimum; // Use '!' to accept NaN.
0220:                        final Category previous = categories[i - 1];
0221:                        if (compare(minimum, previous.maximum) <= 0) {
0222:                            // Two categories have overlapping range;
0223:                            // Format an error message...............
0224:                            final NumberRange range1 = categories[i - 1]
0225:                                    .getRange();
0226:                            final NumberRange range2 = categories[i - 0]
0227:                                    .getRange();
0228:                            final Comparable[] args = new Comparable[] {
0229:                                    range1.getMinValue(), range1.getMaxValue(),
0230:                                    range2.getMinValue(), range2.getMaxValue() };
0231:                            for (int j = 0; j < args.length; j++) {
0232:                                if (args[j] instanceof  Number) {
0233:                                    final float value = ((Number) args[j])
0234:                                            .floatValue();
0235:                                    if (Float.isNaN(value)) {
0236:                                        String hex = Integer.toHexString(Float
0237:                                                .floatToRawIntBits(value));
0238:                                        args[j] = "NaN(" + hex + ')';
0239:                                    }
0240:                                }
0241:                            }
0242:                            throw new IllegalArgumentException(Errors.format(
0243:                                    ErrorKeys.RANGE_OVERLAP_$4, args));
0244:                        }
0245:                        // Check if there is a gap between this category and the previous one.
0246:                        if (!Double.isNaN(minimum)
0247:                                && minimum != previous.getRange().getMaximum(
0248:                                        false)) {
0249:                            hasGaps = true;
0250:                        }
0251:                    }
0252:                }
0253:                this .hasGaps = hasGaps;
0254:                /*
0255:                 * Search for the "nodata" category. This loop looks
0256:                 * for a qualitative category with the NaN value.
0257:                 */
0258:                Category nodata = Category.NODATA;
0259:                final long nodataBits = Double.doubleToRawLongBits(Double.NaN);
0260:                for (int i = categories.length; --i >= 0;) {
0261:                    final Category candidate = categories[i];
0262:                    final double value = candidate.geophysics(true).minimum;
0263:                    if (Double.isNaN(value)) {
0264:                        nodata = candidate;
0265:                        if (Double.doubleToRawLongBits(value) == nodataBits) {
0266:                            // Give a preference for the standard Double.NaN.
0267:                            // We should have only one such value, since the
0268:                            // range check above prevents range overlapping.
0269:                            break;
0270:                        }
0271:                    }
0272:                }
0273:                this .nodata = nodata;
0274:                /*
0275:                 * Search for what seems to be the "main" category. This loop looks for the
0276:                 * quantitative category (if there is one) with the widest range of sample values.
0277:                 */
0278:                double range = 0;
0279:                Category main = null;
0280:                for (int i = categories.length; --i >= 0;) {
0281:                    final Category candidate = categories[i];
0282:                    if (candidate.isQuantitative()) {
0283:                        final Category candidatePeer = candidate
0284:                                .geophysics(false);
0285:                        final double candidateRange = candidatePeer.maximum
0286:                                - candidatePeer.minimum;
0287:                        if (candidateRange >= range) {
0288:                            range = candidateRange;
0289:                            main = candidate;
0290:                        }
0291:                    }
0292:                }
0293:                this .main = main;
0294:                this .last = main;
0295:                /*
0296:                 * Search for the fallback if {@link #getCategory(double)} is invoked with a sample
0297:                 * value greater than all ranges of sample values. This is the last category to have
0298:                 * a range of real numbers.
0299:                 */
0300:                Category overflowFallback = null;
0301:                if (searchNearest) {
0302:                    for (int i = categories.length; --i >= 0;) {
0303:                        final Category category = categories[i];
0304:                        if (!Double.isNaN(category.maximum)) {
0305:                            overflowFallback = category;
0306:                            break;
0307:                        }
0308:                    }
0309:                }
0310:                this .overflowFallback = overflowFallback;
0311:                /*
0312:                 * Set the inverse transform. If no inverse transform has been explicitly specified, then
0313:                 * this is the "normal" construction call (i.e. not the special construction performed by
0314:                 * GeophysicsCategoryList) and we create our internal inverse object.
0315:                 */
0316:                if (inverse == null) {
0317:                    inverse = new GeophysicsCategoryList(categories, units,
0318:                            this );
0319:                }
0320:                this .inverse = inverse;
0321:                assert (this  instanceof  GeophysicsCategoryList) != (inverse instanceof  GeophysicsCategoryList);
0322:            }
0323:
0324:            /**
0325:             * Compare {@link Category} objects according their {@link Category#minimum} value.
0326:             * This is used for sorting the {@link #categories} array at construction time.
0327:             */
0328:            public final int compare(final Object o1, final Object o2) {
0329:                return compare(((Category) o1).minimum, ((Category) o2).minimum);
0330:            }
0331:
0332:            /**
0333:             * Compare deux valeurs de type {@code double}. Cette méthode
0334:             * est similaire à {@link Double#compare(double,double)}, excepté
0335:             * qu'elle ordonne aussi les différentes valeurs NaN.
0336:             */
0337:            private static int compare(final double v1, final double v2) {
0338:                if (Double.isNaN(v1) && Double.isNaN(v2)) {
0339:                    final long bits1 = Double.doubleToRawLongBits(v1);
0340:                    final long bits2 = Double.doubleToRawLongBits(v2);
0341:                    if (bits1 < bits2)
0342:                        return -1;
0343:                    if (bits1 > bits2)
0344:                        return +1;
0345:                }
0346:                return Double.compare(v1, v2);
0347:            }
0348:
0349:            /**
0350:             * Vérifie si le tableau de catégories spécifié est bien en ordre croissant.
0351:             * La comparaison ne tient pas compte des valeurs {@code NaN}. Cette
0352:             * méthode n'est utilisée que pour les {@code assert}.
0353:             */
0354:            static boolean isSorted(final Category[] categories) {
0355:                for (int i = 1; i < categories.length; i++) {
0356:                    Category c;
0357:                    assert !((c = categories[i - 0]).minimum > c.maximum) : c;
0358:                    assert !((c = categories[i - 1]).minimum > c.maximum) : c;
0359:                    if (compare(categories[i - 1].maximum,
0360:                            categories[i].minimum) > 0) {
0361:                        return false;
0362:                    }
0363:                }
0364:                return true;
0365:            }
0366:
0367:            /**
0368:             * Effectue une recherche bi-linéaire de la valeur spécifiée. Cette
0369:             * méthode est semblable à {@link Arrays#binarySearch(double[],double)},
0370:             * excepté qu'elle peut distinguer différentes valeurs de NaN.
0371:             *
0372:             * Note: This method is not private in order to allows testing by {@link CategoryTest}.
0373:             */
0374:            static int binarySearch(final double[] array, final double key) {
0375:                int low = 0;
0376:                int high = array.length - 1;
0377:                final boolean keyIsNaN = Double.isNaN(key);
0378:                while (low <= high) {
0379:                    final int mid = (low + high) >> 1;
0380:                    final double midVal = array[mid];
0381:                    if (midVal < key) { // Neither val is NaN, midVal is smaller
0382:                        low = mid + 1;
0383:                        continue;
0384:                    }
0385:                    if (midVal > key) { // Neither val is NaN, midVal is larger
0386:                        high = mid - 1;
0387:                        continue;
0388:                    }
0389:                    /*
0390:                     * The following is an adaptation of evaluator's comments for bug #4471414
0391:                     * (http://developer.java.sun.com/developer/bugParade/bugs/4471414.html).
0392:                     * Extract from evaluator's comment:
0393:                     *
0394:                     *     [This] code is not guaranteed to give the desired results because
0395:                     *     of laxity in IEEE 754 regarding NaN values. There are actually two
0396:                     *     types of NaNs, signaling NaNs and quiet NaNs. Java doesn't support
0397:                     *     the features necessary to reliably distinguish the two.  However,
0398:                     *     the relevant point is that copying a signaling NaN may (or may not,
0399:                     *     at the implementors discretion) yield a quiet NaN -- a NaN with a
0400:                     *     different bit pattern (IEEE 754 6.2).  Therefore, on IEEE 754 compliant
0401:                     *     platforms it may be impossible to find a signaling NaN stored in an
0402:                     *     array since a signaling NaN passed as an argument to binarySearch may
0403:                     *     get replaced by a quiet NaN.
0404:                     */
0405:                    final long midRawBits = Double.doubleToRawLongBits(midVal);
0406:                    final long keyRawBits = Double.doubleToRawLongBits(key);
0407:                    if (midRawBits == keyRawBits) {
0408:                        return mid; // key found
0409:                    }
0410:                    final boolean midIsNaN = Double.isNaN(midVal);
0411:                    final boolean adjustLow;
0412:                    if (keyIsNaN) {
0413:                        // If (mid,key)==(!NaN, NaN): mid is lower.
0414:                        // If two NaN arguments, compare NaN bits.
0415:                        adjustLow = (!midIsNaN || midRawBits < keyRawBits);
0416:                    } else {
0417:                        // If (mid,key)==(NaN, !NaN): mid is greater.
0418:                        // Otherwise, case for (-0.0, 0.0) and (0.0, -0.0).
0419:                        adjustLow = (!midIsNaN && midRawBits < keyRawBits);
0420:                    }
0421:                    if (adjustLow)
0422:                        low = mid + 1;
0423:                    else
0424:                        high = mid - 1;
0425:                }
0426:                return -(low + 1); // key not found.
0427:            }
0428:
0429:            /**
0430:             * If {@code toGeophysics} is {@code true}, returns a list of categories scaled
0431:             * to geophysics values. This method always returns a list of categories in which
0432:             * <code>{@link Category#geophysics(boolean) Category.geophysics}(toGeophysics)</code>
0433:             * has been invoked for each category.
0434:             */
0435:            public CategoryList geophysics(final boolean toGeophysics) {
0436:                final CategoryList scaled = toGeophysics ? inverse : this ;
0437:                assert scaled.isScaled(toGeophysics);
0438:                return scaled;
0439:            }
0440:
0441:            /**
0442:             * Verify if all categories are scaled to the specified state.
0443:             * This is used mostly in assertion statements.
0444:             *
0445:             * @param  toGeophysics The state to test.
0446:             * @return {@code true} if all categories are in the specified state.
0447:             */
0448:            final boolean isScaled(final boolean toGeophysics) {
0449:                return isScaled(categories, toGeophysics);
0450:            }
0451:
0452:            /**
0453:             * Verify if all categories are scaled to the specified state.
0454:             *
0455:             * @param  categories The categories to test.
0456:             * @param  toGeophysics The state to test.
0457:             * @return {@code true} if all categories are in the specified state.
0458:             */
0459:            static boolean isScaled(final Category[] categories,
0460:                    final boolean toGeophysics) {
0461:                for (int i = 0; i < categories.length; i++) {
0462:                    final Category c = categories[i];
0463:                    if (c.geophysics(toGeophysics) != c) {
0464:                        return false;
0465:                    }
0466:                }
0467:                return true;
0468:            }
0469:
0470:            /**
0471:             * Returns the name of this object. The default implementation returns the name
0472:             * of what seems to be the "main" category (i.e. the quantitative category with
0473:             * the widest range of sample values) followed by the geophysics value range.
0474:             */
0475:            public final InternationalString getName() {
0476:                if (name == null) {
0477:                    name = new Name();
0478:                }
0479:                return name;
0480:            }
0481:
0482:            /**
0483:             * The name for this category list. Will be created only when first needed.
0484:             */
0485:            private final class Name extends AbstractInternationalString {
0486:                /** Returns the name in the specified locale. */
0487:                public String toString(final Locale locale) {
0488:                    final StringBuffer buffer = new StringBuffer(30);
0489:                    if (main != null) {
0490:                        buffer.append(main.getName().toString(locale));
0491:                    } else {
0492:                        buffer.append('(');
0493:                        buffer.append(Vocabulary.getResources(locale)
0494:                                .getString(VocabularyKeys.UNTITLED));
0495:                        buffer.append(')');
0496:                    }
0497:                    buffer.append(' ');
0498:                    return String.valueOf(geophysics(true).formatRange(buffer,
0499:                            locale));
0500:                }
0501:
0502:                /** Returns the name in the default locale. */
0503:                public String toString() {
0504:                    return toString(Locale.getDefault());
0505:                }
0506:            }
0507:
0508:            /**
0509:             * Returns the unit information for quantitative categories in this list. May returns
0510:             * {@code null} if there is no quantitative categories in this list, or if there is no
0511:             * unit information.
0512:             * <p>
0513:             * This method is to be overridden by {@link GeophysicsCategoryList}. The default implementation
0514:             * returns {@code null} since sample values are not geophysics values as long as they have not
0515:             * been transformed. The {@link GridSampleDimension} class will invoke
0516:             * {@code geophysics(true).getUnits()} in order to get a non-null unit.
0517:             */
0518:            public Unit getUnits() {
0519:                return null;
0520:            }
0521:
0522:            /**
0523:             * Returns the range of values in this category list. This is the union of the range
0524:             * of values of every categories, excluding {@code NaN} values. A {@link NumberRange}
0525:             * object give more informations than {@link org.opengis.CV_SampleDimension#getMinimum}
0526:             * and {@link org.opengis.CV_SampleDimension#getMaximum} since it contains also the
0527:             * type (integer, float, etc.) and inclusion/exclusion informations.
0528:             *
0529:             * @return The range of values. May be {@code null} if this category list has no
0530:             *         quantitative category.
0531:             *
0532:             * @see Category#getRange
0533:             */
0534:            public final NumberRange getRange() {
0535:                if (range == null) {
0536:                    NumberRange range = null;
0537:                    for (int i = 0; i < categories.length; i++) {
0538:                        final NumberRange extent = categories[i].getRange();
0539:                        if (!Double.isNaN(extent.getMinimum())
0540:                                && !Double.isNaN(extent.getMaximum())) {
0541:                            if (range != null) {
0542:                                range = NumberRange.wrap(range.union(extent));
0543:                            } else {
0544:                                range = extent;
0545:                            }
0546:                        }
0547:                    }
0548:                    this .range = range;
0549:                }
0550:                return range;
0551:            }
0552:
0553:            /**
0554:             * Format the range of geophysics values.
0555:             *
0556:             * @param  buffer The buffer where to write the range of geophysics values.
0557:             * @param  locale The locale to use for formatting numbers.
0558:             * @return The {@code buffer} for convenience.
0559:             */
0560:            private StringBuffer formatRange(StringBuffer buffer,
0561:                    final Locale locale) {
0562:                final NumberRange range = getRange();
0563:                buffer.append('[');
0564:                if (range != null) {
0565:                    buffer = format(range.getMinimum(), false, locale, buffer);
0566:                    buffer.append(" ... ");
0567:                    buffer = format(range.getMaximum(), true, locale, buffer);
0568:                } else {
0569:                    final Unit unit = getUnits();
0570:                    if (unit != null) {
0571:                        buffer.append(unit);
0572:                    }
0573:                }
0574:                buffer.append(']');
0575:                return buffer;
0576:            }
0577:
0578:            /**
0579:             * Format the specified value using the specified locale convention. This method is to be
0580:             * overridden by {@link GeophysicsCategoryList}. The default implementation do not format
0581:             * the value very properly, since most invocation will be done on
0582:             * {@code geophysics(true).format(...)} anyway.
0583:             *
0584:             * @param  value The value to format.
0585:             * @param  writeUnit {@code true} if unit symbol should be formatted after the number.
0586:             *         Ignored if this category list has no unit.
0587:             * @param  locale The locale, or {@code null} for a default one.
0588:             * @param  buffer The buffer where to format.
0589:             * @return The buffer {@code buffer} for convenience.
0590:             */
0591:            StringBuffer format(final double value, final boolean writeUnits,
0592:                    final Locale locale, StringBuffer buffer) {
0593:                return buffer.append(value);
0594:            }
0595:
0596:            /**
0597:             * Returns a color model for this category list. This method builds up the color model
0598:             * from each category's colors (as returned by {@link Category#getColors}).
0599:             *
0600:             * @param  visibleBand The band to be made visible (usually 0). All other bands, if any
0601:             *         will be ignored.
0602:             * @param  numBands The number of bands for the color model (usually 1). The returned color
0603:             *         model will renderer only the {@code visibleBand} and ignore the others, but
0604:             *         the existence of all {@code numBands} will be at least tolerated. Supplemental
0605:             *         bands, even invisible, are useful for processing with Java Advanced Imaging.
0606:             * @return The requested color model, suitable for {@link RenderedImage} objects with values
0607:             *         in the <code>{@link #getRange}</code> range.
0608:             */
0609:            public final ColorModel getColorModel(final int visibleBand,
0610:                    final int numBands) {
0611:                int type = DataBuffer.TYPE_FLOAT;
0612:                final NumberRange range = getRange();
0613:                final Class rt = range.getElementClass();
0614:                if (Byte.class.equals(rt) || Short.class.equals(rt)
0615:                        || Integer.class.equals(rt)) {
0616:                    final int min = ((Number) range.getMinValue()).intValue();
0617:                    final int max = ((Number) range.getMaxValue()).intValue();
0618:                    if (min >= 0) {
0619:                        if (max < 0x100) {
0620:                            type = DataBuffer.TYPE_BYTE;
0621:                        } else if (max < 0x10000) {
0622:                            type = DataBuffer.TYPE_USHORT;
0623:                        } else {
0624:                            type = DataBuffer.TYPE_INT;
0625:                        }
0626:                    } else if (min >= Short.MIN_VALUE && max <= Short.MAX_VALUE) {
0627:                        type = DataBuffer.TYPE_SHORT;
0628:                    } else {
0629:                        type = DataBuffer.TYPE_INT;
0630:                    }
0631:                }
0632:                return ColorModelFactory.getColorModel(categories, type,
0633:                        visibleBand, numBands);
0634:            }
0635:
0636:            /**
0637:             * Returns a color model for this category list. This method builds up the color model
0638:             * from each category's colors (as returned by {@link Category#getColors}).
0639:             *
0640:             * @param  visibleBand The band to be made visible (usually 0). All other bands, if any
0641:             *         will be ignored.
0642:             * @param  numBands The number of bands for the color model (usually 1). The returned color
0643:             *         model will renderer only the {@code visibleBand} and ignore the others, but
0644:             *         the existence of all {@code numBands} will be at least tolerated. Supplemental
0645:             *         bands, even invisible, are useful for processing with Java Advanced Imaging.
0646:             * @param  type The transfer type used in the sample model
0647:             * @return The requested color model, suitable for {@link RenderedImage} objects with values
0648:             *         in the <code>{@link #getRange}</code> range.
0649:             */
0650:            public final ColorModel getColorModel(final int visibleBand,
0651:                    final int numBands, final int type) {
0652:                return ColorModelFactory.getColorModel(categories, type,
0653:                        visibleBand, numBands);
0654:            }
0655:
0656:            /**
0657:             * Returns the category of the specified sample value.
0658:             * If no category fits, then this method returns {@code null}.
0659:             *
0660:             * @param  sample The value.
0661:             * @return The category of the supplied value, or {@code null}.
0662:             */
0663:            public final Category getCategory(final double sample) {
0664:                /*
0665:                 * Recherche à quelle catégorie pourrait appartenir la valeur.
0666:                 * Note: Les valeurs 'NaN' sont à la fin du tableau 'values'. Donc:
0667:                 *
0668:                 * 1) Si 'value' est NaN,  alors 'i' pointera forcément sur une catégorie NaN.
0669:                 * 2) Si 'value' est réel, alors 'i' peut pointer sur une des catégories de
0670:                 *    valeurs réels ou sur la première catégorie de NaN.
0671:                 */
0672:                int i = binarySearch(minimums, sample); // Special 'binarySearch' for NaN
0673:                if (i >= 0) {
0674:                    // The value is exactly equals to one of Category.minimum,
0675:                    // or is one of NaN values. There is nothing else to do.
0676:                    assert Double.doubleToRawLongBits(sample) == Double
0677:                            .doubleToRawLongBits(minimums[i]);
0678:                    return categories[i];
0679:                }
0680:                if (Double.isNaN(sample)) {
0681:                    // The value is NaN, but not one of the registered ones.
0682:                    // Consequently, we can't map a category to this value.
0683:                    return null;
0684:                }
0685:                assert i == Arrays.binarySearch(minimums, sample) : i;
0686:                // 'binarySearch' found the index of "insertion point" (~i). This means that
0687:                // 'sample' is lower than 'Category.minimum' at this index. Consequently, if
0688:                // this value fits in a category's range, it fits in the previous category (~i-1).
0689:                i = ~i - 1;
0690:                if (i >= 0) {
0691:                    final Category category = categories[i];
0692:                    assert sample > category.minimum : sample;
0693:                    if (sample <= category.maximum) {
0694:                        return category;
0695:                    }
0696:                    if (overflowFallback != null) {
0697:                        if (++i < categories.length) {
0698:                            final Category upper = categories[i];
0699:                            // ASSERT: if 'upper.minimum' was smaller than 'value', it should has been
0700:                            //         found by 'binarySearch'. We use '!' in order to accept NaN values.
0701:                            assert !(upper.minimum <= sample) : sample;
0702:                            return (upper.minimum - sample < sample
0703:                                    - category.maximum) ? upper : category;
0704:                        }
0705:                        return overflowFallback;
0706:                    }
0707:                } else if (overflowFallback != null) {
0708:                    // If the value is smaller than the smallest Category.minimum, returns
0709:                    // the first category (except if there is only NaN categories).
0710:                    if (categories.length != 0) {
0711:                        final Category category = categories[0];
0712:                        if (!Double.isNaN(category.minimum)) {
0713:                            return category;
0714:                        }
0715:                    }
0716:                }
0717:                return null;
0718:            }
0719:
0720:            /**
0721:             * Format a sample value. If {@code value} is a real number, then the value may
0722:             * be formatted with the appropriate number of digits and the units symbol. Otherwise,
0723:             * if {@code value} is {@code NaN}, then the category name is returned.
0724:             *
0725:             * @param  value  The sample value (may be {@code NaN}).
0726:             * @param  locale Locale to use for formatting, or {@code null} for the default locale.
0727:             * @return A string representation of the sample value.
0728:             */
0729:            public final String format(final double value, final Locale locale) {
0730:                if (Double.isNaN(value)) {
0731:                    Category category = last;
0732:                    if (!(value >= category.minimum && value <= category.maximum)
0733:                            && Double.doubleToRawLongBits(value) != Double
0734:                                    .doubleToRawLongBits(category.minimum)) {
0735:                        category = getCategory(value);
0736:                        if (category == null) {
0737:                            return Vocabulary.getResources(locale).getString(
0738:                                    VocabularyKeys.UNTITLED);
0739:                        }
0740:                        last = category;
0741:                    }
0742:                    return category.getName().toString(null);
0743:                }
0744:                return format(value, true, locale, new StringBuffer())
0745:                        .toString();
0746:            }
0747:
0748:            //////////////////////////////////////////////////////////////////////////////////////////
0749:            ////////                                                                          ////////
0750:            ////////       I M P L E M E N T A T I O N   O F   List   I N T E R F A C E       ////////
0751:            ////////                                                                          ////////
0752:            //////////////////////////////////////////////////////////////////////////////////////////
0753:            /**
0754:             * Returns the number of categories in this list.
0755:             */
0756:            public final int size() {
0757:                return categories.length;
0758:            }
0759:
0760:            /**
0761:             * Returns the element at the specified position in this list.
0762:             */
0763:            public final Object get(final int i) {
0764:                return categories[i];
0765:            }
0766:
0767:            /**
0768:             * Returns all categories in this {@code CategoryList}.
0769:             */
0770:            public final Object[] toArray() {
0771:                return (Category[]) categories.clone();
0772:            }
0773:
0774:            /**
0775:             * Returns a string representation of this category list.
0776:             * The returned string is implementation dependent.
0777:             * It is usually provided for debugging purposes only.
0778:             */
0779:            public final String toString() {
0780:                return toString(this );
0781:            }
0782:
0783:            /**
0784:             * Returns a string representation of this category list. The {@code owner}
0785:             * argument allow for a different class name to be formatted.
0786:             */
0787:            final String toString(final Object owner) {
0788:                final String lineSeparator = System.getProperty(
0789:                        "line.separator", "\n");
0790:                StringBuffer buffer = new StringBuffer(Utilities
0791:                        .getShortClassName(owner));
0792:                buffer = formatRange(buffer, null);
0793:                if (hasGaps) {
0794:                    buffer.append(" with gaps");
0795:                }
0796:                buffer.append(lineSeparator);
0797:                /*
0798:                 * Ecrit la liste des catégories en dessous.
0799:                 */
0800:                for (int i = 0; i < categories.length; i++) {
0801:                    buffer.append("   ");
0802:                    buffer.append(categories[i] == main ? '*' : ' ');
0803:                    buffer.append(categories[i]);
0804:                    buffer.append(lineSeparator);
0805:                }
0806:                return buffer.toString();
0807:            }
0808:
0809:            /**
0810:             * Compares the specified object with this category list for equality.
0811:             * If the two objects are instances of {@link CategoryList}, then the
0812:             * test is a little bit stricter than the default {@link AbstractList#equals}.
0813:             */
0814:            public boolean equals(final Object object) {
0815:                if (object instanceof  CategoryList) {
0816:                    final CategoryList that = (CategoryList) object;
0817:                    if (Arrays.equals(this .categories, that.categories)) {
0818:                        assert Arrays.equals(this .minimums, that.minimums);
0819:                        return Utilities.equals(this .overflowFallback,
0820:                                that.overflowFallback);
0821:                    }
0822:                    return false;
0823:                }
0824:                return (overflowFallback == null) && super .equals(object);
0825:            }
0826:
0827:            /**
0828:             * Reset the {@link #last} field to a non-null value after deserialization.
0829:             */
0830:            private void readObject(final ObjectInputStream in)
0831:                    throws IOException, ClassNotFoundException {
0832:                in.defaultReadObject();
0833:                last = main;
0834:            }
0835:
0836:            ///////////////////////////////////////////////////////////////////////////////////////////////
0837:            ////////                                                                               ////////
0838:            ////////    I M P L E M E N T A T I O N   O F   MathTransform1D   I N T E R F A C E    ////////
0839:            ////////                                                                               ////////
0840:            ///////////////////////////////////////////////////////////////////////////////////////////////
0841:            /**
0842:             * Gets the dimension of input points, which is 1.
0843:             */
0844:            public final int getSourceDimensions() {
0845:                return 1;
0846:            }
0847:
0848:            /**
0849:             * Gets the dimension of output points, which is 1.
0850:             */
0851:            public final int getTargetDimensions() {
0852:                return 1;
0853:            }
0854:
0855:            /**
0856:             * Tests whether this transform does not move any points.
0857:             */
0858:            public boolean isIdentity() {
0859:                return false;
0860:            }
0861:
0862:            /**
0863:             * Returns the inverse transform of this object.
0864:             */
0865:            public final MathTransform inverse() {
0866:                return inverse;
0867:            }
0868:
0869:            /**
0870:             * Ensure the specified point is one-dimensional.
0871:             */
0872:            private static void checkDimension(final DirectPosition point) {
0873:                final int dim = point.getDimension();
0874:                if (dim != 1) {
0875:                    throw new MismatchedDimensionException(Errors.format(
0876:                            ErrorKeys.MISMATCHED_DIMENSION_$2, new Integer(1),
0877:                            new Integer(dim)));
0878:                }
0879:            }
0880:
0881:            /**
0882:             * Transforms the specified {@code ptSrc} and stores the result in {@code ptDst}.
0883:             */
0884:            public final DirectPosition transform(final DirectPosition ptSrc,
0885:                    DirectPosition ptDst) throws TransformException {
0886:                checkDimension(ptSrc);
0887:                if (ptDst == null) {
0888:                    ptDst = new GeneralDirectPosition(1);
0889:                } else {
0890:                    checkDimension(ptDst);
0891:                }
0892:                ptDst.setOrdinate(0, transform(ptSrc.getOrdinate(0)));
0893:                return ptDst;
0894:            }
0895:
0896:            /**
0897:             * Gets the derivative of this transform at a point.
0898:             */
0899:            public final Matrix derivative(final DirectPosition point)
0900:                    throws TransformException {
0901:                checkDimension(point);
0902:                return new Matrix1(derivative(point.getOrdinate(0)));
0903:            }
0904:
0905:            /**
0906:             * Gets the derivative of this function at a value.
0907:             *
0908:             * @param  value The value where to evaluate the derivative.
0909:             * @return The derivative at the specified point.
0910:             * @throws TransformException if the derivative can't be evaluated at the specified point.
0911:             */
0912:            public final double derivative(final double value)
0913:                    throws TransformException {
0914:                Category category = last;
0915:                if (!(value >= category.minimum && value <= category.maximum)
0916:                        && Double.doubleToRawLongBits(value) != Double
0917:                                .doubleToRawLongBits(category.minimum)) {
0918:                    category = getCategory(value);
0919:                    if (category == null) {
0920:                        throw new TransformException(Errors.format(
0921:                                ErrorKeys.NO_CATEGORY_FOR_VALUE_$1, new Double(
0922:                                        value)));
0923:                    }
0924:                    last = category;
0925:                }
0926:                return category.transform.derivative(value);
0927:            }
0928:
0929:            /**
0930:             * Transforms the specified value.
0931:             *
0932:             * @param value The value to transform.
0933:             * @return the transformed value.
0934:             * @throws TransformException if the value can't be transformed.
0935:             */
0936:            public final double transform(double value)
0937:                    throws TransformException {
0938:                Category category = last;
0939:                if (!(value >= category.minimum && value <= category.maximum)
0940:                        && Double.doubleToRawLongBits(value) != Double
0941:                                .doubleToRawLongBits(category.minimum)) {
0942:                    category = getCategory(value);
0943:                    if (category == null) {
0944:                        throw new TransformException(Errors.format(
0945:                                ErrorKeys.NO_CATEGORY_FOR_VALUE_$1, new Double(
0946:                                        value)));
0947:                    }
0948:                    last = category;
0949:                }
0950:                value = category.transform.transform(value);
0951:                if (overflowFallback != null) {
0952:                    if (value < category.inverse.minimum)
0953:                        return category.inverse.minimum;
0954:                    if (value > category.inverse.maximum)
0955:                        return category.inverse.maximum;
0956:                }
0957:                assert category == inverse.getCategory(value).inverse : category;
0958:                return value;
0959:            }
0960:
0961:            /**
0962:             * Transforms a list of coordinate point ordinal values. This implementation can work on
0963:             * either float or double arrays, since the quasi-totality of the implementation is the
0964:             * same. Locale variables still {@code double} because this is the type used in
0965:             * {@link Category} objects.
0966:             *
0967:             * @todo We could add an optimisation after the loops checking for category change:
0968:             *       if we were allowed to search for nearest category (overflowFallback!=null),
0969:             *       then make sure that the category really changed. There is already a slight
0970:             *       optimization for the most common cases, but maybe we could go a little bit
0971:             *       further.
0972:             */
0973:            private void transform(final double[] srcPts,
0974:                    final float[] srcFloat, int srcOff, final double[] dstPts,
0975:                    final float[] dstFloat, int dstOff, int numPts,
0976:                    final boolean doublePrecision) throws TransformException {
0977:                final int srcToDst = dstOff - srcOff;
0978:                Category category = last;
0979:                double maximum = category.maximum;
0980:                double minimum = category.minimum;
0981:                long rawBits = Double.doubleToRawLongBits(minimum);
0982:                final int direction;
0983:                if (srcPts != dstPts || srcOff >= dstOff) {
0984:                    direction = +1;
0985:                } else {
0986:                    direction = -1;
0987:                    dstOff += numPts - 1;
0988:                    srcOff += numPts - 1;
0989:                }
0990:                /*
0991:                 * Scan every points. Transforms will be performed by blocks, each time
0992:                 * the loop detects that the category has changed. The break point is near
0993:                 * the end of the loop, after we have done the transformation but before
0994:                 * to change category.
0995:                 */
0996:                for (int peekOff = srcOff; true; peekOff += direction) {
0997:                    // NOTE: We do not need to setup 'value' since we are not going to use it if
0998:                    //       numPts<0.  Unfortunatly, the compiler flow analysis doesn't seem to
0999:                    //       be sophesticated enough to detect this case. So we have to set a dummy
1000:                    //       value in order to avoid compiler error.
1001:                    double value = 0;
1002:                    if (doublePrecision) { // Optimized loop for the 'double' version
1003:                        while (--numPts >= 0) {
1004:                            value = srcPts[peekOff];
1005:                            if ((value >= minimum && value <= maximum)
1006:                                    || Double.doubleToRawLongBits(value) == rawBits) {
1007:                                peekOff += direction;
1008:                                continue;
1009:                            }
1010:                            break; // The category has changed. Stop the search.
1011:                        }
1012:                    } else {
1013:                        while (--numPts >= 0) { // Optimized loop for the 'float' version
1014:                            value = srcFloat[peekOff];
1015:                            if ((value >= minimum && value <= maximum)
1016:                                    || Double.doubleToRawLongBits(value) == rawBits) {
1017:                                peekOff += direction;
1018:                                continue;
1019:                            }
1020:                            break; // The category has changed. Stop the search.
1021:                        }
1022:                    }
1023:                    if (overflowFallback != null) {
1024:                        // TODO: Slight optimization. We could go further by checking if 'value' is closer
1025:                        //       to this category than to the previous category or the next category.  But
1026:                        //       we may need the category index, and binarySearch is a costly operation...
1027:                        if (value > maximum && category == overflowFallback) {
1028:                            continue;
1029:                        }
1030:                        if (value < minimum && category == categories[0]) {
1031:                            continue;
1032:                        }
1033:                    }
1034:                    /*
1035:                     * The category has changed. Compute the start point (which depends of 'direction')
1036:                     * and performs the transformation. If 'getCategory' was allowed to search for the
1037:                     * nearest category, clamp all output values in their category range.
1038:                     */
1039:                    int count = peekOff - srcOff; // May be negative if we are going backward.
1040:                    if (count < 0) {
1041:                        count = -count;
1042:                        srcOff -= count - 1;
1043:                    }
1044:                    if (doublePrecision) { // Optimized loop for the 'double' version.
1045:                        category.transform.transform(srcPts, srcOff, dstPts,
1046:                                srcOff + srcToDst, count);
1047:                        if (overflowFallback != null) {
1048:                            dstOff = srcOff + srcToDst;
1049:                            final double min = category.inverse.minimum;
1050:                            final double max = category.inverse.maximum;
1051:                            while (--count >= 0) { // Optimized loop for the 'double' version.
1052:                                final double check = dstPts[dstOff];
1053:                                if (check < min) {
1054:                                    dstPts[dstOff] = min;
1055:                                } else if (check > max) {
1056:                                    dstPts[dstOff] = max;
1057:                                }
1058:                                dstOff++;
1059:                            }
1060:                        }
1061:                    } else { // Optimized loop for the 'float' version.
1062:                        category.transform.transform(srcFloat, srcOff,
1063:                                dstFloat, srcOff + srcToDst, count);
1064:                        if (overflowFallback != null) {
1065:                            dstOff = srcOff + srcToDst;
1066:                            final float min = (float) category.inverse.minimum;
1067:                            final float max = (float) category.inverse.maximum;
1068:                            while (--count >= 0) { // Optimized loop for the 'double' version.
1069:                                final float check = dstFloat[dstOff];
1070:                                if (check < min) {
1071:                                    dstFloat[dstOff] = min;
1072:                                } else if (check > max) {
1073:                                    dstFloat[dstOff] = max;
1074:                                }
1075:                                dstOff++;
1076:                            }
1077:                        }
1078:                    }
1079:                    /*
1080:                     * Transformation is now finished for all points in the range [srcOff..peekOff]
1081:                     * (not including 'peekOff'). If there is more points to examine, gets the new
1082:                     * category for the next points.
1083:                     */
1084:                    if (numPts < 0) {
1085:                        break;
1086:                    }
1087:                    category = getCategory(value);
1088:                    if (category == null) {
1089:                        throw new TransformException(Errors.format(
1090:                                ErrorKeys.NO_CATEGORY_FOR_VALUE_$1, new Double(
1091:                                        value)));
1092:                    }
1093:                    maximum = category.maximum;
1094:                    minimum = category.minimum;
1095:                    rawBits = Double.doubleToRawLongBits(minimum);
1096:                    srcOff = peekOff;
1097:                }
1098:                last = category;
1099:            }
1100:
1101:            /**
1102:             * Transforms a list of coordinate point ordinal values.
1103:             */
1104:            public final void transform(double[] srcPts, int srcOff,
1105:                    double[] dstPts, int dstOff, int numPts)
1106:                    throws TransformException {
1107:                transform(srcPts, null, srcOff, dstPts, null, dstOff, numPts,
1108:                        true);
1109:            }
1110:
1111:            /**
1112:             * Transforms a list of coordinate point ordinal values.
1113:             */
1114:            public final void transform(float[] srcPts, int srcOff,
1115:                    float[] dstPts, int dstOff, int numPts)
1116:                    throws TransformException {
1117:                transform(null, srcPts, srcOff, null, dstPts, dstOff, numPts,
1118:                        false);
1119:            }
1120:
1121:            /**
1122:             * Transform a raster. Only the current band in {@code iterator} will be transformed.
1123:             * The transformed value are write back in the {@code iterator}. If a different
1124:             * destination raster is wanted, a {@link org.geotools.image.TransfertRectIter}
1125:             * may be used.
1126:             *
1127:             * @param  iterator An iterator to iterate among the samples to transform.
1128:             * @throws RasterFormatException if a problem occurs during the transformation.
1129:             */
1130:            public final void transform(final WritableRectIter iterator)
1131:                    throws RasterFormatException {
1132:                /*
1133:                 * Category of the lowest minimum and highest maximum value (not including NaN),
1134:                 * or <code>null</code in none. Will be used later for range checks.
1135:                 */
1136:                Category categoryMin = null, categoryMax = null;
1137:                for (int i = categories.length; --i >= 0;) {
1138:                    if (!Double.isNaN(categories[i].maximum)) {
1139:                        categoryMax = categories[i];
1140:                        categoryMin = categories[0];
1141:                        break;
1142:                    }
1143:                }
1144:                Category category = main;
1145:                if (main == null) {
1146:                    category = nodata;
1147:                }
1148:                double maximum = category.maximum;
1149:                double minimum = category.minimum;
1150:                long rawBits = Double.doubleToRawLongBits(minimum);
1151:                MathTransform1D tr = category.transform;
1152:                double maxTr, minTr;
1153:                if (overflowFallback == null) {
1154:                    maxTr = Double.POSITIVE_INFINITY;
1155:                    minTr = Double.NEGATIVE_INFINITY;
1156:                } else {
1157:                    maxTr = category.inverse.maximum;
1158:                    minTr = category.inverse.minimum;
1159:                }
1160:                try {
1161:                    iterator.startLines();
1162:                    if (!iterator.finishedLines())
1163:                        do {
1164:                            iterator.startPixels();
1165:                            if (!iterator.finishedPixels())
1166:                                do {
1167:                                    double value = iterator.getSampleDouble();
1168:                                    if (!(value >= minimum && value <= maximum)
1169:                                            && // 'true' if value is NaN...
1170:                                            Double.doubleToRawLongBits(value) != rawBits) // and the NaN bits changed.
1171:                                    {
1172:                                        // Category has changed. Find the new category.
1173:                                        category = getCategory(value);
1174:                                        if (category == null) {
1175:                                            category = nodata;
1176:                                        }
1177:                                        maximum = (category != categoryMax) ? category.maximum
1178:                                                : Double.POSITIVE_INFINITY;
1179:                                        minimum = (category != categoryMin) ? category.minimum
1180:                                                : Double.NEGATIVE_INFINITY;
1181:                                        rawBits = Double
1182:                                                .doubleToRawLongBits(minimum);
1183:                                        tr = category.transform;
1184:                                        if (overflowFallback != null) {
1185:                                            maxTr = category.inverse.maximum;
1186:                                            minTr = category.inverse.minimum;
1187:                                        }
1188:                                    }
1189:                                    /*
1190:                                     * TODO: This assertion fails in some circonstance: during conversions from
1191:                                     *       geophysics to sample values  and  when the sample value is outside
1192:                                     *       the inclusive range but inside the exclusive range... In this case
1193:                                     *       'getCategory(double)' may choose the wrong category. The fix would
1194:                                     *       be to add new fiels in Category: we should have 'minInclusive' and
1195:                                     *       'minExclusive' instead of just 'minimum',  and same for 'maximum'.
1196:                                     *       The CategoryList.minimums array would still inclusive,   but tests
1197:                                     *       for range inclusion should use the exclusive extremas.
1198:                                     */
1199:                                    assert hasGaps
1200:                                            || (category == nodata)
1201:                                            || // Disable assertion in those cases
1202:                                            (Double.isNaN(value) ? Double
1203:                                                    .doubleToRawLongBits(value) == rawBits
1204:                                                    : (value >= minimum && value <= maximum)) : value;
1205:                                    value = tr.transform(value);
1206:                                    if (value > maxTr) {
1207:                                        value = maxTr;
1208:                                    } else if (value < minTr) {
1209:                                        value = minTr;
1210:                                    }
1211:                                    iterator.setSample(value);
1212:                                } while (!iterator.nextPixelDone());
1213:                        } while (!iterator.nextLineDone());
1214:                } catch (TransformException cause) {
1215:                    RasterFormatException exception = new RasterFormatException(
1216:                            Errors.format(ErrorKeys.BAD_TRANSFORM_$1, Utilities
1217:                                    .getShortClassName(tr)));
1218:                    exception.initCause(cause);
1219:                    throw exception;
1220:                }
1221:            }
1222:
1223:            /**
1224:             * Returns a Well Known Text</cite> (WKT) for this object. This operation
1225:             * may fails if an object is too complex for the WKT format capability.
1226:             *
1227:             * @return The Well Know Text for this object.
1228:             * @throws UnsupportedOperationException If this object can't be formatted as WKT.
1229:             *
1230:             * @todo Not yet implemented.
1231:             */
1232:            public String toWKT() throws UnsupportedOperationException {
1233:                throw new UnformattableObjectException("Not yet implemented.",
1234:                        getClass());
1235:            }
1236:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.