sascha@424: package de.intevation.gnv.raster;
sascha@424: 
sascha@424: import org.w3c.dom.Document;
sascha@424: import org.w3c.dom.Element;
sascha@424: import org.w3c.dom.NodeList;
sascha@424: 
sascha@424: import java.util.Arrays;
sascha@424: 
sascha@424: import java.awt.Color;
sascha@424: 
sascha@447: import org.apache.log4j.Logger;
sascha@447: 
sascha@424: import de.intevation.gnv.raster.Raster.ValueToIndex;
sascha@424: 
sascha@424: /**
sascha@424:  * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
sascha@424:  */
sascha@424: public class Palette
sascha@424: implements   ValueToIndex
sascha@447: {   
sascha@447: 	private static Logger log = Logger.getLogger(Palette.class);
sascha@447: 
sascha@424:     public static final class Entry 
sascha@424:     implements                Comparable
sascha@424:     {
sascha@424:         private Entry  left;
sascha@424:         private Entry  right;
sascha@424: 
sascha@424:         private int    index;
sascha@424: 
sascha@483:         private int    externalIndex;
sascha@483: 
sascha@424:         private double from;
sascha@424:         private double to;
sascha@424: 
sascha@424:         private String description;
sascha@424: 
sascha@424:         private Color  color;
sascha@424: 
sascha@424:         public Entry() {
sascha@424:         }
sascha@424: 
sascha@435:         public Entry(Entry other) {
sascha@483:             index         = other.index;
sascha@483:             externalIndex = other.externalIndex;
sascha@483:             from          = other.from;
sascha@483:             to            = other.to;
sascha@483:             description   = other.description;
sascha@483:             color         = other.color;
sascha@435:         }
sascha@435: 
sascha@424:         public Entry(
sascha@424:             int    index, 
sascha@483:             int    externalIndex,
sascha@424:             double from, 
sascha@424:             double to, 
sascha@424:             Color  color,
sascha@424:             String description
sascha@424:         ) {
sascha@483:             this.index         = index;
sascha@483:             this.externalIndex = externalIndex;
sascha@483:             this.from          = from;
sascha@483:             this.to            = to;
sascha@483:             this.color         = color;
sascha@483:             this.description   = description;
sascha@424:         }
sascha@424: 
sascha@424:         public int compareTo(Object other) {
sascha@424:             Entry e = (Entry)other;
sascha@424:             if (from < e.from) return -1;
sascha@424:             if (from > e.from) return +1;
sascha@424:             return 0;
sascha@424:         }
sascha@424: 
sascha@448:         public double getFrom() {
sascha@448:             return from;
sascha@448:         }
sascha@448: 
sascha@448:         public double getTo() {
sascha@448:             return to;
sascha@448:         }
sascha@448: 
sascha@449:         public Color getColor() {
sascha@449:             return color;
sascha@449:         }
sascha@449: 
sascha@483:         public int getExternalIndex() {
sascha@483:             return externalIndex;
sascha@483:         }
sascha@483: 
sascha@424:         public Entry locateEntry(double value) {
sascha@424:             Entry current = this;
sascha@424:             while (current != null) {
sascha@424:                 boolean b = value >= current.from;
sascha@424:                 if (b && value <= current.to) {
sascha@424:                     return current;
sascha@424:                 }
sascha@424:                 current = b
sascha@424:                     ? current.right
sascha@424:                     : current.left;
sascha@424:             }
sascha@424:             return null;
sascha@424:         }
sascha@424: 
sascha@424:         public int locate(double value) {
sascha@424:             Entry entry = locateEntry(value);
sascha@424:             return entry != null
sascha@424:                 ? entry.index
sascha@424:                 : -1;
sascha@424:         }
sascha@424: 
sascha@424:         public Entry findByIndex(int index) {
sascha@424:             Entry current = this;
sascha@424:             while (current != null) {
sascha@424:                 if (current.index == index) {
sascha@424:                     break;
sascha@424:                 }
sascha@424:                 current = index < current.index
sascha@424:                     ? current.left
sascha@424:                     : current.right;
sascha@424:             }
sascha@424:             return current;
sascha@424:         }
sascha@424: 
sascha@424:         public String getDescription() {
sascha@424:             return description;
sascha@424:         }
sascha@435: 
sascha@435:         public boolean isInfinity() {
sascha@435:             return from == -Double.MAX_VALUE
sascha@435:               ||   to   ==  Double.MAX_VALUE;
sascha@435:         }
sascha@424:     } // class Entry
sascha@424: 
sascha@424:     protected Entry [] entries;
sascha@424:     protected Entry    lookup;
sascha@424:     protected Color [] rgbs;
sascha@424: 
sascha@424:     private Entry buildLookup(Entry [] entries, int lo, int hi) {
sascha@424:         if (lo > hi) {
sascha@424:             return null;
sascha@424:         }
sascha@424:         int mid = (lo + hi)/2;
sascha@424:         Entry entry = entries[mid];
sascha@424:         entry.left  = buildLookup(entries, lo, mid-1);
sascha@424:         entry.right = buildLookup(entries, mid+1, hi);
sascha@424:         return entry;
sascha@424:     }
sascha@424: 
sascha@424:     public Palette() {
sascha@424:     }
sascha@424: 
sascha@424:     public Palette(Document document) {
sascha@424: 
sascha@424:         NodeList rangeNodes = document.getElementsByTagName("range");
sascha@424: 
sascha@424:         entries = new Entry[rangeNodes.getLength()];
sascha@424:         rgbs    = new Color[entries.length];
sascha@424: 
sascha@424:         for (int i = 0; i < entries.length; ++i) {
sascha@424:             Element rangeElement = (Element)rangeNodes.item(i);
sascha@483:             double from     = doubleValue(rangeElement.getAttribute("from"));
sascha@483:             double to       = doubleValue(rangeElement.getAttribute("to"));
sascha@483:             int    extIndex = intValue(rangeElement.getAttribute("index"), i);
sascha@483:             Color  color    = color(rangeElement.getAttribute("rgb"));
sascha@483:             String desc     = rangeElement.getAttribute("description");
sascha@424:             if (from > to) { double t = from; from = to; to = t; }
sascha@483:             entries[i] = new Entry(i, extIndex, from, to, color, desc);
sascha@424:             rgbs[i] = color;
sascha@424:         }
sascha@424: 
sascha@424:         buildLookup();
sascha@424:     }
sascha@424: 
sascha@435:     public Palette(Palette original, int N) {
sascha@435:         if (N < 2)  {
sascha@435:             throw new IllegalArgumentException("N < 2");
sascha@435:         }
sascha@435: 
sascha@435:         Entry [] origEntries = original.entries;
sascha@435: 
sascha@435:         int newSize = 0;
sascha@435:         for (int i = 0; i < origEntries.length; ++i) {
sascha@435:             // cannot split infinity intervals
sascha@435:             newSize += origEntries[i].isInfinity() ? 1 : N;
sascha@435:         }
sascha@435: 
sascha@435:         entries = new Entry[newSize];
sascha@435:         rgbs    = new Color[newSize];
sascha@435: 
sascha@435:         for (int i = 0, j = 0; i < origEntries.length; ++i) {
sascha@435:             Entry origEntry = origEntries[i];
sascha@435:             if (origEntry.isInfinity()) {
sascha@435:                 // infinity intervals are just copied
sascha@435:                 Entry nEntry = new Entry(origEntry);
sascha@435:                 entries[j] = nEntry;
sascha@435:                 rgbs[j]    = nEntry.color;
sascha@435:                 nEntry.index = j++;
sascha@435:             }
sascha@435:             else {
sascha@435:                 // split interval into N parts
sascha@435:                 double from  = origEntry.from;
sascha@435:                 double to    = origEntry.to;
sascha@435:                 double delta = (to - from)/N;
sascha@447: 				for (int k = 0; k < N; ++k) {
sascha@435:                     Entry nEntry = new Entry(origEntry);
sascha@435:                     nEntry.from = from;
sascha@435:                     nEntry.to   = from + delta;
sascha@435:                     from += delta;
sascha@435:                     entries[j] = nEntry;
sascha@435:                     rgbs[j]    = nEntry.color;
sascha@435:                     nEntry.index = j++;
sascha@435:                 }
sascha@435:             } // limited interval
sascha@435:         } // for all original entries
sascha@435: 
sascha@435:         buildLookup();
sascha@435:     }
sascha@435: 
sascha@483:     private static final int intValue(String s, int def) {
sascha@483:         if (s == null || (s = s.trim()).length() == 0) {
sascha@483:             return def;
sascha@483:         }
sascha@483:         try {
sascha@483:             return Integer.parseInt(s);
sascha@483:         }
sascha@483:         catch (NumberFormatException nfe) {
sascha@483:             log.error(nfe.getLocalizedMessage(), nfe);
sascha@483:         }
sascha@483:         return def;
sascha@483:     }
sascha@483: 
sascha@424:     private static final double doubleValue(String s) {
sascha@424:         if (s == null || (s = s.trim()).length() == 0) {
sascha@424:             return 0d;
sascha@424:         }
sascha@424:         if ((s = s.toLowerCase()).startsWith("-inf")) {
sascha@424:             return -Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY!
sascha@424:         }
sascha@424: 
sascha@424:         if (s.startsWith("inf")) {
sascha@424:             return Double.MAX_VALUE; // XXX: Not using Double.NEGATIVE_INFINITY!
sascha@424:         }
sascha@424: 
sascha@424:         return Double.parseDouble(s);
sascha@424:     }
sascha@424: 
sascha@424:     private static final Color color(String s) {
sascha@424:         if (s == null || (s = s.trim()).length() == 0) {
sascha@424:             return null;
sascha@424:         }
sascha@424:         return Color.decode(s);
sascha@424:     }
sascha@424: 
sascha@424: 
sascha@424:     protected void buildLookup() {
sascha@424:         Arrays.sort(entries);
sascha@424:         lookup = buildLookup(entries, 0, entries.length-1);
sascha@424:     }
sascha@424: 
sascha@435:     public Palette subdivide(int N) {
sascha@435:         return new Palette(this, N);
sascha@435:     }
sascha@435: 
sascha@424:     public int getSize() {
sascha@424:         return rgbs.length;
sascha@424:     }
sascha@424: 
sascha@424:     public Color getColor(int index) {
sascha@424:         return rgbs[index];
sascha@424:     }
sascha@424: 
sascha@424:     public int indexToRGB(int index) {
sascha@424:         return rgbs[index].getRGB();
sascha@424:     }
sascha@424: 
sascha@424:     public int toIndex(double value) {
sascha@424:         return lookup.locate(value);
sascha@424:     }
sascha@424: 
sascha@424:     public Entry getEntry(double value) {
sascha@424:         return lookup.locateEntry(value);
sascha@424:     }
sascha@424: 
sascha@424:     public Entry getEntryByIndex(int index) {
sascha@424:         return lookup.findByIndex(index);
sascha@424:     }
sascha@424: }
sascha@424: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: