001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 029package edu.wisc.ssec.mcidasv.data.cyclone; 030 031import java.util.ArrayList; 032import java.util.Calendar; 033import java.util.Date; 034import java.util.Hashtable; 035import java.util.List; 036 037import ucar.unidata.data.NamedArray; 038import ucar.unidata.geoloc.LatLonPointImpl; 039import ucar.unidata.geoloc.LatLonRect; 040import ucar.unidata.util.Misc; 041import ucar.visad.Util; 042import visad.CommonUnit; 043import visad.DateTime; 044import visad.Real; 045import visad.RealType; 046import visad.VisADException; 047import 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 054public 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 public void setIsEdited(boolean isEdited) { 255 this.isEdited = isEdited; 256 } 257 258 /** 259 * _more_ 260 * 261 * @return _more_ 262 */ 263 public boolean getIsEdited() { 264 return this.isEdited; 265 } 266 267 /** 268 * _more_ 269 * 270 * @return _more_ 271 */ 272 public int hashCode() { 273 return trackId.hashCode(); 274 } 275 276 /** 277 * _more_ 278 * 279 * @param id 280 * _more_ 281 */ 282 public void setId(String id) { 283 this.trackId = id; 284 } 285 286 /** 287 * _more_ 288 * 289 * @return _more_ 290 */ 291 public String getId() { 292 return trackId; 293 } 294 295 /** 296 * _more_ 297 * 298 * @return _more_ 299 */ 300 public DateTime getStartTime() { 301 StormTrackPoint firstPoint = trackPoints.get(0); 302 return firstPoint.getTime(); 303 304 } 305 306 /** 307 * _more_ 308 * 309 * @param stormInfo 310 * _more_ 311 */ 312 public void setStormInfo(StormInfo stormInfo) { 313 this.stormInfo = stormInfo; 314 } 315 316 /** 317 * _more_ 318 * 319 * @return _more_ 320 */ 321 public StormInfo getStormInfo() { 322 return stormInfo; 323 } 324 325 /** 326 * _more_ 327 * 328 * @param way 329 * _more_ 330 */ 331 public void setWay(Way way) { 332 this.way = way; 333 } 334 335 /** 336 * _more_ 337 * 338 * @return _more_ 339 */ 340 public Way getWay() { 341 return way; 342 } 343 344 /** 345 * _more_ 346 * 347 * @param pts 348 * _more_ 349 */ 350 public void setTrackPoints(List<StormTrackPoint> pts) { 351 this.trackPoints = new ArrayList<StormTrackPoint>(pts); 352 } 353 354 /** 355 * _more_ 356 * 357 * @return _more_ 358 */ 359 public List<StormTrackPoint> getTrackPoints() { 360 return trackPoints; 361 } 362 363 /** 364 * _more_ 365 * 366 * @return _more_ 367 */ 368 public List<DateTime> getTrackTimes() { 369 List<DateTime> trackTimes = new ArrayList(); 370 for (StormTrackPoint stp : trackPoints) { 371 trackTimes.add(stp.getTime()); 372 } 373 return trackTimes; 374 } 375 376 /** 377 * _more_ 378 * 379 * @return _more_ 380 */ 381 public List<StormParam> getParams() { 382 if (params == null) { 383 params = new ArrayList<StormParam>(); 384 Hashtable seenParam = new Hashtable(); 385 for (StormTrackPoint stp : trackPoints) { 386 List<Real> reals = stp.getTrackAttributes(); 387 for (Real r : reals) { 388 RealType type = (RealType) r.getType(); 389 if (seenParam.get(type) == null) { 390 seenParam.put(type, type); 391 params.add(new StormParam(type)); 392 } 393 } 394 } 395 } 396 397 return params; 398 } 399 400 /** 401 * _more_ 402 * 403 * @return _more_ 404 */ 405 public List<EarthLocation> getLocations() { 406 List<EarthLocation> locs = new ArrayList(); 407 for (StormTrackPoint stp : trackPoints) { 408 locs.add(stp.getLocation()); 409 } 410 return locs; 411 } 412 413 /** 414 * _more_ 415 * 416 * 417 * 418 * @param param 419 * _more_ 420 * @return _more_ 421 * 422 * @throws VisADException 423 * _more_ 424 */ 425 public Real[] getTrackAttributeValues(StormParam param) 426 throws VisADException { 427 if (param == null) { 428 return null; 429 } 430 int size = trackPoints.size(); 431 Real[] trackAttributes = new Real[size]; 432 Real missing = null; 433 for (int i = 0; i < size; i++) { 434 Real value = trackPoints.get(i).getAttribute(param); 435 if (value == null) { 436 if (i == 0) { 437 return null; 438 } 439 trackAttributes[i] = null; 440 } else { 441 if (missing == null) { 442 missing = value.cloneButValue(Double.NaN); 443 } 444 trackAttributes[i] = value; 445 } 446 } 447 for (int i = 0; i < size; i++) { 448 if (trackAttributes[i] == null) { 449 trackAttributes[i] = missing; 450 } 451 } 452 return trackAttributes; 453 } 454 455 /** 456 * _more_ 457 * 458 * @param trackAttributes 459 * _more_ 460 * @param i 461 * _more_ 462 * 463 * @return _more_ 464 */ 465 public float findClosestAttr(float[] trackAttributes, int i) { 466 int up = i; 467 int down = i; 468 int size = trackAttributes.length; 469 float value = Float.NaN; 470 while (Float.isNaN(value)) { 471 up++; 472 down--; 473 if ((up > 0) && (up < size)) { 474 value = trackAttributes[up]; 475 } 476 if ((down > 0) && (down < size)) { 477 value = trackAttributes[down]; 478 } 479 } 480 return value; 481 } 482 483 /** 484 * _more_ 485 * 486 * @return _more_ 487 */ 488 public String toString() { 489 return trackId; 490 } 491 492 /** 493 * Return the index of the given track point. This kist finds the point with 494 * the same lat/lon 495 * 496 * @param stp 497 * The track point 498 * @return The index or -1 if not found 499 */ 500 public int indexOf(StormTrackPoint stp) { 501 for (int i = 0; i < trackPoints.size(); i++) { 502 if (trackPoints.get(i).getLocation().equals(stp.getLocation())) { 503 return i; 504 } 505 } 506 return -1; 507 } 508 509 /** 510 * _more_ 511 * 512 * @param o 513 * _more_ 514 * 515 * @return _more_ 516 */ 517 public boolean equals(Object o) { 518 if (o == null) { 519 return false; 520 } 521 if (!(o instanceof StormTrack)) { 522 return false; 523 } 524 StormTrack other = (StormTrack) o; 525 return ((trackId.equals(other.trackId))); 526 } 527 528 public void putTemporaryProperty(Object key, Object value) { 529 temporaryProperties.put(key, value); 530 } 531 532 public Object getTemporaryProperty(Object key) { 533 return temporaryProperties.get(key); 534 } 535 536 static public StringBuffer toDiamond7(List<StormTrack> sts, String id) 537 throws VisADException { 538 StringBuffer sb = new StringBuffer(); 539 sb.append("diamond 7 " + id + "TropicalCycloneTrack" + "\n"); 540 for (StormTrack st : sts) { 541 st.toDiamond7(sb, id); 542 } 543 return sb; 544 } 545 546 public void toDiamond7(StringBuffer sb, String id) throws VisADException { 547 Calendar cal = Calendar.getInstance(); 548 List<StormTrackPoint> tpoints = getTrackPoints(); 549 550 sb.append("Name " + id + " " + way + " " + tpoints.size() + "\n"); 551 for (StormTrackPoint stp : tpoints) { 552 Date dttm = null; 553 554 try { 555 dttm = Util.makeDate(stp.getTime()); 556 } catch (Exception excp) { 557 558 } 559 cal.setTime(dttm); 560 String year = Integer.toString(cal.get(Calendar.YEAR)); 561 int mm = cal.get(Calendar.MONTH); 562 String mon = Integer.toString(mm); 563 if (mm < 10) 564 mon = "0" + mon; 565 int dd = cal.get(Calendar.DAY_OF_MONTH); 566 String day = Integer.toString(dd); 567 if (dd < 10) 568 day = "0" + day; 569 int hour = cal.get(Calendar.HOUR_OF_DAY); 570 int fhour = stp.getForecastHour(); 571 EarthLocation el = stp.getLocation(); 572 List<Real> attrs = stp.getTrackAttributes(); 573 574 sb.append(year.substring(2)); 575 sb.append(" "); 576 sb.append(mon); 577 sb.append(" "); 578 sb.append(day); 579 sb.append(" "); 580 sb.append(hour); 581 sb.append(" "); 582 sb.append(fhour); 583 sb.append(" "); 584 sb.append(el.getLongitude().getValue(CommonUnit.degree)); 585 sb.append(" "); 586 sb.append(el.getLatitude().getValue(CommonUnit.degree)); 587 sb.append(" "); 588 589 // TODO: What to do with units? 590 appendDiamondValue(sb, stp 591 .getAttribute(STIStormDataSource.PARAM_MAXWINDSPEED)); 592 appendDiamondValue(sb, stp 593 .getAttribute(STIStormDataSource.PARAM_MINPRESSURE)); 594 appendDiamondValue(sb, stp 595 .getAttribute(STIStormDataSource.PARAM_RADIUSMODERATEGALE)); 596 appendDiamondValue(sb, stp 597 .getAttribute(STIStormDataSource.PARAM_RADIUSWHOLEGALE)); 598 appendDiamondValue(sb, stp 599 .getAttribute(STIStormDataSource.PARAM_MOVESPEED)); 600 appendDiamondValue(sb, stp 601 .getAttribute(STIStormDataSource.PARAM_MOVEDIRECTION)); 602 603 sb.append("\n"); 604 } 605 } 606 607 private void appendDiamondValue(StringBuffer sb, Real r) { 608 if (r == null || Double.isNaN(r.getValue())) 609 sb.append(DIAMOND_MISSING_VALUE); 610 else 611 sb.append(r.getValue()); 612 sb.append(" "); 613 } 614 615}