diff gnv-artifacts/src/main/java/de/intevation/gnv/raster/Raster.java @ 875:5e9efdda6894

merged gnv-artifacts/1.0
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:13:56 +0200
parents 05bf8534a35a
children f953c9a559d8
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/Raster.java	Fri Sep 28 12:13:56 2012 +0200
@@ -0,0 +1,407 @@
+package de.intevation.gnv.raster;
+
+/**
+ * A 2D double  valued representation of a raster array.
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public class Raster
+{
+    /**
+     * A kernel can be applied to a raster to fold its values.
+     */
+    public static class Kernel {
+
+        /**
+         * The coefficients of the folding matrix.
+         */
+        public static final class Coeff {
+            /**
+             * i delta index of this coefficient.
+             */
+            public int    di;
+            /**
+             * i delta index of this coefficient.
+             */
+            public int    dj;
+            /**
+             * The weight of this coefficient.
+             */
+            public double c;
+
+            /**
+             * Default constructor
+             */
+            public Coeff() {
+            }
+
+            /**
+             * Constructor to set the delta position and the weight of
+             * the coefficient.
+             * @param di The i delta index.
+             * @param dj The j delta index.
+             * @param c The weight of the index.
+             */
+            public Coeff(int di, int dj, double c) {
+                this.di = di;
+                this.dj = dj;
+                this.c  = c;
+            }
+
+            /**
+             * Returns a string representation of this coefficient.
+             * @return The string representation.
+             */
+            @Override
+            public String toString() {
+                return String.valueOf(c);
+            }
+
+        } // class Coeff
+
+        /**
+         * The coefficients of the folding matrix.
+         */
+        protected Coeff [] coeffs;
+        /**
+         * The width of the kernel.
+         */
+        protected int      width;
+
+        /**
+         * Default constrcutor.
+         */
+        public Kernel() {
+        }
+
+        /**
+         * Constructor for a kernel with a given coefficient matrix
+         * with a given width.
+         * @param coeffs The coefficients.
+         * @param width The width of the kernel.
+         */
+        public Kernel(Coeff [] coeffs, int width) {
+            this.coeffs = coeffs;
+            this.width  = width;
+        }
+
+        /**
+         * Evaluates the 2D Gauss normal distribution a given x, y
+         * and a given sigma.
+         * @param x The x location.
+         * @param y The y location.
+         * @param sigma The sigma of the distribution.
+         * @return The value at point x, y.
+         */
+        public static double gauss(double x, double y, double sigma) {
+            double s2 = sigma*sigma;
+            return 1.0d/(2.0d*Math.PI*s2)*Math.exp(-(x*x + y*y)/(2.0d*s2));
+        }
+
+        /**
+         * Creates a Gauss kernel with sigma = 1 and width 5.
+         * @return The new kernel.
+         */
+        public static Kernel createGauss() {
+            return createGauss(1.0d, 5);
+        }
+
+        /**
+         * Creates a Gauss kernel with a given sigma and width.
+         * @param sigma The sigma value
+         * @param width  The width of the kernel.
+         * @return The new kernel.
+         */
+        public static Kernel createGauss(double sigma, int width) {
+            Coeff [] coeffs = new Coeff[width*width];
+            double sum = 0d;
+            for (int j = 0; j < width; ++j) {
+                int y = -width/2 + j;
+                for (int i = 0; i < width; ++i) {
+                    int x = -width/2 + i;
+                    double c = gauss(x, y, sigma);
+                    coeffs[j*width + i] = new Coeff(x, y, c);
+                    sum += c;
+                }
+            }
+            sum = 1.0/sum;
+            for (int i = 0; i < coeffs.length; ++i) {
+                coeffs[i].c *= sum;
+            }
+            return new Kernel(coeffs, width);
+        }
+
+        /**
+         * Evaluates this Kernel at the raster at raster index i, j.
+         * @param access The accessor to the raster.
+         * @param i The i raster index.
+         * @param j The j raster index.
+         * @return The folded value at index i, j.
+         */
+        public double fold(Access access, int i, int j) {
+
+            double s = 0.0d;
+
+            double unused = 0d;
+
+            for (int k = 0; k < coeffs.length; ++k) {
+                Coeff coeff = coeffs[k];
+                double v = access.get(i + coeff.di, j + coeff.dj);
+                if (Double.isNaN(v)) {
+                    unused += coeff.c;
+                }
+                else {
+                    s += v * coeff.c;
+                }
+            }
+
+            return s*(1.0d - unused);
+        }
+
+        /**
+         * Returns a string representation of this kernel.
+         * @return The string representation.
+         */
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+
+            int height = coeffs.length/width;
+
+            for (int j = 0; j < height; ++j) {
+                if (j > 0) {
+                    sb.append(System.getProperty("line.separator"));
+                }
+                for (int i = 0; i < width; ++i) {
+                    if (i > 0) {
+                        sb.append("\t");
+                    }
+                    sb.append(coeffs[j*width + i]);
+                }
+            }
+
+            return sb.toString();
+        }
+    } // class Kernel
+
+    /**
+     * Interface of allow special access patterns to the raster,
+     * especially at the border.
+     */
+    public interface Access {
+
+        /**
+         * Returns the raster value at a given index point.
+         * @param i The i index
+         * @param j The j index
+         * @return The value of the raster.
+         */
+        double get(int i, int j);
+
+    } // interface Access
+
+    /**
+     * Interface to decouple the lookup which is needed
+     * to map double values to integers.
+     */
+    public interface ValueToIndex {
+
+        /**
+         * Returns the corresponding integer to a given value.
+         * @param value The value to be queried.
+         * @return The integer value or -1 if no such value was found.
+         */
+        int toIndex(double value);
+
+    } // interface ValueToIndex;
+
+    /**
+     * Brings raster to an integer representation with equal sized bins
+     * of integer vaules.
+     */
+    public static class IsoClasses
+    implements          ValueToIndex
+    {
+        /**
+         * Linear scaling factor.
+         */
+        protected double m;
+        /**
+         * Linear offset.
+         */
+        protected double b;
+        /**
+         * Number of classes.
+         */
+        protected int    numClasses;
+
+        /**
+         * Constructor to build a lookup to transform
+         * an raster into an equals sized bin integer array.
+         * @param raster The original raster
+         * @param numClasses The number of classes.
+         */
+        public IsoClasses(Raster raster, int numClasses) {
+
+            this.numClasses = numClasses;
+
+            double min =  Double.MAX_VALUE;
+            double max = -Double.MAX_VALUE;
+
+            double [] src = raster.raster;
+
+            for (int i = 0; i < src.length; ++i) {
+                double x = src[i];
+                if (!Double.isNaN(x)) {
+                    if (x < min) min = x;
+                    if (x > max) max = x;
+                }
+            }
+
+            /* f(min) = 0, f(max) = numClasses - 1
+
+               I:  0              = m*min + b
+               II: numClasses - 1 = m*max + b
+
+               II - I:
+               numClasses - 1 = m*(max - min)
+
+               m = (numClasses - 1)/(max - min)
+               b = m*min
+            */
+
+            if (max == min) {
+                m = 0;
+                b = 0;
+            }
+            else {
+                m = (numClasses - 1)/(max - min);
+                b = m*min;
+            }
+        }
+
+        /**
+         * Implements the double to integer lookup.
+         * @param value The value to map.
+         * @return The mapped value.
+         */
+        public int toIndex(double value) {
+            return Double.isNaN(value)
+                ? -1
+                : Math.min(Math.max(0, (int)Math.round(m*value + b)), numClasses-1);
+        }
+    } // class IsoClasses
+
+    /**
+     * Internal representation of the raster.
+     */
+    protected double [] raster;
+    /**
+     * The width of the raster.
+     */
+    protected int       width;
+
+    /**
+     * Default constructor. Creates an 0x0 raster.
+     */
+    public Raster() {
+        this(new double[0], 0);
+    }
+
+    /**
+     * Creates a raster with given values and width.
+     * @param raster
+     * @param width
+     */
+    public Raster(double [] raster, int width) {
+        this.raster = raster;
+        this.width  = width;
+    }
+
+    /**
+     * The width of the raster.
+     * @return The width.
+     */
+    public int getWidth() {
+        return width;
+    }
+
+    /**
+     * The height of the raster.
+     * @return The height.
+     */
+    public int getHeight() {
+        return raster.length / width;
+    }
+
+    /**
+     * The values of the raster.
+     * @return The values.
+     */
+    public double [] getValues() {
+        return raster;
+    }
+
+    /**
+     * Creates an integer representation of this raster using a
+     * given value to index transformer.
+     * @param valueToIndex The transformer to map double to integer values.
+     * @return The new created integer representation.
+     */
+    public int [] toIndexed(ValueToIndex valueToIndex) {
+        int [] dst = new int[raster.length];
+        for (int i = 0; i < raster.length; ++i) {
+            dst[i] = valueToIndex.toIndex(raster[i]);
+        }
+        return dst;
+    }
+
+    /**
+     * Creates a new raster by applying a kernel to this raster.
+     * Access to the raster is continuous at the border.
+     * The original raster is not modified.
+     * @param kernel The kernel to be applied.
+     * @return The new raster.
+     */
+    public Raster create(Kernel kernel) {
+        double [] dst = new double[raster.length];
+        Raster r = new Raster(dst, width);
+        r.apply(kernel, continueBorder());
+        return r;
+    }
+
+    /**
+     * Creates a new raster by applying a kernel to this raster.
+     * Access to the raster is given.
+     * @param kernel The kernel to be applied.
+     * @param access The access method to the raster.
+     */
+    public void apply(Kernel kernel, Access access) {
+        int height = getHeight();
+        for (int j = 0; j < height; ++j) {
+            int row = j*width;
+            for (int i = 0; i < width; ++i) {
+                raster[row + i] = kernel.fold(access, i, j);
+            }
+        }
+    }
+
+    /**
+     * Returns a continuous access to the raster. Values
+     * at the border are repeated til infinity if indices
+     * are accessed outside the defined area.
+     * @return The accessor.
+     */
+    public Access continueBorder() {
+        return new Access() {
+            int height = getHeight()-1;
+            public double get(int i, int j) {
+                     if (i < 0)      i = 0;
+                else if (i >= width) i = width-1;
+                     if (j < 0)      j = 0;
+                else if (j > height) j = height;
+                return raster[j*width + i];
+            }
+        };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org