001 /* 002 * $Id: StormTrackControl.java,v 1.1 2012/01/04 20:39:38 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.control.cyclone; 032 033 import java.awt.Color; 034 import java.awt.Component; 035 import java.awt.Container; 036 import java.awt.Dimension; 037 import java.awt.Point; 038 import java.awt.Window; 039 import java.awt.event.ActionEvent; 040 import java.awt.event.ActionListener; 041 import java.awt.event.InputEvent; 042 import java.beans.PropertyChangeEvent; 043 import java.io.File; 044 import java.io.FileOutputStream; 045 import java.rmi.RemoteException; 046 import java.text.SimpleDateFormat; 047 import java.util.ArrayList; 048 import java.util.Calendar; 049 import java.util.Date; 050 import java.util.Enumeration; 051 import java.util.GregorianCalendar; 052 import java.util.HashMap; 053 import java.util.Hashtable; 054 import java.util.List; 055 import java.util.Vector; 056 057 import javax.swing.BorderFactory; 058 import javax.swing.ButtonGroup; 059 import javax.swing.ImageIcon; 060 import javax.swing.JCheckBox; 061 import javax.swing.JComboBox; 062 import javax.swing.JComponent; 063 import javax.swing.JLabel; 064 import javax.swing.JMenu; 065 import javax.swing.JRadioButton; 066 import javax.swing.JScrollPane; 067 import javax.swing.JTabbedPane; 068 import javax.swing.JWindow; 069 import javax.swing.border.BevelBorder; 070 071 import org.w3c.dom.Element; 072 073 import ucar.unidata.data.BadDataException; 074 import ucar.unidata.data.DataChoice; 075 import ucar.unidata.data.gis.KmlUtil; 076 import ucar.unidata.data.grid.GridUtil; 077 import ucar.unidata.data.point.PointOb; 078 import ucar.unidata.data.point.PointObFactory; 079 import ucar.unidata.data.storm.StormDataSource; 080 import ucar.unidata.data.storm.StormInfo; 081 import ucar.unidata.data.storm.StormParam; 082 import ucar.unidata.data.storm.StormTrack; 083 import ucar.unidata.data.storm.StormTrackCollection; 084 import ucar.unidata.data.storm.StormTrackPoint; 085 import ucar.unidata.data.storm.Way; 086 import ucar.unidata.geoloc.LatLonRect; 087 import ucar.unidata.idv.MapViewManager; 088 import ucar.unidata.idv.control.DisplayControlImpl; 089 import ucar.unidata.idv.control.ReadoutInfo; 090 import ucar.unidata.ui.TreePanel; 091 import ucar.unidata.ui.TwoListPanel; 092 import ucar.unidata.ui.symbol.StationModel; 093 import ucar.unidata.ui.symbol.StationModelManager; 094 import ucar.unidata.util.ColorTable; 095 import ucar.unidata.util.DateUtil; 096 import ucar.unidata.util.FileManager; 097 import ucar.unidata.util.GuiUtils; 098 import ucar.unidata.util.IOUtil; 099 import ucar.unidata.util.MenuUtil; 100 import ucar.unidata.util.Misc; 101 import ucar.unidata.util.Range; 102 import ucar.unidata.util.StringUtil; 103 import ucar.unidata.util.TwoFacedObject; 104 import ucar.unidata.view.geoloc.NavigatedDisplay; 105 import ucar.unidata.xml.XmlUtil; 106 import ucar.visad.Util; 107 import ucar.visad.display.CompositeDisplayable; 108 import ucar.visad.display.DisplayMaster; 109 import ucar.visad.display.StationModelDisplayable; 110 import visad.CoordinateSystem; 111 import visad.Data; 112 import visad.DateTime; 113 import visad.DisplayEvent; 114 import visad.DoubleSet; 115 import visad.FieldImpl; 116 import visad.FlatField; 117 import visad.FunctionType; 118 import visad.GriddedSet; 119 import visad.Real; 120 import visad.RealType; 121 import visad.Set; 122 import visad.SetType; 123 import visad.TextType; 124 import visad.Tuple; 125 import visad.Unit; 126 import visad.VisADException; 127 import visad.georef.EarthLocation; 128 import visad.georef.MapProjection; 129 130 /** 131 * A MetApps Display Control with Displayable and controls for displaying a 132 * track (balloon sounding or aircraft track) 133 * 134 * @author Unidata Development Team 135 * @version $Revision: 1.1 $ 136 */ 137 138 public class StormTrackControl extends DisplayControlImpl { 139 140 /** _more_ */ 141 private final static String PREF_STORMDISPLAYSTATE = "pref.stormtrackcontrol.stormdisplaystate"; 142 143 /** _more_ */ 144 private final static String PREF_OKWAYS = "pref.stormtrackcontrol.okways"; 145 146 /** _more_ */ 147 private final static String PREF_OBWAY = "pref.stormtrackcontrol.observationway"; 148 149 /** _more_ */ 150 private final static String PREF_OKPARAMS = "pref.stormtrackcontrol.okparams"; 151 152 /** _more_ */ 153 private static int cnt = 0; 154 155 /** _more_ */ 156 final ImageIcon ICON_ON = GuiUtils 157 .getImageIcon("/ucar/unidata/idv/control/storm/dot.gif"); 158 159 /** _more_ */ 160 final ImageIcon ICON_OFF = GuiUtils 161 .getImageIcon("/ucar/unidata/idv/control/storm/blank.gif"); 162 163 /** _more_ */ 164 private StormDisplayState localStormDisplayState; 165 166 /** _more_ */ 167 private Hashtable preferences; 168 169 /** _more_ */ 170 private Hashtable<String, Boolean> okWays; 171 172 /** _more_ */ 173 private Way observationWay; 174 175 /** _more_ */ 176 private Hashtable<String, Boolean> okParams; 177 178 /** _more_ */ 179 private String startTime; 180 181 /** _more_ */ 182 private String endTime; 183 184 /** _more_ */ 185 private CompositeDisplayable placeHolder; 186 187 /** _more_ */ 188 private StormDataSource stormDataSource; 189 190 /** _more_ */ 191 private List<StormInfo> stormInfos; 192 193 /** Holds the EarthLocation of the last point clicked */ 194 private EarthLocation lastEarthLocation = null; 195 196 /** _more_ */ 197 private Hashtable<StormInfo, StormDisplayState> stormDisplayStateMap = new Hashtable<StormInfo, StormDisplayState>(); 198 199 /** _more_ */ 200 private List<StormDisplayState> activeStorms; 201 202 /** _more_ */ 203 private TreePanel treePanel; 204 205 /** _more_ */ 206 private static final int YEAR_TIME_MODE_YEAR = 0; 207 208 /** _more_ */ 209 private static final int YEAR_TIME_MODE_STORM = 1; 210 211 /** _more_ */ 212 private int yearTimeMode = YEAR_TIME_MODE_YEAR; 213 214 /** _more_ */ 215 private Hashtable<Integer, YearDisplayState> yearDisplayStateMap = new Hashtable<Integer, YearDisplayState>(); 216 217 /** _more_ */ 218 private Hashtable yearData = new Hashtable(); 219 220 /** _more_ */ 221 private JComboBox timeModeBox; 222 223 /** _more_ */ 224 private JCheckBox obsCbx; 225 226 /** _more_ */ 227 private JCheckBox forecastCbx; 228 229 /** _more_ */ 230 private JCheckBox mostRecentCbx; 231 232 /** _more_ */ 233 private JCheckBox editedCbx; 234 235 /** _more_ */ 236 private TwoListPanel waysToUseSelector; 237 238 /** _more_ */ 239 private TwoListPanel chartParamsSelector; 240 241 /** _more_ */ 242 private JCheckBox waysToUsePreferenceCbx; 243 244 /** _more_ */ 245 private JCheckBox chartParamsPreferenceCbx; 246 247 /** _more_ */ 248 private List<Way> allWays; 249 250 /** _more_ */ 251 private List<Way> useWays; 252 253 /** _more_ */ 254 private List<StormParam> allParams; 255 256 /** _more_ */ 257 private List<StormParam> useParams; 258 259 /** _more_ */ 260 private JCheckBox obsWayPreferenceCbx; 261 262 /** _more_ */ 263 private List<JRadioButton> obsWayRadioButtons; 264 265 /** _more_ */ 266 private boolean editMode = false; 267 268 /** 269 * Create a new Track Control; set the attribute flags 270 */ 271 public StormTrackControl() { 272 setAttributeFlags(FLAG_COLORTABLE); 273 } 274 275 /** 276 * _more_ 277 * 278 * @param basePref 279 * _more_ 280 * 281 * @return _more_ 282 */ 283 protected String getPref(String basePref) { 284 return basePref + "." + stormDataSource.getId(); 285 } 286 287 /** 288 * _more_ 289 * 290 * @return _more_ 291 */ 292 protected boolean isEditable() { 293 return stormDataSource.isEditable(); 294 } 295 296 /** 297 * _more_ 298 * 299 * @return _more_ 300 */ 301 public NavigatedDisplay getVM() { 302 return getNavigatedDisplay(); 303 } 304 305 /** 306 * Call to help make this kind of Display Control; also calls code to made 307 * the Displayable (empty of data thus far). This method is called from 308 * inside DisplayControlImpl.init(several args). 309 * 310 * @param dataChoice 311 * the DataChoice of the moment. 312 * 313 * @return true if successful 314 * 315 * @throws RemoteException 316 * Java RMI error 317 * @throws VisADException 318 * VisAD Error 319 */ 320 public boolean init(DataChoice dataChoice) throws VisADException, 321 RemoteException { 322 323 DataChoice.addCurrentName(new TwoFacedObject( 324 "Storm Track>Forecast Hour", "fhour")); 325 DataChoice.addCurrentName(new TwoFacedObject( 326 "Storm Track>Forecast Time", "rhour")); 327 DataChoice.addCurrentName(new TwoFacedObject( 328 "Storm Track>Forecast STI Time", "shour")); 329 330 placeHolder = new CompositeDisplayable("Place holder"); 331 addDisplayable(placeHolder); 332 333 List dataSources = new ArrayList(); 334 dataChoice.getDataSources(dataSources); 335 336 if (dataSources.size() != 1) { 337 userMessage("Could not find Storm Data Source"); 338 return false; 339 } 340 341 if (!(dataSources.get(0) instanceof StormDataSource)) { 342 userMessage("Could not find Storm Data Source"); 343 return false; 344 } 345 346 getColorTableWidget(new Range(1.0, 1.0)); 347 stormDataSource = (StormDataSource) dataSources.get(0); 348 349 if (okWays == null) { 350 okWays = (Hashtable<String, Boolean>) getPreferences().get( 351 getPref(PREF_OKWAYS)); 352 } 353 if (observationWay == null) { 354 observationWay = (Way) getPreferences().get(getPref(PREF_OBWAY)); 355 if (observationWay == null) { 356 observationWay = stormDataSource.getDefaultObservationWay(); 357 } 358 } 359 if (okWays == null) { 360 okWays = new Hashtable<String, Boolean>(); 361 } 362 if (okParams == null) { 363 okParams = (Hashtable<String, Boolean>) getPreferences().get( 364 getPref(PREF_OKPARAMS)); 365 } 366 if (okParams == null) { 367 okParams = new Hashtable<String, Boolean>(); 368 } 369 370 return true; 371 } 372 373 /** 374 * _more_ 375 * 376 * @return _more_ 377 */ 378 private JComponent getWaysToUseComp() { 379 380 useWays = new ArrayList<Way>(); 381 allWays = new ArrayList<Way>(); 382 for (Way way : stormDataSource.getWays()) { 383 if (way.isObservation()) { 384 continue; 385 } 386 allWays.add(way); 387 if (okToShowWay(way)) { 388 useWays.add(way); 389 } 390 } 391 useWays = (List<Way>) Misc.sort(useWays); 392 allWays = (List<Way>) Misc.sort(allWays); 393 if (waysToUsePreferenceCbx == null) { 394 waysToUsePreferenceCbx = new JCheckBox("Save as preference", false); 395 } 396 waysToUseSelector = new TwoListPanel(allWays, "Don't Use", useWays, 397 "Use", null, false); 398 JComponent contents = GuiUtils.centerBottom(waysToUseSelector, GuiUtils 399 .left(waysToUsePreferenceCbx)); 400 401 return contents; 402 } 403 404 /** 405 * _more_ 406 * 407 * @return _more_ 408 */ 409 private boolean applyWaysToUse() { 410 boolean changed = false; 411 List only = Misc.sort(waysToUseSelector.getCurrentEntries()); 412 if (!useWays.equals(only)) { 413 changed = true; 414 if (only.size() == allWays.size()) { 415 onlyShowTheseWays(new ArrayList<Way>(), waysToUsePreferenceCbx 416 .isSelected()); 417 } else { 418 onlyShowTheseWays((List<Way>) only, waysToUsePreferenceCbx 419 .isSelected()); 420 } 421 } 422 return changed; 423 } 424 425 /** 426 * _more_ 427 */ 428 public void showWaysToUseDialog() { 429 JComponent waysToUseComp = getWaysToUseComp(); 430 JLabel label = GuiUtils.cLabel(getWaysName() + " to use"); 431 JComponent contents = GuiUtils.topCenter(label, waysToUseComp); 432 if (!GuiUtils.showOkCancelDialog(null, getWaysName() + " to use", 433 waysToUseComp, null)) { 434 return; 435 } 436 if (applyWaysToUse()) { 437 // ?? 438 } 439 } 440 441 /** 442 * _more_ 443 * 444 * @param jtp 445 * _more_ 446 */ 447 protected void addPropertiesComponents(JTabbedPane jtp) { 448 super.addPropertiesComponents(jtp); 449 JComponent waysToUseComp = getWaysToUseComp(); 450 jtp.add(getWaysName() + " to use", waysToUseComp); 451 452 // chart parameters selector 453 useParams = new ArrayList<StormParam>(); 454 allParams = new ArrayList<StormParam>(); 455 for (StormParam param : getTrackParams()) { 456 457 allParams.add(param); 458 if (okToShowParam(param)) { 459 useParams.add(param); 460 } 461 } 462 // useParams = (List<StormParam>) Misc.sort(useParams); 463 // allParams = (List<StormParam>) Misc.sort(allParams); 464 465 if (chartParamsPreferenceCbx == null) { 466 chartParamsPreferenceCbx = new JCheckBox("Save as preference", 467 false); 468 } 469 chartParamsSelector = new TwoListPanel(allParams, "All Parameters", 470 useParams, "Selected Parameters", null, false); 471 JComponent paramsContents = GuiUtils.centerBottom(chartParamsSelector, 472 GuiUtils.left(chartParamsPreferenceCbx)); 473 jtp.add("Chart Parameters", paramsContents); 474 475 // observation way selector 476 if (stormDataSource.getIsObservationWayChangeable()) { 477 obsWayRadioButtons = new ArrayList<JRadioButton>(); 478 ButtonGroup bg = new ButtonGroup(); 479 for (Way way : allWays) { 480 if (way.isObservation()) { 481 continue; 482 } 483 JRadioButton jrb = new JRadioButton(way.getId(), Misc.equals( 484 observationWay, way)); 485 obsWayRadioButtons.add(jrb); 486 bg.add(jrb); 487 } 488 489 if (obsWayPreferenceCbx == null) { 490 obsWayPreferenceCbx = new JCheckBox("Save as preference", false); 491 } 492 493 JComponent obsWayContents = GuiUtils.topLeft(GuiUtils.doLayout( 494 obsWayRadioButtons, ((obsWayRadioButtons.size() > 10) ? 2 495 : 1), GuiUtils.WT_N, GuiUtils.WT_N)); 496 int width = 200; 497 int height = 150; 498 if (obsWayRadioButtons.size() > 10) { 499 obsWayContents = GuiUtils.makeScrollPane(obsWayContents, width, 500 height); 501 } 502 jtp.add("Observation " + getWayName(), GuiUtils.centerBottom( 503 obsWayContents, GuiUtils.left(obsWayPreferenceCbx))); 504 } 505 } 506 507 /** 508 * _more_ 509 * 510 * @return _more_ 511 */ 512 public List<StormParam> getTrackParams() { 513 List<StormParam> params = new ArrayList<StormParam>(); 514 515 StormDisplayState sds = getCurrentStormDisplayState(); 516 if (sds == null) { 517 return params; 518 } 519 520 StormTrackCollection stc = sds.getTrackCollection(); 521 if (stc == null) { 522 for (int i = stormInfos.size() - 1; i >= 0; i--) { 523 StormInfo stormInfo = stormInfos.get(i); 524 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 525 stc = sds.getTrackCollection(); 526 if (stc != null) { 527 break; 528 } 529 } 530 } 531 532 if (stc == null) { 533 System.err.println("Unable to find any active storm displays"); 534 return params; 535 } 536 for (StormTrack track : stc.getTracks()) { 537 if (track == null) { 538 continue; 539 } 540 if (!track.isObservation()) { 541 params = track.getParams(); 542 break; 543 } 544 } 545 546 // If we didn't get any from the forecast track use the obs track 547 if (params.size() == 0) { 548 StormTrack obsTrack = stc.getObsTrack(); 549 if (obsTrack != null) { 550 params = obsTrack.getParams(); 551 } 552 } 553 554 return params; 555 } 556 557 /** 558 * _more_ 559 * 560 * @return _more_ 561 */ 562 public boolean doApplyProperties() { 563 if (!super.doApplyProperties()) { 564 return false; 565 } 566 567 boolean changed = false; 568 if (applyWaysToUse()) { 569 changed = true; 570 } 571 572 List onlyCP = chartParamsSelector.getCurrentEntries(); 573 if (!useParams.equals(onlyCP)) { 574 changed = true; 575 if (onlyCP.size() == allParams.size()) { 576 onlyShowTheseParams(new ArrayList<StormParam>(), 577 chartParamsPreferenceCbx.isSelected()); 578 } else { 579 onlyShowTheseParams((List<StormParam>) onlyCP, 580 chartParamsPreferenceCbx.isSelected()); 581 } 582 } 583 584 if (stormDataSource.getIsObservationWayChangeable()) { 585 Way newObsWay = null; 586 for (int i = 0; i < obsWayRadioButtons.size(); i++) { 587 if (obsWayRadioButtons.get(i).isSelected()) { 588 newObsWay = allWays.get(i); 589 break; 590 } 591 } 592 593 if (newObsWay != null) { 594 if (!Misc.equals(newObsWay, observationWay)) { 595 changed = true; 596 observationWay = newObsWay; 597 } 598 if (obsWayPreferenceCbx.isSelected()) { 599 putPreference(getPref(PREF_OBWAY), observationWay); 600 } 601 } 602 } 603 604 if (changed) { 605 reloadStormTracks(); 606 } 607 608 return true; 609 } 610 611 /* 612 * public List<StormParam> getChartParamFromSelector(){ 613 * if(chartParamsSelector!= null) { List pa = 614 * chartParamsSelector.getCurrentEntries(); return pa; } return null; } 615 */ 616 617 /** 618 * Signal base class to add this as a control listener 619 * 620 * @return Add as control listener 621 */ 622 protected boolean shouldAddControlListener() { 623 return true; 624 } 625 626 /** locking object */ 627 private Object MUTEX = new Object(); 628 629 /** 630 * _more_ 631 */ 632 public void viewpointChanged() { 633 super.viewpointChanged(); 634 synchronized (MUTEX) { 635 StormDisplayState sds = getCurrentStormDisplayState(); 636 HashMap<Way, List> wayToTracksMap = sds.getTrackCollection() 637 .getWayToTracksHashMap(); 638 // Way obsWay = new Way(Way.OBSERVATION); 639 java.util.Set<Way> ways = wayToTracksMap.keySet(); 640 641 for (Way way : ways) { 642 643 if (way.equals(Way.OBSERVATION)) { 644 WayDisplayState obsWDS = sds.getWayDisplayState(way); 645 try { 646 obsWDS.updateLayoutModel(); 647 } catch (Exception exc) { 648 logException("view point Changed", exc); 649 return; 650 } 651 } 652 653 } 654 655 /* 656 * if ( !getHaveInitialized() || !getActive()) { return; } 657 * Rectangle2D newBounds = calculateRectangle(); boolean 658 * shouldReload = false; if ((lastViewBounds == null) || 659 * (lastViewBounds.getWidth() == 0) || (lastViewBounds.getHeight() 660 * == 0)) { shouldReload = true; } else if ( 661 * !(newBounds.equals(lastViewBounds))) { double widthratio = 662 * newBounds.getWidth() / lastViewBounds.getWidth(); double 663 * heightratio = newBounds.getHeight() / lastViewBounds.getHeight(); 664 * double xdiff = Math.abs(newBounds.getX() - 665 * lastViewBounds.getX()); double ydiff = Math.abs(newBounds.getY() 666 * - lastViewBounds.getY()); // See if this is 20% greater or 667 * smaller than before. if ((((widthratio < .80) || (widthratio > 668 * 1.20)) && ((heightratio < .80) || (heightratio > 1.20))) || 669 * ((xdiff > .2 * lastViewBounds.getWidth()) || (ydiff > .2 * 670 * lastViewBounds.getHeight()))) { shouldReload = true; } } float 671 * newScale = getScaleFromDisplayable(); if 672 * (Float.floatToIntBits(lastViewScale) != 673 * Float.floatToIntBits(newScale)) { shouldReload = true; } if 674 * (shouldReload) { 675 * 676 * updateLayoutModel(); 677 * 678 * } 679 */ 680 } 681 682 } 683 684 /** _more_ */ 685 private Hashtable rangeTypes = new Hashtable(); 686 687 /** 688 * _more_ 689 * 690 * @param track 691 * _more_ 692 * 693 * @param param 694 * _more_ 695 * 696 * @return _more_ 697 * 698 * @throws Exception 699 * _more_ 700 */ 701 protected FieldImpl makeTrackField(StormTrack track, StormParam param) 702 throws Exception { 703 704 List<StormTrackPoint> points = track.getTrackPoints(); 705 int numPoints = points.size(); 706 RealType rangeType = null; 707 double[][] newRangeVals = new double[1][numPoints]; 708 float[] alts = new float[numPoints]; 709 float[] lats = new float[numPoints]; 710 float[] lons = new float[numPoints]; 711 Real[] values = ((param == null) ? null : track 712 .getTrackAttributeValues(param)); 713 Unit unit = ((param != null) ? param.getUnit() : null); 714 for (int pointIdx = 0; pointIdx < numPoints; pointIdx++) { 715 StormTrackPoint stp = points.get(pointIdx); 716 Real value = ((values == null) ? null : values[pointIdx]); 717 718 // Set the dflt so we can use its unit later 719 if (rangeType == null) { 720 String key = track.getWay() + "_" + track.getId() + "_" + param; 721 if (track.getWay().toString().startsWith("Observation_year")) 722 key = track.getWay() + "_" + param; 723 rangeType = (RealType) rangeTypes.get(key); 724 if (rangeType == null) { 725 cnt++; 726 if (track.getWay().toString() 727 .startsWith("Observation_year")) 728 rangeType = Util.makeRealType("trackrange_" 729 + track.getWay() + "_" + cnt, unit); 730 else 731 rangeType = Util.makeRealType("trackrange_" 732 + track.getId() + "_" + track.getWay() + "_" 733 + cnt, unit); 734 rangeTypes.put(key, rangeType); 735 } 736 } 737 EarthLocation el = stp.getLocation(); 738 newRangeVals[0][pointIdx] = ((value != null) ? value.getValue() : 0); 739 lats[pointIdx] = (float) el.getLatitude().getValue(); 740 lons[pointIdx] = (float) el.getLongitude().getValue(); 741 alts[pointIdx] = 1; 742 // if(Math.abs(lats[i])>90) System.err.println("bad lat:" + 743 // lats[i]); 744 } 745 GriddedSet llaSet = ucar.visad.Util 746 .makeEarthDomainSet(lats, lons, alts); 747 Set[] rangeSets = new Set[] { new DoubleSet(new SetType(rangeType)) }; 748 FunctionType newType = new FunctionType(((SetType) llaSet.getType()) 749 .getDomain(), rangeType); 750 FlatField trackField = new FlatField(newType, llaSet, 751 (CoordinateSystem) null, rangeSets, new Unit[] { unit }); 752 trackField.setSamples(newRangeVals, false); 753 return trackField; 754 } 755 756 /** 757 * _more_ 758 * 759 * @param whichColorTable 760 * _more_ 761 * @param newColorTable 762 * _more_ 763 * 764 * @throws RemoteException 765 * _more_ 766 * @throws VisADException 767 * _more_ 768 */ 769 public void setColorTable(String whichColorTable, ColorTable newColorTable) 770 throws RemoteException, VisADException { 771 super.setColorTable(whichColorTable, newColorTable); 772 for (StormDisplayState sds : getActiveStorms()) { 773 sds.colorTableChanged(); 774 } 775 } 776 777 /** 778 * _more_ 779 * 780 * @return _more_ 781 */ 782 public DisplayMaster getDisplayMaster() { 783 return getDisplayMaster(placeHolder); 784 } 785 786 /** 787 * _more_ 788 * 789 * @param way 790 * _more_ 791 * 792 * @return _more_ 793 */ 794 protected boolean okToShowWay(Way way) { 795 796 if (way.isObservation()) { 797 return true; 798 } 799 if (okWays == null) { 800 showWaysToUseDialog(); 801 } 802 if (okWays == null) { 803 return true; 804 } 805 if ((okWays.size() > 0) && (okWays.get(way.getId()) == null)) { 806 return false; 807 } 808 return true; 809 } 810 811 /** 812 * _more_ 813 * 814 * @param param 815 * _more_ 816 * 817 * @return _more_ 818 */ 819 protected boolean okToShowParam(StormParam param) { 820 if (okParams == null) { 821 return true; 822 } 823 if ((okParams.size() > 0) && (okParams.get(param.getName()) == null)) { 824 return false; 825 } 826 return true; 827 } 828 829 /** 830 * _more_ 831 * 832 * @return _more_ 833 */ 834 public StormDisplayState getCurrentStormDisplayState() { 835 if (localStormDisplayState != null) { 836 return localStormDisplayState; 837 } 838 if (treePanel == null) { 839 return null; 840 } 841 842 Component comp = treePanel.getVisibleComponent(); 843 if (comp == null) { 844 return null; 845 } 846 for (int i = stormInfos.size() - 1; i >= 0; i--) { 847 StormInfo stormInfo = stormInfos.get(i); 848 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 849 if (stormDisplayState.getContents() == comp) { 850 return stormDisplayState; 851 } 852 } 853 return null; 854 } 855 856 /** 857 * This gets called when the control has received notification of a 858 * dataChange event. In this class, it reloads the storm tracks. 859 * 860 * @throws RemoteException 861 * Java RMI problem 862 * @throws VisADException 863 * VisAD problem 864 */ 865 protected void resetData() throws VisADException, RemoteException { 866 reloadStormTracks(); 867 } 868 869 /** 870 * _more_ 871 * 872 * @return _more_ 873 */ 874 private List<StormDisplayState> getStormDisplays() { 875 List<StormDisplayState> states = new ArrayList<StormDisplayState>(); 876 for (int i = stormInfos.size() - 1; i >= 0; i--) { 877 StormInfo stormInfo = stormInfos.get(i); 878 states.add(getStormDisplayState(stormInfo)); 879 } 880 return states; 881 } 882 883 /** 884 * _more_ 885 */ 886 private void reloadStormTracks() { 887 for (StormDisplayState stormDisplayState : getActiveStorms()) { 888 stormDisplayState.reload(); 889 } 890 } 891 892 /** 893 * _more_ 894 * 895 * @param ways 896 * _more_ 897 * @param writeAsPreference 898 * _more_ 899 */ 900 private void onlyShowTheseWays(List<Way> ways, boolean writeAsPreference) { 901 okWays = new Hashtable(); 902 for (Way way : ways) { 903 okWays.put(way.getId(), new Boolean(true)); 904 } 905 if (writeAsPreference) { 906 putPreference(getPref(PREF_OKWAYS), okWays); 907 } 908 909 } 910 911 /** 912 * _more_ 913 * 914 * @param params 915 * _more_ 916 * @param writeAsPreference 917 * _more_ 918 */ 919 private void onlyShowTheseParams(List<StormParam> params, 920 boolean writeAsPreference) { 921 okParams = new Hashtable(); 922 for (StormParam param : params) { 923 okParams.put(param.getName(), new Boolean(true)); 924 } 925 if (writeAsPreference) { 926 putPreference(getPref(PREF_OKPARAMS), okParams); 927 } 928 929 } 930 931 /** 932 * _more_ 933 * 934 * @return _more_ 935 */ 936 public StormDataSource getStormDataSource() { 937 return stormDataSource; 938 } 939 940 /** 941 * _more_ 942 * 943 * @param stormDisplayState 944 * _more_ 945 */ 946 public void viewStorm(StormDisplayState stormDisplayState) { 947 if (treePanel != null) { 948 treePanel.show(stormDisplayState.getContents()); 949 } 950 } 951 952 /** 953 * _more_ 954 */ 955 public void unloadAllTracks() { 956 for (StormDisplayState stormDisplayState : getActiveStorms()) { 957 stormDisplayState.deactivate(); 958 } 959 } 960 961 /** 962 * _more_ 963 * 964 * @return _more_ 965 */ 966 protected boolean canHandleEvents() { 967 if (!editMode || !getHaveInitialized() 968 || (getMakeWindow() && !getWindowVisible())) { 969 return false; 970 } 971 return isGuiShown(); 972 } 973 974 /** 975 * _more_ 976 * 977 * @param event 978 * _more_ 979 */ 980 public void handleDisplayChanged(DisplayEvent event) { 981 982 StormDisplayState current = getCurrentStormDisplayState(); 983 if ((current == null) || !current.getActive()) { 984 return; 985 } 986 int id = event.getId(); 987 if (id == DisplayEvent.MOUSE_MOVED) { 988 return; 989 } 990 if (!canHandleEvents()) { 991 return; 992 } 993 InputEvent inputEvent = event.getInputEvent(); 994 try { 995 current.handleEvent(event); 996 } catch (Exception exc) { 997 logException("Error handling edit", exc); 998 } 999 } 1000 1001 /** 1002 * _more_ 1003 * 1004 * @param items 1005 * _more_ 1006 * @param forMenuBar 1007 * _more_ 1008 */ 1009 protected void getSaveMenuItems(List items, boolean forMenuBar) { 1010 StormDisplayState current = getCurrentStormDisplayState(); 1011 if ((current != null) && current.getActive()) { 1012 items.add(GuiUtils.makeMenuItem("Save Storm Display as Preference", 1013 this, "saveStormDisplayState")); 1014 1015 if (getPreferences().get(getPref(PREF_STORMDISPLAYSTATE)) != null) { 1016 items.add(GuiUtils.makeMenuItem( 1017 "Remove Storm Display Preference", this, 1018 "deleteStormDisplayState")); 1019 } 1020 items.add(GuiUtils.MENU_SEPARATOR); 1021 items.add(GuiUtils.makeMenuItem("Export to Data File", current, 1022 "writeToDataFile")); 1023 1024 } 1025 items.add(GuiUtils.makeMenuItem("Export to Google Earth", this, 1026 "writeToKml")); 1027 super.getSaveMenuItems(items, forMenuBar); 1028 } 1029 1030 /** 1031 * _more_ 1032 * 1033 * @param items 1034 * _more_ 1035 * @param forMenuBar 1036 * _more_ 1037 */ 1038 protected void getEditMenuItems(List items, boolean forMenuBar) { 1039 items.add(MenuUtil.makeCheckboxMenuItem("Edit Mode", this, "editMode", 1040 null)); 1041 1042 StormDisplayState current = getCurrentStormDisplayState(); 1043 if ((current != null) && current.getActive()) { 1044 items.add(GuiUtils.makeMenuItem("Add Forecast Time Chart", current, 1045 "addForecastTimeChart")); 1046 items.add(GuiUtils.makeMenuItem("Add Forecast Hour Chart", current, 1047 "addForecastHourChart")); 1048 } 1049 super.getEditMenuItems(items, forMenuBar); 1050 } 1051 1052 /** 1053 * _more_ 1054 * 1055 * @param items 1056 * _more_ 1057 * @param forMenuBar 1058 * _more_ 1059 */ 1060 protected void getViewMenuItems(List items, boolean forMenuBar) { 1061 try { 1062 List subMenus = new ArrayList(); 1063 GregorianCalendar cal = new GregorianCalendar(DateUtil.TIMEZONE_GMT); 1064 Hashtable menus = new Hashtable(); 1065 List activeItems = new ArrayList(); 1066 for (int i = stormInfos.size() - 1; i >= 0; i--) { 1067 StormInfo stormInfo = stormInfos.get(i); 1068 cal.setTime(ucar.visad.Util.makeDate(stormInfo.getStartTime())); 1069 int year = cal.get(Calendar.YEAR); 1070 JMenu yearMenu = (JMenu) menus.get("" + year); 1071 if (yearMenu == null) { 1072 yearMenu = new JMenu("" + year); 1073 menus.put("" + year, yearMenu); 1074 subMenus.add(yearMenu); 1075 } 1076 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 1077 if (stormDisplayState.getActive()) { 1078 activeItems.add(MenuUtil.makeMenuItem(stormInfo.toString(), 1079 this, "viewStorm", stormDisplayState)); 1080 } 1081 if (stormInfo.getBasin() != null) { 1082 JMenu basinMenu = (JMenu) menus.get(year + "Basin:" 1083 + stormInfo.getBasin()); 1084 if (basinMenu == null) { 1085 basinMenu = new JMenu("Basin:" + stormInfo.getBasin()); 1086 menus.put(year + "Basin:" + stormInfo.getBasin(), 1087 basinMenu); 1088 yearMenu.add(basinMenu); 1089 } 1090 yearMenu = basinMenu; 1091 } 1092 yearMenu.add(GuiUtils.makeMenuItem(stormInfo.toString(), this, 1093 "viewStorm", stormDisplayState)); 1094 } 1095 1096 JMenu trackMenu = GuiUtils.makeMenu("Storm Tracks", subMenus); 1097 GuiUtils.limitMenuSize(trackMenu, "Tracks:", 30); 1098 1099 if (activeItems.size() > 0) { 1100 activeItems.add(0, GuiUtils.MENU_SEPARATOR); 1101 activeItems.add(0, GuiUtils.makeMenuItem("Unload all tracks", 1102 this, "unloadAllTracks", null)); 1103 trackMenu.insert(GuiUtils 1104 .makeMenu("Active Tracks", activeItems), 0); 1105 } 1106 1107 items.add(trackMenu); 1108 super.getViewMenuItems(items, forMenuBar); 1109 } catch (Exception exc) { 1110 logException("Making track menu", exc); 1111 } 1112 } 1113 1114 /** 1115 * _more_ 1116 * 1117 * @return _more_ 1118 */ 1119 public String getWayName() { 1120 return stormDataSource.getWayName(); 1121 } 1122 1123 /** 1124 * _more_ 1125 * 1126 * @return _more_ 1127 */ 1128 public String getWaysName() { 1129 return stormDataSource.getWaysName(); 1130 } 1131 1132 /** 1133 * _more_ 1134 * 1135 * @return _more_ 1136 */ 1137 protected String getDataProjectionLabel() { 1138 return "Use Projection From Tracks"; 1139 } 1140 1141 /** 1142 * _more_ 1143 * 1144 * @return _more_ 1145 */ 1146 public MapProjection getDataProjection() { 1147 return null; 1148 } 1149 1150 /** 1151 * _more_ 1152 * 1153 * @return _more_ 1154 */ 1155 public boolean hasMapProjection() { 1156 return true; 1157 } 1158 1159 /** 1160 * _more_ 1161 * 1162 * @return _more_ 1163 */ 1164 public MapProjection getDataProjectionForMenu() { 1165 try { 1166 double minLon = Double.POSITIVE_INFINITY; 1167 double maxLon = Double.NEGATIVE_INFINITY; 1168 double minLat = Double.POSITIVE_INFINITY; 1169 double maxLat = Double.NEGATIVE_INFINITY; 1170 List<StormDisplayState> stormDisplayStates = getStormDisplayStates(); 1171 boolean didone = false; 1172 for (StormDisplayState stormDisplayState : getActiveStorms()) { 1173 LatLonRect bbox = stormDisplayState.getBoundingBox(); 1174 if (bbox == null) { 1175 continue; 1176 } 1177 minLon = Math.min(minLon, bbox.getLonMin()); 1178 maxLon = Math.max(maxLon, bbox.getLonMax()); 1179 minLat = Math.min(minLat, bbox.getLatMin()); 1180 maxLat = Math.max(maxLat, bbox.getLatMax()); 1181 didone = true; 1182 } 1183 1184 for (YearDisplayState yearDisplayState : getYearDisplayStates()) { 1185 if (!yearDisplayState.getActive()) { 1186 continue; 1187 } 1188 List<StormTrack> yearTracks = yearDisplayState.getStormTracks(); 1189 for (StormTrack track : yearTracks) { 1190 LatLonRect bbox = track.getBoundingBox(); 1191 if (bbox == null) { 1192 continue; 1193 } 1194 minLon = Math.min(minLon, bbox.getLonMin()); 1195 maxLon = Math.max(maxLon, bbox.getLonMax()); 1196 minLat = Math.min(minLat, bbox.getLatMin()); 1197 maxLat = Math.max(maxLat, bbox.getLatMax()); 1198 didone = true; 1199 } 1200 } 1201 1202 if (!didone) { 1203 return null; 1204 } 1205 return ucar.visad.Util.makeMapProjection(minLat, minLon, maxLat, 1206 maxLon); 1207 } catch (Exception exc) { 1208 logException("Error making projection from tracks", exc); 1209 return null; 1210 } 1211 1212 } 1213 1214 /** 1215 * _more_ 1216 * 1217 * @return _more_ 1218 */ 1219 private List<StormDisplayState> getActiveStorms() { 1220 if (activeStorms == null) { 1221 List<StormDisplayState> tmpList = new ArrayList<StormDisplayState>(); 1222 List<StormDisplayState> stormDisplayStates = getStormDisplayStates(); 1223 for (StormDisplayState stormDisplayState : stormDisplayStates) { 1224 if (stormDisplayState.getActive()) { 1225 tmpList.add(stormDisplayState); 1226 } 1227 } 1228 activeStorms = tmpList; 1229 } 1230 return activeStorms; 1231 } 1232 1233 /** 1234 * _more_ 1235 * 1236 * @return _more_ 1237 */ 1238 private Hashtable getPreferences() { 1239 if (preferences == null) { 1240 String path = stormDataSource.getClass().getName() 1241 + ".StormTrackControl.xml"; 1242 preferences = (Hashtable) getIdv().getStore().getEncodedFile(path); 1243 if (preferences == null) { 1244 preferences = new Hashtable(); 1245 } 1246 } 1247 return preferences; 1248 } 1249 1250 /** 1251 * _more_ 1252 */ 1253 public void deleteStormDisplayState() { 1254 String template = (String) getPreferences().get( 1255 getPref(PREF_STORMDISPLAYSTATE)); 1256 if (template != null) { 1257 getPreferences().remove(getPref(PREF_STORMDISPLAYSTATE)); 1258 writePreferences(); 1259 } 1260 } 1261 1262 /** 1263 * _more_ 1264 */ 1265 public void saveStormDisplayState() { 1266 try { 1267 StormDisplayState current = getCurrentStormDisplayState(); 1268 if (current == null) { 1269 return; 1270 } 1271 boolean wasActive = current.getActive(); 1272 current.setActive(false); 1273 current.setStormTrackControl(null); 1274 String xml = getIdv().encodeObject(current, false); 1275 current.setStormTrackControl(this); 1276 current.setActive(wasActive); 1277 putPreference(getPref(PREF_STORMDISPLAYSTATE), xml); 1278 userMessage("<html>Preference saved. <br>Note: This will take effect for new display controls</html>"); 1279 } catch (Exception exc) { 1280 logException("Saving storm display", exc); 1281 } 1282 1283 } 1284 1285 /** 1286 * _more_ 1287 */ 1288 private void writePreferences() { 1289 String path = stormDataSource.getClass().getName() 1290 + ".StormTrackControl.xml"; 1291 getIdv().getStore().putEncodedFile(path, preferences); 1292 } 1293 1294 /** 1295 * _more_ 1296 * 1297 * @param key 1298 * _more_ 1299 * @param object 1300 * _more_ 1301 */ 1302 private void putPreference(String key, Object object) { 1303 getPreferences().put(key, object); 1304 writePreferences(); 1305 } 1306 1307 /** 1308 * _more_ 1309 * 1310 * @param stormInfo 1311 * _more_ 1312 * 1313 * @return _more_ 1314 */ 1315 private StormDisplayState getStormDisplayState(StormInfo stormInfo) { 1316 StormDisplayState stormDisplayState = stormDisplayStateMap 1317 .get(stormInfo); 1318 try { 1319 if (stormDisplayState == null) { 1320 String template = (String) getPreferences().get( 1321 getPref(PREF_STORMDISPLAYSTATE)); 1322 if (template != null) { 1323 try { 1324 stormDisplayState = (StormDisplayState) getIdv() 1325 .decodeObject(template); 1326 stormDisplayState.setStormInfo(stormInfo); 1327 } catch (Exception exc) { 1328 logException("Creating storm display", exc); 1329 System.err.println("Error decoding preference:" + exc); 1330 // noop 1331 } 1332 } 1333 } 1334 if (stormDisplayState == null) { 1335 stormDisplayState = new StormDisplayState(stormInfo); 1336 } 1337 1338 stormDisplayState.setStormTrackControl(this); 1339 stormDisplayStateMap.put(stormInfo, stormDisplayState); 1340 } catch (Exception exc) { 1341 logException("Creating storm display", exc); 1342 } 1343 1344 return stormDisplayState; 1345 } 1346 1347 /** 1348 * _more_ 1349 */ 1350 public void initDone() { 1351 super.initDone(); 1352 try { 1353 for (Enumeration keys = stormDisplayStateMap.keys(); keys 1354 .hasMoreElements();) { 1355 StormInfo key = (StormInfo) keys.nextElement(); 1356 StormDisplayState stormDisplayState = stormDisplayStateMap 1357 .get(key); 1358 stormDisplayState.setStormTrackControl(this); 1359 stormDisplayState.initDone(); 1360 1361 MapProjection mapProjection = getDataProjectionForMenu(); 1362 if (mapProjection != null) { 1363 MapViewManager mvm = getMapViewManager(); 1364 if (mvm != null) { 1365 mvm.setMapProjection(mapProjection, true, 1366 getDisplayConventions().getMapProjectionLabel( 1367 mapProjection, this), true); 1368 } 1369 } 1370 1371 } 1372 } catch (Exception exc) { 1373 logException("Setting new storm info", exc); 1374 } 1375 Misc.run(this, "initYears"); 1376 getControlContext().getStationModelManager().addPropertyChangeListener( 1377 this); 1378 } 1379 1380 /** 1381 * _more_ 1382 * 1383 * @throws RemoteException 1384 * _more_ 1385 * @throws VisADException 1386 * _more_ 1387 */ 1388 public void doRemove() throws VisADException, RemoteException { 1389 getControlContext().getStationModelManager() 1390 .removePropertyChangeListener(this); 1391 super.doRemove(); 1392 } 1393 1394 /** 1395 * _more_ 1396 */ 1397 public void initYears() { 1398 List<YearDisplayState> ydss = getYearDisplayStates(); 1399 for (YearDisplayState yds : ydss) { 1400 if (!yds.getActive()) { 1401 continue; 1402 } 1403 try { 1404 yds.setState(yds.STATE_LOADING); 1405 loadYearInner(yds); 1406 } catch (Exception exc) { 1407 logException("Loading year", exc); 1408 return; 1409 } 1410 } 1411 loadYearPointData(); 1412 } 1413 1414 /** _more_ */ 1415 private StationModelDisplayable yearLabels; 1416 1417 /** 1418 * _more_ 1419 */ 1420 private void loadYearPointData() { 1421 try { 1422 if (yearLabels == null) { 1423 yearLabels = new StationModelDisplayable("storm year labels"); 1424 yearLabels.setScale(getDisplayScale()); 1425 StationModelManager smm = getControlContext() 1426 .getStationModelManager(); 1427 StationModel model = smm.getStationModel("Label"); 1428 yearLabels.setStationModel(model); 1429 addDisplayable(yearLabels); 1430 } 1431 1432 List allPointObs = new ArrayList(); 1433 List<YearDisplayState> ydss = getYearDisplayStates(); 1434 for (YearDisplayState yds : ydss) { 1435 if (!yds.getActive()) { 1436 continue; 1437 } 1438 List tmp = yds.getPointObs(); 1439 if (tmp != null) { 1440 allPointObs.addAll(tmp); 1441 } 1442 } 1443 1444 if (allPointObs.size() == 0) { 1445 removeDisplayable(yearLabels); 1446 yearLabels = null; 1447 } else { 1448 yearLabels.setStationData(PointObFactory 1449 .makeTimeSequenceOfPointObs(allPointObs, -1, -1)); 1450 } 1451 } catch (Exception exc) { 1452 logException("Loading year", exc); 1453 } 1454 } 1455 1456 /** 1457 * _more_ 1458 * 1459 * @param yds 1460 * _more_ 1461 */ 1462 public void unloadYear(final YearDisplayState yds) { 1463 Misc.run(new Runnable() { 1464 public void run() { 1465 try { 1466 loadYearPointData(); 1467 } catch (Exception exc) { 1468 logException("Loading year", exc); 1469 } 1470 } 1471 }); 1472 } 1473 1474 /** 1475 * _more_ 1476 * 1477 * @param yds 1478 * _more_ 1479 */ 1480 public void loadYear(final YearDisplayState yds) { 1481 Misc.run(new Runnable() { 1482 public void run() { 1483 try { 1484 yds.setState(yds.STATE_LOADING); 1485 loadYearInner(yds); 1486 loadYearPointData(); 1487 } catch (Exception exc) { 1488 logException("Loading year", exc); 1489 } 1490 } 1491 }); 1492 1493 } 1494 1495 /** 1496 * _more_ 1497 * 1498 * @param yds 1499 * _more_ 1500 * 1501 * @throws Exception 1502 * _more_ 1503 */ 1504 public void loadYearInner(YearDisplayState yds) throws Exception { 1505 1506 TextType textType = TextType.getTextType("ID"); 1507 List fields = new ArrayList(); 1508 List times = new ArrayList(); 1509 List<StormTrack> obsTracks = new ArrayList<StormTrack>(); 1510 List<PointOb> pointObs = new ArrayList<PointOb>(); 1511 1512 JWindow errorWindow = null; 1513 JLabel errorLabel = null; 1514 1515 SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); 1516 sdf.setTimeZone(DateUtil.TIMEZONE_GMT); 1517 GregorianCalendar cal = new GregorianCalendar(DateUtil.TIMEZONE_GMT); 1518 Hashtable<String, Boolean> obsWays = new Hashtable<String, Boolean>(); 1519 obsWays.put(Way.OBSERVATION.toString(), new Boolean(true)); 1520 String currentMessage = ""; 1521 String errors = ""; 1522 boolean doYearTime = yearTimeMode == YEAR_TIME_MODE_YEAR; 1523 for (int i = stormInfos.size() - 1; i >= 0; i--) { 1524 if (yds.getState() != yds.STATE_LOADING) { 1525 yds.setState(YearDisplayState.STATE_INACTIVE); 1526 yds.setStatus(""); 1527 if (errorWindow != null) { 1528 errorWindow.setVisible(false); 1529 } 1530 return; 1531 } 1532 StormInfo stormInfo = stormInfos.get(i); 1533 cal.setTime(ucar.visad.Util.makeDate(stormInfo.getStartTime())); 1534 int stormYear = cal.get(Calendar.YEAR); 1535 if (stormYear != yds.getYear()) { 1536 continue; 1537 } 1538 1539 Object key = yds.getYear() + "_" + stormInfo.getStormId(); 1540 StormTrack obsTrack = (StormTrack) yearData.get(key); 1541 if (obsTrack == null) { 1542 yds.setStatus("Loading " + stormInfo + "..."); 1543 currentMessage = "Loading " + stormInfo; 1544 try { 1545 StormTrackCollection tracks = stormDataSource 1546 .getTrackCollection(stormInfo, obsWays, 1547 observationWay); 1548 obsTrack = tracks.getObsTrack(); 1549 if (obsTrack == null) { 1550 continue; 1551 } 1552 obsTrack = new StormTrack(obsTrack); 1553 obsTrack.setWay(new Way(obsTrack.getWay() + "_year" 1554 + yds.getYear())); 1555 yearData.put(key, obsTrack); 1556 } catch (BadDataException bde) { 1557 if (errorWindow == null) { 1558 Window parent = GuiUtils.getWindow(yds.getButton()); 1559 errorWindow = new JWindow(parent); 1560 errorWindow.getContentPane().add( 1561 errorLabel = new JLabel(" ")); 1562 errorLabel.setBorder(BorderFactory 1563 .createBevelBorder(BevelBorder.RAISED)); 1564 errorWindow.pack(); 1565 try { 1566 Point loc = yds.getButton().getLocationOnScreen(); 1567 errorWindow.setLocation((int) loc.getX(), 1568 (int) (loc.getY() + yds.getButton() 1569 .bounds().height)); 1570 1571 } catch (Exception exc) { 1572 // Ignore this incase the component isn't being 1573 // shown 1574 } 1575 errorWindow.show(); 1576 } 1577 errors = errors + "Error " + currentMessage + "<br>"; 1578 yds.setStatus("Error:" + currentMessage); 1579 errorLabel.setText("<html><i>" + errors + "</i></html>"); 1580 errorWindow.pack(); 1581 } 1582 } 1583 1584 if (obsTrack != null) { 1585 FieldImpl field = makeTrackField(obsTrack, null); 1586 StormTrackPoint stp = obsTrack.getTrackPoints().get(0); 1587 DateTime dttm = new DateTime(sdf.parse("" + yds.getYear())); 1588 if (!doYearTime) { 1589 dttm = stormInfo.getStartTime(); 1590 } 1591 obsTracks.add(obsTrack); 1592 times.add(dttm); 1593 fields.add(field); 1594 Tuple tuple = new Tuple(new Data[] { new visad.Text(textType, 1595 stormInfo.toString()) }); 1596 pointObs.add(PointObFactory.makePointOb(stp.getLocation(), 1597 dttm, tuple)); 1598 } 1599 } 1600 if (errorWindow != null) { 1601 errorWindow.setVisible(false); 1602 } 1603 // If we can't find an obs track then set the yds to be inactive 1604 if (times.size() == 0) { 1605 yds.setStatus("No observation track found"); 1606 yds.setState(YearDisplayState.STATE_INACTIVE); 1607 } else { 1608 yds.setData(doYearTime, obsTracks, times, fields, pointObs); 1609 yds.setState(YearDisplayState.STATE_ACTIVE); 1610 yds.setStatus(""); 1611 } 1612 1613 } 1614 1615 /** 1616 * _more_ 1617 */ 1618 public void writeToKml() { 1619 if (obsCbx == null) { 1620 obsCbx = new JCheckBox("Observation", true); 1621 forecastCbx = new JCheckBox("Forecast", true); 1622 mostRecentCbx = new JCheckBox("Most Recent Forecasts", false); 1623 } 1624 JComponent accessory = GuiUtils.top(GuiUtils.vbox(obsCbx, forecastCbx, 1625 mostRecentCbx)); 1626 1627 String filename = FileManager.getWriteFile(Misc 1628 .newList(FileManager.FILTER_KML), FileManager.SUFFIX_KML, 1629 accessory); 1630 if (filename == null) { 1631 return; 1632 } 1633 1634 try { 1635 writeToKml(filename, obsCbx.isSelected(), forecastCbx.isSelected(), 1636 mostRecentCbx.isSelected()); 1637 } catch (Exception exc) { 1638 logException("Writing KML", exc); 1639 } 1640 } 1641 1642 /** 1643 * _more_ 1644 * 1645 * @param filename 1646 * _more_ 1647 * @param doObs 1648 * _more_ 1649 * @param doForecast 1650 * _more_ 1651 * @param mostRecent 1652 * _more_ 1653 * 1654 * @throws RemoteException 1655 * _more_ 1656 * @throws VisADException 1657 * _more_ 1658 */ 1659 public void writeToKml(String filename, boolean doObs, boolean doForecast, 1660 boolean mostRecent) throws VisADException, RemoteException { 1661 try { 1662 Element kmlNode = KmlUtil.kml(""); 1663 Element docNode = KmlUtil.document(kmlNode, ""); 1664 KmlUtil 1665 .iconstyle(docNode, "hurricaneicon", 1666 "http://www.unidata.ucar.edu/software/idv/kml/images/hurricane.png"); 1667 Hashtable state = new Hashtable(); 1668 for (StormDisplayState stormDisplayState : getActiveStorms()) { 1669 stormDisplayState.writeToKml(docNode, state, doObs, doForecast, 1670 mostRecent); 1671 } 1672 1673 List<YearDisplayState> ydss = getYearDisplayStates(); 1674 for (YearDisplayState yds : ydss) { 1675 if (!yds.getActive()) { 1676 continue; 1677 } 1678 Element yearNode = KmlUtil.folder(docNode, "Year:" 1679 + yds.getYear()); 1680 for (StormTrack track : yds.getStormTracks()) { 1681 writeToGE(docNode, state, yearNode, track, yds.getColor()); 1682 } 1683 } 1684 1685 FileOutputStream fileOut = new FileOutputStream(filename); 1686 IOUtil.writeBytes(new File(filename), XmlUtil.toString(kmlNode) 1687 .getBytes()); 1688 1689 } catch (Exception exc) { 1690 logException("Writing KML", exc); 1691 } 1692 } 1693 1694 /** 1695 * _more_ 1696 * 1697 * 1698 * @param docNode 1699 * _more_ 1700 * @param state 1701 * _more_ 1702 * @param parent 1703 * _more_ 1704 * @param track 1705 * _more_ 1706 * @param color 1707 * _more_ 1708 * 1709 * 1710 * @throws RemoteException 1711 * _more_ 1712 * @throws VisADException 1713 * _more_ 1714 * 1715 * @throws Exception 1716 * _more_ 1717 */ 1718 protected void writeToGE(Element docNode, Hashtable state, Element parent, 1719 StormTrack track, Color color) throws Exception { 1720 Element placemark = KmlUtil.placemark(parent, "Track", "<html>" 1721 + getWayName() + ":" + track.getWay() + "<br>" + "" 1722 + track.getStartTime() + "</html>"); 1723 1724 int cnt = 0; 1725 String dateString = track.getStartTime().formattedString( 1726 "yyyy-MM-dd hhmm", DateUtil.TIMEZONE_GMT); 1727 String sheetName = track.getWay() + " - " + dateString; 1728 int rowCnt = 0; 1729 List<StormParam> params = track.getParams(); 1730 StringBuffer sb = new StringBuffer(); 1731 for (StormTrackPoint stp : track.getTrackPoints()) { 1732 EarthLocation el = stp.getLocation(); 1733 if (track.getWay().isObservation()) { 1734 Element icon = KmlUtil.placemark(parent, "Time:" 1735 + stp.getTime(), 1736 "<html><table>" + formatStormTrackPoint(track, stp) 1737 + "</table></html>", el.getLatitude().getValue( 1738 visad.CommonUnit.degree), el.getLongitude() 1739 .getValue(visad.CommonUnit.degree), (el 1740 .getAltitude() != null ? el.getAltitude() 1741 .getValue() : 0), "#hurricaneicon"); 1742 KmlUtil 1743 .timestamp(icon, ucar.visad.Util 1744 .makeDate(stp.getTime())); 1745 } 1746 1747 sb.append(el.getLongitude().getValue()); 1748 sb.append(","); 1749 sb.append(el.getLatitude().getValue()); 1750 sb.append(","); 1751 sb.append(el.getAltitude().getValue()); 1752 sb.append("\n"); 1753 } 1754 1755 String styleUrl = "linestyle" + track.getWay(); 1756 if (state.get(styleUrl) == null) { 1757 Element style = KmlUtil.linestyle(docNode, styleUrl, color, track 1758 .getWay().isObservation() ? 3 : 2); 1759 state.put(styleUrl, style); 1760 } 1761 KmlUtil.styleurl(placemark, "#" + styleUrl); 1762 Element linestring = KmlUtil.linestring(placemark, false, false, sb 1763 .toString()); 1764 // KmlUtil.timestamp(linestring, track.getStartTime()); 1765 if (!track.getWay().isObservation()) { 1766 KmlUtil.timestamp(placemark, ucar.visad.Util.makeDate(track 1767 .getStartTime())); 1768 } else { 1769 } 1770 } 1771 1772 /** 1773 * Make the gui 1774 * 1775 * @return The gui 1776 * 1777 * @throws RemoteException 1778 * On Badness 1779 * @throws VisADException 1780 * On Badness 1781 */ 1782 protected Container doMakeContents() throws VisADException, RemoteException { 1783 1784 // Get the storm infos and sort them 1785 stormInfos = (List<StormInfo>) Misc.sort(stormDataSource 1786 .getStormInfos()); 1787 1788 if (stormInfos.size() == 1) { 1789 try { 1790 if (localStormDisplayState == null) { 1791 localStormDisplayState = new StormDisplayState(stormInfos 1792 .get(0)); 1793 } 1794 stormDisplayStateMap = new Hashtable<StormInfo, StormDisplayState>(); 1795 localStormDisplayState.setStormTrackControl(this); 1796 stormDisplayStateMap.put(stormInfos.get(0), 1797 localStormDisplayState); 1798 localStormDisplayState.setIsOnlyChild(true); 1799 JComponent comp = localStormDisplayState.getContents(); 1800 localStormDisplayState.loadStorm(); 1801 return comp; 1802 } catch (Exception exc) { 1803 logException("Creating storm display", exc); 1804 return new JLabel("Error"); 1805 } 1806 } 1807 localStormDisplayState = null; 1808 treePanel = new TreePanel(true, 150); 1809 Hashtable years = new Hashtable(); 1810 JComponent firstComponent = null; 1811 JComponent firstSelectedComponent = null; 1812 GregorianCalendar cal = new GregorianCalendar(DateUtil.TIMEZONE_GMT); 1813 1814 List yearPanels = new ArrayList(); 1815 List yearComps = new ArrayList(); 1816 for (int i = stormInfos.size() - 1; i >= 0; i--) { 1817 StormInfo stormInfo = stormInfos.get(i); 1818 cal.setTime(ucar.visad.Util.makeDate(stormInfo.getStartTime())); 1819 int year = cal.get(Calendar.YEAR); 1820 if (years.get(new Integer(year)) == null) { 1821 YearDisplayState yds = getYearDisplayState(year); 1822 yearComps.add(new JLabel("" + year)); 1823 yearComps.add(yds.getButton()); 1824 yearComps.add(GuiUtils.wrap(yds.getColorSwatch())); 1825 yearComps.add(yds.getLabel()); 1826 years.put(new Integer(year), ""); 1827 if (yearComps.size() > 20) { 1828 GuiUtils.tmpInsets = GuiUtils.INSETS_5; 1829 yearPanels.add(GuiUtils.doLayout(yearComps, 4, 1830 GuiUtils.WT_NNNY, GuiUtils.WT_N)); 1831 yearComps = new ArrayList(); 1832 } 1833 } 1834 } 1835 GuiUtils.tmpInsets = GuiUtils.INSETS_5; 1836 yearPanels.add(GuiUtils.doLayout(yearComps, 4, GuiUtils.WT_NNNY, 1837 GuiUtils.WT_N)); 1838 1839 JComponent yearComponent = GuiUtils.vbox(yearPanels); 1840 if (yearPanels.size() > 0) { 1841 int width = 300; 1842 int height = 400; 1843 JScrollPane scroller = GuiUtils.makeScrollPane(GuiUtils 1844 .top(yearComponent), width, height); 1845 scroller.setBorder(BorderFactory.createLoweredBevelBorder()); 1846 scroller.setPreferredSize(new Dimension(width, height)); 1847 scroller.setMinimumSize(new Dimension(width, height)); 1848 yearComponent = scroller; 1849 } 1850 timeModeBox = new JComboBox(new Vector(Misc.newList("Start Year", 1851 "Storm Date"))); 1852 timeModeBox.setSelectedIndex(yearTimeMode); 1853 timeModeBox.addActionListener(new ActionListener() { 1854 public void actionPerformed(ActionEvent ae) { 1855 yearTimeMode = timeModeBox.getSelectedIndex(); 1856 Misc.run(StormTrackControl.this, "initYears"); 1857 } 1858 }); 1859 1860 JComponent yearTopComp = GuiUtils.inset(GuiUtils.left(GuiUtils.label( 1861 "Time Mode: ", timeModeBox)), 5); 1862 1863 treePanel.addComponent(GuiUtils.topCenter(yearTopComp, yearComponent), 1864 null, "Yearly Tracks", null); 1865 1866 years = new Hashtable(); 1867 1868 // Go in reverse order so we get the latest first 1869 for (int i = stormInfos.size() - 1; i >= 0; i--) { 1870 StormInfo stormInfo = stormInfos.get(i); 1871 cal.setTime(ucar.visad.Util.makeDate(stormInfo.getStartTime())); 1872 int year = cal.get(Calendar.YEAR); 1873 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 1874 1875 String category = "" + year; 1876 JComponent panelContents = stormDisplayState.getContents(); 1877 if (stormInfo.getBasin() != null) { 1878 category = category + TreePanel.CATEGORY_DELIMITER + "Basin:" 1879 + stormInfo.getBasin(); 1880 } 1881 treePanel.addComponent(panelContents, category, stormInfo 1882 .toString(), stormDisplayState.getActive() ? ICON_ON 1883 : ICON_OFF); 1884 1885 if (stormDisplayState.getActive() 1886 && (firstSelectedComponent == null)) { 1887 firstSelectedComponent = panelContents; 1888 } 1889 if (firstComponent == null) { 1890 firstComponent = panelContents; 1891 } 1892 } 1893 1894 // Show the first selected component or the first component 1895 if (firstSelectedComponent != null) { 1896 treePanel.show(firstSelectedComponent); 1897 } else if (firstComponent != null) { 1898 treePanel.show(firstComponent); 1899 } 1900 1901 // treePanel.setPreferredSize(new Dimension(500, 400)); 1902 JComponent contents = treePanel; 1903 1904 // JComponent contents = GuiUtils.topCenter(GuiUtils.left(box), 1905 // scroller); 1906 // contents.setPreferredSize(new Dimension(500, 400)); 1907 1908 if ((startTime != null) && (endTime != null)) { 1909 try { 1910 1911 Date[] range = DateUtil.getDateRange(startTime, endTime, 1912 new Date()); 1913 double fromDate = range[0].getTime(); 1914 double toDate = range[1].getTime(); 1915 for (StormInfo stormInfo : stormInfos) { 1916 double date = Util.makeDate(stormInfo.getStartTime()) 1917 .getTime(); 1918 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 1919 if ((date >= fromDate) && (date <= toDate)) { 1920 stormDisplayState.loadStorm(); 1921 } else if (stormDisplayState.getActive()) { 1922 stormDisplayState.deactivate(); 1923 } 1924 } 1925 } catch (java.text.ParseException pe) { 1926 logException("Error parsing start/end dates:" + startTime + " " 1927 + endTime, pe); 1928 } 1929 } 1930 1931 return contents; 1932 } 1933 1934 /** 1935 * _more_ 1936 * 1937 * @param stormDisplayState 1938 * _more_ 1939 */ 1940 public void stormChanged(StormDisplayState stormDisplayState) { 1941 activeStorms = null; 1942 if (treePanel != null) { 1943 treePanel.setIcon(stormDisplayState.getContents(), 1944 stormDisplayState.getActive() ? ICON_ON : ICON_OFF); 1945 } 1946 } 1947 1948 /** 1949 * Respond to a timeChange event 1950 * 1951 * @param time 1952 * new time 1953 */ 1954 protected void timeChanged(Real time) { 1955 try { 1956 List<StormDisplayState> active = getActiveStorms(); 1957 for (StormDisplayState stormDisplayState : active) { 1958 stormDisplayState.timeChanged(time); 1959 } 1960 } catch (Exception exc) { 1961 logException("changePosition", exc); 1962 } 1963 super.timeChanged(time); 1964 } 1965 1966 /** 1967 * Property change method. 1968 * 1969 * @param evt 1970 * event to act on 1971 */ 1972 public void propertyChange(PropertyChangeEvent evt) { 1973 if (evt.getPropertyName().equals( 1974 StationModelManager.PROP_RESOURCECHANGE)) { 1975 StationModel changedModel = (StationModel) evt.getNewValue(); 1976 handleChangedStationModel(changedModel.getName()); 1977 } else if (evt.getPropertyName().equals( 1978 StationModelManager.PROP_RESOURCEREMOVE)) { 1979 StationModel changedModel = (StationModel) evt.getOldValue(); 1980 handleChangedStationModel(changedModel.getName()); 1981 } 1982 super.propertyChange(evt); 1983 } 1984 1985 /** 1986 * _more_ 1987 * 1988 * @param name 1989 * _more_ 1990 */ 1991 private void handleChangedStationModel(String name) { 1992 for (int i = stormInfos.size() - 1; i >= 0; i--) { 1993 StormInfo stormInfo = stormInfos.get(i); 1994 StormDisplayState stormDisplayState = getStormDisplayState(stormInfo); 1995 if (stormDisplayState.getActive()) { 1996 stormDisplayState.handleChangedStationModel(name); 1997 } 1998 } 1999 2000 } 2001 2002 /** 2003 * Set the StormDisplayStates property. 2004 * 2005 * @param value 2006 * The new value for StormDisplayStates 2007 */ 2008 public void setStormDisplayStates(List<StormDisplayState> value) { 2009 if (value != null) { 2010 for (StormDisplayState stormDisplayState : value) { 2011 stormDisplayStateMap.put(stormDisplayState.getStormInfo(), 2012 stormDisplayState); 2013 } 2014 } 2015 } 2016 2017 /** 2018 * Get the StormDisplayStates property. 2019 * 2020 * @return The StormDisplayStates 2021 */ 2022 public List<StormDisplayState> getStormDisplayStates() { 2023 List<StormDisplayState> stormDisplayStates = new ArrayList<StormDisplayState>(); 2024 for (Enumeration keys = stormDisplayStateMap.keys(); keys 2025 .hasMoreElements();) { 2026 StormInfo key = (StormInfo) keys.nextElement(); 2027 StormDisplayState stormDisplayState = stormDisplayStateMap.get(key); 2028 // TODO: We don't want to add every state, just the ones that have 2029 // been changed 2030 // if(stormDisplayState.getChanged()) { 2031 if (stormDisplayState.getActive()) { 2032 stormDisplayStates.add(stormDisplayState); 2033 } 2034 } 2035 return stormDisplayStates; 2036 } 2037 2038 /** 2039 * _more_ 2040 * 2041 * @param year 2042 * _more_ 2043 * 2044 * @return _more_ 2045 */ 2046 public YearDisplayState getYearDisplayState(int year) { 2047 YearDisplayState yearDisplayState = yearDisplayStateMap 2048 .get(new Integer(year)); 2049 if (yearDisplayState == null) { 2050 yearDisplayState = new YearDisplayState(this, year); 2051 yearDisplayStateMap.put(new Integer(year), yearDisplayState); 2052 } 2053 return yearDisplayState; 2054 } 2055 2056 /** 2057 * Set the YearDisplayStates property. 2058 * 2059 * @param value 2060 * The new value for YearDisplayStates 2061 */ 2062 public void setYearDisplayStates(List<YearDisplayState> value) { 2063 if (value != null) { 2064 yearDisplayStateMap = new Hashtable<Integer, YearDisplayState>(); 2065 for (YearDisplayState yearDisplayState : value) { 2066 yearDisplayStateMap.put( 2067 new Integer(yearDisplayState.getYear()), 2068 yearDisplayState); 2069 } 2070 } 2071 } 2072 2073 /** 2074 * Get the YearDisplayStates property. 2075 * 2076 * @return The YearDisplayStates 2077 */ 2078 public List<YearDisplayState> getYearDisplayStates() { 2079 List<YearDisplayState> yearDisplayStates = new ArrayList<YearDisplayState>(); 2080 for (Enumeration keys = yearDisplayStateMap.keys(); keys 2081 .hasMoreElements();) { 2082 Object key = keys.nextElement(); 2083 YearDisplayState yearDisplayState = yearDisplayStateMap.get(key); 2084 if (yearDisplayState.getActive()) { 2085 yearDisplayStates.add(yearDisplayState); 2086 } 2087 } 2088 return yearDisplayStates; 2089 } 2090 2091 /** 2092 * _more_ 2093 * 2094 * @param el 2095 * _more_ 2096 * @param animationValue 2097 * _more_ 2098 * @param animationStep 2099 * _more_ 2100 * @param samples 2101 * _more_ 2102 * 2103 * @return _more_ 2104 * 2105 * @throws Exception 2106 * _more_ 2107 */ 2108 protected List getCursorReadoutInner(EarthLocation el, Real animationValue, 2109 int animationStep, List<ReadoutInfo> samples) throws Exception { 2110 2111 StormTrackPoint ob = null; 2112 2113 List result = new ArrayList(); 2114 List theStormStates = getStormDisplayStates(); 2115 if (theStormStates != null) { 2116 Object[] pair = findClosestPoint(el, theStormStates, 2117 animationValue, 20); 2118 if (pair != null) { 2119 StormTrack closestTrack = (StormTrack) pair[0]; 2120 StormTrackPoint closestOb = (StormTrackPoint) pair[1]; 2121 result.add("<tr><td>" + "Way: " + closestTrack.getWay() 2122 + "</td></tr> " 2123 + formatStormTrackPoint(closestTrack, closestOb)); 2124 2125 } 2126 } 2127 2128 return result; 2129 } 2130 2131 /** 2132 * _more_ 2133 * 2134 * 2135 * @param stormTrack 2136 * _more_ 2137 * @param stp 2138 * _more_ 2139 * 2140 * @return _more_ 2141 * 2142 * @throws RemoteException 2143 * _more_ 2144 * @throws VisADException 2145 * _more_ 2146 */ 2147 protected String formatStormTrackPoint(StormTrack stormTrack, 2148 StormTrackPoint stp) throws VisADException, RemoteException { 2149 Unit displayUnit = getDisplayUnit(); 2150 double value; 2151 if (stp == null) { 2152 return ""; 2153 } 2154 List<StormParam> params = stormTrack.getParams(); 2155 // result = "<tr><td>" + "Storm: " 2156 // + stp.toString() + "</td></tr>"; 2157 String result = "<tr><td>" + "Track Point Time:</td><td align=right>" 2158 + stp.getTime() + "</td></tr>"; 2159 for (StormParam param : params) { 2160 Real r = stp.getAttribute(param); 2161 if (r == null) { 2162 continue; 2163 } 2164 Unit unit = param.getUnit(); 2165 result = result + "<tr><td>" + param.toString() 2166 + ":</td><td align=right>" + Misc.format(r.getValue()) 2167 + ((unit != null) ? ("[" + unit + "]") : "") + "</td></tr>"; 2168 } 2169 2170 int length = result.length(); 2171 return StringUtil.padLeft(result, 5 * (20 - length), " "); 2172 } 2173 2174 /** 2175 * This finds the StormTrack and StormTrackPoint that is closest to the 2176 * given location 2177 * 2178 * 2179 * @param el 2180 * _more_ 2181 * @param theStates 2182 * _more_ 2183 * @param animationValue 2184 * _more_ 2185 * @param distanceThresholdPixels 2186 * _more_ 2187 * @return A 2-tuple. First element is the StormTrack. Second element is the 2188 * ob. Or null if none found 2189 * 2190 * @throws Exception 2191 * _more_ 2192 */ 2193 protected Object[] findClosestPoint(EarthLocation el, 2194 List<StormDisplayState> theStates, Real animationValue, 2195 int distanceThresholdPixels) throws Exception { 2196 if ((el == null) || (theStates == null)) { 2197 return null; 2198 } 2199 2200 int numStates = theStates.size(); 2201 StormTrackPoint closestOb = null; 2202 StormTrack closestTrack = null; 2203 2204 int[] clickPt = boxToScreen(earthToBox(el)); 2205 double minDistance = distanceThresholdPixels; 2206 // System.err.println ("click:" + clickPt[0]+"/"+clickPt[1] + " " 2207 // +minDistance); 2208 2209 for (int i = 0; i < numStates; i++) { 2210 StormDisplayState sds = theStates.get(i); 2211 if (sds == null) { 2212 continue; 2213 } 2214 StormTrackCollection trackCollection = sds.getTrackCollection(); 2215 if (trackCollection == null) { 2216 continue; 2217 } 2218 StormInfo sinfo = sds.getStormInfo(); 2219 HashMap<Way, List> wayToTracksMap = trackCollection 2220 .getWayToTracksHashMap(); 2221 // Way obsWay = new Way(Way.OBSERVATION); 2222 java.util.Set<Way> ways = wayToTracksMap.keySet(); 2223 2224 for (Way way : ways) { 2225 StormTrack track = null; 2226 if (way.equals(Way.OBSERVATION)) { 2227 // WayDisplayState trackWDS = wayToTracksMap.get(way); 2228 // //get(Way.OBSERVATION); 2229 List<StormTrack> tracks = wayToTracksMap.get(way); 2230 if (tracks.size() > 0) { 2231 track = tracks.get(0); 2232 } 2233 } else { 2234 WayDisplayState trackWDS = sds.getWayDisplayState(way); // get(Way.OBSERVATION); 2235 boolean visible = checkTracksVisible(animationValue, 2236 trackWDS); 2237 if (visible) { 2238 List<StormTrack> tracks = wayToTracksMap.get(way); 2239 track = getClosestTimeForecastTrack(tracks, 2240 animationValue); 2241 } 2242 } 2243 2244 if (track == null) { 2245 continue; 2246 } 2247 // System.err.println(way + " track time is: " + 2248 // track.getStartTime()); 2249 List<StormTrackPoint> stpList = track.getTrackPoints(); 2250 int size = stpList.size(); 2251 for (int j = 0; j < size; j++) { 2252 StormTrackPoint stp = stpList.get(j); 2253 EarthLocation stpLoc = stp.getLocation(); 2254 int[] obScreen = boxToScreen(earthToBox(stpLoc)); 2255 double distance = GuiUtils.distance(obScreen, clickPt); 2256 if (distance < minDistance) { 2257 closestOb = stp; 2258 minDistance = distance; 2259 closestTrack = track; 2260 } 2261 } 2262 } 2263 // System.err.println ("\t" + obScreen[0]+"/"+obScreen[1] + " d:" + 2264 // distance); 2265 2266 } 2267 2268 if (closestOb != null) { 2269 return new Object[] { closestTrack, closestOb }; 2270 } 2271 2272 return null; 2273 } 2274 2275 /** 2276 * _more_ 2277 * 2278 * @param currentAnimationTime 2279 * _more_ 2280 * @param wds 2281 * _more_ 2282 * 2283 * @return _more_ 2284 * 2285 * @throws Exception 2286 * _more_ 2287 */ 2288 private boolean checkTracksVisible(Real currentAnimationTime, 2289 WayDisplayState wds) throws Exception { 2290 if ((currentAnimationTime == null) || currentAnimationTime.isMissing()) { 2291 return false; 2292 } 2293 // Iterate way display states 2294 boolean visible = false; 2295 if (wds.shouldShowTrack() && wds.hasTrackDisplay()) { 2296 FieldImpl field = (FieldImpl) wds.getTrackDisplay().getData(); 2297 if (field == null) { 2298 return false; 2299 } 2300 Set timeSet = GridUtil.getTimeSet(field); 2301 if (timeSet == null) { 2302 return false; 2303 } 2304 if (timeSet.getLength() == 1) { 2305 return true; 2306 } else { 2307 // Else work the visad magic 2308 float timeValueFloat = (float) currentAnimationTime 2309 .getValue(timeSet.getSetUnits()[0]); 2310 // System.err.println("multiple times:" + timeValueFloat); 2311 float[][] value = { { timeValueFloat } }; 2312 int[] index = timeSet.valueToIndex(value); 2313 // System.err.println("index:" + index[0]); 2314 return visible = (index[0] >= 0); 2315 } 2316 2317 } 2318 return visible; 2319 } 2320 2321 /** 2322 * _more_ 2323 * 2324 * @param tracks 2325 * _more_ 2326 * @param pTime 2327 * _more_ 2328 * 2329 * @return _more_ 2330 * 2331 * @throws VisADException 2332 * _more_ 2333 */ 2334 private StormTrack getClosestTimeForecastTrack(List<StormTrack> tracks, 2335 Real pTime) throws VisADException { 2336 2337 DateTime dt = new DateTime(pTime); // pTime. 2338 double timeToLookFor = dt.getValue(); 2339 int numPoints = tracks.size(); 2340 double lastTime = -1; 2341 2342 // for(StormTrack track: tracks){ 2343 // if(track.getTrackStartTime().equals(dt)) 2344 // return track; 2345 // } 2346 for (int i = 0; i < numPoints; i++) { 2347 StormTrack st = tracks.get(i); 2348 double currentTime = st.getStartTime().getValue(); 2349 if (timeToLookFor == currentTime) { 2350 return st; 2351 } 2352 if (timeToLookFor < currentTime) { 2353 if (i == 0) { 2354 return null; 2355 } 2356 if (timeToLookFor > lastTime) { 2357 return tracks.get(i - 1); 2358 } 2359 } 2360 lastTime = currentTime; 2361 } 2362 return null; 2363 } 2364 2365 /** 2366 * Set the OkWays property. 2367 * 2368 * @param value 2369 * The new value for OkWays 2370 */ 2371 public void setOkWays(Hashtable<String, Boolean> value) { 2372 okWays = value; 2373 } 2374 2375 /** 2376 * _more_ 2377 * 2378 * @param value 2379 * _more_ 2380 */ 2381 public void setObservationWay(Way value) { 2382 observationWay = value; 2383 } 2384 2385 /** 2386 * Get the OkWays property. 2387 * 2388 * @return The OkWays 2389 */ 2390 public Hashtable<String, Boolean> getOkWays() { 2391 return okWays; 2392 } 2393 2394 /** 2395 * _more_ 2396 * 2397 * @return _more_ 2398 */ 2399 public Way getObservationWay() { 2400 return observationWay; 2401 } 2402 2403 /** 2404 * Set the OkParams property. 2405 * 2406 * @param value 2407 * The new value for OkParams 2408 */ 2409 public void setOkParams(Hashtable<String, Boolean> value) { 2410 okParams = value; 2411 } 2412 2413 /** 2414 * Get the OkParams property. 2415 * 2416 * @return The OkParams 2417 */ 2418 public Hashtable<String, Boolean> getOkParams() { 2419 return okParams; 2420 } 2421 2422 /** 2423 * Set the StartTime property. 2424 * 2425 * @param value 2426 * The new value for StartTime 2427 */ 2428 public void setStartTime(String value) { 2429 startTime = value; 2430 } 2431 2432 /** 2433 * Get the StartTime property. 2434 * 2435 * @return The StartTime 2436 */ 2437 public String getStartTime() { 2438 return startTime; 2439 } 2440 2441 /** 2442 * Set the EndTime property. 2443 * 2444 * @param value 2445 * The new value for EndTime 2446 */ 2447 public void setEndTime(String value) { 2448 endTime = value; 2449 } 2450 2451 /** 2452 * Get the EndTime property. 2453 * 2454 * @return The EndTime 2455 */ 2456 public String getEndTime() { 2457 return endTime; 2458 } 2459 2460 /** 2461 * Set the LocalStormDisplayState property. 2462 * 2463 * @param value 2464 * The new value for LocalStormDisplayState 2465 */ 2466 public void setLocalStormDisplayState(StormDisplayState value) { 2467 localStormDisplayState = value; 2468 } 2469 2470 /** 2471 * Get the LocalStormDisplayState property. 2472 * 2473 * @return The LocalStormDisplayState 2474 */ 2475 public StormDisplayState getLocalStormDisplayState() { 2476 return localStormDisplayState; 2477 } 2478 2479 /** 2480 * Set the YearTimeMode property. 2481 * 2482 * @param value 2483 * The new value for YearTimeMode 2484 */ 2485 public void setYearTimeMode(int value) { 2486 yearTimeMode = value; 2487 } 2488 2489 /** 2490 * Get the YearTimeMode property. 2491 * 2492 * @return The YearTimeMode 2493 */ 2494 public int getYearTimeMode() { 2495 return yearTimeMode; 2496 } 2497 2498 /** 2499 * Set the EditMode property. 2500 * 2501 * @param value 2502 * The new value for EditMode 2503 */ 2504 public void setEditMode(boolean value) { 2505 editMode = value; 2506 } 2507 2508 /** 2509 * Get the EditMode property. 2510 * 2511 * @return The EditMode 2512 */ 2513 public boolean getEditMode() { 2514 return editMode; 2515 } 2516 2517 protected void applyRange() throws VisADException, RemoteException { 2518 for (StormDisplayState sds : getActiveStorms()) { 2519 sds.colorRangeChanged(); 2520 } 2521 2522 } 2523 2524 }