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