comparison 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
comparison
equal deleted inserted replaced
722:bb3ffe7d719e 875:5e9efdda6894
1 package de.intevation.gnv.raster;
2
3 /**
4 * A 2D double valued representation of a raster array.
5 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
6 */
7 public class Raster
8 {
9 /**
10 * A kernel can be applied to a raster to fold its values.
11 */
12 public static class Kernel {
13
14 /**
15 * The coefficients of the folding matrix.
16 */
17 public static final class Coeff {
18 /**
19 * i delta index of this coefficient.
20 */
21 public int di;
22 /**
23 * i delta index of this coefficient.
24 */
25 public int dj;
26 /**
27 * The weight of this coefficient.
28 */
29 public double c;
30
31 /**
32 * Default constructor
33 */
34 public Coeff() {
35 }
36
37 /**
38 * Constructor to set the delta position and the weight of
39 * the coefficient.
40 * @param di The i delta index.
41 * @param dj The j delta index.
42 * @param c The weight of the index.
43 */
44 public Coeff(int di, int dj, double c) {
45 this.di = di;
46 this.dj = dj;
47 this.c = c;
48 }
49
50 /**
51 * Returns a string representation of this coefficient.
52 * @return The string representation.
53 */
54 @Override
55 public String toString() {
56 return String.valueOf(c);
57 }
58
59 } // class Coeff
60
61 /**
62 * The coefficients of the folding matrix.
63 */
64 protected Coeff [] coeffs;
65 /**
66 * The width of the kernel.
67 */
68 protected int width;
69
70 /**
71 * Default constrcutor.
72 */
73 public Kernel() {
74 }
75
76 /**
77 * Constructor for a kernel with a given coefficient matrix
78 * with a given width.
79 * @param coeffs The coefficients.
80 * @param width The width of the kernel.
81 */
82 public Kernel(Coeff [] coeffs, int width) {
83 this.coeffs = coeffs;
84 this.width = width;
85 }
86
87 /**
88 * Evaluates the 2D Gauss normal distribution a given x, y
89 * and a given sigma.
90 * @param x The x location.
91 * @param y The y location.
92 * @param sigma The sigma of the distribution.
93 * @return The value at point x, y.
94 */
95 public static double gauss(double x, double y, double sigma) {
96 double s2 = sigma*sigma;
97 return 1.0d/(2.0d*Math.PI*s2)*Math.exp(-(x*x + y*y)/(2.0d*s2));
98 }
99
100 /**
101 * Creates a Gauss kernel with sigma = 1 and width 5.
102 * @return The new kernel.
103 */
104 public static Kernel createGauss() {
105 return createGauss(1.0d, 5);
106 }
107
108 /**
109 * Creates a Gauss kernel with a given sigma and width.
110 * @param sigma The sigma value
111 * @param width The width of the kernel.
112 * @return The new kernel.
113 */
114 public static Kernel createGauss(double sigma, int width) {
115 Coeff [] coeffs = new Coeff[width*width];
116 double sum = 0d;
117 for (int j = 0; j < width; ++j) {
118 int y = -width/2 + j;
119 for (int i = 0; i < width; ++i) {
120 int x = -width/2 + i;
121 double c = gauss(x, y, sigma);
122 coeffs[j*width + i] = new Coeff(x, y, c);
123 sum += c;
124 }
125 }
126 sum = 1.0/sum;
127 for (int i = 0; i < coeffs.length; ++i) {
128 coeffs[i].c *= sum;
129 }
130 return new Kernel(coeffs, width);
131 }
132
133 /**
134 * Evaluates this Kernel at the raster at raster index i, j.
135 * @param access The accessor to the raster.
136 * @param i The i raster index.
137 * @param j The j raster index.
138 * @return The folded value at index i, j.
139 */
140 public double fold(Access access, int i, int j) {
141
142 double s = 0.0d;
143
144 double unused = 0d;
145
146 for (int k = 0; k < coeffs.length; ++k) {
147 Coeff coeff = coeffs[k];
148 double v = access.get(i + coeff.di, j + coeff.dj);
149 if (Double.isNaN(v)) {
150 unused += coeff.c;
151 }
152 else {
153 s += v * coeff.c;
154 }
155 }
156
157 return s*(1.0d - unused);
158 }
159
160 /**
161 * Returns a string representation of this kernel.
162 * @return The string representation.
163 */
164 @Override
165 public String toString() {
166 StringBuilder sb = new StringBuilder();
167
168 int height = coeffs.length/width;
169
170 for (int j = 0; j < height; ++j) {
171 if (j > 0) {
172 sb.append(System.getProperty("line.separator"));
173 }
174 for (int i = 0; i < width; ++i) {
175 if (i > 0) {
176 sb.append("\t");
177 }
178 sb.append(coeffs[j*width + i]);
179 }
180 }
181
182 return sb.toString();
183 }
184 } // class Kernel
185
186 /**
187 * Interface of allow special access patterns to the raster,
188 * especially at the border.
189 */
190 public interface Access {
191
192 /**
193 * Returns the raster value at a given index point.
194 * @param i The i index
195 * @param j The j index
196 * @return The value of the raster.
197 */
198 double get(int i, int j);
199
200 } // interface Access
201
202 /**
203 * Interface to decouple the lookup which is needed
204 * to map double values to integers.
205 */
206 public interface ValueToIndex {
207
208 /**
209 * Returns the corresponding integer to a given value.
210 * @param value The value to be queried.
211 * @return The integer value or -1 if no such value was found.
212 */
213 int toIndex(double value);
214
215 } // interface ValueToIndex;
216
217 /**
218 * Brings raster to an integer representation with equal sized bins
219 * of integer vaules.
220 */
221 public static class IsoClasses
222 implements ValueToIndex
223 {
224 /**
225 * Linear scaling factor.
226 */
227 protected double m;
228 /**
229 * Linear offset.
230 */
231 protected double b;
232 /**
233 * Number of classes.
234 */
235 protected int numClasses;
236
237 /**
238 * Constructor to build a lookup to transform
239 * an raster into an equals sized bin integer array.
240 * @param raster The original raster
241 * @param numClasses The number of classes.
242 */
243 public IsoClasses(Raster raster, int numClasses) {
244
245 this.numClasses = numClasses;
246
247 double min = Double.MAX_VALUE;
248 double max = -Double.MAX_VALUE;
249
250 double [] src = raster.raster;
251
252 for (int i = 0; i < src.length; ++i) {
253 double x = src[i];
254 if (!Double.isNaN(x)) {
255 if (x < min) min = x;
256 if (x > max) max = x;
257 }
258 }
259
260 /* f(min) = 0, f(max) = numClasses - 1
261
262 I: 0 = m*min + b
263 II: numClasses - 1 = m*max + b
264
265 II - I:
266 numClasses - 1 = m*(max - min)
267
268 m = (numClasses - 1)/(max - min)
269 b = m*min
270 */
271
272 if (max == min) {
273 m = 0;
274 b = 0;
275 }
276 else {
277 m = (numClasses - 1)/(max - min);
278 b = m*min;
279 }
280 }
281
282 /**
283 * Implements the double to integer lookup.
284 * @param value The value to map.
285 * @return The mapped value.
286 */
287 public int toIndex(double value) {
288 return Double.isNaN(value)
289 ? -1
290 : Math.min(Math.max(0, (int)Math.round(m*value + b)), numClasses-1);
291 }
292 } // class IsoClasses
293
294 /**
295 * Internal representation of the raster.
296 */
297 protected double [] raster;
298 /**
299 * The width of the raster.
300 */
301 protected int width;
302
303 /**
304 * Default constructor. Creates an 0x0 raster.
305 */
306 public Raster() {
307 this(new double[0], 0);
308 }
309
310 /**
311 * Creates a raster with given values and width.
312 * @param raster
313 * @param width
314 */
315 public Raster(double [] raster, int width) {
316 this.raster = raster;
317 this.width = width;
318 }
319
320 /**
321 * The width of the raster.
322 * @return The width.
323 */
324 public int getWidth() {
325 return width;
326 }
327
328 /**
329 * The height of the raster.
330 * @return The height.
331 */
332 public int getHeight() {
333 return raster.length / width;
334 }
335
336 /**
337 * The values of the raster.
338 * @return The values.
339 */
340 public double [] getValues() {
341 return raster;
342 }
343
344 /**
345 * Creates an integer representation of this raster using a
346 * given value to index transformer.
347 * @param valueToIndex The transformer to map double to integer values.
348 * @return The new created integer representation.
349 */
350 public int [] toIndexed(ValueToIndex valueToIndex) {
351 int [] dst = new int[raster.length];
352 for (int i = 0; i < raster.length; ++i) {
353 dst[i] = valueToIndex.toIndex(raster[i]);
354 }
355 return dst;
356 }
357
358 /**
359 * Creates a new raster by applying a kernel to this raster.
360 * Access to the raster is continuous at the border.
361 * The original raster is not modified.
362 * @param kernel The kernel to be applied.
363 * @return The new raster.
364 */
365 public Raster create(Kernel kernel) {
366 double [] dst = new double[raster.length];
367 Raster r = new Raster(dst, width);
368 r.apply(kernel, continueBorder());
369 return r;
370 }
371
372 /**
373 * Creates a new raster by applying a kernel to this raster.
374 * Access to the raster is given.
375 * @param kernel The kernel to be applied.
376 * @param access The access method to the raster.
377 */
378 public void apply(Kernel kernel, Access access) {
379 int height = getHeight();
380 for (int j = 0; j < height; ++j) {
381 int row = j*width;
382 for (int i = 0; i < width; ++i) {
383 raster[row + i] = kernel.fold(access, i, j);
384 }
385 }
386 }
387
388 /**
389 * Returns a continuous access to the raster. Values
390 * at the border are repeated til infinity if indices
391 * are accessed outside the defined area.
392 * @return The accessor.
393 */
394 public Access continueBorder() {
395 return new Access() {
396 int height = getHeight()-1;
397 public double get(int i, int j) {
398 if (i < 0) i = 0;
399 else if (i >= width) i = width-1;
400 if (j < 0) j = 0;
401 else if (j > height) j = height;
402 return raster[j*width + i];
403 }
404 };
405 }
406 }
407 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org