001 /* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2013 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029 package edu.wisc.ssec.mcidasv.data.cyclone; 030 031 import java.util.ArrayList; 032 import java.util.Calendar; 033 import java.util.Date; 034 import java.util.Hashtable; 035 import java.util.List; 036 037 import ucar.unidata.data.NamedArray; 038 import ucar.unidata.geoloc.LatLonPointImpl; 039 import ucar.unidata.geoloc.LatLonRect; 040 import ucar.unidata.util.Misc; 041 import ucar.visad.Util; 042 import visad.CommonUnit; 043 import visad.DateTime; 044 import visad.Real; 045 import visad.RealType; 046 import visad.VisADException; 047 import visad.georef.EarthLocation; 048 049 /** 050 * Created by IntelliJ IDEA. User: yuanho Date: Apr 9, 2008 Time: 5:00:17 PM To 051 * change this template use File | Settings | File Templates. 052 */ 053 054 public class StormTrack implements Comparable { 055 056 /** _more_ */ 057 private List<StormParam> params = null; 058 059 /** _more_ */ 060 private LatLonRect bbox; 061 062 /** _more_ */ 063 private String trackId; 064 065 /** _more_ */ 066 private StormInfo stormInfo; 067 068 /** _more_ */ 069 private Way way; 070 071 /** _more_ */ 072 private NamedArray lats; 073 074 /** _more_ */ 075 private NamedArray lons; 076 077 /** _more_ */ 078 private List<StormTrackPoint> trackPoints; 079 080 // private Date trackStartTime; 081 082 private Hashtable temporaryProperties = new Hashtable(); 083 084 private static final int DIAMOND_MISSING_VALUE = 9999; 085 086 private boolean isEdited = false; 087 088 /** 089 * _more_ 090 * 091 * @param track 092 * _more_ 093 */ 094 public StormTrack(StormTrack track) { 095 this.stormInfo = track.stormInfo; 096 this.way = track.way; 097 this.params = track.params; 098 this.trackId = track.trackId; 099 this.trackPoints = new ArrayList<StormTrackPoint>(track.trackPoints); 100 } 101 102 /** 103 * _more_ 104 * 105 * @param stormInfo 106 * _more_ 107 * @param way 108 * _more_ 109 * @param pts 110 * _more_ 111 * @param params 112 * _more_ 113 */ 114 public StormTrack(StormInfo stormInfo, Way way, List<StormTrackPoint> pts, 115 StormParam[] params) { 116 this.stormInfo = stormInfo; 117 this.way = way; 118 if (params != null) { 119 this.params = (List<StormParam>) Misc.toList(params); 120 } 121 this.trackPoints = new ArrayList<StormTrackPoint>(pts); 122 StormTrackPoint firstPoint = (StormTrackPoint) pts.get(0); 123 DateTime trackStartTime = firstPoint.getTime(); 124 this.trackId = stormInfo.toString() + "_" + way + "_" 125 + trackStartTime.getValue(); 126 } 127 128 /** 129 * _more_ 130 * 131 * @param stormInfo 132 * _more_ 133 * @param way 134 * _more_ 135 * @param startTime 136 * _more_ 137 * @param params 138 * _more_ 139 */ 140 public StormTrack(StormInfo stormInfo, Way way, DateTime startTime, 141 StormParam[] params) { 142 this.stormInfo = stormInfo; 143 this.way = way; 144 if (params != null) { 145 this.params = (List<StormParam>) Misc.toList(params); 146 } 147 this.trackPoints = new ArrayList(); 148 this.trackId = stormInfo.toString() + "_" + way + "_" 149 + startTime.getValue(); 150 } 151 152 /** 153 * _more_ 154 * 155 * @return _more_ 156 */ 157 public LatLonRect getBoundingBox() { 158 if (trackPoints.size() == 0) { 159 return null; 160 } 161 if (bbox == null) { 162 // public LatLonRect(LatLonPoint left, LatLonPoint right) { 163 double minLon = Double.POSITIVE_INFINITY; 164 double maxLon = Double.NEGATIVE_INFINITY; 165 double minLat = Double.POSITIVE_INFINITY; 166 double maxLat = Double.NEGATIVE_INFINITY; 167 for (StormTrackPoint stp : trackPoints) { 168 EarthLocation el = stp.getLocation(); 169 minLat = Math.min(minLat, el.getLatitude().getValue()); 170 maxLat = Math.max(maxLat, el.getLatitude().getValue()); 171 minLon = Math.min(minLon, el.getLongitude().getValue()); 172 maxLon = Math.max(maxLon, el.getLongitude().getValue()); 173 } 174 175 bbox = new LatLonRect(new LatLonPointImpl(maxLat, minLon), 176 new LatLonPointImpl(minLat, maxLon)); 177 } 178 return bbox; 179 } 180 181 /** 182 * _more_ 183 * 184 * @param o 185 * _more_ 186 * 187 * @return _more_ 188 */ 189 public int compareTo(Object o) { 190 if (o instanceof StormTrack) { 191 StormTrack that = (StormTrack) o; 192 193 double v1 = getStartTime().getValue(); 194 double v2 = that.getStartTime().getValue(); 195 if (v1 < v2) { 196 return -1; 197 } 198 if (v1 > v2) { 199 return 1; 200 } 201 return 0; 202 } 203 return toString().compareTo(o.toString()); 204 } 205 206 /** 207 * _more_ 208 * 209 * @param hour 210 * _more_ 211 * 212 * @return _more_ 213 */ 214 public StormTrackPoint findPointWithForecastHour(int hour) { 215 for (StormTrackPoint stp : trackPoints) { 216 if (stp.getForecastHour() == hour) { 217 return stp; 218 } 219 } 220 return null; 221 } 222 223 /** 224 * _more_ 225 * 226 * @param point 227 * _more_ 228 */ 229 public void addPoint(StormTrackPoint point) { 230 trackPoints.add(point); 231 } 232 233 /** 234 * _more_ 235 * 236 * @return _more_ 237 */ 238 public boolean isObservation() { 239 return way.isObservation(); 240 } 241 242 /** 243 * _more_ 244 * 245 * @return _more_ 246 */ 247 public boolean isEdited() { 248 return isEdited; 249 } 250 251 /** 252 * _more_ 253 * 254 * @return _more_ 255 */ 256 public void setIsEdited(boolean isEdited) { 257 this.isEdited = isEdited; 258 } 259 260 /** 261 * _more_ 262 * 263 * @return _more_ 264 */ 265 public boolean getIsEdited() { 266 return this.isEdited; 267 } 268 269 /** 270 * _more_ 271 * 272 * @return _more_ 273 */ 274 public int hashCode() { 275 return trackId.hashCode(); 276 } 277 278 /** 279 * _more_ 280 * 281 * @param id 282 * _more_ 283 */ 284 public void setId(String id) { 285 this.trackId = id; 286 } 287 288 /** 289 * _more_ 290 * 291 * @return _more_ 292 */ 293 public String getId() { 294 return trackId; 295 } 296 297 /** 298 * _more_ 299 * 300 * @return _more_ 301 */ 302 public DateTime getStartTime() { 303 StormTrackPoint firstPoint = trackPoints.get(0); 304 return firstPoint.getTime(); 305 306 } 307 308 /** 309 * _more_ 310 * 311 * @param stormInfo 312 * _more_ 313 */ 314 public void setStormInfo(StormInfo stormInfo) { 315 this.stormInfo = stormInfo; 316 } 317 318 /** 319 * _more_ 320 * 321 * @return _more_ 322 */ 323 public StormInfo getStormInfo() { 324 return stormInfo; 325 } 326 327 /** 328 * _more_ 329 * 330 * @param way 331 * _more_ 332 */ 333 public void setWay(Way way) { 334 this.way = way; 335 } 336 337 /** 338 * _more_ 339 * 340 * @return _more_ 341 */ 342 public Way getWay() { 343 return way; 344 } 345 346 /** 347 * _more_ 348 * 349 * @param pts 350 * _more_ 351 */ 352 public void setTrackPoints(List<StormTrackPoint> pts) { 353 this.trackPoints = new ArrayList<StormTrackPoint>(pts); 354 } 355 356 /** 357 * _more_ 358 * 359 * @return _more_ 360 */ 361 public List<StormTrackPoint> getTrackPoints() { 362 return trackPoints; 363 } 364 365 /** 366 * _more_ 367 * 368 * @return _more_ 369 */ 370 public List<DateTime> getTrackTimes() { 371 List<DateTime> trackTimes = new ArrayList(); 372 for (StormTrackPoint stp : trackPoints) { 373 trackTimes.add(stp.getTime()); 374 } 375 return trackTimes; 376 } 377 378 /** 379 * _more_ 380 * 381 * @return _more_ 382 */ 383 public List<StormParam> getParams() { 384 if (params == null) { 385 params = new ArrayList<StormParam>(); 386 Hashtable seenParam = new Hashtable(); 387 for (StormTrackPoint stp : trackPoints) { 388 List<Real> reals = stp.getTrackAttributes(); 389 for (Real r : reals) { 390 RealType type = (RealType) r.getType(); 391 if (seenParam.get(type) == null) { 392 seenParam.put(type, type); 393 params.add(new StormParam(type)); 394 } 395 } 396 } 397 } 398 399 return params; 400 } 401 402 /** 403 * _more_ 404 * 405 * @return _more_ 406 */ 407 public List<EarthLocation> getLocations() { 408 List<EarthLocation> locs = new ArrayList(); 409 for (StormTrackPoint stp : trackPoints) { 410 locs.add(stp.getLocation()); 411 } 412 return locs; 413 } 414 415 /** 416 * _more_ 417 * 418 * 419 * 420 * @param param 421 * _more_ 422 * @return _more_ 423 * 424 * @throws VisADException 425 * _more_ 426 */ 427 public Real[] getTrackAttributeValues(StormParam param) 428 throws VisADException { 429 if (param == null) { 430 return null; 431 } 432 int size = trackPoints.size(); 433 Real[] trackAttributes = new Real[size]; 434 Real missing = null; 435 for (int i = 0; i < size; i++) { 436 Real value = trackPoints.get(i).getAttribute(param); 437 if (value == null) { 438 if (i == 0) { 439 return null; 440 } 441 trackAttributes[i] = null; 442 } else { 443 if (missing == null) { 444 missing = value.cloneButValue(Double.NaN); 445 } 446 trackAttributes[i] = value; 447 } 448 } 449 for (int i = 0; i < size; i++) { 450 if (trackAttributes[i] == null) { 451 trackAttributes[i] = missing; 452 } 453 } 454 return trackAttributes; 455 } 456 457 /** 458 * _more_ 459 * 460 * @param trackAttributes 461 * _more_ 462 * @param i 463 * _more_ 464 * 465 * @return _more_ 466 */ 467 public float findClosestAttr(float[] trackAttributes, int i) { 468 int up = i; 469 int down = i; 470 int size = trackAttributes.length; 471 float value = Float.NaN; 472 while (Float.isNaN(value)) { 473 up++; 474 down--; 475 if ((up > 0) && (up < size)) { 476 value = trackAttributes[up]; 477 } 478 if ((down > 0) && (down < size)) { 479 value = trackAttributes[down]; 480 } 481 } 482 return value; 483 } 484 485 /** 486 * _more_ 487 * 488 * @return _more_ 489 */ 490 public String toString() { 491 return trackId; 492 } 493 494 /** 495 * Return the index of the given track point. This kist finds the point with 496 * the same lat/lon 497 * 498 * @param stp 499 * The track point 500 * @return The index or -1 if not found 501 */ 502 public int indexOf(StormTrackPoint stp) { 503 for (int i = 0; i < trackPoints.size(); i++) { 504 if (trackPoints.get(i).getLocation().equals(stp.getLocation())) { 505 return i; 506 } 507 } 508 return -1; 509 } 510 511 /** 512 * _more_ 513 * 514 * @param o 515 * _more_ 516 * 517 * @return _more_ 518 */ 519 public boolean equals(Object o) { 520 if (o == null) { 521 return false; 522 } 523 if (!(o instanceof StormTrack)) { 524 return false; 525 } 526 StormTrack other = (StormTrack) o; 527 return ((trackId.equals(other.trackId))); 528 } 529 530 public void putTemporaryProperty(Object key, Object value) { 531 temporaryProperties.put(key, value); 532 } 533 534 public Object getTemporaryProperty(Object key) { 535 return temporaryProperties.get(key); 536 } 537 538 static public StringBuffer toDiamond7(List<StormTrack> sts, String id) 539 throws VisADException { 540 StringBuffer sb = new StringBuffer(); 541 sb.append("diamond 7 " + id + "TropicalCycloneTrack" + "\n"); 542 for (StormTrack st : sts) { 543 st.toDiamond7(sb, id); 544 } 545 return sb; 546 } 547 548 public void toDiamond7(StringBuffer sb, String id) throws VisADException { 549 Calendar cal = Calendar.getInstance(); 550 List<StormTrackPoint> tpoints = getTrackPoints(); 551 552 sb.append("Name " + id + " " + way + " " + tpoints.size() + "\n"); 553 for (StormTrackPoint stp : tpoints) { 554 Date dttm = null; 555 556 try { 557 dttm = Util.makeDate(stp.getTime()); 558 } catch (Exception excp) { 559 560 } 561 cal.setTime(dttm); 562 String year = Integer.toString(cal.get(Calendar.YEAR)); 563 int mm = cal.get(Calendar.MONTH); 564 String mon = Integer.toString(mm); 565 if (mm < 10) 566 mon = "0" + mon; 567 int dd = cal.get(Calendar.DAY_OF_MONTH); 568 String day = Integer.toString(dd); 569 if (dd < 10) 570 day = "0" + day; 571 int hour = cal.get(Calendar.HOUR_OF_DAY); 572 int fhour = stp.getForecastHour(); 573 EarthLocation el = stp.getLocation(); 574 List<Real> attrs = stp.getTrackAttributes(); 575 576 sb.append(year.substring(2)); 577 sb.append(" "); 578 sb.append(mon); 579 sb.append(" "); 580 sb.append(day); 581 sb.append(" "); 582 sb.append(hour); 583 sb.append(" "); 584 sb.append(fhour); 585 sb.append(" "); 586 sb.append(el.getLongitude().getValue(CommonUnit.degree)); 587 sb.append(" "); 588 sb.append(el.getLatitude().getValue(CommonUnit.degree)); 589 sb.append(" "); 590 591 // TODO: What to do with units? 592 appendDiamondValue(sb, stp 593 .getAttribute(STIStormDataSource.PARAM_MAXWINDSPEED)); 594 appendDiamondValue(sb, stp 595 .getAttribute(STIStormDataSource.PARAM_MINPRESSURE)); 596 appendDiamondValue(sb, stp 597 .getAttribute(STIStormDataSource.PARAM_RADIUSMODERATEGALE)); 598 appendDiamondValue(sb, stp 599 .getAttribute(STIStormDataSource.PARAM_RADIUSWHOLEGALE)); 600 appendDiamondValue(sb, stp 601 .getAttribute(STIStormDataSource.PARAM_MOVESPEED)); 602 appendDiamondValue(sb, stp 603 .getAttribute(STIStormDataSource.PARAM_MOVEDIRECTION)); 604 605 sb.append("\n"); 606 } 607 } 608 609 private void appendDiamondValue(StringBuffer sb, Real r) { 610 if (r == null || Double.isNaN(r.getValue())) 611 sb.append(DIAMOND_MISSING_VALUE); 612 else 613 sb.append(r.getValue()); 614 sb.append(" "); 615 } 616 617 }