Mercurial > dive4elements > gnv-client
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/raster/Palette.java Fri Sep 28 12:14:00 2012 +0200 @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.gnv.raster; + +import de.intevation.gnv.raster.Raster.ValueToIndex; + +import java.awt.Color; + +import java.util.Arrays; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * Lookup mechanism to map double values from a list of + * ranges onto a set of colors and vice versa. + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class Palette +implements ValueToIndex +{ + private static Logger log = Logger.getLogger(Palette.class); + + /** + * An entry in the lookup table. + */ + public static final class Entry + implements Comparable + { + private Entry left; + private Entry right; + + private int index; + + private int externalIndex; + + private double from; + private double to; + + private String description; + + private Color color; + + /** + * Default constructor + */ + public Entry() { + } + + /** + * Copy constructor + * @param other The entry to copy. + */ + public Entry(Entry other) { + index = other.index; + externalIndex = other.externalIndex; + from = other.from; + to = other.to; + description = other.description; + color = other.color; + } + + /** + * Constructor to set the palette index, the exteral index, + * the value range, the color and the description. + * @param index The pallette index + * @param externalIndex The external index + * @param from Start of the interval + * @param to End of the interval + * @param color The color belonging to this interval. + * @param description The description of this range. + */ + public Entry( + int index, + int externalIndex, + double from, + double to, + Color color, + String description + ) { + this.index = index; + this.externalIndex = externalIndex; + this.from = from; + this.to = to; + this.color = color; + this.description = description; + } + + /** + * Compares two entries by the start point of the interval. + * @param other + * @return -1 if this start point is before other. +1 if this + * start point is after, else 0. + */ + public int compareTo(Object other) { + Entry e = (Entry)other; + if (from < e.from) return -1; + if (from > e.from) return +1; + return 0; + } + + /** + * The starting point of the interval. + * @return The starting point. + */ + public double getFrom() { + return from; + } + + /** + * The end point of the interval. + * @return The end point. + */ + public double getTo() { + return to; + } + + /** + * The color of the entry. + * @return The color. + */ + public Color getColor() { + return color; + } + + /** + * The external index of this entry. Useful to model + * external joins. + * @return The external index. + */ + public int getExternalIndex() { + return externalIndex; + } + + /** + * Searches for an entry which has a range that contains + * the given value. + * @param value The value to be searched. + * @return The entry containing the value or null if no such + * entry exists. + */ + public Entry locateEntry(double value) { + Entry current = this; + while (current != null) { + boolean b = value >= current.from; + if (b && value <= current.to) { + return current; + } + current = b + ? current.right + : current.left; + } + return null; + } + + /** + * Returns the palette index for a given value. + * @param value The value to search. + * @return The index of the palette entry or -1 if + * no such entry exists. + */ + public int locate(double value) { + Entry entry = locateEntry(value); + return entry != null + ? entry.index + : -1; + } + + /** + * Searches for an interval with a given index. + * @param index The index to be searched. + * @return The entry with the given index or null if no + * such entry exists. + */ + public Entry findByIndex(int index) { + Entry current = this; + while (current != null) { + if (current.index == index) { + break; + } + current = index < current.index + ? current.left + : current.right; + } + return current; + } + + /** + * The description of this entry. + * @return The description. + */ + public String getDescription() { + return description; + } + + /** + * Determines if the range of this entry has + * infinitive length. + * @return true if the range of this entry has infinitive + * length else false. + */ + public boolean isInfinity() { + return from == -Double.MAX_VALUE + || to == Double.MAX_VALUE; + } + } // class Entry + + /** + * Internal table of the entrys. + */ + protected Entry [] entries; + + /** + * Root of the entry tree used to map values to entries. + */ + protected Entry lookup; + + /** + * The list of colors used by this palette. + */ + protected Color [] rgbs; + + private Entry buildLookup(Entry [] entries, int lo, int hi) { + if (lo > hi) { + return null; + } + int mid = (lo + hi)/2; + Entry entry = entries[mid]; + entry.left = buildLookup(entries, lo, mid-1); + entry.right = buildLookup(entries, mid+1, hi); + return entry; + } + + /** + * Default constructor. + */ + public Palette() { + } + + /** + * Constructor to create the palette from an XML document. + * @param document The document with palette information. + */ + public Palette(Document document) { + + NodeList rangeNodes = document.getElementsByTagName("range"); + + entries = new Entry[rangeNodes.getLength()]; + rgbs = new Color[entries.length]; + + for (int i = 0; i < entries.length; ++i) { + Element rangeElement = (Element)rangeNodes.item(i); + double from = doubleValue(rangeElement.getAttribute("from")); + double to = doubleValue(rangeElement.getAttribute("to")); + int extIndex = intValue(rangeElement.getAttribute("index"), i); + Color color = color(rangeElement.getAttribute("rgb")); + String desc = rangeElement.getAttribute("description"); + if (from > to) { double t = from; from = to; to = t; } + entries[i] = new Entry(i, extIndex, from, to, color, desc); + rgbs[i] = color; + } + + buildLookup(); + } + + /** + * Constructor to split a given palette into a given number of + * equal sized sub entries. Ranges of infinitive length are not + * splitted. + * @param original The source palette. + * @param N The number of chunks to split single ranges into. + */ + public Palette(Palette original, int N) { + if (N < 2) { + throw new IllegalArgumentException("N < 2"); + } + + Entry [] origEntries = original.entries; + + int newSize = 0; + for (int i = 0; i < origEntries.length; ++i) { + // cannot split infinity intervals + newSize += origEntries[i].isInfinity() ? 1 : N; + } + + entries = new Entry[newSize]; + rgbs = new Color[newSize]; + + for (int i = 0, j = 0; i < origEntries.length; ++i) { + Entry origEntry = origEntries[i]; + if (origEntry.isInfinity()) { + // infinity intervals are just copied + Entry nEntry = new Entry(origEntry); + entries[j] = nEntry; + rgbs[j] = nEntry.color; + nEntry.index = j++; + } + else { + // split interval into N parts + double from = origEntry.from; + double to = origEntry.to; + double delta = (to - from)/N; + for (int k = 0; k < N; ++k) { + Entry nEntry = new Entry(origEntry); + nEntry.from = from; + nEntry.to = from + delta; + from += delta; + entries[j] = nEntry; + rgbs[j] = nEntry.color; + nEntry.index = j++; + } + } // limited interval + } // for all original entries + + buildLookup(); + } + + private static final int intValue(String s, int def) { + if (s == null || (s = s.trim()).length() == 0) { + return def; + } + try { + return Integer.parseInt(s); + } + catch (NumberFormatException nfe) { + log.error(nfe.getLocalizedMessage(), nfe); + } + return def; + } + + private static final double doubleValue(String s) { + if (s == null || (s = s.trim()).length() == 0) { + return 0d; + } + if ((s = s.toLowerCase()).startsWith("-inf")) { + return -Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY! + } + + if (s.startsWith("inf")) { + return Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY! + } + + return Double.parseDouble(s); + } + + private static final Color color(String s) { + if (s == null || (s = s.trim()).length() == 0) { + return null; + } + return Color.decode(s); + } + + + /** + * Internal method to build the lookup tree. + */ + protected void buildLookup() { + Arrays.sort(entries); + lookup = buildLookup(entries, 0, entries.length-1); + } + + /** + * Creates a new palette which has ranges that are subdivided + * into a given number of sub ranges each. + * @param N The number od sub divisions of each range. + * @return The new created palette. + */ + public Palette subdivide(int N) { + return new Palette(this, N); + } + + /** + * Return the number of entries in this palette. + * @return The number of entries. + */ + public int getSize() { + return rgbs.length; + } + + /** + * Return the color of an entry for a given index. + * @param index The index. + * @return The color. + */ + public Color getColor(int index) { + return rgbs[index]; + } + + /** + * Return the RGB value + * @param index of an entry for a given index. + * @return rgb value + */ + public int indexToRGB(int index) { + return rgbs[index].getRGB(); + } + + /** + * Searches for the index of an entry which contains the + * given value. + * @param value The value to be searched. + * @return The index of the entry or -1 if no entry is found. + */ + public int toIndex(double value) { + return lookup.locate(value); + } + + /** + * Searches for the an entry which contains the given value. + * @param value The value to be searched. + * @return The entry which contains the value or null if + * no such entry is found. + */ + public Entry getEntry(double value) { + return lookup.locateEntry(value); + } + + /** + * Searches the entry for a given index. + * @param index The index of the entry. + * @return The entry or null if no such entry is found. + */ + public Entry getEntryByIndex(int index) { + return lookup.findByIndex(index); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :