1 | /* |
---|
2 | * $Id: Contour.java,v 1.15 2001/08/03 22:50:12 dwd Exp $ |
---|
3 | * |
---|
4 | * This software is provided by NOAA for full, free and open release. It is |
---|
5 | * understood by the recipient/user that NOAA assumes no liability for any |
---|
6 | * errors contained in the code. Although this software is released without |
---|
7 | * conditions or restrictions in its use, it is expected that appropriate |
---|
8 | * credit be given to its author and to the National Oceanic and Atmospheric |
---|
9 | * Administration should the software be included by the recipient as an |
---|
10 | * element in other product development. |
---|
11 | */ |
---|
12 | |
---|
13 | package gov.noaa.pmel.sgt.contour; |
---|
14 | |
---|
15 | import gov.noaa.pmel.sgt.CartesianGraph; |
---|
16 | import gov.noaa.pmel.sgt.ContourLineAttribute; |
---|
17 | import gov.noaa.pmel.sgt.DefaultContourLineAttribute; |
---|
18 | import gov.noaa.pmel.sgt.SGLabel; |
---|
19 | import gov.noaa.pmel.sgt.LayerNotFoundException; |
---|
20 | import gov.noaa.pmel.sgt.ContourLevels; |
---|
21 | import gov.noaa.pmel.sgt.ContourLevelNotFoundException; |
---|
22 | |
---|
23 | import gov.noaa.pmel.sgt.dm.SGTGrid; |
---|
24 | |
---|
25 | import gov.noaa.pmel.util.Range2D; |
---|
26 | import gov.noaa.pmel.util.Point2D; |
---|
27 | import gov.noaa.pmel.util.GeoDate; |
---|
28 | import gov.noaa.pmel.util.Debug; |
---|
29 | |
---|
30 | import java.awt.Graphics; |
---|
31 | import java.awt.Color; |
---|
32 | import java.awt.Font; |
---|
33 | import java.awt.FontMetrics; |
---|
34 | |
---|
35 | import java.util.Vector; |
---|
36 | import java.util.Enumeration; |
---|
37 | import java.beans.PropertyChangeListener; |
---|
38 | import java.beans.PropertyChangeEvent; |
---|
39 | |
---|
40 | /** |
---|
41 | * Contour constructs a set of <code>ContourLine</code> objects based on |
---|
42 | * the <code>ContourLevels</code>, <code>SGTGrid</code>, and mask |
---|
43 | * supplied. Used by |
---|
44 | * <code>GridCartesianRenderer</code> for <code>GridAttribute</code> |
---|
45 | * types of CONTOUR. |
---|
46 | * |
---|
47 | * @author D. W. Denbo |
---|
48 | * @version $Revision: 1.15 $, $Date: 2001/08/03 22:50:12 $ |
---|
49 | * @since 2.0 |
---|
50 | * @see ContourLine |
---|
51 | * @see ContourLevels |
---|
52 | * @see gov.noaa.pmel.sgt.GridCartesianRenderer |
---|
53 | * @see gov.noaa.pmel.sgt.GridAttribute |
---|
54 | * @see ContourLineAttribute |
---|
55 | * @see DefaultContourLineAttribute |
---|
56 | */ |
---|
57 | public class Contour implements PropertyChangeListener { |
---|
58 | /** |
---|
59 | * @label grid |
---|
60 | */ |
---|
61 | private SGTGrid grid_; |
---|
62 | /** |
---|
63 | * A non-zero mask value will cause a point to be excluded. |
---|
64 | * @label mask |
---|
65 | */ |
---|
66 | private SGTGrid mask_; |
---|
67 | private CartesianGraph cg_; |
---|
68 | /** |
---|
69 | * @label contourLevels |
---|
70 | */ |
---|
71 | private ContourLevels contourLevels_; |
---|
72 | private double zmin_; |
---|
73 | private double zmax_; |
---|
74 | private boolean upToDate_; |
---|
75 | private GeoDate tref_ = null; |
---|
76 | private boolean xTime_ = false; |
---|
77 | private boolean yTime_ = false; |
---|
78 | private double[] px_; |
---|
79 | private double[] py_; |
---|
80 | private double[] z_; |
---|
81 | private double[] xx_; |
---|
82 | private double[] yy_; |
---|
83 | private double[] zz_; |
---|
84 | private boolean[] used_; |
---|
85 | private int[] kabov_; |
---|
86 | private int[] isin_ = {0, 1, 0, -1}; |
---|
87 | private double weezee_; |
---|
88 | private int nx_; |
---|
89 | private int ny_; |
---|
90 | |
---|
91 | /** |
---|
92 | * @associates <oiref:gov.noaa.pmel.sgt.contour.ContourLine:oiref> |
---|
93 | * @link aggregation |
---|
94 | * @supplierCardinality 1..* |
---|
95 | * @label contourLines |
---|
96 | */ |
---|
97 | private Vector contourLines_; |
---|
98 | |
---|
99 | /** |
---|
100 | * @link aggregationByValue |
---|
101 | * @supplierCardinality 1 |
---|
102 | * @label sides |
---|
103 | */ |
---|
104 | private Sides sides_; |
---|
105 | |
---|
106 | /** |
---|
107 | * @link aggregationByValue |
---|
108 | * @supplierCardinality 1 |
---|
109 | * @label gridFlag |
---|
110 | */ |
---|
111 | private GridFlag gridFlag_; |
---|
112 | // |
---|
113 | private static double DSLAB = 2.0; |
---|
114 | private static double SLAB1F = 0.4; |
---|
115 | /** |
---|
116 | * Construct a <code>Contour</code> object using a range to define |
---|
117 | * the <code>ContourLevels</code>. |
---|
118 | */ |
---|
119 | public Contour(CartesianGraph cg, SGTGrid grid, Range2D range) { |
---|
120 | cg_ = cg; |
---|
121 | grid_ = grid; |
---|
122 | contourLevels_ = ContourLevels.getDefault(range); |
---|
123 | init(); |
---|
124 | upToDate_ = false; |
---|
125 | } |
---|
126 | /** |
---|
127 | * Construct a <code>Contour</code> object using an array of levels to define |
---|
128 | * the <code>ContourLevels</code>. |
---|
129 | */ |
---|
130 | public Contour(CartesianGraph cg, SGTGrid grid, double[] levels) { |
---|
131 | cg_ = cg; |
---|
132 | grid_ = grid; |
---|
133 | contourLevels_ = ContourLevels.getDefault(levels); |
---|
134 | init(); |
---|
135 | upToDate_ = false; |
---|
136 | } |
---|
137 | |
---|
138 | /** |
---|
139 | * Construct a <code>Contour</code> object using a |
---|
140 | * <code>ContourLevels</code> object. |
---|
141 | */ |
---|
142 | public Contour(CartesianGraph cg, SGTGrid grid, ContourLevels conLevels) { |
---|
143 | cg_ = cg; |
---|
144 | grid_ = grid; |
---|
145 | contourLevels_ = conLevels; |
---|
146 | contourLevels_.addPropertyChangeListener(this); |
---|
147 | init(); |
---|
148 | upToDate_ = false; |
---|
149 | } |
---|
150 | /** |
---|
151 | * Get a reference to the <code>ContourLevels</code> object. |
---|
152 | */ |
---|
153 | public ContourLevels getContourLevels() { |
---|
154 | return contourLevels_; |
---|
155 | } |
---|
156 | /** |
---|
157 | * Set a <code>SGTGrid</code> object to be used to mask the data |
---|
158 | * grid. The Z values are used to determine the masking, values of |
---|
159 | * NaN and non-zero are set as MISSING. |
---|
160 | */ |
---|
161 | public void setMask(SGTGrid mask) { |
---|
162 | if(mask_ == null || !mask_.equals(mask)) upToDate_ = false; |
---|
163 | mask_ = mask; |
---|
164 | } |
---|
165 | /** |
---|
166 | * Get the mask. |
---|
167 | */ |
---|
168 | public SGTGrid getMask() { |
---|
169 | return mask_; |
---|
170 | } |
---|
171 | /** |
---|
172 | * Return the <code>Enumeration</code> of a <code>Vector</code> |
---|
173 | * containing the <code>ContourLine</code> objects. |
---|
174 | */ |
---|
175 | public Enumeration elements() { |
---|
176 | if(!upToDate_) generateContourLines(); |
---|
177 | return contourLines_.elements(); |
---|
178 | } |
---|
179 | /** |
---|
180 | * Reponds to changes in the <code>ContourLevels</code> object. |
---|
181 | */ |
---|
182 | public void propertyChange(PropertyChangeEvent event) { |
---|
183 | } |
---|
184 | |
---|
185 | private void init() { |
---|
186 | GeoDate[] ttemp; |
---|
187 | if(grid_.isXTime()) { |
---|
188 | xTime_ = true; |
---|
189 | ttemp = grid_.getTimeArray(); |
---|
190 | tref_ = ttemp[0]; |
---|
191 | px_ = getTimeOffsetArray(ttemp, tref_); |
---|
192 | nx_ = grid_.getTSize(); |
---|
193 | } else { |
---|
194 | px_ = grid_.getXArray(); |
---|
195 | nx_ = grid_.getXSize(); |
---|
196 | } |
---|
197 | if(grid_.isYTime()) { |
---|
198 | yTime_ = true; |
---|
199 | ttemp = grid_.getTimeArray(); |
---|
200 | tref_ = ttemp[0]; |
---|
201 | py_ = getTimeOffsetArray(ttemp, tref_); |
---|
202 | ny_ = grid_.getTSize(); |
---|
203 | } else { |
---|
204 | py_ = grid_.getYArray(); |
---|
205 | ny_ = grid_.getYSize(); |
---|
206 | } |
---|
207 | if(Debug.CONTOUR) { |
---|
208 | System.out.println("nx_ = " + nx_ + ", ny_ = " + ny_); |
---|
209 | } |
---|
210 | /** |
---|
211 | * side definitions |
---|
212 | * ________ |
---|
213 | * | 2 | |
---|
214 | * | | |
---|
215 | * 3| |1 |
---|
216 | * | | |
---|
217 | * |________| |
---|
218 | * 0 |
---|
219 | */ |
---|
220 | sides_ = new Sides(nx_, ny_); |
---|
221 | // |
---|
222 | // make the corner arrays index from 0 to 3 |
---|
223 | // |
---|
224 | xx_ = new double[4]; |
---|
225 | yy_ = new double[4]; |
---|
226 | zz_ = new double[4]; |
---|
227 | used_ = new boolean[4]; |
---|
228 | kabov_ = new int[4]; |
---|
229 | z_ = grid_.getZArray(); |
---|
230 | } |
---|
231 | |
---|
232 | private double[] getTimeOffsetArray(GeoDate[] tarray, GeoDate tref) { |
---|
233 | double[] array = new double[tarray.length]; |
---|
234 | for(int i=0; i < tarray.length; i++) { |
---|
235 | array[i] = tarray[i].offset(tref); |
---|
236 | } |
---|
237 | return array; |
---|
238 | } |
---|
239 | |
---|
240 | private GeoDate[] getTimeArray(double[] array, GeoDate tref) { |
---|
241 | GeoDate[] tarray = new GeoDate[array.length]; |
---|
242 | for(int i=0; i < array.length; i++) { |
---|
243 | tarray[i] = new GeoDate(tref); |
---|
244 | tarray[i].increment(array[i], GeoDate.DAYS); |
---|
245 | } |
---|
246 | return tarray; |
---|
247 | } |
---|
248 | /** |
---|
249 | * Given the current <code>ContourLevels</code>, mask, and |
---|
250 | * <code>SGTGrid</code> generate the <code>ContourLine</code>s. |
---|
251 | */ |
---|
252 | public void generateContourLines() { |
---|
253 | double zrange; |
---|
254 | double zc; |
---|
255 | int kabij, kabip1, kabjp1; |
---|
256 | int used0, used3; |
---|
257 | int ll; |
---|
258 | int i=0; |
---|
259 | int j=0; |
---|
260 | int ii, jj; |
---|
261 | ContourLine cl; |
---|
262 | int lin, k, kmax, nseg; |
---|
263 | boolean reversed; |
---|
264 | double xt, yt, frac; |
---|
265 | double flm1, flp1; |
---|
266 | int lp1, lp2, lm1; |
---|
267 | int kda, kdb; |
---|
268 | int exit; |
---|
269 | |
---|
270 | if(upToDate_) return; |
---|
271 | |
---|
272 | contourLines_ = new Vector(); |
---|
273 | upToDate_ = true; |
---|
274 | computeMinMax(); |
---|
275 | // |
---|
276 | // compute a small non-zero value for testing |
---|
277 | // |
---|
278 | zrange = (zmax_ - zmin_)*1.1; |
---|
279 | if(zrange <= 0.0) return; |
---|
280 | weezee_ = zrange*0.0002; |
---|
281 | // |
---|
282 | // loop over all contour levels |
---|
283 | // |
---|
284 | Enumeration lenum = contourLevels_.levelElements(); |
---|
285 | while(lenum.hasMoreElements()) { |
---|
286 | // |
---|
287 | // initialize for level |
---|
288 | // |
---|
289 | zc = ((Double)lenum.nextElement()).doubleValue(); |
---|
290 | if(Debug.CONTOUR) { |
---|
291 | System.out.println("zc = " + zc); |
---|
292 | } |
---|
293 | if(zc <= zmin_ || zc >= zmax_) continue; |
---|
294 | sides_.clear(); |
---|
295 | if(mask_ != null) { |
---|
296 | gridFlag_ = new GridFlag(grid_, mask_, zc); |
---|
297 | } else { |
---|
298 | gridFlag_ = new GridFlag(grid_, zc); |
---|
299 | } |
---|
300 | for(ii=0; ii < nx_; ii++) { /* 1001 */ |
---|
301 | loop1000: |
---|
302 | for(jj=0; jj < ny_; jj++) { /* 1000 */ |
---|
303 | i = ii; |
---|
304 | j = jj; |
---|
305 | kabij = gridFlag_.getValue(i, j); |
---|
306 | if(i < nx_-1) { |
---|
307 | kabip1 = gridFlag_.getValue(i+1, j); |
---|
308 | } else { |
---|
309 | kabip1 = 10; |
---|
310 | } |
---|
311 | if(j < ny_-1) { |
---|
312 | kabjp1 = gridFlag_.getValue(i, j+1); |
---|
313 | } else { |
---|
314 | kabjp1 = 10; |
---|
315 | } |
---|
316 | used0 = sides_.getSide(i, j, 0); |
---|
317 | used3 = sides_.getSide(i, j, 3); |
---|
318 | ll=0; /* bottom side */ |
---|
319 | if(kabij + kabip1 + used0 == 0) { |
---|
320 | computeCorners(i, j, zc); |
---|
321 | } else if(kabij + kabjp1 + used3 == 0) { |
---|
322 | ll=3; /* left side */ |
---|
323 | computeCorners(i, j, zc); |
---|
324 | } else { |
---|
325 | continue; |
---|
326 | } |
---|
327 | // |
---|
328 | // setup for new contour line |
---|
329 | // |
---|
330 | lin = ll; |
---|
331 | k = 0; |
---|
332 | nseg = 1; |
---|
333 | reversed = false; |
---|
334 | cl = new ContourLine(); |
---|
335 | try { |
---|
336 | cl.setAttributes(contourLevels_.getDefaultContourLineAttribute(), |
---|
337 | contourLevels_.getContourLineAttribute(zc)); |
---|
338 | } catch (ContourLevelNotFoundException e) { |
---|
339 | System.out.println(e); |
---|
340 | } |
---|
341 | cl.setLevel(zc); |
---|
342 | cl.setCartesianGraph(cg_); |
---|
343 | cl.setTime(tref_, xTime_, yTime_); |
---|
344 | contourLines_.addElement(cl); /* add to list */ |
---|
345 | cl.addPoint(0.0, 0.0); /* dummy k=1 point */ |
---|
346 | // |
---|
347 | // Given entrance to square(i,j) on side lin, |
---|
348 | // record the entrance point x(k), y(k). |
---|
349 | // Set lin to used |
---|
350 | // |
---|
351 | // lin lp1 lp2 lm1 |
---|
352 | // _________________________ |
---|
353 | // 0 1 2 3 |
---|
354 | // 1 2 3 0 |
---|
355 | // 2 3 0 1 |
---|
356 | // 3 0 1 2 |
---|
357 | // |
---|
358 | loop350: |
---|
359 | while(true) { /* 350 */ |
---|
360 | lp1 = lin + 1 - ((lin+1)/4)*4; |
---|
361 | lp2 = lp1 + 1 - ((lp1+1)/4)*4; |
---|
362 | lm1 = lp2 + 1 - ((lp2+1)/4)*4; |
---|
363 | if(!reversed) { |
---|
364 | k = k + 1; |
---|
365 | frac = (zc - zz_[lin])/(zz_[lp1] - zz_[lin]); |
---|
366 | xt = xx_[lin] + (xx_[lp1] - xx_[lin])*frac; |
---|
367 | yt = yy_[lin] + (yy_[lp1] - yy_[lin])*frac; |
---|
368 | cl.addPoint(xt, yt); |
---|
369 | sides_.setSideUsed(i, j, lin, true); |
---|
370 | } |
---|
371 | // |
---|
372 | // See if an exit exists on side l-1, l+1, or l+2. |
---|
373 | // If so choose the one closest to side l. If the |
---|
374 | // exit already used terminate x,y. |
---|
375 | // |
---|
376 | reversed = false; |
---|
377 | exit = lm1; |
---|
378 | if(kabov_[lin] + kabov_[lm1] == 0) { |
---|
379 | if(kabov_[lp1] + kabov_[lp2] == 0) { |
---|
380 | flm1 = (zc - zz_[lin])/(zz_[lm1] - zc); |
---|
381 | flp1 = (zc - zz_[lp1])/(zz_[lp2] - zc); |
---|
382 | if(!(used_[lp1] || |
---|
383 | (flm1 <= flp1 && !used_[lm1]))) { |
---|
384 | exit = lp1; |
---|
385 | } |
---|
386 | } |
---|
387 | } else { |
---|
388 | if(kabov_[lp1] + kabov_[lp2] == 0) { |
---|
389 | exit = lp1; |
---|
390 | } else { |
---|
391 | exit = lp2; |
---|
392 | if(kabov_[lp2] + kabov_[lm1] != 0) { /* 470 */ |
---|
393 | if(kabov_[lp2] + kabov_[lm1] <= 15) { |
---|
394 | kda = lin; |
---|
395 | kdb = lp2; |
---|
396 | if(kabov_[lp2] > 5) { |
---|
397 | kda = lm1; |
---|
398 | kdb = lp1; |
---|
399 | } |
---|
400 | k = k + 1; |
---|
401 | frac = (zc - zz_[kda])/(zz_[kdb] - zz_[kda]); |
---|
402 | xt = xx_[kda] + (xx_[kdb] - xx_[kda])*frac; |
---|
403 | yt = yy_[kda] + (yy_[kdb] - yy_[kda])*frac; |
---|
404 | cl.addPoint(xt, yt); |
---|
405 | } |
---|
406 | if(nseg > 1) { |
---|
407 | kmax = k; |
---|
408 | // |
---|
409 | // pt(1) = pt(2) |
---|
410 | // pt(kmax+1) = pt(kmax) |
---|
411 | // |
---|
412 | cl.setElementAt((Point2D.Double)cl.elementAt(1), 0); |
---|
413 | cl.addPoint((Point2D.Double)cl.elementAt(k)); |
---|
414 | cl.setClosed(false); |
---|
415 | cl.setKmax(kmax); |
---|
416 | continue loop1000; |
---|
417 | } |
---|
418 | reversed = true; |
---|
419 | nseg = 2; |
---|
420 | cl.reverseElements(k); |
---|
421 | i=ii; |
---|
422 | j=jj; |
---|
423 | exit = ll; |
---|
424 | // |
---|
425 | // Find square entered by present exit. |
---|
426 | // |
---|
427 | // exit i j lin |
---|
428 | // _________________________ |
---|
429 | // 0 i j-1 2 |
---|
430 | // 1 i+1 j 3 |
---|
431 | // 2 i j+1 0 |
---|
432 | // 3 i-1 j 1 |
---|
433 | // |
---|
434 | i = i + isin_[exit]; |
---|
435 | j = j + isin_[3-exit]; |
---|
436 | lin = exit + 2 - ((exit+2)/4)*4; |
---|
437 | computeCorners(i, j, zc); |
---|
438 | continue loop350; |
---|
439 | } |
---|
440 | } |
---|
441 | } |
---|
442 | if(used_[exit]) { |
---|
443 | kmax = k + 1; |
---|
444 | // |
---|
445 | // pt(kmax) = pt(2) |
---|
446 | // pt(1) = pt(k) |
---|
447 | // pt(kmax+1) = pt(3) |
---|
448 | // |
---|
449 | cl.addPoint((Point2D.Double)cl.elementAt(1)); |
---|
450 | cl.setElementAt((Point2D.Double)cl.elementAt(k), 0); |
---|
451 | cl.addPoint((Point2D.Double)cl.elementAt(2)); |
---|
452 | cl.setClosed(true); |
---|
453 | cl.setKmax(kmax); |
---|
454 | continue loop1000; |
---|
455 | } |
---|
456 | i = i + isin_[exit]; |
---|
457 | j = j + isin_[3-exit]; |
---|
458 | lin = exit + 2 - ((exit+2)/4)*4; |
---|
459 | computeCorners(i, j, zc); |
---|
460 | } /* 350 */ |
---|
461 | } /* 1000 */ |
---|
462 | } /* 1001 */ |
---|
463 | } /* levels */ |
---|
464 | } |
---|
465 | |
---|
466 | private void computeCorners(int i, int j, double zc) { |
---|
467 | int jl, lp1, il; |
---|
468 | boolean[] used = {false, false, false, false}; |
---|
469 | // |
---|
470 | // Get xx,yy,zz,kabov for each corner and |
---|
471 | // used for each side. |
---|
472 | // |
---|
473 | // lcorner il jl lp1 |
---|
474 | // 0 i j 1 |
---|
475 | // 1 i+1 j 2 |
---|
476 | // 2 i+1 j+1 3 |
---|
477 | // 3 i j+1 0 |
---|
478 | // |
---|
479 | // |
---|
480 | // lcorner definitions: |
---|
481 | // (i,j+1) __________ (i+1,j+1) |
---|
482 | // | 2 | |
---|
483 | // | | |
---|
484 | // 3| |1 |
---|
485 | // | | |
---|
486 | // |__________| |
---|
487 | // (i,j) 0 (i+1,j) |
---|
488 | // |
---|
489 | for(int lcorner=0; lcorner < 4; lcorner++) { /* 620 */ |
---|
490 | jl = j + lcorner/2; |
---|
491 | lp1 = lcorner + 1 - ((lcorner+1)/4)*4; |
---|
492 | il = i + lp1/2; |
---|
493 | zz_[lcorner] = Double.NaN; |
---|
494 | kabov_[lcorner] = 10; |
---|
495 | if(((il+1)*(nx_-il) > 0) && ((jl+1)*(ny_-jl) > 0) && |
---|
496 | (!gridFlag_.isMissing(il, jl))) { |
---|
497 | used[lcorner] = sides_.isSideUsed(i, j, lcorner); |
---|
498 | zz_[lcorner] = setZ(z(il, jl), zc); |
---|
499 | if(zz_[lcorner] < zc) { |
---|
500 | kabov_[lcorner] = -1; |
---|
501 | } else { |
---|
502 | kabov_[lcorner] = 1; |
---|
503 | } |
---|
504 | } |
---|
505 | xx_[lcorner] = px_[Math.max(0,Math.min(il,nx_-1))]; |
---|
506 | yy_[lcorner] = py_[Math.max(0,Math.min(jl,ny_-1))]; |
---|
507 | } /* 620 */ |
---|
508 | used_[0] = used[0]; |
---|
509 | used_[1] = used[1]; |
---|
510 | used_[2] = used[2]; |
---|
511 | used_[3] = used[3]; |
---|
512 | } |
---|
513 | |
---|
514 | private double z(int i, int j) { |
---|
515 | return z_[j + i*ny_]; |
---|
516 | } |
---|
517 | |
---|
518 | private double setZ(double z, double zc) { |
---|
519 | double diff = z - zc; |
---|
520 | if(Math.abs(diff) < weezee_) { |
---|
521 | return zc + weezee_ * (diff>0.0?1.0:-1.0); |
---|
522 | } else { |
---|
523 | return z; |
---|
524 | } |
---|
525 | } |
---|
526 | |
---|
527 | private void computeMinMax() { |
---|
528 | double[] grid = grid_.getZArray(); |
---|
529 | double[] mask = null; |
---|
530 | boolean haveMask = mask_ != null; |
---|
531 | if(haveMask) mask = mask_.getZArray(); |
---|
532 | |
---|
533 | zmin_ = Double.POSITIVE_INFINITY; |
---|
534 | zmax_ = Double.NEGATIVE_INFINITY; |
---|
535 | |
---|
536 | for(int i=0; i < grid.length; i++) { |
---|
537 | if(!Double.isNaN(grid[i]) && |
---|
538 | !(haveMask && (mask[i] == 0.0))) { |
---|
539 | zmin_ = Math.min(zmin_, grid[i]); |
---|
540 | zmax_ = Math.max(zmax_, grid[i]); |
---|
541 | } |
---|
542 | } |
---|
543 | } |
---|
544 | /** |
---|
545 | * Given the computed <code>ContourLine</code>s and the |
---|
546 | * <code>ContourLineAttribute</code> generate the contour labels. |
---|
547 | * Must be only invoked after generateConourLines(). |
---|
548 | */ |
---|
549 | public void generateContourLabels(Graphics g) { |
---|
550 | int i, j, k, kk, km1, kp1; |
---|
551 | int nx, ny; |
---|
552 | double dx, dy, dzdx, dzdy, dzdg; |
---|
553 | GeoDate tref = null; |
---|
554 | GeoDate time; |
---|
555 | double[] x, y, z, s; |
---|
556 | double dxx, dyy, smax; |
---|
557 | double space = 0.0; |
---|
558 | double ark = 1.0; |
---|
559 | boolean roomFound; |
---|
560 | double slab1, stest; |
---|
561 | double width, hgt; |
---|
562 | double xa, xb, ya, yb, aa, bb, cc, zxy; |
---|
563 | double xendl, yendl; |
---|
564 | // double xst, yst, xstp, ystp; |
---|
565 | double xlab, ylab, hhgt, angle; |
---|
566 | SGLabel label; |
---|
567 | double[] px, py; |
---|
568 | boolean xIncreasing, yIncreasing; |
---|
569 | double dlev, cspace; |
---|
570 | Enumeration elem = contourLines_.elements(); |
---|
571 | ContourLine cl; |
---|
572 | while(elem.hasMoreElements()) { |
---|
573 | cl = (ContourLine)elem.nextElement(); |
---|
574 | int kmax = cl.getKmax(); |
---|
575 | int lev = contourLevels_.getIndex(cl.getLevel()); |
---|
576 | if(Debug.CONTOUR) { |
---|
577 | // System.out.println("drawLabelContourLine: lev = " + lev + |
---|
578 | // ", level = " + cl.getLevel()); |
---|
579 | } |
---|
580 | DefaultContourLineAttribute cattr = cl.getDefaultContourLineAttribute(); |
---|
581 | cattr.setContourLineAttribute(cl.getContourLineAttribute()); |
---|
582 | if(kmax <= 1 || !cattr.isLabelEnabled()) continue; |
---|
583 | // |
---|
584 | // create SGLabel at a dummy location and no rotation |
---|
585 | // |
---|
586 | label = new SGLabel("CLevel", |
---|
587 | cattr.getLabelText(), |
---|
588 | cattr.getLabelHeightP(), |
---|
589 | new Point2D.Double(0.0,0.0), |
---|
590 | SGLabel.BOTTOM, |
---|
591 | SGLabel.LEFT); |
---|
592 | if(cattr.getLabelFont() != null) label.setFont(cattr.getLabelFont()); |
---|
593 | label.setColor(cattr.getLabelColor()); |
---|
594 | label.setLayer(cg_.getLayer()); |
---|
595 | // |
---|
596 | // compute hgt and width from font |
---|
597 | // |
---|
598 | int swidth = (int)label.getStringWidth(g); |
---|
599 | int sheight = (int)label.getStringHeight(g); |
---|
600 | width = cg_.getLayer().getXDtoP(swidth) - cg_.getLayer().getXDtoP(0); |
---|
601 | // |
---|
602 | hgt = cg_.getLayer().getYDtoP(0) - cg_.getLayer().getYDtoP(sheight); |
---|
603 | // |
---|
604 | hhgt = hgt*0.5; |
---|
605 | width = width + hhgt; |
---|
606 | if(Debug.CONTOUR) { |
---|
607 | // System.out.println("drawLabeledContourLine: hhgt,width = " + |
---|
608 | // hhgt + ", " + width); |
---|
609 | } |
---|
610 | // |
---|
611 | px = xArrayP(); |
---|
612 | py = yArrayP(); |
---|
613 | z = grid_.getZArray(); |
---|
614 | xIncreasing = px[0] < px[1]; |
---|
615 | yIncreasing = py[0] < py[1]; |
---|
616 | nx = px.length; |
---|
617 | ny = py.length; |
---|
618 | // |
---|
619 | x = new double[kmax+1]; |
---|
620 | y = new double[kmax+1]; |
---|
621 | s = new double[kmax+1]; |
---|
622 | if(cl.isXTime() || cl.isYTime()) { |
---|
623 | tref = cl.getReferenceTime(); |
---|
624 | } |
---|
625 | s[1] = 0.0; |
---|
626 | // |
---|
627 | // convert ContourLine to physical units |
---|
628 | // |
---|
629 | x = cl.getXArrayP(); |
---|
630 | y = cl.getYArrayP(); |
---|
631 | // |
---|
632 | // compute s[k] |
---|
633 | // |
---|
634 | for(k=2; k <= kmax; k++) { |
---|
635 | dxx = x[k] - x[k-1]; |
---|
636 | dyy = y[k] - y[k-1]; |
---|
637 | s[k] = s[k-1] + Math.sqrt(dxx*dxx+dyy*dyy); |
---|
638 | } |
---|
639 | smax = s[kmax]; |
---|
640 | slab1 = smax*SLAB1F; |
---|
641 | stest = Math.max(0.0, DSLAB - slab1); |
---|
642 | k=1; |
---|
643 | // |
---|
644 | // check conditions for labelling |
---|
645 | // |
---|
646 | while(k < kmax) { /* 755 */ |
---|
647 | km1 = Math.max(k-1, 1); |
---|
648 | stest = stest + s[k] - s[km1]; |
---|
649 | // |
---|
650 | // Test if there is enough room for a label |
---|
651 | // |
---|
652 | if(stest < DSLAB || (smax - s[k]) <= 2.0*width) { |
---|
653 | // drawLineSegment(g, x[k], y[k], x[k+1], y[k+1]); |
---|
654 | k=k+1; |
---|
655 | continue; |
---|
656 | } |
---|
657 | kp1 = k + 1; |
---|
658 | // |
---|
659 | // gradient test |
---|
660 | // |
---|
661 | if(lev != 0) { |
---|
662 | try { |
---|
663 | dlev = Math.abs(contourLevels_.getLevel(lev) - |
---|
664 | contourLevels_.getLevel(lev-1)); |
---|
665 | if(xIncreasing) { |
---|
666 | for(i=0; i < nx-1; i++) { |
---|
667 | if(x[k] >= px[i] && x[k] <= px[i+1]) break; |
---|
668 | } |
---|
669 | } else { |
---|
670 | for(i=0; i < nx-1; i++) { |
---|
671 | if(x[k] <= px[i] && x[k] >= px[i+1]) break; |
---|
672 | } |
---|
673 | } |
---|
674 | if(yIncreasing) { |
---|
675 | for(j=0; j < ny-1; j++) { |
---|
676 | if(y[k] >= py[j] && y[k] <= py[j+1]) break; |
---|
677 | } |
---|
678 | } else { |
---|
679 | for(j=0; j < ny-1; j++) { |
---|
680 | if(y[k] <= py[j] && y[k] >= py[j+1]) break; |
---|
681 | } |
---|
682 | } |
---|
683 | i = Math.min(i, nx-2); |
---|
684 | j = Math.min(j, ny-2); |
---|
685 | dx = px[i+1] - px[i]; |
---|
686 | dy = py[j+1] - py[j]; |
---|
687 | if(Double.isNaN(z[j+(i+1)*ny])) { |
---|
688 | dzdx = 0.0; |
---|
689 | } else { |
---|
690 | dzdx = (z[j+(i+1)*ny] - z[j+i*ny])/dx; |
---|
691 | } |
---|
692 | if(Double.isNaN(z[j+1+i*ny])) { |
---|
693 | dzdy = 0.0; |
---|
694 | } else { |
---|
695 | dzdy = (z[j+1+i*ny] - z[j+i*ny])/dy; |
---|
696 | } |
---|
697 | // |
---|
698 | // dzdg = sqrt(dzdx*dzdx+dzdy*dzdy) |
---|
699 | // |
---|
700 | // replace with less prone to overflow |
---|
701 | // calculation |
---|
702 | // |
---|
703 | dzdg = Math.abs(dzdx) + Math.abs(dzdy); |
---|
704 | if(dzdg != 0.0) { |
---|
705 | cspace = dlev/dzdg; |
---|
706 | // |
---|
707 | // is there room for label height? |
---|
708 | // (was 0.75) |
---|
709 | if(cspace < hgt*1.0) { |
---|
710 | // drawLineSegment(g, x[k], y[k], x[k+1], y[k+1]); |
---|
711 | k=k+1; |
---|
712 | continue; |
---|
713 | } |
---|
714 | } |
---|
715 | } catch (ContourLevelNotFoundException e) { |
---|
716 | System.out.println(e); |
---|
717 | } |
---|
718 | } |
---|
719 | // |
---|
720 | // test line arc |
---|
721 | // |
---|
722 | roomFound = false; |
---|
723 | for(kk=kp1; kk <= kmax; kk++) { |
---|
724 | dxx = x[kk] - x[k]; |
---|
725 | dyy = y[kk] - y[k]; |
---|
726 | space = Math.sqrt(dxx*dxx + dyy*dyy); |
---|
727 | ark = s[kk] - s[k]; |
---|
728 | if(space >= width) { |
---|
729 | roomFound = true; |
---|
730 | break; |
---|
731 | } |
---|
732 | } |
---|
733 | if(space/ark < 0.80 || !roomFound) { |
---|
734 | // drawLineSegment(g, x[k], y[k], x[k+1], y[k+1]); |
---|
735 | k=k+1; |
---|
736 | continue; |
---|
737 | } else { |
---|
738 | // |
---|
739 | // add label to contour line |
---|
740 | // |
---|
741 | cl.addLabel(k, (SGLabel)label.copy(), hgt, width); |
---|
742 | // |
---|
743 | // draw the label |
---|
744 | // |
---|
745 | stest = 0.0; /* 810 */ |
---|
746 | // xa = x[kk-1] - x[k]; |
---|
747 | // xb = x[kk] - x[kk-1]; |
---|
748 | // ya = y[kk-1] - y[k]; |
---|
749 | // yb = y[kk] - y[kk-1]; |
---|
750 | // aa = xb*xb + yb*yb; |
---|
751 | // bb = xa*xb + ya*yb; |
---|
752 | // cc = xa*xa + ya*ya - width*width; |
---|
753 | // zxy = (-bb + Math.sqrt(bb*bb - aa*cc))/aa; |
---|
754 | // dxx = xa + xb*zxy; |
---|
755 | // dyy = ya + yb*zxy; |
---|
756 | // xendl = x[k] + dxx; |
---|
757 | // yendl = y[k] + dyy; |
---|
758 | // // |
---|
759 | // // compute label angle |
---|
760 | // // |
---|
761 | // angle = 90.0; |
---|
762 | // if(dyy < 0.0) angle = -90.0; |
---|
763 | // if(dxx != 0.0) { |
---|
764 | // angle = Math.atan(dyy/dxx)*180.0/Math.PI; |
---|
765 | // } |
---|
766 | // // |
---|
767 | // // compute label position |
---|
768 | // // |
---|
769 | // if(dxx >= 0) { |
---|
770 | // xlab = x[k] + hhgt*(0.5*dxx + dyy)/width; |
---|
771 | // ylab = y[k] + hhgt*(0.5*dyy - dxx)/width; |
---|
772 | // } else { |
---|
773 | // xlab = xendl - hhgt*(0.5*dxx + dyy)/width; |
---|
774 | // ylab = yendl - hhgt*(0.5*dyy - dxx)/width; |
---|
775 | // } |
---|
776 | // label.setAngle(angle); |
---|
777 | // label.setLocationP(new Point2D.Double(xlab, ylab)); |
---|
778 | // try { |
---|
779 | // label.draw(g); |
---|
780 | // // drawRotatedRectangle(g, angle, xlab, ylab, width-hhgt, hgt); |
---|
781 | // } catch (LayerNotFoundException e) { |
---|
782 | // System.out.println(e); |
---|
783 | // } |
---|
784 | // drawLineSegment(g, xendl, yendl, x[kk], y[kk]); |
---|
785 | k = kk; |
---|
786 | } |
---|
787 | } |
---|
788 | } |
---|
789 | } |
---|
790 | |
---|
791 | private void drawRotatedRectangle(Graphics g, |
---|
792 | double angle, |
---|
793 | double x0, double y0, |
---|
794 | double width, double height) { |
---|
795 | double sinthta = Math.sin(angle*Math.PI/180.0); |
---|
796 | double costhta = Math.cos(angle*Math.PI/180.0); |
---|
797 | double[] x, y; |
---|
798 | double xp, yp; |
---|
799 | int[] xd, yd; |
---|
800 | double xorig, yorig; |
---|
801 | x = new double[4]; |
---|
802 | y = new double[4]; |
---|
803 | xd = new int[5]; |
---|
804 | yd = new int[5]; |
---|
805 | xorig = x0; |
---|
806 | yorig = y0; |
---|
807 | x[0] = x0; |
---|
808 | y[0] = y0; |
---|
809 | x[1] = x[0] + width; |
---|
810 | y[1] = y[0]; |
---|
811 | x[2] = x[1]; |
---|
812 | y[2] = y[0] + height; |
---|
813 | x[3] = x[0]; |
---|
814 | y[3] = y[2]; |
---|
815 | for(int i=0; i < 4; i++) { |
---|
816 | xp = (x[i]-xorig)*costhta - (y[i]-yorig)*sinthta + xorig; |
---|
817 | yp = (y[i]-yorig)*costhta + (x[i]-xorig)*sinthta + yorig; |
---|
818 | xd[i] = cg_.getLayer().getXPtoD(xp); |
---|
819 | yd[i] = cg_.getLayer().getYPtoD(yp); |
---|
820 | } |
---|
821 | xd[4] = xd[0]; |
---|
822 | yd[4] = yd[0]; |
---|
823 | g.setColor(Color.blue); |
---|
824 | g.drawPolyline(xd, yd, 5); |
---|
825 | g.setColor(Color.black); |
---|
826 | } |
---|
827 | |
---|
828 | // private void drawLineSegment(Graphics g, double x0, double y0, |
---|
829 | // double x1, double y1) { /* 900 */ |
---|
830 | // int xd0, yd0, xd1, yd1; |
---|
831 | // xd0 = cg_.getLayer().getXPtoD(x0); |
---|
832 | // yd0 = cg_.getLayer().getYPtoD(y0); |
---|
833 | // xd1 = cg_.getLayer().getXPtoD(x1); |
---|
834 | // yd1 = cg_.getLayer().getYPtoD(y1); |
---|
835 | // g.drawLine(xd0, yd0, xd1, yd1); |
---|
836 | // } |
---|
837 | |
---|
838 | private double[] xArrayP() { |
---|
839 | int i; |
---|
840 | double[] p; |
---|
841 | if(grid_.isXTime()) { |
---|
842 | GeoDate[] t = grid_.getTimeArray(); |
---|
843 | p = new double[t.length]; |
---|
844 | for(i=0; i < t.length; i++) { |
---|
845 | p[i] = cg_.getXUtoP(t[i]); |
---|
846 | } |
---|
847 | } else { |
---|
848 | double[] x = grid_.getXArray(); |
---|
849 | p = new double[x.length]; |
---|
850 | for(i=0; i < x.length; i++) { |
---|
851 | p[i] = cg_.getXUtoP(x[i]); |
---|
852 | } |
---|
853 | } |
---|
854 | return p; |
---|
855 | } |
---|
856 | |
---|
857 | private double[] yArrayP() { |
---|
858 | int i; |
---|
859 | double[] p; |
---|
860 | if(grid_.isYTime()) { |
---|
861 | GeoDate[] t = grid_.getTimeArray(); |
---|
862 | p = new double[t.length]; |
---|
863 | for(i=0; i < t.length; i++) { |
---|
864 | p[i] = cg_.getYUtoP(t[i]); |
---|
865 | } |
---|
866 | } else { |
---|
867 | double[] y = grid_.getYArray(); |
---|
868 | p = new double[y.length]; |
---|
869 | for(i=0; i < y.length; i++) { |
---|
870 | p[i] = cg_.getYUtoP(y[i]); |
---|
871 | } |
---|
872 | } |
---|
873 | return p; |
---|
874 | } |
---|
875 | |
---|
876 | // private void drawContourLine(Graphics g, ContourLine cl) { |
---|
877 | // GeoDate tref, time; |
---|
878 | // Point2D.Double[] pt; |
---|
879 | // int i; |
---|
880 | // // int size = cl.size(); |
---|
881 | // int size = cl.getKmax() + 1; |
---|
882 | // if(size <= 3) return; |
---|
883 | // int[] xp = new int[size]; |
---|
884 | // int[] yp = new int[size]; |
---|
885 | // pt = new Point2D.Double[size]; |
---|
886 | // for(i=0; i < size; i++) { |
---|
887 | // pt[i] = (Point2D.Double)cl.elementAt(i); |
---|
888 | // } |
---|
889 | // if(cl.isXTime()) { |
---|
890 | // tref = cl.getReferenceTime(); |
---|
891 | // for(i=0; i < size; i++) { |
---|
892 | // time = (new GeoDate(tref)).increment(pt[i].x, GeoDate.DAYS); |
---|
893 | // xp[i] = cg_.getXUtoD(time); |
---|
894 | // } |
---|
895 | // } else { |
---|
896 | // for(i=0; i < size; i++) { |
---|
897 | // xp[i] = cg_.getXUtoD(pt[i].x); |
---|
898 | // } |
---|
899 | // } |
---|
900 | // if(cl.isYTime()) { |
---|
901 | // tref = cl.getReferenceTime(); |
---|
902 | // for(i=0; i < size; i++) { |
---|
903 | // time = (new GeoDate(tref)).increment(pt[i].y, GeoDate.DAYS); |
---|
904 | // yp[i] = cg_.getYUtoD(time); |
---|
905 | // } |
---|
906 | // } else { |
---|
907 | // for(i=0; i < size; i++) { |
---|
908 | // yp[i] = cg_.getYUtoD(pt[i].y); |
---|
909 | // } |
---|
910 | // } |
---|
911 | // g.drawPolyline(xp, yp, size); |
---|
912 | // } |
---|
913 | } |
---|