Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/raster/Vectorizer.java @ 801:d766fe2d917a
More javadoc.
gnv-artifacts/trunk@883 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Tue, 06 Apr 2010 16:53:43 +0000 |
parents | 6cff63d0c434 |
children | f953c9a559d8 |
comparison
equal
deleted
inserted
replaced
800:db5b04ecb426 | 801:d766fe2d917a |
---|---|
8 import java.util.List; | 8 import java.util.List; |
9 | 9 |
10 import org.apache.log4j.Logger; | 10 import org.apache.log4j.Logger; |
11 | 11 |
12 /** | 12 /** |
13 * Instances of this class are able to vectorize 2D integer arrays | |
14 * with a kind of flood filling regions detection mechanism to | |
15 * a set of line strings and polygons. | |
16 * | |
13 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | 17 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> |
14 */ | 18 */ |
15 public class Vectorizer | 19 public class Vectorizer |
16 { | 20 { |
17 private static Logger log = Logger.getLogger(Vectorizer.class); | 21 private static Logger log = Logger.getLogger(Vectorizer.class); |
18 | 22 |
23 /** | |
24 * Callback to process the found line strings and polygons. | |
25 */ | |
19 public interface RingsHandler { | 26 public interface RingsHandler { |
20 | 27 |
28 /** | |
29 * Called from {@link #process(de.intevation.gnv.raster.Vectorizer.RingsHandler) } | |
30 * to give the found features to the postprocessing backend. | |
31 * @param rings The found line strings and polygons. | |
32 * @param value The integer value of the raster surrounded by | |
33 * the features. | |
34 * @param width The width of the index space of the vectorized data. | |
35 * @param height The height of the index space of the vectorized data. | |
36 */ | |
21 void handleRings( | 37 void handleRings( |
22 List<Edge> rings, | 38 List<Edge> rings, |
23 int value, | 39 int value, |
24 int width, | 40 int width, |
25 int height); | 41 int height); |
26 | 42 |
27 } // interface RingsHandler | 43 } // interface RingsHandler |
28 | 44 |
45 /** | |
46 * Doubly link list representing a line string or a polygon. | |
47 */ | |
29 public static final class Edge { | 48 public static final class Edge { |
30 | 49 |
50 /** | |
51 * The predecessor. | |
52 */ | |
31 public Edge prev; | 53 public Edge prev; |
54 /** | |
55 * The successor. | |
56 */ | |
32 public Edge next; | 57 public Edge next; |
33 | 58 |
59 /** | |
60 * Index of first vertex. To separate x and y values | |
61 * you have to divide the value by the width of the | |
62 * index space. | |
63 */ | |
34 public int a; | 64 public int a; |
65 /** | |
66 * Index of second vertex. To separate x and y values | |
67 * you have to divide the value by the width of the | |
68 * index space. | |
69 */ | |
35 public int b; | 70 public int b; |
36 | 71 |
72 /** | |
73 * Default constructor. | |
74 */ | |
37 public Edge() { | 75 public Edge() { |
38 } | 76 } |
39 | 77 |
78 /** | |
79 * Constructor to create Edge with two vertices. | |
80 * @param a The first vertex. | |
81 * @param b The second vertex. | |
82 */ | |
40 public Edge(int a, int b) { | 83 public Edge(int a, int b) { |
41 this.a = a; | 84 this.a = a; |
42 this.b = b; | 85 this.b = b; |
43 } | 86 } |
44 | 87 |
88 /** | |
89 * Copy constructor | |
90 * @param other The edge to clone. | |
91 */ | |
45 public Edge(Edge other) { | 92 public Edge(Edge other) { |
46 a = other.a; | 93 a = other.a; |
47 b = other.b; | 94 b = other.b; |
48 } | 95 } |
49 | 96 |
97 /** | |
98 * Chain a given edge segment to this segment. The | |
99 * side of the chaining is determined by a given | |
100 * parameter. | |
101 * @param other The segment to chain. | |
102 * @param found The side to chain. | |
103 */ | |
50 public void chain(Edge other, int found) { | 104 public void chain(Edge other, int found) { |
51 | 105 |
52 if (found == a) { | 106 if (found == a) { |
53 other.next = this; | 107 other.next = this; |
54 prev = other; | 108 prev = other; |
62 } | 116 } |
63 | 117 |
64 throw new IllegalStateException("cannot chain"); | 118 throw new IllegalStateException("cannot chain"); |
65 } | 119 } |
66 | 120 |
121 /** | |
122 * Tells if the list is complete which means that this | |
123 * edge list is a closed polygon. | |
124 * @return true if edge list is closed else false. | |
125 */ | |
67 public boolean isComplete() { | 126 public boolean isComplete() { |
68 Edge current = this; | 127 Edge current = this; |
69 do { | 128 do { |
70 if (current.prev == null || current.next == null) { | 129 if (current.prev == null || current.next == null) { |
71 return false; | 130 return false; |
74 } | 133 } |
75 while (current != this); | 134 while (current != this); |
76 return true; | 135 return true; |
77 } | 136 } |
78 | 137 |
138 /** | |
139 * Returns the length of this edge list in next direction. | |
140 * Segments in prev direction are ignored. | |
141 * @return The length of this edge list. | |
142 */ | |
79 public int length() { | 143 public int length() { |
80 int length = 0; | 144 int length = 0; |
81 Edge current = this; | 145 Edge current = this; |
82 do { ++length; } | 146 do { ++length; } |
83 while ((current = current.next) != null && current != this); | 147 while ((current = current.next) != null && current != this); |
84 return length; | 148 return length; |
85 } | 149 } |
86 | 150 |
87 public Edge head() { | 151 /** |
88 Edge current = this; | 152 * Returns the head node of this edge list. |
89 while (current.prev != null) { | 153 * @return The head node. |
90 current = current.prev; | 154 */ |
91 } | 155 public Edge head() { |
92 return current; | 156 Edge current = this; |
93 } | 157 while (current.prev != null) { |
94 | 158 current = current.prev; |
159 } | |
160 return current; | |
161 } | |
162 | |
163 /** | |
164 * Hashes the two vertex indices to a common value. | |
165 * @return The hash value. | |
166 */ | |
167 @Override | |
95 public int hashCode() { | 168 public int hashCode() { |
96 return (a << 16) | b; | 169 return (a << 16) | b; |
97 } | 170 } |
98 | 171 |
172 /** | |
173 * Two edges are considered equal if they have the same | |
174 * a and b vertices. | |
175 * @param other The other edge. | |
176 * @return true if the edges are equal else false. | |
177 */ | |
178 @Override | |
99 public boolean equals(Object other) { | 179 public boolean equals(Object other) { |
100 Edge e = (Edge)other; | 180 Edge e = (Edge)other; |
101 return a == e.a && b == e.b; | 181 return a == e.a && b == e.b; |
102 } | 182 } |
103 } // class Edge | 183 } // class Edge |
104 | 184 |
185 /** | |
186 * Simplifies a given edge list by removing collinear vertices. | |
187 * Attention: The original list is modified. | |
188 * @param edge The edge list to simplify. | |
189 * @param width The width of the vertex index space. | |
190 * @return The simplified list. | |
191 */ | |
105 protected static Edge simplify(Edge edge, int width) { | 192 protected static Edge simplify(Edge edge, int width) { |
106 | 193 |
107 Edge e1 = edge, start = edge; | 194 Edge e1 = edge, start = edge; |
108 | 195 |
109 int length = edge.length(); | 196 int length = edge.length(); |
148 while (length > 1 && e2 != null && count < length + 2); | 235 while (length > 1 && e2 != null && count < length + 2); |
149 | 236 |
150 return start; | 237 return start; |
151 } | 238 } |
152 | 239 |
240 /** | |
241 * The raster to be traced. | |
242 */ | |
153 protected int [] raster; | 243 protected int [] raster; |
244 /** | |
245 * The width of the raster. | |
246 */ | |
154 protected int width; | 247 protected int width; |
248 /** | |
249 * Map of the currently open edges. | |
250 */ | |
155 protected TIntObjectHashMap openEdges; | 251 protected TIntObjectHashMap openEdges; |
252 /** | |
253 * List of rings already found. | |
254 */ | |
156 protected List<Edge> rings; | 255 protected List<Edge> rings; |
256 /** | |
257 * Flag to signal if a simplification should be performed | |
258 * after a ring is completed. | |
259 */ | |
157 protected boolean simplify; | 260 protected boolean simplify; |
158 | 261 |
262 /** | |
263 * Default constructor. Simplification is turned on. | |
264 */ | |
159 public Vectorizer() { | 265 public Vectorizer() { |
160 this(true); | 266 this(true); |
161 } | 267 } |
162 | 268 |
269 /** | |
270 * Constructor to create a vectorized with an explicit | |
271 * simplification policy. | |
272 * @param simplify Indicates if simplification should be | |
273 * used on ring completion. | |
274 */ | |
163 public Vectorizer(boolean simplify) { | 275 public Vectorizer(boolean simplify) { |
164 openEdges = new TIntObjectHashMap(); | 276 openEdges = new TIntObjectHashMap(); |
165 rings = new ArrayList<Edge>(); | 277 rings = new ArrayList<Edge>(); |
166 this.simplify = simplify; | 278 this.simplify = simplify; |
167 } | 279 } |
168 | 280 |
281 /** | |
282 * Constructor to create a vectorizer with a given raster and width. | |
283 * Simplification is turn on. | |
284 * @param raster The raster to be vectorized. | |
285 * @param width The width of the raster. | |
286 */ | |
169 public Vectorizer(int [] raster, int width) { | 287 public Vectorizer(int [] raster, int width) { |
170 this(true, raster, width); | 288 this(true, raster, width); |
171 } | 289 } |
172 | 290 |
291 /** | |
292 * Constructor to create a vectorizer with a given raster, width | |
293 * and an explicit simplification policy. | |
294 * @param simplify Indicates if simplification should be | |
295 * used on ring completion. | |
296 * @param raster The raster to be vectorized. | |
297 * @param width The width of the raster. | |
298 */ | |
173 public Vectorizer(boolean simplify, int [] raster, int width) { | 299 public Vectorizer(boolean simplify, int [] raster, int width) { |
174 this(simplify); | 300 this(simplify); |
175 this.raster = raster; | 301 this.raster = raster; |
176 this.width = width; | 302 this.width = width; |
177 } | 303 } |
178 | 304 |
305 /** | |
306 * Returns (x, y+1) for given vertex in index space. | |
307 * @param i vertex in index space. | |
308 * @param w width of raster. | |
309 * @return (x, y+1) in index space. | |
310 */ | |
179 public static final int tl(int i, int w) { | 311 public static final int tl(int i, int w) { |
180 int x = i % w; | 312 int x = i % w; |
181 int y = i / w; | 313 int y = i / w; |
182 return x + (w + 1)*y; | 314 return x + (w + 1)*y; |
183 } | 315 } |
184 | 316 |
317 /** | |
318 * Returns tl(i, w) + 1. | |
319 * @param i vertex in index space. | |
320 * @param w width of raster. | |
321 * @return tl(i, w) + 1. | |
322 */ | |
185 public static final int tr(int i, int w) { | 323 public static final int tr(int i, int w) { |
186 return tl(i, w) + 1; | 324 return tl(i, w) + 1; |
187 } | 325 } |
188 | 326 |
327 /** | |
328 * Returns tl(i, w) + w + 1. | |
329 * @param i vertex in index space. | |
330 * @param w width of raster. | |
331 * @return tl(i, w) + w + 1. | |
332 */ | |
189 public static final int bl(int i, int w) { | 333 public static final int bl(int i, int w) { |
190 return tl(i, w) + w + 1; | 334 return tl(i, w) + w + 1; |
191 } | 335 } |
192 | 336 |
337 /** | |
338 * Returns bl(i, w) + 1. | |
339 * @param i vertex in index space. | |
340 * @param w width of raster. | |
341 * @return bl(i, w) + 1. | |
342 */ | |
193 public static final int br(int i, int w) { | 343 public static final int br(int i, int w) { |
194 return bl(i, w) + 1; | 344 return bl(i, w) + 1; |
195 } | 345 } |
196 | 346 |
347 /** | |
348 * Resets open resources after a set of features were found. | |
349 */ | |
197 protected void resetRegion() { | 350 protected void resetRegion() { |
198 openEdges.clear(); | 351 openEdges.clear(); |
199 rings.clear(); | 352 rings.clear(); |
200 } | 353 } |
201 | 354 |
355 /** | |
356 * Adds an edge to the map of open edges, joins it | |
357 * with its direct neighbors of if complete add the | |
358 * list to the complete features. | |
359 * @param edge | |
360 */ | |
202 protected void emit(Edge edge) { | 361 protected void emit(Edge edge) { |
203 | 362 |
204 Edge otherA = (Edge)openEdges.remove(edge.a); | 363 Edge otherA = (Edge)openEdges.remove(edge.a); |
205 if (otherA != null) { | 364 if (otherA != null) { |
206 otherA.chain(edge, edge.a); | 365 otherA.chain(edge, edge.a); |
222 openEdges.put(edge.b, edge); | 381 openEdges.put(edge.b, edge); |
223 } | 382 } |
224 } | 383 } |
225 } | 384 } |
226 | 385 |
386 /** | |
387 * Vectorize the raster. The found features are fed into | |
388 * the given ring handler. | |
389 * @param handler The RingHandler to postprocess the found features. | |
390 * @return The number of regions found. | |
391 */ | |
227 public int process(RingsHandler handler) { | 392 public int process(RingsHandler handler) { |
228 | 393 |
229 BitSet visited = new BitSet(raster.length); | 394 BitSet visited = new BitSet(raster.length); |
230 | 395 |
231 TIntStack stack = new TIntStack(); | 396 TIntStack stack = new TIntStack(); |