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