001 /* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2013 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029 package edu.wisc.ssec.mcidasv.control.cyclone; 030 031 import java.awt.Color; 032 import java.awt.Dimension; 033 import java.awt.Rectangle; 034 import java.awt.geom.Rectangle2D; 035 import java.rmi.RemoteException; 036 import java.text.DecimalFormat; 037 import java.util.ArrayList; 038 import java.util.Calendar; 039 import java.util.Date; 040 import java.util.Iterator; 041 import java.util.List; 042 import java.util.Vector; 043 044 import javax.swing.JComponent; 045 046 import ucar.unidata.data.grid.GridUtil; 047 import ucar.unidata.data.point.PointOb; 048 import ucar.unidata.data.point.PointObFactory; 049 import ucar.unidata.data.storm.StormParam; 050 import ucar.unidata.data.storm.StormTrack; 051 import ucar.unidata.data.storm.StormTrackPoint; 052 import ucar.unidata.data.storm.Way; 053 import ucar.unidata.geoloc.Bearing; 054 import ucar.unidata.geoloc.LatLonPoint; 055 import ucar.unidata.geoloc.LatLonPointImpl; 056 import ucar.unidata.geoloc.ProjectionPoint; 057 import ucar.unidata.geoloc.ProjectionPointImpl; 058 import ucar.unidata.geoloc.projection.FlatEarth; 059 import ucar.unidata.geoloc.projection.LatLonProjection; 060 import ucar.unidata.gis.SpatialGrid; 061 import ucar.unidata.ui.colortable.ColorTableDefaults; 062 import ucar.unidata.ui.symbol.StationModel; 063 import ucar.unidata.util.ColorTable; 064 import ucar.unidata.util.GuiUtils; 065 import ucar.unidata.util.LogUtil; 066 import ucar.unidata.util.Misc; 067 import ucar.unidata.util.Range; 068 import ucar.unidata.view.geoloc.NavigatedDisplay; 069 import ucar.visad.Util; 070 import ucar.visad.display.CompositeDisplayable; 071 import ucar.visad.display.Displayable; 072 import ucar.visad.display.StationModelDisplayable; 073 import ucar.visad.display.TrackDisplayable; 074 import visad.CommonUnit; 075 import visad.Data; 076 import visad.DateTime; 077 import visad.FieldImpl; 078 import visad.FunctionType; 079 import visad.Integer1DSet; 080 import visad.Real; 081 import visad.RealType; 082 import visad.Set; 083 import visad.SetType; 084 import visad.TextType; 085 import visad.Tuple; 086 import visad.Unit; 087 import visad.VisADException; 088 import visad.georef.EarthLocation; 089 import visad.georef.EarthLocationLite; 090 091 /** 092 * 093 * @author Unidata Development Team 094 * @version $Revision$ 095 */ 096 097 public class WayDisplayState { 098 099 /** Type for Azimuth */ 100 private final RealType azimuthType = RealType.getRealType("Azimuth", 101 CommonUnit.degree); 102 103 /** _more_ */ 104 private Way way; 105 106 /** _more_ */ 107 private StormDisplayState stormDisplayState; 108 109 /** _more_ */ 110 private DisplayState trackState; 111 112 /** _more_ */ 113 private DisplayState coneState; 114 115 /** _more_ */ 116 private DisplayState wayState; 117 118 /** _more_ */ 119 private DisplayState ringsState; 120 121 /** _more_ */ 122 List<PointOb> pointObs = new ArrayList<PointOb>(); 123 124 /** _more_ */ 125 List<PointOb> allPointObs = new ArrayList<PointOb>(); 126 127 /** _more_ */ 128 private List<StormTrack> tracks = new ArrayList<StormTrack>(); 129 130 /** _more_ */ 131 private List<DateTime> times = new ArrayList<DateTime>(); 132 133 /** _more_ */ 134 private Color color; 135 136 /** _more_ */ 137 private GuiUtils.ColorSwatch colorSwatch; 138 139 /** _more_ */ 140 private CompositeDisplayable holder; 141 142 /** _more_ */ 143 private StationModelDisplayable labelDisplay; 144 145 /** _more_ */ 146 private StationModelDisplayable obsPointDisplay; 147 148 /** _more_ */ 149 private TrackDisplayable trackDisplay; 150 151 /** _more_ */ 152 private TrackDisplayable ringsDisplay; 153 154 /** _more_ */ 155 private CompositeDisplayable conesHolder; 156 157 /** _more_ */ 158 private List<StormParam> coneParams; 159 160 /** _more_ */ 161 private StormParam ringsParam; 162 163 /** _more_ */ 164 private StormParam colorParam; 165 166 /** _more_ */ 167 private int modeParam = 99; 168 169 /** 170 * _more_ 171 */ 172 public WayDisplayState() { 173 } 174 175 /** 176 * _more_ 177 * 178 * 179 * @param stormDisplayState 180 * _more_ 181 * @param way 182 * _more_ 183 */ 184 public WayDisplayState(StormDisplayState stormDisplayState, Way way) { 185 this.stormDisplayState = stormDisplayState; 186 this.way = way; 187 wayState = new DisplayState(this, "Show/Hide All", true); 188 trackState = new DisplayState(this, "Show/Hide Track", true); 189 coneState = new DisplayState(this, "Show/Hide Cone", false); 190 ringsState = new DisplayState(this, "Show/Hide Rings", false); 191 } 192 193 /** 194 * _more_ 195 * 196 * @return _more_ 197 * 198 * @throws RemoteException 199 * _more_ 200 * @throws VisADException 201 * _more_ 202 */ 203 protected CompositeDisplayable getHolder() throws VisADException, 204 RemoteException { 205 if (holder == null) { 206 holder = new CompositeDisplayable("way holder"); 207 stormDisplayState.addDisplayable(holder); 208 } 209 return holder; 210 } 211 212 /** 213 * _more_ 214 * 215 * @return _more_ 216 */ 217 public boolean hasTrackDisplay() { 218 return trackDisplay != null; 219 } 220 221 /** 222 * _more_ 223 * 224 * @return _more_ 225 */ 226 public boolean hasLabelDisplay() { 227 return labelDisplay != null; 228 } 229 230 /** 231 * _more_ 232 * 233 * @throws RemoteException 234 * _more_ 235 * @throws VisADException 236 * _more_ 237 */ 238 private void removeTrackDisplay() throws VisADException, RemoteException { 239 if (trackDisplay != null) { 240 removeDisplayable(trackDisplay); 241 trackDisplay = null; 242 } 243 } 244 245 /** 246 * _more_ 247 * 248 * @throws RemoteException 249 * _more_ 250 * @throws VisADException 251 * _more_ 252 */ 253 private void removeLabelDisplay() throws VisADException, RemoteException { 254 if (labelDisplay != null) { 255 removeDisplayable(labelDisplay); 256 labelDisplay = null; 257 } 258 } 259 260 /** 261 * _more_ 262 * 263 * @throws RemoteException 264 * _more_ 265 * @throws VisADException 266 * _more_ 267 */ 268 private void removeObsPointDisplay() throws VisADException, RemoteException { 269 if (obsPointDisplay != null) { 270 removeDisplayable(obsPointDisplay); 271 obsPointDisplay = null; 272 } 273 } 274 275 /** 276 * _more_ 277 * 278 * @return _more_ 279 */ 280 public boolean hasObsPointDisplay() { 281 return obsPointDisplay != null; 282 } 283 284 /** 285 * _more_ 286 * 287 * @return _more_ 288 */ 289 public boolean hasRingsDisplay() { 290 return ringsDisplay != null; 291 } 292 293 /** 294 * _more_ 295 * 296 * @return _more_ 297 */ 298 public boolean hasConeDisplay() { 299 return conesHolder != null; 300 } 301 302 /** 303 * _more_ 304 * 305 * 306 * @param force 307 * _more_ 308 * @throws Exception 309 * _more_ 310 */ 311 public void updateDisplay(boolean force) throws Exception { 312 313 if (!shouldShow()) { 314 if (holder != null) { 315 holder.setVisible(false); 316 } 317 return; 318 } 319 320 getHolder().setVisible(true); 321 int forecastAnimationMode = stormDisplayState 322 .getForecastAnimationMode(); 323 if (shouldShowTrack()) { 324 StormParam tmpParam = stormDisplayState.getColorParam(this); 325 boolean hadTrack = hasTrackDisplay(); 326 boolean paramChanged = !Misc.equals(colorParam, tmpParam); 327 boolean modeChanged = !(modeParam == forecastAnimationMode); 328 if (force || !hadTrack || paramChanged || modeChanged 329 || stormDisplayState.isColorRangeChanged()) { 330 // System.err.println("makeing field"); 331 colorParam = tmpParam; 332 // modeParam = forecastAnimationMode; 333 FieldImpl trackField = makeTrackField(forecastAnimationMode); 334 if (trackField != null) { 335 if (paramChanged) { 336 trackDisplay = null; 337 initTrackDisplay(); 338 } 339 getTrackDisplay().setUseTimesInAnimation(false); 340 getTrackDisplay().setTrack(trackField); 341 Range range = null; 342 if (colorParam != null) { 343 String paramName = colorParam.getName(); 344 range = stormDisplayState.getStormTrackControl() 345 .getIdv().getParamDefaultsEditor() 346 .getParamRange(paramName); 347 if (stormDisplayState.isColorRangeChanged()) { 348 range = stormDisplayState.getStormTrackControl() 349 .getRangeForColorTable(); 350 stormDisplayState.getStormTrackControl() 351 .getColorTableWidget(range); 352 } 353 354 Unit displayUnit = stormDisplayState 355 .getStormTrackControl().getIdv() 356 .getParamDefaultsEditor().getParamDisplayUnit( 357 paramName); 358 if (displayUnit != null) { 359 getTrackDisplay().setDisplayUnit(displayUnit); 360 } else { 361 Unit[] u = GridUtil.getParamUnits(trackField); 362 if (u[0] != null) { 363 getTrackDisplay().setDisplayUnit(u[0]); 364 } 365 } 366 } 367 if (range == null) { 368 range = GridUtil.getMinMax(trackField)[0]; 369 } 370 getTrackDisplay().setRangeForColor(range.getMin(), 371 range.getMax()); 372 } 373 } 374 setTrackColor(); 375 getTrackDisplay().setVisible(true); 376 } else { 377 if (hasTrackDisplay()) { 378 getTrackDisplay().setVisible(false); 379 } 380 } 381 382 updateLayoutModel(); 383 384 if (shouldShowCone()) { 385 List<StormParam> tmp = stormDisplayState.getConeParams(this); 386 if (!hasConeDisplay() || !Misc.equals(tmp, coneParams) 387 || !(modeParam == forecastAnimationMode)) { 388 this.coneParams = tmp; 389 getConesHolder().clearDisplayables(); 390 setConeColor(); 391 for (StormParam param : coneParams) { 392 TrackDisplayable coneDisplay = makeConeDisplay(param, 393 forecastAnimationMode); 394 if (coneDisplay != null) { 395 getConesHolder().addDisplayable(coneDisplay); 396 } 397 } 398 setConeColor(); 399 } 400 getConesHolder().setVisible(true); 401 } else { 402 if (hasConeDisplay()) { 403 getConesHolder().setVisible(false); 404 } 405 } 406 407 if (shouldShowRings()) { 408 StormParam tmp = stormDisplayState.getRingsParam(this); 409 TrackDisplayable ringDisplay = getRingsDisplay(); 410 if (!hasRingsDisplay() || !Misc.equals(tmp, ringsParam) 411 || !(modeParam == forecastAnimationMode)) { 412 this.ringsParam = tmp; 413 setRingsColor(); 414 FieldImpl field = makeRingsField(ringsParam, 415 forecastAnimationMode); 416 if ((field == null) || (field.getLength() == 0)) { 417 ringDisplay.setData(new Real(0)); 418 } else { 419 ringDisplay.setTrack(field); 420 } 421 setRingsColor(); 422 } 423 ringsDisplay.setVisible(true); 424 } else { 425 if (hasRingsDisplay()) { 426 getRingsDisplay().setVisible(false); 427 } 428 } 429 430 modeParam = forecastAnimationMode; 431 432 } 433 434 /** 435 * _more_ 436 * 437 * @return _more_ 438 */ 439 public boolean shouldShow() { 440 if (tracks.size() == 0) { 441 return false; 442 } 443 if (!way.isObservation() 444 && !stormDisplayState.getForecastState().getWayState() 445 .getVisible()) { 446 return false; 447 } 448 // return visible; 449 return wayState.getVisible(); 450 } 451 452 /** 453 * _more_ 454 * 455 * @return _more_ 456 */ 457 public boolean shouldShowTrack() { 458 if (!way.isObservation() 459 && !stormDisplayState.getForecastState().getTrackState() 460 .getVisible()) { 461 return false; 462 } 463 return shouldShow() && trackState.getVisible(); 464 } 465 466 /** 467 * _more_ 468 * 469 * @return _more_ 470 */ 471 public boolean shouldShowRings() { 472 if (!way.isObservation() 473 && !stormDisplayState.getForecastState().getRingsState() 474 .getVisible()) { 475 return false; 476 } 477 return shouldShow() && ringsState.getVisible(); 478 } 479 480 /** 481 * _more_ 482 * 483 * @return _more_ 484 */ 485 public boolean shouldShowCone() { 486 if (!way.isObservation() 487 && !stormDisplayState.getForecastState().getConeState() 488 .getVisible()) { 489 return false; 490 } 491 return shouldShow() && coneState.getVisible(); 492 } 493 494 /** 495 * _more_ 496 * 497 * 498 * @throws Exception 499 * _more_ 500 */ 501 public void updateLayoutModel() throws Exception { 502 StationModel sm; 503 // If we are showing the track then create (if needed) the station model 504 // displays 505 if (shouldShowTrack()) { 506 if (way.isObservation()) { 507 sm = stormDisplayState.getObsPointLayoutModel(); 508 // We won't create them (or will remove them) if the layout 509 // model is null 510 if (sm == null) { 511 removeObsPointDisplay(); 512 } else { 513 if (true) { // (!hasObsPointDisplay()) { 514 FieldImpl pointField; 515 pointField = PointObFactory.makeTimeSequenceOfPointObs( 516 allPointObs, -1, -1); 517 518 FieldImpl pointField1 = doDeclutter(pointField, sm); 519 getObsPointDisplay().setStationData(pointField1); 520 521 } 522 if (hasObsPointDisplay()) { // && !Misc.equals(sm, 523 // getObsPointDisplay().getStationModel())) 524 // { 525 getObsPointDisplay().setStationModel(sm); 526 } 527 } 528 } 529 530 sm = (way.isObservation() ? stormDisplayState.getObsLayoutModel() 531 : stormDisplayState.getForecastLayoutModel()); 532 if (sm == null) { 533 removeLabelDisplay(); 534 } else { 535 if (pointObs.size() > 0) { // (!hasLabelDisplay()) { 536 FieldImpl pointField = PointObFactory 537 .makeTimeSequenceOfPointObs(pointObs, -1, -1); 538 539 getLabelDisplay().setStationData(pointField); 540 getLabelDisplay().setStationModel(sm); 541 } 542 } 543 } 544 545 setLabelColor(); 546 if (hasObsPointDisplay()) { 547 getObsPointDisplay().setVisible(shouldShowTrack()); 548 } 549 550 if (hasLabelDisplay()) { 551 getLabelDisplay().setVisible(shouldShowTrack()); 552 } 553 554 } 555 556 /** 557 * Declutters the observations. This is just a wrapper around the real 558 * decluttering in doTheActualDecluttering(FieldImpl) to handle the case 559 * where there is a time sequence of observations. 560 * 561 * @param obs 562 * initial field of observations. 563 * @param sModel 564 * _more_ 565 * 566 * @return a decluttered version of obs 567 * 568 * @throws RemoteException 569 * Java RMI error 570 * @throws VisADException 571 * VisAD Error 572 */ 573 private FieldImpl doDeclutter(FieldImpl obs, StationModel sModel) 574 throws VisADException, RemoteException { 575 576 // long millis = System.currentTimeMillis(); 577 boolean isTimeSequence = GridUtil.isTimeSequence(obs); 578 FieldImpl declutteredField = null; 579 if (isTimeSequence) { 580 Set timeSet = obs.getDomainSet(); 581 declutteredField = new FieldImpl((FunctionType) obs.getType(), 582 timeSet); 583 int numTimes = timeSet.getLength(); 584 for (int i = 0; i < numTimes; i++) { 585 FieldImpl oneTime = (FieldImpl) obs.getSample(i); 586 FieldImpl subTime = doTheActualDecluttering(oneTime, sModel); 587 if (subTime != null) { 588 declutteredField.setSample(i, subTime, false); 589 } 590 } 591 } else { 592 declutteredField = doTheActualDecluttering(obs, sModel); 593 } 594 // System.out.println("Subsetting took : " + 595 // (System.currentTimeMillis() - millis) + " ms"); 596 return declutteredField; 597 } 598 599 /** 600 * a * Declutters a single timestep of observations. 601 * 602 * @param pointObs 603 * point observations for one timestep. 604 * 605 * @return a decluttered version of pointObs 606 * 607 * @throws RemoteException 608 * Java RMI error 609 * @throws VisADException 610 * VisAD Error 611 */ 612 613 /** grid for decluttering */ 614 private SpatialGrid stationGrid; 615 616 /** 617 * _more_ 618 * 619 * @param pointObs 620 * _more_ 621 * @param sm 622 * _more_ 623 * 624 * @return _more_ 625 * 626 * @throws RemoteException 627 * _more_ 628 * @throws VisADException 629 * _more_ 630 */ 631 private FieldImpl doTheActualDecluttering(FieldImpl pointObs, 632 StationModel sm) throws VisADException, RemoteException { 633 if ((pointObs == null) || pointObs.isMissing()) { 634 return pointObs; 635 } 636 FieldImpl retField = null; 637 Set domainSet = pointObs.getDomainSet(); 638 int numObs = domainSet.getLength(); 639 Vector v = new Vector(); 640 641 long t1 = System.currentTimeMillis(); 642 Rectangle glyphBounds = sm.getBounds(); 643 float myScale = getObsPointDisplay().getScale() * .0025f 644 * getDeclutterFilter(); 645 // System.out.println("\ndecluttering myScale=" + myScale + 646 // " filter=" +getDeclutterFilter()); 647 Rectangle2D scaledGlyphBounds = new Rectangle2D.Double(glyphBounds 648 .getX() 649 * myScale, glyphBounds.getY() * myScale, glyphBounds.getWidth() 650 * myScale, glyphBounds.getHeight() * myScale); 651 NavigatedDisplay navDisplay = stormDisplayState.getStormTrackControl() 652 .getNavigatedDisplay(); 653 654 Rectangle2D.Double obBounds = new Rectangle2D.Double(); 655 obBounds.width = scaledGlyphBounds.getWidth(); 656 obBounds.height = scaledGlyphBounds.getHeight(); 657 658 if (stationGrid == null) { 659 stationGrid = new SpatialGrid(200, 200); 660 } 661 stationGrid.clear(); 662 stationGrid.setGrid(getBounds(), scaledGlyphBounds); 663 if (getDeclutterFilter() < 0.3f) { 664 // stationGrid.setOverlap((int)((1.0-getDeclutterFilter())*100)); 665 // stationGrid.setOverlap( (int)((.5f-getDeclutterFilter())*100)); 666 } else { 667 // stationGrid.setOverlap(0); 668 } 669 670 double[] xyz = new double[3]; 671 // TODO: The repeated getSpatialCoords is a bit expensive 672 for (int i = 0; i < numObs; i++) { 673 PointOb ob = (PointOb) pointObs.getSample(i); 674 xyz = navDisplay.getSpatialCoordinates(ob.getEarthLocation(), xyz); 675 obBounds.x = xyz[0]; 676 obBounds.y = xyz[1]; 677 if (stationGrid.markIfClear(obBounds, "")) { 678 v.add(ob); // is in the bounds 679 } 680 } 681 // stationGrid.print(); 682 683 if (v.isEmpty()) { 684 retField = new FieldImpl((FunctionType) pointObs.getType(), 685 new Integer1DSet(((SetType) domainSet.getType()) 686 .getDomain(), 1)); 687 retField.setSample(0, pointObs.getSample(0), false); 688 } else if (v.size() == numObs) { 689 retField = pointObs; // all were in domain, just return input 690 } else { 691 retField = new FieldImpl((FunctionType) pointObs.getType(), 692 new Integer1DSet(((SetType) domainSet.getType()) 693 .getDomain(), v.size())); 694 retField.setSamples((PointOb[]) v.toArray(new PointOb[v.size()]), 695 false, false); 696 } 697 698 return retField; 699 } 700 701 /** decluttering filter factor */ 702 private float declutterFilter = 1.0f; 703 704 /** 705 * _more_ 706 * 707 * @return _more_ 708 */ 709 public float getDeclutterFilter() { 710 return declutterFilter; 711 } 712 713 /** 714 * _more_ 715 * 716 * @return _more_ 717 */ 718 protected Rectangle2D getBounds() { 719 return calculateRectangle(); 720 } 721 722 /** 723 * _more_ 724 * 725 * @return _more_ 726 */ 727 protected Rectangle2D calculateRectangle() { 728 try { 729 Rectangle2D.Double box = stormDisplayState.getStormTrackControl() 730 .getNavigatedDisplay().getVisadBox(); 731 if (!box.isEmpty()) { 732 // pad rectangle by 5% 733 double deltaWidth = (double) (.05 * box.width); 734 double deltaHeight = (double) (.05 * box.height); 735 double newX = box.x - deltaWidth; 736 double newY = box.y - deltaHeight; 737 box.setRect(newX, newY, box.width + (2.0 * deltaWidth), 738 box.height + (2.0 * deltaHeight)); 739 } 740 return box; 741 } catch (Exception excp) { 742 LogUtil.logException("calculating Rectangle ", excp); 743 return new Rectangle2D.Double(0, 0, 0, 0); 744 } 745 } 746 747 /** 748 * _more_ 749 * 750 * @return _more_ 751 * 752 * @throws Exception 753 * _more_ 754 */ 755 public StationModelDisplayable getLabelDisplay() throws Exception { 756 if (labelDisplay == null) { 757 StationModel sm = (way.isObservation() ? stormDisplayState 758 .getObsLayoutModel() : stormDisplayState 759 .getForecastLayoutModel()); 760 if (sm != null) { 761 labelDisplay = new StationModelDisplayable("dots"); 762 labelDisplay.setRotateShapes(true); 763 labelDisplay.setUseTimesInAnimation(false); 764 addDisplayable(labelDisplay); 765 labelDisplay.setScale(stormDisplayState.getStormTrackControl() 766 .getDisplayScale()); 767 } 768 } 769 return labelDisplay; 770 } 771 772 /** 773 * _more_ 774 * 775 * @return _more_ 776 * 777 * 778 * @throws RemoteException 779 * _more_ 780 * @throws VisADException 781 * _more_ 782 */ 783 public StationModelDisplayable getObsPointDisplay() throws VisADException, 784 RemoteException { 785 if (obsPointDisplay == null) { 786 obsPointDisplay = new StationModelDisplayable("dots"); 787 obsPointDisplay.setRotateShapes(true); 788 obsPointDisplay.setUseTimesInAnimation(false); 789 addDisplayable(obsPointDisplay); 790 obsPointDisplay.setScale(stormDisplayState.getStormTrackControl() 791 .getDisplayScale()); 792 } 793 return obsPointDisplay; 794 } 795 796 /** 797 * _more_ 798 * 799 * @return _more_ 800 * 801 * @throws Exception 802 * _more_ 803 */ 804 public void initTrackDisplay() throws Exception { 805 806 trackDisplay = new TrackDisplayable("track_" 807 + stormDisplayState.getStormInfo().getStormId()); // + 808 // stormDisplayState.getColorParam(this)); 809 if (way.isObservation()) { 810 trackDisplay.setLineWidth(3); 811 } else { 812 trackDisplay.setLineWidth(2); 813 trackDisplay.setUseTimesInAnimation(false); 814 } 815 // setTrackColor(); 816 int cnt = holder.displayableCount(); 817 818 for (int i = 0; i < cnt; i++) { 819 Displayable dp = holder.getDisplayable(i); 820 if (dp.getClass().isInstance(trackDisplay)) { 821 TrackDisplayable dd = (TrackDisplayable) dp; 822 if (dd.toString().equalsIgnoreCase(trackDisplay.toString())) { 823 holder.removeDisplayable(dp); 824 cnt = cnt - 1; 825 } 826 } 827 } 828 829 addDisplayable(trackDisplay); 830 831 } 832 833 /** 834 * _more_ 835 * 836 * @return _more_ 837 * 838 * @throws Exception 839 * _more_ 840 */ 841 public TrackDisplayable getTrackDisplay() throws Exception { 842 if (trackDisplay == null) { 843 initTrackDisplay(); 844 } 845 return trackDisplay; 846 } 847 848 /** 849 * _more_ 850 * 851 * @return _more_ 852 * 853 * @throws Exception 854 * _more_ 855 */ 856 public CompositeDisplayable getConesHolder() throws Exception { 857 if (conesHolder == null) { 858 conesHolder = new CompositeDisplayable("cone_" 859 + stormDisplayState.getStormInfo().getStormId()); 860 conesHolder.setVisible(true); 861 conesHolder.setUseTimesInAnimation(false); 862 addDisplayable(conesHolder); 863 } 864 return conesHolder; 865 } 866 867 /** 868 * _more_ 869 * 870 * @return _more_ 871 * 872 * @throws Exception 873 * _more_ 874 */ 875 public TrackDisplayable getRingsDisplay() throws Exception { 876 if (ringsDisplay == null) { 877 ringsDisplay = new TrackDisplayable("ring_" 878 + stormDisplayState.getStormInfo().getStormId() + "_" 879 + getWay()); 880 ringsDisplay.setVisible(true); 881 ringsDisplay.setUseTimesInAnimation(false); 882 addDisplayable(ringsDisplay); 883 } 884 return ringsDisplay; 885 } 886 887 /** 888 * _more_ 889 * 890 * @param param 891 * _more_ 892 * @param mode 893 * _more_ 894 * 895 * @return _more_ 896 * 897 * @throws Exception 898 * _more_ 899 */ 900 public TrackDisplayable makeConeDisplay(StormParam param, int mode) 901 throws Exception { 902 FieldImpl field = makeConeField(param, mode); 903 if (field == null) { 904 return null; 905 } 906 TrackDisplayable coneDisplay = new TrackDisplayable("cone_" 907 + stormDisplayState.getStormInfo().getStormId()); 908 coneDisplay.setUseTimesInAnimation(false); 909 coneDisplay.setTrack(field); 910 coneDisplay.setUseTimesInAnimation(false); 911 return coneDisplay; 912 } 913 914 /** 915 * _more_ 916 * 917 * @param param 918 * _more_ 919 * @param mode 920 * _more_ 921 * 922 * @return _more_ 923 * 924 * @throws Exception 925 * _more_ 926 */ 927 public TrackDisplayable makeRingDisplay(StormParam param, int mode) 928 throws Exception { 929 FieldImpl field = makeRingsField(param, mode); 930 if (field == null) { 931 return null; 932 } 933 TrackDisplayable ringDisplay = new TrackDisplayable("ring_" 934 + stormDisplayState.getStormInfo().getStormId()); 935 ringDisplay.setUseTimesInAnimation(false); 936 ringDisplay.setTrack(field); 937 ringDisplay.setUseTimesInAnimation(false); 938 return ringDisplay; 939 } 940 941 /** 942 * _more_ 943 * 944 * @return _more_ 945 */ 946 protected JComponent getColorSwatch() { 947 if (colorSwatch == null) { 948 colorSwatch = new GuiUtils.ColorSwatch(getColor(), 949 "Set track color") { 950 public void setBackground(Color newColor) { 951 super.setBackground(newColor); 952 WayDisplayState.this.color = newColor; 953 try { 954 setTrackColor(); 955 setConeColor(); 956 setRingsColor(); 957 setLabelColor(); 958 } catch (Exception exc) { 959 LogUtil.logException("Setting color", exc); 960 } 961 } 962 }; 963 colorSwatch.setMinimumSize(new Dimension(15, 15)); 964 colorSwatch.setPreferredSize(new Dimension(15, 15)); 965 } 966 return colorSwatch; 967 } 968 969 /** 970 * _more_ 971 * 972 * @return _more_ 973 */ 974 public float[][] getColorPalette() { 975 ColorTable ct = stormDisplayState.getColorTable(colorParam); 976 if (ct != null) { 977 return stormDisplayState.getStormTrackControl() 978 .getColorTableForDisplayable(ct); 979 } 980 return getColorPalette(getColor()); 981 } 982 983 /** 984 * _more_ 985 * 986 * @param c 987 * _more_ 988 * 989 * @return _more_ 990 */ 991 public static float[][] getColorPalette(Color c) { 992 if (c == null) { 993 c = Color.red; 994 } 995 return ColorTableDefaults.allOneColor(c, true); 996 } 997 998 /** 999 * _more_ 1000 * 1001 * @throws Exception 1002 * _more_ 1003 */ 1004 private void setTrackColor() throws Exception { 1005 if (trackDisplay != null) { 1006 if (colorParam == null 1007 || colorParam.getName().equalsIgnoreCase("Fixed")) { 1008 trackDisplay.setColor(getColor()); 1009 } else 1010 trackDisplay.setColorPalette(getColorPalette()); 1011 } 1012 1013 } 1014 1015 /** 1016 * _more_ 1017 * 1018 * @throws Exception 1019 * _more_ 1020 */ 1021 private void setLabelColor() throws Exception { 1022 Color c = getColor(); 1023 if (labelDisplay != null) { // && !Misc.equals(c, 1024 // labelDisplay.getColor())) { 1025 labelDisplay.setColor(c); 1026 } 1027 if (obsPointDisplay != null) { // && !Misc.equals(c, 1028 // obsPointDisplay.getColor())) { 1029 obsPointDisplay.setColor(c); 1030 } 1031 } 1032 1033 /** 1034 * _more_ 1035 * 1036 * @throws Exception 1037 * _more_ 1038 */ 1039 private void setConeColor() throws Exception { 1040 if (conesHolder != null) { 1041 conesHolder.setColorPalette(getColorPalette(getColor())); 1042 } 1043 } 1044 1045 /** 1046 * _more_ 1047 * 1048 * @throws Exception 1049 * _more_ 1050 */ 1051 private void setRingsColor() throws Exception { 1052 if (ringsDisplay != null) { 1053 ringsDisplay.setColor(getColor()); 1054 } 1055 } 1056 1057 /** 1058 * _more_ 1059 * 1060 * @throws RemoteException 1061 * _more_ 1062 * @throws VisADException 1063 * _more_ 1064 */ 1065 public void deactivate() throws VisADException, RemoteException { 1066 ringsDisplay = null; 1067 conesHolder = null; 1068 if (holder != null) { 1069 } 1070 trackDisplay = null; 1071 labelDisplay = null; 1072 obsPointDisplay = null; 1073 holder = null; 1074 tracks = new ArrayList<StormTrack>(); 1075 times = new ArrayList<DateTime>(); 1076 } 1077 1078 /** _more_ */ 1079 private static TextType fhourType; 1080 1081 /** _more_ */ 1082 private static TextType rhourType; 1083 1084 /** _more_ */ 1085 private static TextType shourType; 1086 1087 /** 1088 * _more_ 1089 * 1090 * @param track 1091 * _more_ 1092 * 1093 * @throws Exception 1094 * _more_ 1095 */ 1096 public void addTrack(StormTrack track) throws Exception { 1097 tracks.add(track); 1098 } 1099 1100 /** 1101 * _more_ 1102 * 1103 * 1104 * 1105 * @param mode 1106 * _more_ 1107 * @return _more_ 1108 * @throws Exception 1109 * _more_ 1110 */ 1111 1112 protected FieldImpl makeTrackField(int mode) throws Exception { 1113 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1114 List<DateTime> times = new ArrayList<DateTime>(); 1115 1116 // Use a local list to hold the point obs so we don't run into a race 1117 // condition 1118 List<PointOb> localPointObs = new ArrayList<PointOb>(); 1119 List<PointOb> localAllPointObs = new ArrayList<PointOb>(); 1120 Data[] datas = new Data[tracks.size()]; 1121 int i = 0; 1122 for (StormTrack track : tracks) { 1123 FieldImpl field = stormDisplayState.getStormTrackControl() 1124 .makeTrackField(track, colorParam); 1125 if (field == null) { 1126 continue; 1127 } 1128 if (i == 0) 1129 datas[i++] = field; 1130 else 1131 datas[i++] = field.changeMathType(datas[0].getType()); 1132 fields.add(field); 1133 times.add(track.getStartTime()); 1134 // if(!way.isObservation() && mode == 0) 1135 localPointObs.addAll(makePointObs(track, !way.isObservation())); 1136 if (way.isObservation()) { 1137 localAllPointObs.addAll(makeObsPointObs(track)); 1138 } 1139 } 1140 1141 pointObs = localPointObs; 1142 allPointObs = localAllPointObs; 1143 1144 if (fields.size() == 0) { 1145 return null; 1146 } 1147 // if(fields.size()==1) return fields.get(0); 1148 1149 if (!way.isObservation() && (mode == 1)) { 1150 return Util.indexedField(datas, false); 1151 } else { 1152 return Util.makeTimeField(fields, times); 1153 } 1154 } 1155 1156 /** 1157 * _more_ 1158 * 1159 * 1160 * @param stormParam 1161 * _more_ 1162 * @param mode 1163 * _more_ 1164 * 1165 * @return _more_ 1166 * @throws Exception 1167 * _more_ 1168 */ 1169 protected FieldImpl makeConeField(StormParam stormParam, int mode) 1170 throws Exception { 1171 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1172 List<DateTime> times = new ArrayList<DateTime>(); 1173 Data[] datas = new Data[tracks.size()]; 1174 int i = 0; 1175 for (StormTrack track : tracks) { 1176 StormTrack coneTrack = makeConeTrack(track, stormParam); 1177 if (coneTrack == null) { 1178 continue; 1179 } 1180 FieldImpl field = stormDisplayState.getStormTrackControl() 1181 .makeTrackField(coneTrack, null); 1182 fields.add(field); 1183 1184 times.add(track.getStartTime()); 1185 datas[i++] = field; 1186 } 1187 1188 if (fields.size() == 0) { 1189 return null; 1190 } 1191 1192 if (!way.isObservation() && (mode == 1)) { 1193 return Util.indexedField(datas, false); 1194 } else { 1195 return Util.makeTimeField(fields, times); 1196 } 1197 } 1198 1199 /** 1200 * _more_ 1201 * 1202 * @param track 1203 * _more_ 1204 * @param useStartTime 1205 * _more_ 1206 * 1207 * 1208 * @return _more_ 1209 * @throws Exception 1210 * _more_ 1211 */ 1212 private List<PointOb> makePointObs(StormTrack track, boolean useStartTime) 1213 throws Exception { 1214 boolean isObservation = way.isObservation(); 1215 DateTime startTime = track.getStartTime(); 1216 List<StormTrackPoint> stps = track.getTrackPoints(); 1217 if (fhourType == null) { 1218 fhourType = new TextType("fhour"); 1219 } 1220 1221 if (rhourType == null) { 1222 rhourType = new TextType("rhour"); 1223 } 1224 1225 if (shourType == null) { 1226 shourType = new TextType("shour"); 1227 } 1228 List<PointOb> pointObs = new ArrayList<PointOb>(); 1229 1230 DecimalFormat format = new DecimalFormat("0.#"); 1231 Date startDate = Util.makeDate(startTime); 1232 List<StormParam> params = track.getParams(); 1233 for (int i = 0; i < stps.size(); i++) { 1234 StormTrackPoint stp = stps.get(i); 1235 DateTime time = (useStartTime ? startTime : stp.getTime()); 1236 String flabel = ""; 1237 String rlabel = ""; 1238 String slabel = ""; 1239 if (!isObservation) { 1240 if (i == 0) { 1241 // label = way.getId() + ": " + track.getStartTime(); 1242 } else { 1243 flabel = "" + stp.getForecastHour() + "H"; 1244 Date dttm = Util.makeDate(stp.getTime()); 1245 rlabel = "" + dttm.toString(); 1246 slabel = "" + getMonDayHour(dttm); 1247 ; 1248 } 1249 } else if (useStartTime && (i > 0)) { 1250 Date dttm = Util.makeDate(stp.getTime()); 1251 double diffSeconds = (dttm.getTime() - startDate.getTime()) / 1000.0; 1252 double diffHours = diffSeconds / 3600.0; 1253 1254 flabel = format.format(diffHours) + "H"; 1255 rlabel = "" + dttm.toString(); 1256 slabel = "" + getMonDayHour(dttm); 1257 } 1258 Data[] data = new Data[params.size() + 3]; 1259 1260 data[0] = new visad.Text(rhourType, rlabel); 1261 data[1] = new visad.Text(fhourType, flabel); 1262 data[2] = new visad.Text(shourType, slabel); 1263 for (int paramIdx = 0; paramIdx < params.size(); paramIdx++) { 1264 Real r = stp.getAttribute(params.get(paramIdx)); 1265 if (r == null) { 1266 r = params.get(paramIdx).getReal(Double.NaN); 1267 } 1268 data[paramIdx + 3] = r; 1269 1270 } 1271 Tuple tuple = new Tuple(data); 1272 pointObs.add(PointObFactory.makePointOb(stp.getLocation(), time, 1273 tuple)); 1274 1275 } 1276 return pointObs; 1277 } 1278 1279 /** 1280 * _more_ 1281 * 1282 * @param dt 1283 * _more_ 1284 * 1285 * @return _more_ 1286 */ 1287 private String getMonDayHour(Date dt) { 1288 Calendar cal = Calendar.getInstance(); 1289 1290 cal.setTime(dt); 1291 int m = cal.get(Calendar.MONTH) + 1; // 0 base, ie 0 for Jan 1292 int d = cal.get(Calendar.DAY_OF_MONTH); 1293 int h = cal.get(Calendar.HOUR_OF_DAY); 1294 1295 return "" + m + "/" + d + "/" + h + "H"; 1296 } 1297 1298 /** 1299 * _more_ 1300 * 1301 * @param track 1302 * _more_ 1303 * 1304 * @return _more_ 1305 * 1306 * @throws Exception 1307 * _more_ 1308 */ 1309 private List<PointOb> makeObsPointObs(StormTrack track) throws Exception { 1310 DateTime startTime = track.getStartTime(); 1311 List<StormTrackPoint> stps = track.getTrackPoints(); 1312 if (fhourType == null) { 1313 fhourType = new TextType("fhour"); 1314 } 1315 if (rhourType == null) { 1316 rhourType = new TextType("rhour"); 1317 } 1318 if (shourType == null) { 1319 shourType = new TextType("shour"); 1320 } 1321 List<PointOb> pointObs = new ArrayList<PointOb>(); 1322 DecimalFormat format = new DecimalFormat("0.#"); 1323 List<StormParam> params = track.getParams(); 1324 for (int i = 0; i < stps.size(); i++) { 1325 StormTrackPoint baseStp = stps.get(i); 1326 DateTime baseTime = baseStp.getTime(); 1327 Date baseDate = Util.makeDate(baseTime); 1328 for (int j = i; j < stps.size(); j++) { 1329 StormTrackPoint stp = stps.get(j); 1330 String flabel = ""; 1331 String rlabel = ""; 1332 String slabel = ""; 1333 if (j > 0) { 1334 Date dttm = Util.makeDate(stp.getTime()); 1335 double diffSeconds = (dttm.getTime() - baseDate.getTime()) / 1000.0; 1336 double diffHours = diffSeconds / 3600.0; 1337 flabel = format.format(diffHours) + "H"; 1338 rlabel = "" + dttm.toString(); 1339 slabel = "" + getMonDayHour(dttm); 1340 } 1341 Data[] data = new Data[params.size() + 3]; 1342 data[0] = new visad.Text(fhourType, flabel); 1343 data[1] = new visad.Text(rhourType, rlabel); 1344 data[2] = new visad.Text(shourType, slabel); 1345 for (int paramIdx = 0; paramIdx < params.size(); paramIdx++) { 1346 Real r = stp.getAttribute(params.get(paramIdx)); 1347 if (r == null) { 1348 r = params.get(paramIdx).getReal(Double.NaN); 1349 } 1350 data[paramIdx + 3] = r; 1351 } 1352 Tuple tuple = new Tuple(data); 1353 pointObs.add(PointObFactory.makePointOb(stp.getLocation(), 1354 baseTime, tuple)); 1355 } 1356 } 1357 return pointObs; 1358 } 1359 1360 /** 1361 * _more_ 1362 * 1363 * @return _more_ 1364 */ 1365 public List<StormTrack> getTracks() { 1366 return tracks; 1367 } 1368 1369 /** 1370 * _more_ 1371 * 1372 * @return _more_ 1373 */ 1374 public List<DateTime> getTimes() { 1375 return times; 1376 } 1377 1378 /** 1379 * _more_ 1380 * 1381 * @param displayable 1382 * _more_ 1383 * 1384 * 1385 * @throws RemoteException 1386 * _more_ 1387 * @throws VisADException 1388 * _more_ 1389 */ 1390 public void addDisplayable(Displayable displayable) throws VisADException, 1391 RemoteException { 1392 getHolder().addDisplayable(displayable); 1393 } 1394 1395 /** 1396 * _more_ 1397 * 1398 * @param displayable 1399 * _more_ 1400 * 1401 * @throws RemoteException 1402 * _more_ 1403 * @throws VisADException 1404 * _more_ 1405 */ 1406 public void removeDisplayable(Displayable displayable) 1407 throws VisADException, RemoteException { 1408 getHolder().removeDisplayable(displayable); 1409 } 1410 1411 /** 1412 * Set the ConeState property. 1413 * 1414 * @param value 1415 * The new value for ConeState 1416 */ 1417 public void setConeState(DisplayState value) { 1418 coneState = value; 1419 } 1420 1421 /** 1422 * Get the ConeState property. 1423 * 1424 * @return The ConeState 1425 */ 1426 public DisplayState getConeState() { 1427 return coneState; 1428 } 1429 1430 /** 1431 * Set the TrackState property. 1432 * 1433 * @param value 1434 * The new value for TrackState 1435 */ 1436 public void setTrackState(DisplayState value) { 1437 trackState = value; 1438 } 1439 1440 /** 1441 * Get the TrackState property. 1442 * 1443 * @return The TrackState 1444 */ 1445 public DisplayState getTrackState() { 1446 return trackState; 1447 } 1448 1449 /** 1450 * Set the RingsState property. 1451 * 1452 * @param value 1453 * The new value for RingsState 1454 */ 1455 public void setRingsState(DisplayState value) { 1456 ringsState = value; 1457 } 1458 1459 /** 1460 * Get the RingsState property. 1461 * 1462 * @return The RingsState 1463 */ 1464 public DisplayState getRingsState() { 1465 return ringsState; 1466 } 1467 1468 /** 1469 * Set the WayState property. 1470 * 1471 * @param value 1472 * The new value for WayState 1473 */ 1474 public void setWayState(DisplayState value) { 1475 wayState = value; 1476 } 1477 1478 /** 1479 * Get the WayState property. 1480 * 1481 * @return The WayState 1482 */ 1483 public DisplayState getWayState() { 1484 return wayState; 1485 } 1486 1487 /** 1488 * Set the Color property. 1489 * 1490 * @param value 1491 * The new value for Color 1492 */ 1493 public void setColor(Color value) { 1494 color = value; 1495 } 1496 1497 /** 1498 * Get the Color property. 1499 * 1500 * @return The Color 1501 */ 1502 public Color getColor() { 1503 return color; 1504 } 1505 1506 /** 1507 * Set the StormDisplayState property. 1508 * 1509 * @param value 1510 * The new value for StormDisplayState 1511 */ 1512 public void setStormDisplayState(StormDisplayState value) { 1513 stormDisplayState = value; 1514 } 1515 1516 /** 1517 * Get the StormDisplayState property. 1518 * 1519 * @return The StormDisplayState 1520 */ 1521 public StormDisplayState getStormDisplayState() { 1522 return stormDisplayState; 1523 } 1524 1525 /** 1526 * Set the Way property. 1527 * 1528 * @param value 1529 * The new value for Way 1530 */ 1531 public void setWay(Way value) { 1532 way = value; 1533 } 1534 1535 /** 1536 * Get the Way property. 1537 * 1538 * @return The Way 1539 */ 1540 public Way getWay() { 1541 return way; 1542 } 1543 1544 /** 1545 * Set the ColorTable property. 1546 * 1547 * @param value 1548 * The new value for ColorTable 1549 */ 1550 public void setColorTable(String value) { 1551 } 1552 1553 /** 1554 * _more_ 1555 * 1556 * @param track 1557 * _more_ 1558 * @param param 1559 * _more_ 1560 * 1561 * @return _more_ 1562 */ 1563 private List<StormTrackPoint> getRealTrackPoints(StormTrack track, 1564 StormParam param) { 1565 List<StormTrackPoint> newStps = new ArrayList(); 1566 List<StormTrackPoint> stps = track.getTrackPoints(); 1567 1568 newStps.add(stps.get(0)); 1569 Iterator<StormTrackPoint> it = stps.iterator(); 1570 1571 while (it.hasNext()) { 1572 StormTrackPoint stp = it.next(); 1573 if (stp.getAttribute(param) != null) { 1574 newStps.add(stp); 1575 } 1576 } 1577 return newStps; 1578 } 1579 1580 /** 1581 * _more_ 1582 * 1583 * @param stormParam 1584 * _more_ 1585 * @param mode 1586 * _more_ 1587 * 1588 * @return _more_ 1589 * 1590 * @throws Exception 1591 * _more_ 1592 */ 1593 protected FieldImpl makeRingsField(StormParam stormParam, int mode) 1594 throws Exception { 1595 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1596 List<DateTime> times = new ArrayList<DateTime>(); 1597 Data[] datas = new Data[tracks.size() * 10]; 1598 int i = 0; 1599 1600 if (!way.isObservation() && (mode == 1)) { 1601 for (StormTrack track : tracks) { 1602 List<StormTrack> stList = makeRingTrackList(track, stormParam); 1603 for (StormTrack stk : stList) { 1604 FieldImpl field = stormDisplayState.getStormTrackControl() 1605 .makeTrackField(stk, null); 1606 fields.add(field); 1607 datas[i++] = field; 1608 } 1609 1610 } 1611 1612 return Util.indexedField(datas, false); 1613 } 1614 1615 for (StormTrack track : tracks) { 1616 FieldImpl ringField = makeRingTracks(track, stormParam); 1617 fields.add(ringField); 1618 times.add(track.getStartTime()); 1619 } 1620 1621 if (fields.size() == 0) { 1622 return null; 1623 } 1624 1625 return Util.makeTimeField(fields, times); 1626 } 1627 1628 /** 1629 * _more_ 1630 * 1631 * @param track 1632 * _more_ 1633 * @param param 1634 * _more_ 1635 * 1636 * @return _more_ 1637 * 1638 * @throws Exception 1639 * _more_ 1640 */ 1641 public List makeRingTrackList(StormTrack track, StormParam param) 1642 throws Exception { 1643 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1644 List<StormTrack> stracks = new ArrayList(); 1645 1646 int size = stps.size(); 1647 DateTime dt = stps.get(0).getTime(); 1648 Way ringWay = new Way(getWay() + "_RING"); 1649 int numberOfPoints = 73; 1650 double angleDelta = 360.0 / (numberOfPoints - 1); 1651 for (int i = 0; i < size; i++) { 1652 StormTrackPoint stp = stps.get(i); 1653 Real r = stp.getAttribute(param); 1654 if (r != null) { 1655 double rr = r.getValue(); 1656 double azi = 0.0; 1657 List ringList = new ArrayList<StormTrackPoint>(); 1658 for (int j = 0; j < numberOfPoints; j++) { 1659 ringList.add(getCirclePoint(stp, rr, azi, dt)); 1660 azi = azi + angleDelta; 1661 } 1662 stracks.add(new StormTrack(track.getStormInfo(), ringWay, 1663 ringList, null)); 1664 } 1665 } 1666 1667 return stracks; 1668 1669 } 1670 1671 /** 1672 * _more_ 1673 * 1674 * @param track 1675 * _more_ 1676 * @param param 1677 * _more_ 1678 * 1679 * @return _more_ 1680 * 1681 * 1682 * @throws Exception 1683 * _more_ 1684 */ 1685 public FieldImpl makeRingTracks(StormTrack track, StormParam param) 1686 throws Exception { 1687 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1688 List<StormTrack> stracks = new ArrayList(); 1689 int size = stps.size(); 1690 DateTime dt = stps.get(0).getTime(); 1691 Way ringWay = new Way(getWay() + "_RING"); 1692 int numberOfPoints = 73; 1693 double angleDelta = 360.0 / (numberOfPoints - 1); 1694 for (int i = 0; i < size; i++) { 1695 StormTrackPoint stp = stps.get(i); 1696 Real r = stp.getAttribute(param); 1697 if (r != null) { 1698 double rr = r.getValue(); 1699 double azi = 0.0; 1700 List ringList = new ArrayList<StormTrackPoint>(); 1701 for (int j = 0; j < numberOfPoints; j++) { 1702 ringList.add(getCirclePoint(stp, rr, azi, dt)); 1703 azi = azi + angleDelta; 1704 } 1705 stracks.add(new StormTrack(track.getStormInfo(), ringWay, 1706 ringList, null)); 1707 } 1708 } 1709 1710 Data[] datas = new Data[stracks.size()]; 1711 int i = 0; 1712 for (StormTrack ringTrack : stracks) { 1713 datas[i++] = stormDisplayState.getStormTrackControl() 1714 .makeTrackField(ringTrack, null); 1715 } 1716 return Util.indexedField(datas, false); 1717 } 1718 1719 /** 1720 * _more_ 1721 * 1722 * @param stp 1723 * _more_ 1724 * @param r0 1725 * _more_ 1726 * @param azimuth 1727 * _more_ 1728 * @param dt 1729 * _more_ 1730 * 1731 * @return _more_ 1732 * 1733 * @throws VisADException 1734 * _more_ 1735 */ 1736 public StormTrackPoint getCirclePoint(StormTrackPoint stp, double r0, 1737 double azimuth, DateTime dt) throws VisADException { 1738 // 1739 1740 EarthLocation el = stp.getLocation(); 1741 double lat0 = el.getLatitude().getValue(); 1742 double lon0 = el.getLongitude().getValue(); 1743 // DateTime dt = stp.getTime(); 1744 LatLonPointImpl lp = Bearing.findPoint(lat0, lon0, azimuth, r0, null); 1745 1746 EarthLocation el1 = new EarthLocationLite(lp.getLatitude(), lp 1747 .getLongitude(), 0); 1748 StormTrackPoint stp1 = new StormTrackPoint(el1, dt, 0, null); 1749 1750 return stp1; 1751 } 1752 1753 /** 1754 * old 1755 * 1756 * @param track 1757 * _more_ 1758 * @param param 1759 * _more_ 1760 * 1761 * @return _more_ 1762 * 1763 * @throws VisADException 1764 * _more_ 1765 */ 1766 public StormTrack makeConeTrack_Old(StormTrack track, StormParam param) 1767 throws VisADException { 1768 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1769 int size = stps.size(); 1770 int numberOfPoint = size * 2 + 11; 1771 StormTrackPoint[] conePoints = new StormTrackPoint[numberOfPoint]; 1772 1773 StormTrackPoint stp1 = stps.get(0); 1774 conePoints[0] = stp1; // first point & last point 1775 conePoints[numberOfPoint - 1] = stp1; 1776 1777 StormTrackPoint stp2; 1778 StormTrackPoint stp; 1779 1780 // circle 1 to n 1781 1782 for (int i = 1; i < size; i++) { 1783 stp2 = stps.get(i); 1784 // right point 1785 stp = getPointToCircleTangencyPoint(stp1, stp2, param, true); 1786 conePoints[i] = stp; 1787 // left point 1788 stp = getPointToCircleTangencyPoint(stp1, stp2, param, false); 1789 conePoints[numberOfPoint - i - 1] = stp; 1790 stp1 = stp2; 1791 } 1792 1793 // end point half circle take 11 points 1794 StormTrackPoint last = stps.get(size - 1); 1795 EarthLocation lastEl = last.getLocation(); 1796 StormTrackPoint endSTP = conePoints[size - 1]; 1797 int ii = 0; 1798 while ((endSTP == null) && (ii < (size - 2))) { 1799 ii++; 1800 last = stps.get(size - 1 - ii); 1801 lastEl = last.getLocation(); 1802 endSTP = conePoints[size - 1 - ii]; 1803 } 1804 1805 if ((endSTP == null) || (ii == (size - 2))) { 1806 return null; 1807 } 1808 EarthLocation endEl = endSTP.getLocation(); 1809 1810 double ang = getCircleAngleRange(lastEl, endEl); 1811 1812 Real r = last.getAttribute(param); 1813 StormTrackPoint[] halfCircle = getHalfCircleTrackPoint(lastEl, ang, 1814 ((r != null) ? r.getValue() : 0), last.getTime()); 1815 1816 for (int i = 0; i < 11; i++) { 1817 conePoints[size + i] = halfCircle[i]; 1818 } 1819 1820 List coneList = new ArrayList<StormTrackPoint>(); 1821 for (int i = 0; i < numberOfPoint; i++) { 1822 if (conePoints[i] != null) { 1823 coneList.add(conePoints[i]); 1824 } 1825 } 1826 1827 return new StormTrack(track.getStormInfo(), 1828 new Way(getWay() + "_CONE"), coneList, null); 1829 1830 } 1831 1832 /** 1833 * construct the cone track as track of point to circle and circle to circle 1834 * 1835 * @param track 1836 * _more_ 1837 * @param param 1838 * _more_ 1839 * 1840 * @return _more_ 1841 * 1842 * @throws VisADException 1843 * _more_ 1844 */ 1845 public StormTrack makeConeTrack(StormTrack track, StormParam param) 1846 throws VisADException { 1847 1848 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1849 int size = stps.size(); 1850 int numberOfPoint = size * 2 + 100; 1851 List<StormTrackPoint> conePointsLeft = new ArrayList<StormTrackPoint>(); 1852 List<StormTrackPoint> conePointsRight = new ArrayList<StormTrackPoint>(); 1853 1854 StormTrackPoint stp1 = stps.get(0); 1855 conePointsRight.add(stp1); // first point & last point 1856 conePointsLeft.add(stp1); 1857 StormTrackPoint stp2 = stps.get(1); 1858 StormTrackPoint stp3 = stps.get(2); 1859 int nn = 3; 1860 // first point to circle 1861 List<StormTrackPoint> p2c = getPointToCircleTangencyPointA(stp1, stp2, 1862 stp3, param, true); 1863 while (p2c == null) { // need to find the first point with param value 1864 stp2 = stp3; 1865 if (nn < size) { 1866 stp3 = stps.get(nn); 1867 } else { 1868 stp3 = null; 1869 // return null; 1870 } 1871 p2c = getPointToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1872 nn++; 1873 if (nn >= size) { 1874 break; 1875 } 1876 } 1877 if (p2c != null) { 1878 conePointsRight.addAll(p2c); 1879 p2c = getPointToCircleTangencyPointA(stp1, stp2, stp3, param, false); 1880 conePointsLeft.addAll(p2c); 1881 } 1882 1883 // circle to circle 1 to n 1884 stp1 = stp2; 1885 stp2 = stp3; 1886 for (int i = nn; i < size; i++) { 1887 stp3 = stps.get(i); 1888 // right point 1889 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1890 if (p2c != null) { 1891 conePointsRight.addAll(p2c); 1892 // left point 1893 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, 1894 false); 1895 conePointsLeft.addAll(p2c); 1896 stp1 = stp2; // update the first point only after the valid 1897 // second point 1898 } 1899 1900 stp2 = stp3; 1901 } 1902 // last circle 1903 stp3 = null; 1904 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1905 if (p2c != null) { 1906 conePointsRight.addAll(p2c); 1907 // left point 1908 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, 1909 false); 1910 conePointsLeft.addAll(p2c); 1911 stp1 = stp2; 1912 } 1913 1914 // end point half circle take 11 points 1915 StormTrackPoint last = stp2; 1916 if (last == null) { 1917 last = stp1; 1918 } 1919 if (last == null) { 1920 return null; 1921 } 1922 EarthLocation lastEl = last.getLocation(); 1923 StormTrackPoint endSTP = conePointsRight 1924 .get(conePointsRight.size() - 1); 1925 /* 1926 * int ii = 0; while ((endSTP == null) && (ii < (size - 2))) { ii++; 1927 * last = stps.get(size - 1 - ii); lastEl = last.getLocation(); endSTP = 1928 * conePointsRight.get(size - 1 - ii); } 1929 * 1930 * if ((endSTP == null) || (ii == (size - 2))) { return null; } 1931 */ 1932 if (endSTP == null) { 1933 return null; 1934 } 1935 EarthLocation endEl = endSTP.getLocation(); 1936 1937 double ang = getCircleAngleRange(lastEl, endEl); 1938 1939 Real r = last.getAttribute(param); 1940 StormTrackPoint[] halfCircle = getHalfCircleTrackPoint(lastEl, ang, 1941 ((r != null) ? r.getValue() : 0), last.getTime()); 1942 1943 for (int i = 0; i < 11; i++) { 1944 } 1945 // merge three lists 1946 List<StormTrackPoint> coneList = new ArrayList<StormTrackPoint>(); 1947 int s1 = conePointsRight.size(); 1948 for (int i = 0; i < s1; i++) { 1949 if (conePointsRight.get(i) != null) { 1950 coneList.add(conePointsRight.get(i)); 1951 } 1952 } 1953 for (int i = 0; i < 11; i++) { 1954 coneList.add(halfCircle[i]); 1955 } 1956 int s2 = conePointsLeft.size(); 1957 for (int i = s2; i > 0; i--) { 1958 if (conePointsLeft.get(i - 1) != null) { 1959 coneList.add(conePointsLeft.get(i - 1)); 1960 } 1961 } 1962 1963 return new StormTrack(track.getStormInfo(), 1964 new Way(getWay() + "_CONE"), coneList, null); 1965 1966 } 1967 1968 /** 1969 * calculate the bearing of two storm track points 1970 * 1971 * @param sp1 1972 * _more_ 1973 * @param sp2 1974 * _more_ 1975 * 1976 * @return _more_ 1977 */ 1978 public Bearing getStormPoinsBearing(StormTrackPoint sp1, StormTrackPoint sp2) { 1979 EarthLocation el1 = sp1.getLocation(); 1980 EarthLocation el2 = sp2.getLocation(); 1981 return Bearing.calculateBearing(el1.getLatitude().getValue(), el1 1982 .getLongitude().getValue(), el2.getLatitude().getValue(), el2 1983 .getLongitude().getValue(), null); 1984 1985 } 1986 1987 /** 1988 * get the tangency point to the circle of the second point and the third 1989 * point as its direction of adding additional points 1990 * 1991 * @param sp1 1992 * outside point 1993 * @param sp2 1994 * the center of the circle 1995 * @param sp3 1996 * _more_ 1997 * @param param 1998 * _more_ 1999 * @param right 2000 * _more_ 2001 * 2002 * @return _more_ 2003 * 2004 * @throws VisADException 2005 * _more_ 2006 */ 2007 public List<StormTrackPoint> getPointToCircleTangencyPointA( 2008 StormTrackPoint sp1, StormTrackPoint sp2, StormTrackPoint sp3, 2009 StormParam param, boolean right) throws VisADException { 2010 2011 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2012 if (sp3 == null) { 2013 return getPointToCircleTangencyPointB(sp1, sp2, param, right); 2014 } 2015 2016 EarthLocation el1 = sp1.getLocation(); 2017 EarthLocation el2 = sp2.getLocation(); 2018 EarthLocation el3 = sp3.getLocation(); 2019 2020 Real rl = sp2.getAttribute(param); 2021 double r = rl.getValue(); 2022 2023 if (Float.isNaN((float) r) || (r == 0.0)) { 2024 return null; 2025 } 2026 2027 double lat1 = el1.getLatitude().getValue(); 2028 double lon1 = el1.getLongitude().getValue(); 2029 2030 double lat2 = el2.getLatitude().getValue(); 2031 double lon2 = el2.getLongitude().getValue(); 2032 2033 double lat3 = el3.getLatitude().getValue(); 2034 double lon3 = el3.getLongitude().getValue(); 2035 2036 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2037 Bearing c = Bearing.calculateBearing(lat2, lon2, lat3, lon3, null); 2038 double dist1 = b.getDistance(); 2039 2040 if (dist1 < r) { // first point is inside the circle 2041 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2042 right)); 2043 return trackPoints; 2044 } 2045 2046 double af = getCircleAngleRange(el1, el2); 2047 double ddt = Math.abs(b.getAngle() - c.getAngle()); 2048 double bt = getCircleTangencyAngle(el1, el2, r); 2049 2050 af = af * 180.0 / Math.PI; 2051 bt = bt * 180.0 / Math.PI; 2052 if (right) { 2053 af = af - 90; 2054 } else { 2055 af = af + 90; 2056 } 2057 // change angle to azimuth 2058 double az = af; 2059 if ((af <= 90) && (af >= 0)) { 2060 az = 90 - af; 2061 } else if ((af > 90) && (af <= 180)) { 2062 az = 360 + (90 - af); 2063 } else if ((af < 0) && (af >= -180)) { 2064 az = 90 - af; 2065 } else if ((af > 180) && (af <= 360)) { 2066 az = 450 - af; 2067 } else if ((af < -180) && (af >= -360)) { 2068 az = -270 - af; 2069 } 2070 if (right) { 2071 az = az + bt; 2072 } else { 2073 az = az - bt; 2074 } 2075 2076 if (ddt > 270) { 2077 ddt = 360 - ddt; 2078 } else if (ddt > 180) { 2079 ddt = ddt - 180; 2080 } else if (ddt > 90) { 2081 ddt = ddt - 90; 2082 } 2083 2084 double dt = bt; 2085 2086 if (right) { 2087 if ((c.getAngle() < b.getAngle()) 2088 && (Math.abs(b.getAngle() - c.getAngle()) < 90)) { 2089 dt = bt + ddt; 2090 } else if ((c.getAngle() > b.getAngle()) 2091 && (Math.abs(b.getAngle() - c.getAngle()) > 180)) { 2092 dt = bt + ddt; 2093 } else { 2094 dt = bt - ddt; 2095 } 2096 } else { 2097 if ((c.getAngle() > b.getAngle()) 2098 && (Math.abs(b.getAngle() - c.getAngle()) < 90)) { 2099 dt = bt + ddt; 2100 } else if ((c.getAngle() < b.getAngle()) 2101 && (Math.abs(b.getAngle() - c.getAngle()) > 180)) { 2102 dt = bt + ddt; 2103 } else { 2104 dt = bt - ddt; 2105 } 2106 2107 } 2108 2109 int n = (int) dt / 5 + 1; 2110 if (n <= 0) { 2111 n = 1; 2112 } 2113 double dtt = dt / n; 2114 if (dtt < 0) { 2115 dtt = 0; 2116 n = 1; 2117 } 2118 for (int i = 0; i < n; i++) { 2119 2120 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2121 // add more points along the circle 2122 2123 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2124 .getLongitude(), 0); 2125 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2126 if (right) { 2127 az = az - dtt; 2128 } else { 2129 az = az + dtt; 2130 } 2131 } 2132 2133 return trackPoints; 2134 } 2135 2136 /** 2137 * get the tangency point to the circle of the second point 2138 * 2139 * @param sp1 2140 * _more_ 2141 * @param sp2 2142 * _more_ 2143 * @param param 2144 * _more_ 2145 * @param right 2146 * _more_ 2147 * 2148 * @return _more_ 2149 * 2150 * @throws VisADException 2151 * _more_ 2152 */ 2153 public List<StormTrackPoint> getPointToCircleTangencyPointB( 2154 StormTrackPoint sp1, StormTrackPoint sp2, StormParam param, 2155 boolean right) throws VisADException { 2156 2157 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2158 2159 if (sp2 == null) { 2160 return null; 2161 } 2162 EarthLocation el1 = sp1.getLocation(); 2163 EarthLocation el2 = sp2.getLocation(); 2164 2165 Real rl = sp2.getAttribute(param); 2166 double r = rl.getValue(); 2167 2168 if (Float.isNaN((float) r) || (r == 0.0)) { 2169 return null; 2170 } 2171 2172 double lat1 = el1.getLatitude().getValue(); 2173 double lon1 = el1.getLongitude().getValue(); 2174 2175 double lat2 = el2.getLatitude().getValue(); 2176 double lon2 = el2.getLongitude().getValue(); 2177 2178 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2179 double dist1 = b.getDistance(); 2180 2181 if (dist1 < r) { // first point is inside the circle 2182 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2183 right)); 2184 return trackPoints; 2185 } 2186 2187 double af = getCircleAngleRange(el1, el2); 2188 double bt = getCircleTangencyAngle(el1, el2, r); 2189 2190 af = af * 180.0 / Math.PI; 2191 bt = bt * 180.0 / Math.PI; 2192 if (right) { 2193 af = af - 90; 2194 } else { 2195 af = af + 90; 2196 } 2197 // change angle to azimuth 2198 double az = af; 2199 if ((af <= 90) && (af >= 0)) { 2200 az = 90 - af; 2201 } else if ((af > 90) && (af <= 180)) { 2202 az = 360 + (90 - af); 2203 } else if ((af < 0) && (af >= -180)) { 2204 az = 90 - af; 2205 } else if ((af > 180) && (af <= 360)) { 2206 az = 450 - af; 2207 } else if ((af < -180) && (af >= -360)) { 2208 az = -270 - af; 2209 } 2210 if (right) { 2211 az = az + bt; 2212 } else { 2213 az = az - bt; 2214 } 2215 2216 double dt = bt; 2217 2218 int n = (int) dt / 5 + 1; 2219 double dtt = dt / n; 2220 for (int i = 0; i < n; i++) { 2221 2222 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2223 // add more points along the circle 2224 2225 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2226 .getLongitude(), 0); 2227 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2228 if (right) { 2229 az = az - dtt; 2230 } else { 2231 az = az + dtt; 2232 } 2233 } 2234 2235 return trackPoints; 2236 } 2237 2238 /** 2239 * get the approximate tangency points of circle to the circle 2240 * 2241 * @param sp1 2242 * outside point 2243 * @param sp2 2244 * the center of the circle 2245 * @param sp3 2246 * _more_ 2247 * @param param 2248 * _more_ 2249 * @param right 2250 * _more_ 2251 * 2252 * @return _more_ 2253 * 2254 * @throws VisADException 2255 * _more_ 2256 */ 2257 public List<StormTrackPoint> getCircleToCircleTangencyPointA( 2258 StormTrackPoint sp1, StormTrackPoint sp2, StormTrackPoint sp3, 2259 StormParam param, boolean right) throws VisADException { 2260 2261 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2262 if (sp3 == null) { 2263 if (sp2 == null) { 2264 return null; 2265 } 2266 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2267 right)); 2268 return trackPoints; 2269 } 2270 2271 EarthLocation el1 = sp1.getLocation(); 2272 EarthLocation el2 = sp2.getLocation(); 2273 EarthLocation el3 = sp3.getLocation(); 2274 2275 Real rl = sp2.getAttribute(param); 2276 double r = rl.getValue(); 2277 2278 if (Float.isNaN((float) r) || (r == 0.0)) { 2279 return null; 2280 } 2281 2282 double lat1 = el1.getLatitude().getValue(); 2283 double lon1 = el1.getLongitude().getValue(); 2284 2285 double lat2 = el2.getLatitude().getValue(); 2286 double lon2 = el2.getLongitude().getValue(); 2287 2288 double lat3 = el3.getLatitude().getValue(); 2289 double lon3 = el3.getLongitude().getValue(); 2290 2291 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2292 double dist1 = b.getDistance(); 2293 Bearing c = Bearing.calculateBearing(lat2, lon2, lat3, lon3, null); 2294 double x = Math.abs(c.getAngle() - b.getAngle()); 2295 2296 if (right) { 2297 if ((c.getAngle() > b.getAngle()) || (x > 180)) { 2298 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2299 right)); 2300 return trackPoints; 2301 } 2302 } 2303 2304 if (!right) { 2305 if ((c.getAngle() < b.getAngle()) && (x < 90)) { 2306 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2307 right)); 2308 return trackPoints; 2309 } 2310 } 2311 double af = getCircleAngleRange(el1, el2); 2312 double dt = 0; // = Math.abs(b.getAngle() - c.getAngle()); 2313 2314 if (x > 270) { 2315 dt = 360 - x; 2316 } else if (x > 180) { 2317 dt = x - 180; 2318 } else if (x > 90) { 2319 dt = x - 90; 2320 } else { 2321 dt = x; 2322 } 2323 2324 af = af * 180.0 / Math.PI; 2325 if (right) { 2326 af = af - 90; 2327 } else { 2328 af = af + 90; 2329 } 2330 // change angle to azimuth 2331 double az = af; 2332 if ((af <= 90) && (af >= 0)) { 2333 az = 90 - af; 2334 } else if ((af > 90) && (af <= 180)) { 2335 az = 360 + (90 - af); 2336 } else if ((af < 0) && (af >= -180)) { 2337 az = 90 - af; 2338 } else if ((af > 180) && (af <= 360)) { 2339 az = 450 - af; 2340 } else if ((af < -180) && (af >= -360)) { 2341 az = -270 - af; 2342 } 2343 2344 int n = (int) dt / 5 + 1; 2345 double dtt = dt / n; 2346 2347 for (int i = 0; i < n; i++) { 2348 2349 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2350 // add more points along the circle 2351 2352 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2353 .getLongitude(), 0); 2354 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2355 if (right) { 2356 az = az - dtt; 2357 } else { 2358 az = az + dtt; 2359 } 2360 } 2361 2362 return trackPoints; 2363 } 2364 2365 /** 2366 * get the 90 degree point to the line of the two points 2367 * 2368 * @param sp1 2369 * _more_ 2370 * @param sp2 2371 * _more_ 2372 * @param param 2373 * _more_ 2374 * @param right 2375 * _more_ 2376 * 2377 * @return _more_ 2378 * 2379 * @throws VisADException 2380 * _more_ 2381 */ 2382 public StormTrackPoint getPointToCircleTangencyPoint(StormTrackPoint sp1, 2383 StormTrackPoint sp2, StormParam param, boolean right) 2384 throws VisADException { 2385 2386 EarthLocation el1 = sp1.getLocation(); 2387 EarthLocation el2 = sp2.getLocation(); 2388 2389 Real rl = sp2.getAttribute(param); 2390 double r = rl.getValue(); 2391 2392 if (Float.isNaN((float) r) || (r == 0.0)) { 2393 return null; 2394 } 2395 2396 double lat2 = el2.getLatitude().getValue(); 2397 double lon2 = el2.getLongitude().getValue(); 2398 2399 double af = getCircleAngleRange(el1, el2); 2400 af = af * 180.0 / Math.PI; 2401 if (right) { 2402 af = af - 90; 2403 } else { 2404 af = af + 90; 2405 } 2406 // change angle to azimuth 2407 if ((af <= 90) && (af >= 0)) { 2408 af = 90 - af; 2409 } else if ((af > 90) && (af <= 180)) { 2410 af = 360 + (90 - af); 2411 } else if ((af < 0) && (af >= -180)) { 2412 af = 90 - af; 2413 } else if ((af > 180) && (af <= 360)) { 2414 af = 450 - af; 2415 } else if ((af < -180) && (af >= -360)) { 2416 af = -270 - af; 2417 } 2418 2419 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, af, r, null); 2420 2421 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2422 .getLongitude(), 0); 2423 StormTrackPoint sp = new StormTrackPoint(el, sp1.getTime(), 0, null); 2424 return sp; 2425 } 2426 2427 /** 2428 * _more_ 2429 * 2430 * @param c 2431 * _more_ 2432 * @param d 2433 * _more_ 2434 * @param r 2435 * _more_ 2436 * 2437 * @return _more_ 2438 */ 2439 public double getCircleTangencyAngle(EarthLocation c, EarthLocation d, 2440 double r) { 2441 2442 double lat1 = c.getLatitude().getValue(); 2443 double lon1 = c.getLongitude().getValue(); 2444 2445 double lat2 = d.getLatitude().getValue(); 2446 double lon2 = d.getLongitude().getValue(); 2447 2448 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2449 double dist = b.getDistance(); 2450 double a = Math.asin(r / dist); 2451 2452 return a; 2453 2454 } 2455 2456 /** 2457 * _more_ 2458 * 2459 * @param c 2460 * _more_ 2461 * @param d 2462 * _more_ 2463 * 2464 * @return _more_ 2465 */ 2466 public double getCircleAngleRange(EarthLocation c, EarthLocation d) { 2467 2468 double lat1 = c.getLatitude().getValue(); 2469 double lon1 = c.getLongitude().getValue(); 2470 LatLonPointImpl p1 = new LatLonPointImpl(lat1, lon1); 2471 2472 double lat2 = d.getLatitude().getValue(); 2473 double lon2 = d.getLongitude().getValue(); 2474 LatLonPointImpl p2 = new LatLonPointImpl(lat2, lon2); 2475 2476 LatLonProjection pj1 = new LatLonProjection(); 2477 ProjectionPoint pp1 = pj1.latLonToProj(p1); 2478 LatLonProjection pj2 = new LatLonProjection(); 2479 ProjectionPoint pp2 = pj2.latLonToProj(p2); 2480 double dx = pp2.getX() - pp1.getX(); 2481 double dy = pp2.getY() - pp1.getY(); 2482 2483 double a = Math.atan2(dy, dx); 2484 2485 return a; 2486 } 2487 2488 /** 2489 * _more_ 2490 * 2491 * @param c 2492 * _more_ 2493 * @param angle 2494 * _more_ 2495 * @param r 2496 * _more_ 2497 * @param dt 2498 * _more_ 2499 * 2500 * @return _more_ 2501 * 2502 * @throws VisADException 2503 * _more_ 2504 */ 2505 public StormTrackPoint[] getHalfCircleTrackPoint(EarthLocation c, 2506 double angle, double r, DateTime dt) throws VisADException { 2507 // return 10 track point 2508 int size = 11; 2509 2510 StormTrackPoint[] track = new StormTrackPoint[size]; 2511 2512 double lat0 = c.getLatitude().getValue(); 2513 double lon0 = c.getLongitude().getValue(); 2514 2515 for (int i = 0; i < size; i++) { 2516 double af = (angle + (i + 1) * 15 * Math.PI / 180.0) * 180.0 2517 / Math.PI; 2518 // change angle to azimuth 2519 if ((af <= 90) && (af >= 0)) { 2520 af = 90 - af; 2521 } else if ((af > 90) && (af <= 180)) { 2522 af = 360 + (90 - af); 2523 } else if ((af < 0) && (af >= -180)) { 2524 af = 90 - af; 2525 } else if ((af > 180) && (af <= 360)) { 2526 af = 450 - af; 2527 } else if ((af < -180) && (af >= -360)) { 2528 af = -270 - af; 2529 } 2530 2531 LatLonPointImpl lp = Bearing.findPoint(lat0, lon0, af, r, null); 2532 2533 EarthLocation el = new EarthLocationLite(lp.getLatitude(), lp 2534 .getLongitude(), 0); 2535 StormTrackPoint sp = new StormTrackPoint(el, dt, 0, null); 2536 2537 track[i] = sp; 2538 } 2539 2540 return track; 2541 } 2542 2543 /** 2544 * _more_ 2545 * 2546 * @param c 2547 * _more_ 2548 * @param angle 2549 * _more_ 2550 * @param r 2551 * _more_ 2552 * @param dt 2553 * _more_ 2554 * 2555 * @return _more_ 2556 * 2557 * @throws VisADException 2558 * _more_ 2559 */ 2560 public StormTrackPoint[] getHalfCircleTrackPointOld(EarthLocation c, 2561 double angle, double r, DateTime dt) throws VisADException { 2562 // return 10 track point 2563 int size = 11; 2564 2565 StormTrackPoint[] track = new StormTrackPoint[size]; 2566 FlatEarth e = new FlatEarth(); 2567 ProjectionPointImpl p0 = e.latLonToProj(c.getLatitude().getValue(), c 2568 .getLongitude().getValue()); 2569 2570 for (int i = 0; i < size; i++) { 2571 double af = angle + i * 15 * Math.PI / 180.0; 2572 double x = p0.getX() + r * Math.cos(af); 2573 double y = p0.getY() + r * Math.sin(af); 2574 2575 ProjectionPoint pp = new ProjectionPointImpl(x, y); 2576 LatLonPointImpl lp = new LatLonPointImpl(); 2577 FlatEarth e3 = new FlatEarth(); 2578 LatLonPoint lp11 = e3.projToLatLon(pp, lp); 2579 EarthLocation el = new EarthLocationLite(lp11.getLatitude(), lp11 2580 .getLongitude(), 0); 2581 StormTrackPoint sp = new StormTrackPoint(el, dt, 0, null); 2582 2583 track[i] = sp; 2584 } 2585 2586 return track; 2587 } 2588 2589 }