comparison gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java @ 1119:7c4f81f74c47

merged gnv-artifacts
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:00 +0200
parents f953c9a559d8
children
comparison
equal deleted inserted replaced
1027:fca4b5eb8d2f 1119:7c4f81f74c47
1 /*
2 * Copyright (c) 2010 by Intevation GmbH
3 *
4 * This program is free software under the LGPL (>=v2.1)
5 * Read the file LGPL.txt coming with the software for details
6 * or visit http://www.gnu.org/licenses/ if it does not exist.
7 */
8
9 package de.intevation.gnv.raster;
10
11 import de.intevation.gnv.raster.Raster.ValueToIndex;
12
13 import java.awt.Color;
14
15 import java.util.Arrays;
16
17 import org.apache.log4j.Logger;
18
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Element;
21 import org.w3c.dom.NodeList;
22
23 /**
24 * Lookup mechanism to map double values from a list of
25 * ranges onto a set of colors and vice versa.
26 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
27 */
28 public class Palette
29 implements ValueToIndex
30 {
31 private static Logger log = Logger.getLogger(Palette.class);
32
33 /**
34 * An entry in the lookup table.
35 */
36 public static final class Entry
37 implements Comparable
38 {
39 private Entry left;
40 private Entry right;
41
42 private int index;
43
44 private int externalIndex;
45
46 private double from;
47 private double to;
48
49 private String description;
50
51 private Color color;
52
53 /**
54 * Default constructor
55 */
56 public Entry() {
57 }
58
59 /**
60 * Copy constructor
61 * @param other The entry to copy.
62 */
63 public Entry(Entry other) {
64 index = other.index;
65 externalIndex = other.externalIndex;
66 from = other.from;
67 to = other.to;
68 description = other.description;
69 color = other.color;
70 }
71
72 /**
73 * Constructor to set the palette index, the exteral index,
74 * the value range, the color and the description.
75 * @param index The pallette index
76 * @param externalIndex The external index
77 * @param from Start of the interval
78 * @param to End of the interval
79 * @param color The color belonging to this interval.
80 * @param description The description of this range.
81 */
82 public Entry(
83 int index,
84 int externalIndex,
85 double from,
86 double to,
87 Color color,
88 String description
89 ) {
90 this.index = index;
91 this.externalIndex = externalIndex;
92 this.from = from;
93 this.to = to;
94 this.color = color;
95 this.description = description;
96 }
97
98 /**
99 * Compares two entries by the start point of the interval.
100 * @param other
101 * @return -1 if this start point is before other. +1 if this
102 * start point is after, else 0.
103 */
104 public int compareTo(Object other) {
105 Entry e = (Entry)other;
106 if (from < e.from) return -1;
107 if (from > e.from) return +1;
108 return 0;
109 }
110
111 /**
112 * The starting point of the interval.
113 * @return The starting point.
114 */
115 public double getFrom() {
116 return from;
117 }
118
119 /**
120 * The end point of the interval.
121 * @return The end point.
122 */
123 public double getTo() {
124 return to;
125 }
126
127 /**
128 * The color of the entry.
129 * @return The color.
130 */
131 public Color getColor() {
132 return color;
133 }
134
135 /**
136 * The external index of this entry. Useful to model
137 * external joins.
138 * @return The external index.
139 */
140 public int getExternalIndex() {
141 return externalIndex;
142 }
143
144 /**
145 * Searches for an entry which has a range that contains
146 * the given value.
147 * @param value The value to be searched.
148 * @return The entry containing the value or null if no such
149 * entry exists.
150 */
151 public Entry locateEntry(double value) {
152 Entry current = this;
153 while (current != null) {
154 boolean b = value >= current.from;
155 if (b && value <= current.to) {
156 return current;
157 }
158 current = b
159 ? current.right
160 : current.left;
161 }
162 return null;
163 }
164
165 /**
166 * Returns the palette index for a given value.
167 * @param value The value to search.
168 * @return The index of the palette entry or -1 if
169 * no such entry exists.
170 */
171 public int locate(double value) {
172 Entry entry = locateEntry(value);
173 return entry != null
174 ? entry.index
175 : -1;
176 }
177
178 /**
179 * Searches for an interval with a given index.
180 * @param index The index to be searched.
181 * @return The entry with the given index or null if no
182 * such entry exists.
183 */
184 public Entry findByIndex(int index) {
185 Entry current = this;
186 while (current != null) {
187 if (current.index == index) {
188 break;
189 }
190 current = index < current.index
191 ? current.left
192 : current.right;
193 }
194 return current;
195 }
196
197 /**
198 * The description of this entry.
199 * @return The description.
200 */
201 public String getDescription() {
202 return description;
203 }
204
205 /**
206 * Determines if the range of this entry has
207 * infinitive length.
208 * @return true if the range of this entry has infinitive
209 * length else false.
210 */
211 public boolean isInfinity() {
212 return from == -Double.MAX_VALUE
213 || to == Double.MAX_VALUE;
214 }
215 } // class Entry
216
217 /**
218 * Internal table of the entrys.
219 */
220 protected Entry [] entries;
221
222 /**
223 * Root of the entry tree used to map values to entries.
224 */
225 protected Entry lookup;
226
227 /**
228 * The list of colors used by this palette.
229 */
230 protected Color [] rgbs;
231
232 private Entry buildLookup(Entry [] entries, int lo, int hi) {
233 if (lo > hi) {
234 return null;
235 }
236 int mid = (lo + hi)/2;
237 Entry entry = entries[mid];
238 entry.left = buildLookup(entries, lo, mid-1);
239 entry.right = buildLookup(entries, mid+1, hi);
240 return entry;
241 }
242
243 /**
244 * Default constructor.
245 */
246 public Palette() {
247 }
248
249 /**
250 * Constructor to create the palette from an XML document.
251 * @param document The document with palette information.
252 */
253 public Palette(Document document) {
254
255 NodeList rangeNodes = document.getElementsByTagName("range");
256
257 entries = new Entry[rangeNodes.getLength()];
258 rgbs = new Color[entries.length];
259
260 for (int i = 0; i < entries.length; ++i) {
261 Element rangeElement = (Element)rangeNodes.item(i);
262 double from = doubleValue(rangeElement.getAttribute("from"));
263 double to = doubleValue(rangeElement.getAttribute("to"));
264 int extIndex = intValue(rangeElement.getAttribute("index"), i);
265 Color color = color(rangeElement.getAttribute("rgb"));
266 String desc = rangeElement.getAttribute("description");
267 if (from > to) { double t = from; from = to; to = t; }
268 entries[i] = new Entry(i, extIndex, from, to, color, desc);
269 rgbs[i] = color;
270 }
271
272 buildLookup();
273 }
274
275 /**
276 * Constructor to split a given palette into a given number of
277 * equal sized sub entries. Ranges of infinitive length are not
278 * splitted.
279 * @param original The source palette.
280 * @param N The number of chunks to split single ranges into.
281 */
282 public Palette(Palette original, int N) {
283 if (N < 2) {
284 throw new IllegalArgumentException("N < 2");
285 }
286
287 Entry [] origEntries = original.entries;
288
289 int newSize = 0;
290 for (int i = 0; i < origEntries.length; ++i) {
291 // cannot split infinity intervals
292 newSize += origEntries[i].isInfinity() ? 1 : N;
293 }
294
295 entries = new Entry[newSize];
296 rgbs = new Color[newSize];
297
298 for (int i = 0, j = 0; i < origEntries.length; ++i) {
299 Entry origEntry = origEntries[i];
300 if (origEntry.isInfinity()) {
301 // infinity intervals are just copied
302 Entry nEntry = new Entry(origEntry);
303 entries[j] = nEntry;
304 rgbs[j] = nEntry.color;
305 nEntry.index = j++;
306 }
307 else {
308 // split interval into N parts
309 double from = origEntry.from;
310 double to = origEntry.to;
311 double delta = (to - from)/N;
312 for (int k = 0; k < N; ++k) {
313 Entry nEntry = new Entry(origEntry);
314 nEntry.from = from;
315 nEntry.to = from + delta;
316 from += delta;
317 entries[j] = nEntry;
318 rgbs[j] = nEntry.color;
319 nEntry.index = j++;
320 }
321 } // limited interval
322 } // for all original entries
323
324 buildLookup();
325 }
326
327 private static final int intValue(String s, int def) {
328 if (s == null || (s = s.trim()).length() == 0) {
329 return def;
330 }
331 try {
332 return Integer.parseInt(s);
333 }
334 catch (NumberFormatException nfe) {
335 log.error(nfe.getLocalizedMessage(), nfe);
336 }
337 return def;
338 }
339
340 private static final double doubleValue(String s) {
341 if (s == null || (s = s.trim()).length() == 0) {
342 return 0d;
343 }
344 if ((s = s.toLowerCase()).startsWith("-inf")) {
345 return -Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY!
346 }
347
348 if (s.startsWith("inf")) {
349 return Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY!
350 }
351
352 return Double.parseDouble(s);
353 }
354
355 private static final Color color(String s) {
356 if (s == null || (s = s.trim()).length() == 0) {
357 return null;
358 }
359 return Color.decode(s);
360 }
361
362
363 /**
364 * Internal method to build the lookup tree.
365 */
366 protected void buildLookup() {
367 Arrays.sort(entries);
368 lookup = buildLookup(entries, 0, entries.length-1);
369 }
370
371 /**
372 * Creates a new palette which has ranges that are subdivided
373 * into a given number of sub ranges each.
374 * @param N The number od sub divisions of each range.
375 * @return The new created palette.
376 */
377 public Palette subdivide(int N) {
378 return new Palette(this, N);
379 }
380
381 /**
382 * Return the number of entries in this palette.
383 * @return The number of entries.
384 */
385 public int getSize() {
386 return rgbs.length;
387 }
388
389 /**
390 * Return the color of an entry for a given index.
391 * @param index The index.
392 * @return The color.
393 */
394 public Color getColor(int index) {
395 return rgbs[index];
396 }
397
398 /**
399 * Return the RGB value
400 * @param index of an entry for a given index.
401 * @return rgb value
402 */
403 public int indexToRGB(int index) {
404 return rgbs[index].getRGB();
405 }
406
407 /**
408 * Searches for the index of an entry which contains the
409 * given value.
410 * @param value The value to be searched.
411 * @return The index of the entry or -1 if no entry is found.
412 */
413 public int toIndex(double value) {
414 return lookup.locate(value);
415 }
416
417 /**
418 * Searches for the an entry which contains the given value.
419 * @param value The value to be searched.
420 * @return The entry which contains the value or null if
421 * no such entry is found.
422 */
423 public Entry getEntry(double value) {
424 return lookup.locateEntry(value);
425 }
426
427 /**
428 * Searches the entry for a given index.
429 * @param index The index of the entry.
430 * @return The entry or null if no such entry is found.
431 */
432 public Entry getEntryByIndex(int index) {
433 return lookup.findByIndex(index);
434 }
435 }
436 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org