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


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2006, Geotools Project Managment Committee (PMC)
005:         *
006:         *    This library is free software; you can redistribute it and/or
007:         *    modify it under the terms of the GNU Lesser General Public
008:         *    License as published by the Free Software Foundation; either
009:         *    version 2.1 of the License, or (at your option) any later version.
010:         *
011:         *    This library is distributed in the hope that it will be useful,
012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014:         *    Lesser General Public License for more details.
015:         */
016:        package org.geotools.coverage.processing;
017:
018:        import java.awt.Color;
019:        import java.io.Serializable;
020:        import java.util.Arrays;
021:        import java.util.Collections;
022:        import java.util.HashMap;
023:        import java.util.HashSet;
024:        import java.util.Iterator;
025:        import java.util.Map;
026:        import java.util.Set;
027:        import javax.units.Unit;
028:        import javax.units.ConversionException;
029:
030:        import org.opengis.util.InternationalString;
031:        import org.opengis.referencing.operation.MathTransform1D;
032:        import org.opengis.referencing.operation.TransformException;
033:
034:        import org.geotools.coverage.Category;
035:        import org.geotools.coverage.GridSampleDimension;
036:        import org.geotools.io.TableWriter;
037:        import org.geotools.util.logging.Logging;
038:        import org.geotools.util.NumberRange;
039:        import org.geotools.util.MeasurementRange;
040:        import org.geotools.resources.Utilities;
041:        import org.geotools.resources.i18n.Errors;
042:        import org.geotools.resources.i18n.ErrorKeys;
043:        import org.geotools.resources.i18n.Vocabulary;
044:        import org.geotools.resources.i18n.VocabularyKeys;
045:        import org.geotools.resources.image.ColorUtilities;
046:
047:        /**
048:         * Colors associated to categories. This is the parameter type for the
049:         * {@link org.geotools.coverage.processing.operation.Recolor} operation.
050:         * 
051:         * @since 2.4
052:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/processing/ColorMap.java $
053:         * @version $Id: ColorMap.java 27848 2007-11-12 13:10:32Z desruisseaux $
054:         * @author Martin Desruisseaux
055:         *
056:         * @see org.geotools.coverage.processing.operation.Recolor
057:         *
058:         * @todo We need to investigage if this object should be defined as an implementation of
059:         *       {@link org.geotools.styling.ColorMap}.
060:         */
061:        public class ColorMap implements  Serializable {
062:            /**
063:             * For cross-version compatibility.
064:             */
065:            private static final long serialVersionUID = 1688030908496323012L;
066:
067:            /**
068:             * A special category name meaning "<cite>any quantitative value</cite>".
069:             */
070:            public static final CharSequence ANY_QUANTITATIVE_CATEGORY = Vocabulary
071:                    .formatInternational(VocabularyKeys.ALL); // TODO: Find a better name.
072:
073:            /**
074:             * The colors to apply to categories. Keys are {@link String} objects.
075:             * Values may be {@link Color} singletons or {@code Color[]} arrays.
076:             * <p>
077:             * The {@link #ANY_QUANTITATIVE_CATEGORY} key is replaced by {@code null} in
078:             * order to avoid confusion with user-specified category with the exact name.
079:             */
080:            private Map/*<String,Object>*/colorMap;
081:
082:            /**
083:             * The range of values for quantitative categories. Values are {@link NumberRange} instances
084:             * if the range is relative, or {@link MeasurementRange} if the range is geophysics.
085:             * <p>
086:             * The {@link #ANY_QUANTITATIVE_CATEGORY} key is replaced by {@code null} in
087:             * order to avoid confusion with user-specified category with the exact name.
088:             */
089:            private Map/*<String,NumberRange>*/colorRanges;
090:
091:            /**
092:             * If {@code true}, the ARGB values corresponding to any {@linkplain Category category}
093:             * <strong>not</strong> specified in this color map will be reset to the color specified
094:             * by the category. The default value is {@code false}.
095:             */
096:            private boolean resetUnspecifiedColors;
097:
098:            /**
099:             * Creates an initially empty color map.
100:             */
101:            public ColorMap() {
102:            }
103:
104:            /**
105:             * Creates a color map initialized to the specified color ramp to be applied on
106:             * {@linkplain #ANY_QUANTITATIVE_CATEGORY any quantitative category}.
107:             */
108:            public ColorMap(final Color[] colors) {
109:                setColors(ANY_QUANTITATIVE_CATEGORY, colors);
110:            }
111:
112:            /**
113:             * Creates a color map initialized to the specified map.
114:             *
115:             * @param map A map of ({@linkplain Category#getName category name},
116:             *        {@linkplain Color colors}) pairs.
117:             */
118:            public ColorMap(
119:                    final Map/*<? extends CharSequence,Color[]>*/colorMap) {
120:                for (final Iterator it = colorMap.entrySet().iterator(); it
121:                        .hasNext();) {
122:                    final Map.Entry entry = (Map.Entry) it.next();
123:                    setColors((CharSequence) entry.getKey(), (Color[]) entry
124:                            .getValue());
125:                }
126:            }
127:
128:            /**
129:             * Returns the unlocalized flavor of the given name
130:             * (not to be confused with the default locale).
131:             *
132:             * @param category The {@linkplain Category#getName category name}.
133:             * @return The unlocalized name, or {@code null}.
134:             */
135:            private static String unlocalized(final CharSequence name) {
136:                if (name == ANY_QUANTITATIVE_CATEGORY) {
137:                    return null;
138:                }
139:                if (name instanceof  InternationalString) {
140:                    return ((InternationalString) name).toString(null);
141:                } else {
142:                    return name.toString();
143:                }
144:            }
145:
146:            /**
147:             * Applies colors to the given category.
148:             *
149:             * @param category The {@linkplain Category#getName name of the category}
150:             *        for which to set the colors.
151:             * @param colors Colors to apply to the specified category, or {@code null}.
152:             */
153:            private void setColorObject(final CharSequence category,
154:                    final Object colors) {
155:                final String name = unlocalized(category);
156:                if (colors != null) {
157:                    if (colorMap == null) {
158:                        colorMap = new HashMap();
159:                    }
160:                    colorMap.put(name, colors);
161:                } else if (colorMap != null) {
162:                    colorMap.remove(name);
163:                    if (colorMap.isEmpty()) {
164:                        colorMap = null; // For more accurate 'equals' implementation.
165:                    }
166:                }
167:            }
168:
169:            /**
170:             * Applies a uniform color to the given (usually <cite>qualitative</cite>) category.
171:             *
172:             * @param category The {@linkplain Category#getName name of the category}
173:             *        for which to set the color.
174:             * @param color A uniform color to apply to the specified category, or {@code null}
175:             *        for removing the color mapping.
176:             *
177:             * @see #recolor
178:             */
179:            public void setColor(final CharSequence category, final Color color) {
180:                setColorObject(category, color);
181:            }
182:
183:            /**
184:             * Applies a color ramp to the given (usually <cite>quantitative</cite>) category.
185:             * The color array may have any length; colors will be interpolated as needed.
186:             *
187:             * @param category The {@linkplain Category#getName name of the category} for which to set
188:             *        the colors, or {@link #ANY_QUANTITATIVE_CATEGORY} if the colors should apply to
189:             *        any quantitative category.
190:             * @param colors The colors to apply to the specified category, or {@code null}
191:             *        or an empty array for removing the color mapping.
192:             *
193:             * @see #recolor
194:             */
195:            public void setColors(final CharSequence category,
196:                    final Color[] colors) {
197:                final Object value;
198:                if (colors != null) {
199:                    switch (colors.length) {
200:                    default:
201:                        value = colors.clone();
202:                        break;
203:                    case 1:
204:                        value = colors[0];
205:                        break;
206:                    case 0:
207:                        value = null;
208:                        break;
209:                    }
210:                } else {
211:                    value = null;
212:                }
213:                setColorObject(category, value);
214:            }
215:
216:            /**
217:             * Returns the color ramp for the given category.
218:             *
219:             * @param  category The {@linkplain Category#getName category name}, or
220:             *         {@link #ANY_QUANTITATIVE_CATEGORY} for fetching the colors to
221:             *         apply to any quantitative category.
222:             * @return The color ramp, or {@code null} if none.
223:             */
224:            public Color[] getColors(final CharSequence category) {
225:                if (colorMap == null) {
226:                    return null;
227:                }
228:                final String name = unlocalized(category);
229:                Object colors = colorMap.get(name);
230:                if (colors == null) {
231:                    if (name != null && category instanceof  InternationalString) {
232:                        // Unlocalized name not found. Search using the localized flavor.
233:                        colors = getColors(category.toString());
234:                        if (colors == null) {
235:                            return null;
236:                        }
237:                    } else {
238:                        return null;
239:                    }
240:                }
241:                if (colors instanceof  Color) {
242:                    return new Color[] { (Color) colors };
243:                }
244:                return (Color[]) ((Color[]) colors).clone();
245:            }
246:
247:            /**
248:             * Sets a range of geophysics values for the color ramp associated with a quantitative category.
249:             * For example if the category "<cite>Height</cite>" applies to geophysics values in the range
250:             * [0..500] metres and if a range of [100..400] metres is defined as below:
251:             *
252:             * <blockquote><code>
253:             * setRelativeRange("Height", new MeasurementRange(0, 100, SI.METRE));
254:             * setColors("Height", myColorPalette);
255:             * </code><blockquote>
256:             *
257:             * Then {@code myColorPalette} will applies to pixel values in the range [100..400] instead
258:             * of [0..500]. This is typically used in order to augment the contrast in a range of values
259:             * of special interest.
260:             * <p>
261:             * This method is exclusive with {@link #setRelativeRange}.
262:             *
263:             * @param category The {@linkplain Category#getName name of the category}
264:             *        for which to set the geophysics range.
265:             * @param range The minimal and maximal values for the color ramp. A {@code null}
266:             *        value removes the range mapping.
267:             *
268:             * @see #recolor
269:             */
270:            public void setGeophysicsRange(final CharSequence category,
271:                    final MeasurementRange range) {
272:                setRange(category, range);
273:            }
274:
275:            /**
276:             * Sets a relative range of values for the color ramp associated to a quantitative category.
277:             * For example if the category "<cite>Height</cite>" applies to pixel values in the range
278:             * [0..200] and if a relative range of [20%..80%] is defined as below:
279:             *
280:             * <blockquote><code>
281:             * setRelativeRange("Height", new NumberRange(20, 80));
282:             * setColors("Height", myColorPalette);
283:             * </code><blockquote>
284:             *
285:             * Then {@code myColorPalette} will applies to pixel values in the range [40..160] instead
286:             * of [0..200]. This is typically used in order to augment the contrast in a range of values
287:             * of special interest.
288:             * <p>
289:             * This method is exclusive with {@link #setGeophysicsRange}.
290:             *
291:             * @param category The {@linkplain Category#getName name of the category}
292:             *        for which to set the relative range.
293:             * @param range The minimal and maximal relative values for the color ramp, as percentages
294:             *        between 0 and 100. A {@code null} value removes the range mapping.
295:             *
296:             * @see #recolor
297:             */
298:            public void setRelativeRange(final CharSequence category,
299:                    final NumberRange range) {
300:                if (range instanceof  MeasurementRange) {
301:                    // The MeasurementRange type is reserved for geophysics ranges.
302:                    throw new IllegalArgumentException(Errors.format(
303:                            ErrorKeys.ILLEGAL_ARGUMENT_$1, "range"));
304:                }
305:                setRange(category, range);
306:            }
307:
308:            /**
309:             * Sets a relative or geophysics range.
310:             * This method is exclusive with {@link #setGeophysicsRange}.
311:             *
312:             * @param category The {@linkplain Category#getName name of the category}
313:             *        for which to set the relative or geophysics range.
314:             * @param range The minimal and maximal values for the color ramp.
315:             */
316:            private void setRange(final CharSequence category,
317:                    final NumberRange range) {
318:                final String name = unlocalized(category);
319:                if (range != null) {
320:                    if (colorRanges == null) {
321:                        colorRanges = new HashMap();
322:                    }
323:                    colorRanges.put(name, range);
324:                } else if (colorRanges != null) {
325:                    colorRanges.remove(name);
326:                    if (colorRanges.isEmpty()) {
327:                        colorRanges = null; // For more accurate 'equals' implementation.
328:                    }
329:                }
330:            }
331:
332:            /**
333:             * Returns the range of geophysics values for the given category.
334:             *
335:             * @param  category The {@linkplain Category#getName category name}, or
336:             *         {@link #ANY_QUANTITATIVE_CATEGORY} for fetching the range to
337:             *         apply to any quantitative category.
338:             * @return The geophysics range, or {@code null} if none.
339:             */
340:            public MeasurementRange getGeophysicsRange(
341:                    final CharSequence category) {
342:                final NumberRange range = getRange(category);
343:                return (range instanceof  MeasurementRange) ? (MeasurementRange) range
344:                        : null;
345:            }
346:
347:            /**
348:             * Returns the relative range of values for the given category.
349:             *
350:             * @param  category The {@linkplain Category#getName category name}, or
351:             *         {@link #ANY_QUANTITATIVE_CATEGORY} for fetching the relative
352:             *         range to apply to any quantitative category.
353:             * @return The relative range, or {@code null} if none.
354:             */
355:            public NumberRange getRelativeRange(final CharSequence category) {
356:                final NumberRange range = getRange(category);
357:                return (range instanceof  MeasurementRange) ? null : range;
358:            }
359:
360:            /**
361:             * Returns the range of relative or geophysics values. If the returned range is an instance of
362:             * {@link MeasurementRange}, then is is a {@linkplain #getGeophysicsRange geophysics range}.
363:             * Otherwise it is a {@linkplain #getRelativeRange relative range}.
364:             *
365:             * @param  category The {@linkplain Category#getName category name}, or
366:             *         {@link #ANY_QUANTITATIVE_CATEGORY} for fetching the range to
367:             *         apply to any quantitative category.
368:             * @return The relative or geophysics range, or {@code null} if none.
369:             */
370:            private NumberRange getRange(final CharSequence category) {
371:                if (colorRanges == null) {
372:                    return null;
373:                }
374:                final String name = unlocalized(category);
375:                NumberRange range = (NumberRange) colorRanges.get(name);
376:                if (range == null) {
377:                    if (name != null && category instanceof  InternationalString) {
378:                        // Unlocalized name not found. Search using the localized flavor.
379:                        range = (NumberRange) colorRanges.get(category
380:                                .toString());
381:                    }
382:                }
383:                return range;
384:            }
385:
386:            /**
387:             * Returns the range of sample values for the given category, or {@code null} if none.
388:             * This range is computed from the {@linkplain #getRange relative or geophysics range}.
389:             *
390:             * @param  category The category for which to compute the range.
391:             * @param  units The category units, usually {@link GridSampleDimension#getUnits}.
392:             * @return The range, or {@code null} if none. The lower index is always inclusive
393:             *         and the upper index is always exclusive.
394:             */
395:            private NumberRange getTargetRange(final Category category,
396:                    final Unit units) {
397:                NumberRange scale = getRange(category.getName());
398:                if (scale == null) {
399:                    if (category.isQuantitative()) {
400:                        scale = getRange(ANY_QUANTITATIVE_CATEGORY);
401:                    }
402:                    if (scale == null) {
403:                        return null;
404:                    }
405:                }
406:                double minimum = scale.getMinimum();
407:                double maximum = scale.getMaximum();
408:                boolean minIncluded = scale.isMinIncluded();
409:                boolean maxIncluded = scale.isMaxIncluded();
410:                if (scale instanceof  MeasurementRange) {
411:                    try {
412:                        scale = ((MeasurementRange) scale).convertTo(units);
413:                    } catch (ConversionException e) {
414:                        Logging.unexpectedException(AbstractProcessor.LOGGER,
415:                                ColorMap.class, "recolor", e);
416:                        return null; // This is allowed by this method contract.
417:                    }
418:                    MathTransform1D tr = category.getSampleToGeophysics();
419:                    if (tr != null)
420:                        try {
421:                            tr = (MathTransform1D) tr.inverse();
422:                            minimum = tr.transform(minimum);
423:                            maximum = tr.transform(maximum);
424:                        } catch (TransformException e) {
425:                            Logging.unexpectedException(
426:                                    AbstractProcessor.LOGGER, ColorMap.class,
427:                                    "recolor", e);
428:                            return null; // This is allowed by this method contract.
429:                        }
430:                } else {
431:                    final NumberRange range = category.getRange();
432:                    final double lower = range.getMinimum();
433:                    final double extent = range.getMaximum() - lower;
434:                    minimum = (minimum / 100) * extent + lower;
435:                    maximum = (maximum / 100) * extent + lower;
436:                    minIncluded &= range.isMinIncluded();
437:                    maxIncluded &= range.isMaxIncluded();
438:                }
439:                final int lower, upper;
440:                if (minimum > maximum) {
441:                    lower = round(maximum, maxIncluded);
442:                    upper = round(minimum, !minIncluded);
443:                } else {
444:                    lower = round(minimum, minIncluded);
445:                    upper = round(maximum, !maxIncluded);
446:                }
447:                return new NumberRange(lower, true, upper, false);
448:            }
449:
450:            /**
451:             * Round the specified number to the {@linkplain Math#floor lower} or {@linkplain Math#ceil
452:             * upper} value, depending if the value is inclusive or not. This method is appropriate for
453:             * minimal range value. In order to apply it to the maximal range value, {@code included}
454:             * must be replaced by {@code !included}.
455:             */
456:            private static int round(final double value, final boolean included) {
457:                final double rounded = included ? Math.floor(value) : Math
458:                        .ceil(value);
459:                int asInteger = (int) rounded;
460:                if (!included && value == rounded) {
461:                    asInteger++;
462:                }
463:                return asInteger;
464:            }
465:
466:            /**
467:             * If {@code true}, the ARGB values corresponding to any {@linkplain Category category}
468:             * <strong>not</strong> specified in this color map will be reset to the color specified
469:             * by the category. The default value is {@code false}.
470:             */
471:            public void setResetUnspecifiedColors(final boolean reset) {
472:                resetUnspecifiedColors = reset;
473:            }
474:
475:            /**
476:             * If {@code true}, the ARGB values corresponding to any {@linkplain Category category}
477:             * <strong>not</strong> specified in this color map will be reset to the color specified
478:             * by the category. The default value is {@code false}.
479:             */
480:            public boolean getResetUnspecifiedColors() {
481:                return resetUnspecifiedColors;
482:            }
483:
484:            /**
485:             * Applies to the specified sample dimension the colors given to this color map. This method
486:             * iterates throug every {@linkplain Category categories} in the given sample dimension. For
487:             * each category with a {@linkplain Category#getName name} matching one of the (<var>name</var>,
488:             * <var>colors</var>) or (<var>name</var>, <var>range</var>) entries given to this color map,
489:             * the {@link Category#recolor recolor} method is invoked on that category and the result
490:             * inserted into a new sample dimension to be returned.
491:             * <p>
492:             * If the optional {@code ARGB} array is non-null, then the ARGB colors for recolorized
493:             * categories will be written in this array. Only the elements with index in the
494:             * {@linkplain Category#getRange category range} will be overwritten; other elements
495:             * will not be modified.
496:             * <p>
497:             * <strong>NOTE:</strong> The {@linkplain #setGeophysicsRange geophysics} and
498:             * {@linkplain #setRelativeRange relative} ranges are taken in account for the
499:             * {@code ARGB} array only; they do not have impact on the categories to be
500:             * included in the returned sample dimension.
501:             *
502:             * @param  sampleDimension The sample dimension to recolorize.
503:             * @param  ARGB An optional array where to store the ARGB values of recolorized categories,
504:             *         or {@code null} if none.
505:             * @return A new sample dimension, or {@code sampleDimension} if no color change were applied.
506:             *
507:             * @see Category#recolor
508:             */
509:            public GridSampleDimension recolor(
510:                    final GridSampleDimension sampleDimension, final int[] ARGB) {
511:                final GridSampleDimension displayDimension = sampleDimension
512:                        .geophysics(false);
513:                boolean changed = false;
514:                final Category categories[] = (Category[]) displayDimension
515:                        .getCategories().toArray();
516:                for (int i = 0; i < categories.length; i++) {
517:                    Category category = categories[i];
518:                    Color[] colors = getColors(category.getName());
519:                    if (colors == null) {
520:                        if (category.isQuantitative()) {
521:                            colors = getColors(ANY_QUANTITATIVE_CATEGORY);
522:                        }
523:                        if (colors == null && resetUnspecifiedColors) {
524:                            colors = category.getColors();
525:                        }
526:                        // 'colors' may still null, so we will need to check.
527:                    }
528:                    if (ARGB != null) {
529:                        final NumberRange range = category.getRange();
530:                        // TODO: Remove casts when we will be allowed to compile for J2SE 1.5.
531:                        int lower = ((Number) range.getMinValue()).intValue();
532:                        int upper = ((Number) range.getMaxValue()).intValue();
533:                        if (!range.isMinIncluded())
534:                            lower++;
535:                        if (range.isMaxIncluded())
536:                            upper++;
537:                        boolean outOfBounds = false;
538:                        if (lower < 0) {
539:                            lower = 0;
540:                            outOfBounds = true;
541:                        }
542:                        if (upper > ARGB.length) {
543:                            upper = ARGB.length;
544:                            outOfBounds = true;
545:                        }
546:                        if (outOfBounds) {
547:                            AbstractProcessor.LOGGER.warning(Errors.format(
548:                                    ErrorKeys.VALUE_OUT_OF_BOUNDS_$3, category,
549:                                    new Integer(0),
550:                                    new Integer(ARGB.length - 1)));
551:                        }
552:                        if (upper <= lower) {
553:                            continue;
554:                        }
555:                        final NumberRange target = getTargetRange(category,
556:                                sampleDimension.getUnits());
557:                        if (target != null) {
558:                            if (colors == null) {
559:                                colors = category.getColors();
560:                            }
561:                            if (colors.length >= 2) {
562:                                assert target.isMinIncluded()
563:                                        && !target.isMaxIncluded() : target;
564:                                final int lo = Math.max(lower, ((Number) target
565:                                        .getMinValue()).intValue());
566:                                final int hi = Math.min(upper, ((Number) target
567:                                        .getMaxValue()).intValue());
568:                                if (lo != lower || hi != upper) {
569:                                    Arrays.fill(ARGB, lower, lo, colors[0]
570:                                            .getRGB());
571:                                    Arrays.fill(ARGB, hi, upper,
572:                                            colors[colors.length - 1].getRGB());
573:                                    lower = lo;
574:                                    upper = hi;
575:                                }
576:                            }
577:                        } else if (colors == null) {
578:                            /*
579:                             * If there is no range to change (target == null) and no colors explicitly
580:                             * specified by the user (colors == null), then there is nothing to do.
581:                             */
582:                            continue;
583:                        }
584:                        ColorUtilities.expand(colors, ARGB, lower, upper);
585:                    } else if (colors == null) {
586:                        continue;
587:                    }
588:                    category = category.recolor(colors);
589:                    if (!categories[i].equals(category)) {
590:                        categories[i] = category;
591:                        changed = true;
592:                    }
593:                }
594:                if (!changed) {
595:                    return sampleDimension;
596:                }
597:                GridSampleDimension result = new GridSampleDimension(
598:                        displayDimension.getDescription(), categories,
599:                        displayDimension.getUnits());
600:                if (sampleDimension != displayDimension) {
601:                    result = result.geophysics(true);
602:                }
603:                return result;
604:            }
605:
606:            /**
607:             * Returns all category names declared in this color map, in alphabetical order.
608:             * If the {@link #ANY_QUANTITATIVE_CATEGORY} special value is presents, it will
609:             * appears last.
610:             */
611:            private CharSequence[] getCategoryNames() {
612:                final Set/*<String>*/names;
613:                if (colorMap != null) {
614:                    if (colorRanges != null) {
615:                        names = new HashSet(colorMap.keySet());
616:                        names.addAll(colorRanges.keySet());
617:                    } else {
618:                        names = colorMap.keySet();
619:                    }
620:                } else {
621:                    if (colorRanges != null) {
622:                        names = colorRanges.keySet();
623:                    } else {
624:                        names = Collections.EMPTY_SET;
625:                    }
626:                }
627:                int count = names.size();
628:                final CharSequence[] asArray = (CharSequence[]) names
629:                        .toArray(new CharSequence[count]);
630:                for (int i = count; --i >= 0;) {
631:                    if (asArray[i] == null) {
632:                        System.arraycopy(asArray, i + 1, asArray, i, --count
633:                                - i);
634:                        asArray[count] = ANY_QUANTITATIVE_CATEGORY;
635:                        // We could stop the loop here since we should not have any additional
636:                        // null values. However we let the loop continue as a paranoiac check.
637:                    }
638:                }
639:                Arrays.sort(asArray, 0, count);
640:                return asArray;
641:            }
642:
643:            /**
644:             * Returns a hash code value for this color map.
645:             */
646:            //@Override
647:            public int hashCode() {
648:                return (int) serialVersionUID
649:                        ^ ((colorMap != null ? colorMap.hashCode() : 31) + 37 * (colorRanges != null ? colorRanges
650:                                .hashCode()
651:                                : 31));
652:            }
653:
654:            /**
655:             * Compares this color map with the specified object for equality.
656:             */
657:            //@Override
658:            public boolean equals(final Object object) {
659:                if (object != null && getClass().equals(object.getClass())) {
660:                    final ColorMap that = (ColorMap) object;
661:                    return Utilities.equals(this .colorMap, that.colorMap)
662:                            && Utilities.equals(this .colorRanges,
663:                                    that.colorRanges);
664:                }
665:                return false;
666:            }
667:
668:            /**
669:             * Returns a string representation of this color map.
670:             */
671:            //@Override
672:            public String toString() {
673:                final CharSequence[] names = getCategoryNames();
674:                final TableWriter writer = new TableWriter(null, 1);
675:                for (int i = 0; i < names.length; i++) {
676:                    final CharSequence name = names[i];
677:                    writer.write(name.toString());
678:                    if (colorRanges != null) {
679:                        final NumberRange range = getRange(name);
680:                        if (range != null) {
681:                            writer.write(' ');
682:                            writer.write(range.toString());
683:                            if (!(range instanceof  MeasurementRange)) {
684:                                writer.write('%');
685:                            }
686:                        }
687:                    }
688:                    writer.nextColumn();
689:                    writer.write(':');
690:                    writer.nextColumn();
691:                    final Color[] colors = getColors(name);
692:                    if (colors != null) {
693:                        final String message;
694:                        if (colors.length == 1) {
695:                            message = Integer.toHexString(colors[0].getRGB())
696:                                    .toUpperCase();
697:                        } else {
698:                            message = Vocabulary.format(
699:                                    VocabularyKeys.COLOR_COUNT_$1, new Integer(
700:                                            ((Color[]) colors).length));
701:                        }
702:                        writer.write(message);
703:                    }
704:                    writer.nextLine();
705:                }
706:                return writer.toString();
707:            }
708:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.