Mercurial > dive4elements > gnv-client
view gnv-artifacts/src/main/java/de/intevation/gnv/raster/Raster.java @ 1129:ccfa07b88476
merged geo-backend
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:01 +0200 |
parents | f953c9a559d8 |
children |
line wrap: on
line source
/* * 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; /** * 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 :