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