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