Mercurial > dive4elements > gnv-client
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 : |