001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Management Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.coverage;
021:
022: // J2SE dependencies
023: import java.awt.Color;
024:
025: // OpenGIS dependencies
026: import org.opengis.referencing.operation.MathTransform1D;
027: import org.opengis.referencing.operation.TransformException;
028: import org.opengis.util.InternationalString;
029:
030: // Geotools dependencies
031: import org.geotools.referencing.operation.transform.ConcatenatedTransform;
032: import org.geotools.referencing.operation.transform.LinearTransform1D;
033: import org.geotools.resources.Utilities;
034: import org.geotools.resources.i18n.Errors;
035: import org.geotools.resources.i18n.ErrorKeys;
036: import org.geotools.util.NumberRange;
037:
038: /**
039: * A "geophysics" view of a category. Sample values in this category are equal to geophysics
040: * values. By definition, the {@link #getSampleToGeophysics} method for this class returns
041: * the identity transform, or {@code null} if this category is a qualitative one.
042: *
043: * @since 2.1
044: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/GeophysicsCategory.java $
045: * @version $Id: GeophysicsCategory.java 20970 2006-08-11 07:53:22Z jgarnett $
046: * @author Martin Desruisseaux
047: */
048: final class GeophysicsCategory extends Category {
049: /**
050: * Serial number for interoperability with different versions.
051: */
052: private static final long serialVersionUID = -7164422654831370784L;
053:
054: /**
055: * Creates a new instance of geophysics category.
056: *
057: * @param inverse The originating {@link Category}.
058: * @param isQuantitative {@code true} if the originating category is quantitative.
059: * @throws TransformException if a transformation failed.
060: */
061: GeophysicsCategory(Category inverse, boolean isQuantitative)
062: throws TransformException {
063: super (inverse, isQuantitative);
064: }
065:
066: /**
067: * Returns the category name localized in the specified locale.
068: */
069: public InternationalString getName() {
070: assert !(inverse instanceof GeophysicsCategory);
071: return inverse.getName();
072: }
073:
074: /**
075: * Returns the set of colors for this category.
076: */
077: public Color[] getColors() {
078: assert !(inverse instanceof GeophysicsCategory);
079: return inverse.getColors();
080: }
081:
082: /**
083: * Returns the range of geophysics value.
084: *
085: * @return The range of geophysics values.
086: * @throws IllegalStateException if sample values can't be transformed into geophysics values.
087: *
088: * @todo The algorithm for finding minimum and maximum values is very simple for
089: * now and will not work if the transformation has local extremas. We would
090: * need some more sophesticated algorithm for the most general cases. Such
091: * a general algorithm would be usefull in the super-class constructor as well.
092: */
093: public NumberRange getRange() throws IllegalStateException {
094: if (range == null)
095: try {
096: final MathTransform1D tr = inverse.transform;
097: final NumberRange r = inverse.range;
098: boolean minIncluded = r.isMinIncluded();
099: boolean maxIncluded = r.isMaxIncluded();
100: double min = tr.transform(r.getMinimum());
101: double max = tr.transform(r.getMaximum());
102: double min2 = tr.transform(r.getMinimum(!minIncluded));
103: double max2 = tr.transform(r.getMaximum(!maxIncluded));
104: if ((minIncluded ? min2 : min) > (maxIncluded ? max2
105: : max)) {
106: final double tmp, tmp2;
107: final boolean tmpIncluded;
108: tmp = min;
109: tmp2 = min2;
110: tmpIncluded = minIncluded;
111: min = max;
112: min2 = max2;
113: minIncluded = maxIncluded;
114: max = tmp;
115: max2 = tmp2;
116: maxIncluded = tmpIncluded;
117: }
118: assert Double.doubleToLongBits(minimum) == Double
119: .doubleToLongBits(minIncluded ? min : min2);
120: assert Double.doubleToLongBits(maximum) == Double
121: .doubleToLongBits(maxIncluded ? max : max2);
122: range = new Range(min, minIncluded, max, maxIncluded,
123: min2, max2);
124:
125: } catch (TransformException cause) {
126: IllegalStateException exception = new IllegalStateException(
127: Errors
128: .format(
129: ErrorKeys.BAD_TRANSFORM_$1,
130: Utilities
131: .getShortClassName(inverse.transform)));
132: exception.initCause(cause);
133: throw exception;
134: }
135: return range;
136: }
137:
138: /**
139: * Returns a transform from sample values to geophysics values, which (by definition)
140: * is an identity transformation. If this category is not a quantitative one, then
141: * this method returns {@code null}.
142: */
143: public MathTransform1D getSampleToGeophysics() {
144: return isQuantitative() ? LinearTransform1D.IDENTITY : null;
145: }
146:
147: /**
148: * Returns {@code true} if this category is quantitative.
149: */
150: public boolean isQuantitative() {
151: assert !(inverse instanceof GeophysicsCategory) : inverse;
152: return inverse.isQuantitative();
153: }
154:
155: /**
156: * Returns a new category for the same range of sample values but a different color palette.
157: */
158: public Category recolor(final Color[] colors) {
159: assert !(inverse instanceof GeophysicsCategory) : inverse;
160: return inverse.recolor(colors).inverse;
161: }
162:
163: /**
164: * Changes the mapping from sample to geophysics values.
165: */
166: public Category rescale(MathTransform1D sampleToGeophysics) {
167: if (sampleToGeophysics.isIdentity()) {
168: return this ;
169: }
170: sampleToGeophysics = (MathTransform1D) ConcatenatedTransform
171: .create(inverse.getSampleToGeophysics(),
172: sampleToGeophysics);
173: return inverse.rescale(sampleToGeophysics).inverse;
174: }
175:
176: /**
177: * If {@code false}, returns a category with the original sample values.
178: */
179: public Category geophysics(final boolean toGeophysics) {
180: assert !(inverse instanceof GeophysicsCategory) : inverse;
181: return inverse.geophysics(toGeophysics);
182: }
183:
184: /**
185: * Range of geophysics values computed from the range of the {@linkplain #inverse indexed
186: * category}. The {@code inverse.transform} transformation is used for computing the
187: * inclusive and exclusive minimum and maximum values of this range. We compute both the
188: * inclusive and exclusive values because we can't rely on the default implementation, which
189: * looks for the nearest representable number. For example is the range of index values is 0
190: * to 10 exclusive (or 0 to 9 inclusive) and the scale is 2, then the range of geophysics
191: * values is 0 to 20 exclusive or 0 to 18 inclusive, not 0 to 19.9999... The numbers between
192: * 18 and 20 is a "gray area" where we don't know for sure what the user intend to do.
193: *
194: * @version $Id: GeophysicsCategory.java 20970 2006-08-11 07:53:22Z jgarnett $
195: * @author Martin Desruisseaux
196: *
197: * @see GeophysicsCategory#getRange
198: */
199: private static final class Range extends NumberRange {
200: /**
201: * Serial number for interoperability with different versions.
202: */
203: private static final long serialVersionUID = -1416908614729956928L;
204:
205: /**
206: * The minimal value to be returned by {@link #getMinimum(boolean)} when the
207: * {@code inclusive} flag is the opposite of {@link #isMinIncluded()}.
208: */
209: private final double minimum2;
210:
211: /**
212: * The maximal value to be returned by {@link #getMaximum(boolean)} when the
213: * {@code inclusive} flag is the opposite of {@link #isMaxIncluded()}.
214: */
215: private final double maximum2;
216:
217: /**
218: * Constructs a range of {@code double} values.
219: */
220: public Range(final double minimum, final boolean isMinIncluded,
221: final double maximum, final boolean isMaxIncluded,
222: final double minimum2, final double maximum2) {
223: super (minimum, isMinIncluded, maximum, isMaxIncluded);
224: this .minimum2 = minimum2;
225: this .maximum2 = maximum2;
226: }
227:
228: /**
229: * Returns the minimum value with the specified inclusive or exclusive state.
230: */
231: public double getMinimum(final boolean inclusive) {
232: return (inclusive == isMinIncluded()) ? getMinimum()
233: : minimum2;
234: }
235:
236: /**
237: * Returns the maximum value with the specified inclusive or exclusive state.
238: */
239: public double getMaximum(final boolean inclusive) {
240: return (inclusive == isMaxIncluded()) ? getMaximum()
241: : maximum2;
242: }
243: }
244: }
|