001 /* 002 * $Id: WayDisplayState.java,v 1.1 2012/01/04 20:39:38 tommyj Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031 package edu.wisc.ssec.mcidasv.control.cyclone; 032 033 import java.awt.Color; 034 import java.awt.Dimension; 035 import java.awt.Rectangle; 036 import java.awt.geom.Rectangle2D; 037 import java.rmi.RemoteException; 038 import java.text.DecimalFormat; 039 import java.util.ArrayList; 040 import java.util.Calendar; 041 import java.util.Date; 042 import java.util.Iterator; 043 import java.util.List; 044 import java.util.Vector; 045 046 import javax.swing.JComponent; 047 048 import ucar.unidata.data.grid.GridUtil; 049 import ucar.unidata.data.point.PointOb; 050 import ucar.unidata.data.point.PointObFactory; 051 import ucar.unidata.data.storm.StormParam; 052 import ucar.unidata.data.storm.StormTrack; 053 import ucar.unidata.data.storm.StormTrackPoint; 054 import ucar.unidata.data.storm.Way; 055 import ucar.unidata.geoloc.Bearing; 056 import ucar.unidata.geoloc.LatLonPoint; 057 import ucar.unidata.geoloc.LatLonPointImpl; 058 import ucar.unidata.geoloc.ProjectionPoint; 059 import ucar.unidata.geoloc.ProjectionPointImpl; 060 import ucar.unidata.geoloc.projection.FlatEarth; 061 import ucar.unidata.geoloc.projection.LatLonProjection; 062 import ucar.unidata.gis.SpatialGrid; 063 import ucar.unidata.ui.colortable.ColorTableDefaults; 064 import ucar.unidata.ui.symbol.StationModel; 065 import ucar.unidata.util.ColorTable; 066 import ucar.unidata.util.GuiUtils; 067 import ucar.unidata.util.LogUtil; 068 import ucar.unidata.util.Misc; 069 import ucar.unidata.util.Range; 070 import ucar.unidata.view.geoloc.NavigatedDisplay; 071 import ucar.visad.Util; 072 import ucar.visad.display.CompositeDisplayable; 073 import ucar.visad.display.Displayable; 074 import ucar.visad.display.StationModelDisplayable; 075 import ucar.visad.display.TrackDisplayable; 076 import visad.CommonUnit; 077 import visad.Data; 078 import visad.DateTime; 079 import visad.FieldImpl; 080 import visad.FunctionType; 081 import visad.Integer1DSet; 082 import visad.Real; 083 import visad.RealType; 084 import visad.Set; 085 import visad.SetType; 086 import visad.TextType; 087 import visad.Tuple; 088 import visad.Unit; 089 import visad.VisADException; 090 import visad.georef.EarthLocation; 091 import visad.georef.EarthLocationLite; 092 093 /** 094 * 095 * @author Unidata Development Team 096 * @version $Revision: 1.1 $ 097 */ 098 099 public 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 GuiUtils.ColorSwatch 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 long t2 = System.currentTimeMillis(); 685 686 if (v.isEmpty()) { 687 retField = new FieldImpl((FunctionType) pointObs.getType(), 688 new Integer1DSet(((SetType) domainSet.getType()) 689 .getDomain(), 1)); 690 retField.setSample(0, pointObs.getSample(0), false); 691 } else if (v.size() == numObs) { 692 retField = pointObs; // all were in domain, just return input 693 } else { 694 retField = new FieldImpl((FunctionType) pointObs.getType(), 695 new Integer1DSet(((SetType) domainSet.getType()) 696 .getDomain(), v.size())); 697 retField.setSamples((PointOb[]) v.toArray(new PointOb[v.size()]), 698 false, false); 699 } 700 701 long t3 = System.currentTimeMillis(); 702 // System.err.println("size:" + v.size() +" declutter:" + (t2-t1) + " " 703 // + (t3-t2)); 704 705 return retField; 706 } 707 708 /** decluttering filter factor */ 709 private float declutterFilter = 1.0f; 710 711 /** 712 * _more_ 713 * 714 * @return _more_ 715 */ 716 public float getDeclutterFilter() { 717 return declutterFilter; 718 } 719 720 /** 721 * _more_ 722 * 723 * @return _more_ 724 */ 725 protected Rectangle2D getBounds() { 726 return calculateRectangle(); 727 } 728 729 /** 730 * _more_ 731 * 732 * @return _more_ 733 */ 734 protected Rectangle2D calculateRectangle() { 735 try { 736 Rectangle2D.Double box = stormDisplayState.getStormTrackControl() 737 .getNavigatedDisplay().getVisadBox(); 738 if (!box.isEmpty()) { 739 // pad rectangle by 5% 740 double deltaWidth = (double) (.05 * box.width); 741 double deltaHeight = (double) (.05 * box.height); 742 double newX = box.x - deltaWidth; 743 double newY = box.y - deltaHeight; 744 box.setRect(newX, newY, box.width + (2.0 * deltaWidth), 745 box.height + (2.0 * deltaHeight)); 746 } 747 return box; 748 } catch (Exception excp) { 749 LogUtil.logException("calculating Rectangle ", excp); 750 return new Rectangle2D.Double(0, 0, 0, 0); 751 } 752 } 753 754 /** 755 * _more_ 756 * 757 * @return _more_ 758 * 759 * @throws Exception 760 * _more_ 761 */ 762 public StationModelDisplayable getLabelDisplay() throws Exception { 763 if (labelDisplay == null) { 764 StationModel sm = (way.isObservation() ? stormDisplayState 765 .getObsLayoutModel() : stormDisplayState 766 .getForecastLayoutModel()); 767 if (sm != null) { 768 labelDisplay = new StationModelDisplayable("dots"); 769 labelDisplay.setRotateShapes(true); 770 labelDisplay.setUseTimesInAnimation(false); 771 addDisplayable(labelDisplay); 772 labelDisplay.setScale(stormDisplayState.getStormTrackControl() 773 .getDisplayScale()); 774 } 775 } 776 return labelDisplay; 777 } 778 779 /** 780 * _more_ 781 * 782 * @return _more_ 783 * 784 * 785 * @throws RemoteException 786 * _more_ 787 * @throws VisADException 788 * _more_ 789 */ 790 public StationModelDisplayable getObsPointDisplay() throws VisADException, 791 RemoteException { 792 if (obsPointDisplay == null) { 793 obsPointDisplay = new StationModelDisplayable("dots"); 794 obsPointDisplay.setRotateShapes(true); 795 obsPointDisplay.setUseTimesInAnimation(false); 796 addDisplayable(obsPointDisplay); 797 obsPointDisplay.setScale(stormDisplayState.getStormTrackControl() 798 .getDisplayScale()); 799 } 800 return obsPointDisplay; 801 } 802 803 /** 804 * _more_ 805 * 806 * @return _more_ 807 * 808 * @throws Exception 809 * _more_ 810 */ 811 public void initTrackDisplay() throws Exception { 812 813 trackDisplay = new TrackDisplayable("track_" 814 + stormDisplayState.getStormInfo().getStormId()); // + 815 // stormDisplayState.getColorParam(this)); 816 if (way.isObservation()) { 817 trackDisplay.setLineWidth(3); 818 } else { 819 trackDisplay.setLineWidth(2); 820 trackDisplay.setUseTimesInAnimation(false); 821 } 822 // setTrackColor(); 823 int cnt = holder.displayableCount(); 824 825 for (int i = 0; i < cnt; i++) { 826 Displayable dp = holder.getDisplayable(i); 827 if (dp.getClass().isInstance(trackDisplay)) { 828 TrackDisplayable dd = (TrackDisplayable) dp; 829 if (dd.toString().equalsIgnoreCase(trackDisplay.toString())) { 830 holder.removeDisplayable(dp); 831 cnt = cnt - 1; 832 } 833 } 834 } 835 836 addDisplayable(trackDisplay); 837 838 } 839 840 /** 841 * _more_ 842 * 843 * @return _more_ 844 * 845 * @throws Exception 846 * _more_ 847 */ 848 public TrackDisplayable getTrackDisplay() throws Exception { 849 if (trackDisplay == null) { 850 initTrackDisplay(); 851 } 852 return trackDisplay; 853 } 854 855 /** 856 * _more_ 857 * 858 * @return _more_ 859 * 860 * @throws Exception 861 * _more_ 862 */ 863 public CompositeDisplayable getConesHolder() throws Exception { 864 if (conesHolder == null) { 865 conesHolder = new CompositeDisplayable("cone_" 866 + stormDisplayState.getStormInfo().getStormId()); 867 conesHolder.setVisible(true); 868 conesHolder.setUseTimesInAnimation(false); 869 addDisplayable(conesHolder); 870 } 871 return conesHolder; 872 } 873 874 /** 875 * _more_ 876 * 877 * @return _more_ 878 * 879 * @throws Exception 880 * _more_ 881 */ 882 public TrackDisplayable getRingsDisplay() throws Exception { 883 if (ringsDisplay == null) { 884 ringsDisplay = new TrackDisplayable("ring_" 885 + stormDisplayState.getStormInfo().getStormId() + "_" 886 + getWay()); 887 ringsDisplay.setVisible(true); 888 ringsDisplay.setUseTimesInAnimation(false); 889 addDisplayable(ringsDisplay); 890 } 891 return ringsDisplay; 892 } 893 894 /** 895 * _more_ 896 * 897 * @param param 898 * _more_ 899 * @param mode 900 * _more_ 901 * 902 * @return _more_ 903 * 904 * @throws Exception 905 * _more_ 906 */ 907 public TrackDisplayable makeConeDisplay(StormParam param, int mode) 908 throws Exception { 909 FieldImpl field = makeConeField(param, mode); 910 if (field == null) { 911 return null; 912 } 913 TrackDisplayable coneDisplay = new TrackDisplayable("cone_" 914 + stormDisplayState.getStormInfo().getStormId()); 915 coneDisplay.setUseTimesInAnimation(false); 916 coneDisplay.setTrack(field); 917 coneDisplay.setUseTimesInAnimation(false); 918 return coneDisplay; 919 } 920 921 /** 922 * _more_ 923 * 924 * @param param 925 * _more_ 926 * @param mode 927 * _more_ 928 * 929 * @return _more_ 930 * 931 * @throws Exception 932 * _more_ 933 */ 934 public TrackDisplayable makeRingDisplay(StormParam param, int mode) 935 throws Exception { 936 FieldImpl field = makeRingsField(param, mode); 937 if (field == null) { 938 return null; 939 } 940 TrackDisplayable ringDisplay = new TrackDisplayable("ring_" 941 + stormDisplayState.getStormInfo().getStormId()); 942 ringDisplay.setUseTimesInAnimation(false); 943 ringDisplay.setTrack(field); 944 ringDisplay.setUseTimesInAnimation(false); 945 return ringDisplay; 946 } 947 948 /** 949 * _more_ 950 * 951 * @return _more_ 952 */ 953 protected JComponent getColorSwatch() { 954 if (colorSwatch == null) { 955 colorSwatch = new GuiUtils.ColorSwatch(getColor(), 956 "Set track color") { 957 public void setBackground(Color newColor) { 958 super.setBackground(newColor); 959 WayDisplayState.this.color = newColor; 960 try { 961 setTrackColor(); 962 setConeColor(); 963 setRingsColor(); 964 setLabelColor(); 965 } catch (Exception exc) { 966 LogUtil.logException("Setting color", exc); 967 } 968 } 969 }; 970 colorSwatch.setMinimumSize(new Dimension(15, 15)); 971 colorSwatch.setPreferredSize(new Dimension(15, 15)); 972 } 973 return colorSwatch; 974 } 975 976 /** 977 * _more_ 978 * 979 * @return _more_ 980 */ 981 public float[][] getColorPalette() { 982 ColorTable ct = stormDisplayState.getColorTable(colorParam); 983 if (ct != null) { 984 return stormDisplayState.getStormTrackControl() 985 .getColorTableForDisplayable(ct); 986 } 987 return getColorPalette(getColor()); 988 } 989 990 /** 991 * _more_ 992 * 993 * @param c 994 * _more_ 995 * 996 * @return _more_ 997 */ 998 public static float[][] getColorPalette(Color c) { 999 if (c == null) { 1000 c = Color.red; 1001 } 1002 return ColorTableDefaults.allOneColor(c, true); 1003 } 1004 1005 /** 1006 * _more_ 1007 * 1008 * @throws Exception 1009 * _more_ 1010 */ 1011 private void setTrackColor() throws Exception { 1012 if (trackDisplay != null) { 1013 if (colorParam == null 1014 || colorParam.getName().equalsIgnoreCase("Fixed")) { 1015 trackDisplay.setColor(getColor()); 1016 } else 1017 trackDisplay.setColorPalette(getColorPalette()); 1018 } 1019 1020 } 1021 1022 /** 1023 * _more_ 1024 * 1025 * @throws Exception 1026 * _more_ 1027 */ 1028 private void setLabelColor() throws Exception { 1029 Color c = getColor(); 1030 if (labelDisplay != null) { // && !Misc.equals(c, 1031 // labelDisplay.getColor())) { 1032 labelDisplay.setColor(c); 1033 } 1034 if (obsPointDisplay != null) { // && !Misc.equals(c, 1035 // obsPointDisplay.getColor())) { 1036 obsPointDisplay.setColor(c); 1037 } 1038 } 1039 1040 /** 1041 * _more_ 1042 * 1043 * @throws Exception 1044 * _more_ 1045 */ 1046 private void setConeColor() throws Exception { 1047 if (conesHolder != null) { 1048 conesHolder.setColorPalette(getColorPalette(getColor())); 1049 } 1050 } 1051 1052 /** 1053 * _more_ 1054 * 1055 * @throws Exception 1056 * _more_ 1057 */ 1058 private void setRingsColor() throws Exception { 1059 if (ringsDisplay != null) { 1060 ringsDisplay.setColor(getColor()); 1061 } 1062 } 1063 1064 /** 1065 * _more_ 1066 * 1067 * @throws RemoteException 1068 * _more_ 1069 * @throws VisADException 1070 * _more_ 1071 */ 1072 public void deactivate() throws VisADException, RemoteException { 1073 ringsDisplay = null; 1074 conesHolder = null; 1075 if (holder != null) { 1076 } 1077 trackDisplay = null; 1078 labelDisplay = null; 1079 obsPointDisplay = null; 1080 holder = null; 1081 tracks = new ArrayList<StormTrack>(); 1082 times = new ArrayList<DateTime>(); 1083 } 1084 1085 /** _more_ */ 1086 private static TextType fhourType; 1087 1088 /** _more_ */ 1089 private static TextType rhourType; 1090 1091 /** _more_ */ 1092 private static TextType shourType; 1093 1094 /** 1095 * _more_ 1096 * 1097 * @param track 1098 * _more_ 1099 * 1100 * @throws Exception 1101 * _more_ 1102 */ 1103 public void addTrack(StormTrack track) throws Exception { 1104 tracks.add(track); 1105 } 1106 1107 /** 1108 * _more_ 1109 * 1110 * 1111 * 1112 * @param mode 1113 * _more_ 1114 * @return _more_ 1115 * @throws Exception 1116 * _more_ 1117 */ 1118 1119 protected FieldImpl makeTrackField(int mode) throws Exception { 1120 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1121 List<DateTime> times = new ArrayList<DateTime>(); 1122 1123 // Use a local list to hold the point obs so we don't run into a race 1124 // condition 1125 List<PointOb> localPointObs = new ArrayList<PointOb>(); 1126 List<PointOb> localAllPointObs = new ArrayList<PointOb>(); 1127 Data[] datas = new Data[tracks.size()]; 1128 int i = 0; 1129 for (StormTrack track : tracks) { 1130 FieldImpl field = stormDisplayState.getStormTrackControl() 1131 .makeTrackField(track, colorParam); 1132 if (field == null) { 1133 continue; 1134 } 1135 if (i == 0) 1136 datas[i++] = field; 1137 else 1138 datas[i++] = field.changeMathType(datas[0].getType()); 1139 fields.add(field); 1140 times.add(track.getStartTime()); 1141 // if(!way.isObservation() && mode == 0) 1142 localPointObs.addAll(makePointObs(track, !way.isObservation())); 1143 if (way.isObservation()) { 1144 localAllPointObs.addAll(makeObsPointObs(track)); 1145 } 1146 } 1147 1148 pointObs = localPointObs; 1149 allPointObs = localAllPointObs; 1150 1151 if (fields.size() == 0) { 1152 return null; 1153 } 1154 // if(fields.size()==1) return fields.get(0); 1155 1156 if (!way.isObservation() && (mode == 1)) { 1157 return Util.indexedField(datas, false); 1158 } else { 1159 return Util.makeTimeField(fields, times); 1160 } 1161 } 1162 1163 /** 1164 * _more_ 1165 * 1166 * 1167 * @param stormParam 1168 * _more_ 1169 * @param mode 1170 * _more_ 1171 * 1172 * @return _more_ 1173 * @throws Exception 1174 * _more_ 1175 */ 1176 protected FieldImpl makeConeField(StormParam stormParam, int mode) 1177 throws Exception { 1178 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1179 List<DateTime> times = new ArrayList<DateTime>(); 1180 Data[] datas = new Data[tracks.size()]; 1181 int i = 0; 1182 for (StormTrack track : tracks) { 1183 StormTrack coneTrack = makeConeTrack(track, stormParam); 1184 if (coneTrack == null) { 1185 continue; 1186 } 1187 FieldImpl field = stormDisplayState.getStormTrackControl() 1188 .makeTrackField(coneTrack, null); 1189 fields.add(field); 1190 1191 times.add(track.getStartTime()); 1192 datas[i++] = field; 1193 } 1194 1195 if (fields.size() == 0) { 1196 return null; 1197 } 1198 1199 if (!way.isObservation() && (mode == 1)) { 1200 return Util.indexedField(datas, false); 1201 } else { 1202 return Util.makeTimeField(fields, times); 1203 } 1204 } 1205 1206 /** 1207 * _more_ 1208 * 1209 * @param track 1210 * _more_ 1211 * @param useStartTime 1212 * _more_ 1213 * 1214 * 1215 * @return _more_ 1216 * @throws Exception 1217 * _more_ 1218 */ 1219 private List<PointOb> makePointObs(StormTrack track, boolean useStartTime) 1220 throws Exception { 1221 boolean isObservation = way.isObservation(); 1222 DateTime startTime = track.getStartTime(); 1223 List<StormTrackPoint> stps = track.getTrackPoints(); 1224 if (fhourType == null) { 1225 fhourType = new TextType("fhour"); 1226 } 1227 1228 if (rhourType == null) { 1229 rhourType = new TextType("rhour"); 1230 } 1231 1232 if (shourType == null) { 1233 shourType = new TextType("shour"); 1234 } 1235 List<PointOb> pointObs = new ArrayList<PointOb>(); 1236 1237 DecimalFormat format = new DecimalFormat("0.#"); 1238 Date startDate = Util.makeDate(startTime); 1239 List<StormParam> params = track.getParams(); 1240 for (int i = 0; i < stps.size(); i++) { 1241 StormTrackPoint stp = stps.get(i); 1242 DateTime time = (useStartTime ? startTime : stp.getTime()); 1243 String flabel = ""; 1244 String rlabel = ""; 1245 String slabel = ""; 1246 if (!isObservation) { 1247 if (i == 0) { 1248 // label = way.getId() + ": " + track.getStartTime(); 1249 } else { 1250 flabel = "" + stp.getForecastHour() + "H"; 1251 Date dttm = Util.makeDate(stp.getTime()); 1252 rlabel = "" + dttm.toString(); 1253 slabel = "" + getMonDayHour(dttm); 1254 ; 1255 } 1256 } else if (useStartTime && (i > 0)) { 1257 Date dttm = Util.makeDate(stp.getTime()); 1258 double diffSeconds = (dttm.getTime() - startDate.getTime()) / 1000.0; 1259 double diffHours = diffSeconds / 3600.0; 1260 1261 flabel = format.format(diffHours) + "H"; 1262 rlabel = "" + dttm.toString(); 1263 slabel = "" + getMonDayHour(dttm); 1264 } 1265 Data[] data = new Data[params.size() + 3]; 1266 1267 data[0] = new visad.Text(rhourType, rlabel); 1268 data[1] = new visad.Text(fhourType, flabel); 1269 data[2] = new visad.Text(shourType, slabel); 1270 for (int paramIdx = 0; paramIdx < params.size(); paramIdx++) { 1271 Real r = stp.getAttribute(params.get(paramIdx)); 1272 if (r == null) { 1273 r = params.get(paramIdx).getReal(Double.NaN); 1274 } 1275 data[paramIdx + 3] = r; 1276 1277 } 1278 Tuple tuple = new Tuple(data); 1279 pointObs.add(PointObFactory.makePointOb(stp.getLocation(), time, 1280 tuple)); 1281 1282 } 1283 return pointObs; 1284 } 1285 1286 /** 1287 * _more_ 1288 * 1289 * @param dt 1290 * _more_ 1291 * 1292 * @return _more_ 1293 */ 1294 private String getMonDayHour(Date dt) { 1295 Calendar cal = Calendar.getInstance(); 1296 1297 cal.setTime(dt); 1298 int m = cal.get(Calendar.MONTH) + 1; // 0 base, ie 0 for Jan 1299 int d = cal.get(Calendar.DAY_OF_MONTH); 1300 int h = cal.get(Calendar.HOUR_OF_DAY); 1301 1302 return "" + m + "/" + d + "/" + h + "H"; 1303 } 1304 1305 /** 1306 * _more_ 1307 * 1308 * @param track 1309 * _more_ 1310 * 1311 * @return _more_ 1312 * 1313 * @throws Exception 1314 * _more_ 1315 */ 1316 private List<PointOb> makeObsPointObs(StormTrack track) throws Exception { 1317 DateTime startTime = track.getStartTime(); 1318 List<StormTrackPoint> stps = track.getTrackPoints(); 1319 if (fhourType == null) { 1320 fhourType = new TextType("fhour"); 1321 } 1322 if (rhourType == null) { 1323 rhourType = new TextType("rhour"); 1324 } 1325 if (shourType == null) { 1326 shourType = new TextType("shour"); 1327 } 1328 List<PointOb> pointObs = new ArrayList<PointOb>(); 1329 DecimalFormat format = new DecimalFormat("0.#"); 1330 List<StormParam> params = track.getParams(); 1331 for (int i = 0; i < stps.size(); i++) { 1332 StormTrackPoint baseStp = stps.get(i); 1333 DateTime baseTime = baseStp.getTime(); 1334 Date baseDate = Util.makeDate(baseTime); 1335 for (int j = i; j < stps.size(); j++) { 1336 StormTrackPoint stp = stps.get(j); 1337 String flabel = ""; 1338 String rlabel = ""; 1339 String slabel = ""; 1340 if (j > 0) { 1341 Date dttm = Util.makeDate(stp.getTime()); 1342 double diffSeconds = (dttm.getTime() - baseDate.getTime()) / 1000.0; 1343 double diffHours = diffSeconds / 3600.0; 1344 flabel = format.format(diffHours) + "H"; 1345 rlabel = "" + dttm.toString(); 1346 slabel = "" + getMonDayHour(dttm); 1347 } 1348 Data[] data = new Data[params.size() + 3]; 1349 data[0] = new visad.Text(fhourType, flabel); 1350 data[1] = new visad.Text(rhourType, rlabel); 1351 data[2] = new visad.Text(shourType, slabel); 1352 for (int paramIdx = 0; paramIdx < params.size(); paramIdx++) { 1353 Real r = stp.getAttribute(params.get(paramIdx)); 1354 if (r == null) { 1355 r = params.get(paramIdx).getReal(Double.NaN); 1356 } 1357 data[paramIdx + 3] = r; 1358 } 1359 Tuple tuple = new Tuple(data); 1360 pointObs.add(PointObFactory.makePointOb(stp.getLocation(), 1361 baseTime, tuple)); 1362 } 1363 } 1364 return pointObs; 1365 } 1366 1367 /** 1368 * _more_ 1369 * 1370 * @return _more_ 1371 */ 1372 public List<StormTrack> getTracks() { 1373 return tracks; 1374 } 1375 1376 /** 1377 * _more_ 1378 * 1379 * @return _more_ 1380 */ 1381 public List<DateTime> getTimes() { 1382 return times; 1383 } 1384 1385 /** 1386 * _more_ 1387 * 1388 * @param displayable 1389 * _more_ 1390 * 1391 * 1392 * @throws RemoteException 1393 * _more_ 1394 * @throws VisADException 1395 * _more_ 1396 */ 1397 public void addDisplayable(Displayable displayable) throws VisADException, 1398 RemoteException { 1399 getHolder().addDisplayable(displayable); 1400 } 1401 1402 /** 1403 * _more_ 1404 * 1405 * @param displayable 1406 * _more_ 1407 * 1408 * @throws RemoteException 1409 * _more_ 1410 * @throws VisADException 1411 * _more_ 1412 */ 1413 public void removeDisplayable(Displayable displayable) 1414 throws VisADException, RemoteException { 1415 getHolder().removeDisplayable(displayable); 1416 } 1417 1418 /** 1419 * Set the ConeState property. 1420 * 1421 * @param value 1422 * The new value for ConeState 1423 */ 1424 public void setConeState(DisplayState value) { 1425 coneState = value; 1426 } 1427 1428 /** 1429 * Get the ConeState property. 1430 * 1431 * @return The ConeState 1432 */ 1433 public DisplayState getConeState() { 1434 return coneState; 1435 } 1436 1437 /** 1438 * Set the TrackState property. 1439 * 1440 * @param value 1441 * The new value for TrackState 1442 */ 1443 public void setTrackState(DisplayState value) { 1444 trackState = value; 1445 } 1446 1447 /** 1448 * Get the TrackState property. 1449 * 1450 * @return The TrackState 1451 */ 1452 public DisplayState getTrackState() { 1453 return trackState; 1454 } 1455 1456 /** 1457 * Set the RingsState property. 1458 * 1459 * @param value 1460 * The new value for RingsState 1461 */ 1462 public void setRingsState(DisplayState value) { 1463 ringsState = value; 1464 } 1465 1466 /** 1467 * Get the RingsState property. 1468 * 1469 * @return The RingsState 1470 */ 1471 public DisplayState getRingsState() { 1472 return ringsState; 1473 } 1474 1475 /** 1476 * Set the WayState property. 1477 * 1478 * @param value 1479 * The new value for WayState 1480 */ 1481 public void setWayState(DisplayState value) { 1482 wayState = value; 1483 } 1484 1485 /** 1486 * Get the WayState property. 1487 * 1488 * @return The WayState 1489 */ 1490 public DisplayState getWayState() { 1491 return wayState; 1492 } 1493 1494 /** 1495 * Set the Color property. 1496 * 1497 * @param value 1498 * The new value for Color 1499 */ 1500 public void setColor(Color value) { 1501 color = value; 1502 } 1503 1504 /** 1505 * Get the Color property. 1506 * 1507 * @return The Color 1508 */ 1509 public Color getColor() { 1510 return color; 1511 } 1512 1513 /** 1514 * Set the StormDisplayState property. 1515 * 1516 * @param value 1517 * The new value for StormDisplayState 1518 */ 1519 public void setStormDisplayState(StormDisplayState value) { 1520 stormDisplayState = value; 1521 } 1522 1523 /** 1524 * Get the StormDisplayState property. 1525 * 1526 * @return The StormDisplayState 1527 */ 1528 public StormDisplayState getStormDisplayState() { 1529 return stormDisplayState; 1530 } 1531 1532 /** 1533 * Set the Way property. 1534 * 1535 * @param value 1536 * The new value for Way 1537 */ 1538 public void setWay(Way value) { 1539 way = value; 1540 } 1541 1542 /** 1543 * Get the Way property. 1544 * 1545 * @return The Way 1546 */ 1547 public Way getWay() { 1548 return way; 1549 } 1550 1551 /** 1552 * Set the ColorTable property. 1553 * 1554 * @param value 1555 * The new value for ColorTable 1556 */ 1557 public void setColorTable(String value) { 1558 } 1559 1560 /** 1561 * _more_ 1562 * 1563 * @param track 1564 * _more_ 1565 * @param param 1566 * _more_ 1567 * 1568 * @return _more_ 1569 */ 1570 private List<StormTrackPoint> getRealTrackPoints(StormTrack track, 1571 StormParam param) { 1572 List<StormTrackPoint> newStps = new ArrayList(); 1573 List<StormTrackPoint> stps = track.getTrackPoints(); 1574 1575 newStps.add(stps.get(0)); 1576 Iterator<StormTrackPoint> it = stps.iterator(); 1577 1578 while (it.hasNext()) { 1579 StormTrackPoint stp = it.next(); 1580 if (stp.getAttribute(param) != null) { 1581 newStps.add(stp); 1582 } 1583 } 1584 return newStps; 1585 } 1586 1587 /** 1588 * _more_ 1589 * 1590 * @param stormParam 1591 * _more_ 1592 * @param mode 1593 * _more_ 1594 * 1595 * @return _more_ 1596 * 1597 * @throws Exception 1598 * _more_ 1599 */ 1600 protected FieldImpl makeRingsField(StormParam stormParam, int mode) 1601 throws Exception { 1602 List<FieldImpl> fields = new ArrayList<FieldImpl>(); 1603 List<DateTime> times = new ArrayList<DateTime>(); 1604 Data[] datas = new Data[tracks.size() * 10]; 1605 int i = 0; 1606 1607 if (!way.isObservation() && (mode == 1)) { 1608 for (StormTrack track : tracks) { 1609 List<StormTrack> stList = makeRingTrackList(track, stormParam); 1610 for (StormTrack stk : stList) { 1611 FieldImpl field = stormDisplayState.getStormTrackControl() 1612 .makeTrackField(stk, null); 1613 fields.add(field); 1614 datas[i++] = field; 1615 } 1616 1617 } 1618 1619 return Util.indexedField(datas, false); 1620 } 1621 1622 for (StormTrack track : tracks) { 1623 FieldImpl ringField = makeRingTracks(track, stormParam); 1624 fields.add(ringField); 1625 times.add(track.getStartTime()); 1626 } 1627 1628 if (fields.size() == 0) { 1629 return null; 1630 } 1631 1632 return Util.makeTimeField(fields, times); 1633 } 1634 1635 /** 1636 * _more_ 1637 * 1638 * @param track 1639 * _more_ 1640 * @param param 1641 * _more_ 1642 * 1643 * @return _more_ 1644 * 1645 * @throws Exception 1646 * _more_ 1647 */ 1648 public List makeRingTrackList(StormTrack track, StormParam param) 1649 throws Exception { 1650 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1651 List<StormTrack> stracks = new ArrayList(); 1652 1653 int size = stps.size(); 1654 DateTime dt = stps.get(0).getTime(); 1655 Way ringWay = new Way(getWay() + "_RING"); 1656 int numberOfPoints = 73; 1657 double angleDelta = 360.0 / (numberOfPoints - 1); 1658 for (int i = 0; i < size; i++) { 1659 StormTrackPoint stp = stps.get(i); 1660 Real r = stp.getAttribute(param); 1661 if (r != null) { 1662 double rr = r.getValue(); 1663 double azi = 0.0; 1664 List ringList = new ArrayList<StormTrackPoint>(); 1665 for (int j = 0; j < numberOfPoints; j++) { 1666 ringList.add(getCirclePoint(stp, rr, azi, dt)); 1667 azi = azi + angleDelta; 1668 } 1669 stracks.add(new StormTrack(track.getStormInfo(), ringWay, 1670 ringList, null)); 1671 } 1672 } 1673 1674 return stracks; 1675 1676 } 1677 1678 /** 1679 * _more_ 1680 * 1681 * @param track 1682 * _more_ 1683 * @param param 1684 * _more_ 1685 * 1686 * @return _more_ 1687 * 1688 * 1689 * @throws Exception 1690 * _more_ 1691 */ 1692 public FieldImpl makeRingTracks(StormTrack track, StormParam param) 1693 throws Exception { 1694 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1695 List<StormTrack> stracks = new ArrayList(); 1696 int size = stps.size(); 1697 DateTime dt = stps.get(0).getTime(); 1698 Way ringWay = new Way(getWay() + "_RING"); 1699 int numberOfPoints = 73; 1700 double angleDelta = 360.0 / (numberOfPoints - 1); 1701 for (int i = 0; i < size; i++) { 1702 StormTrackPoint stp = stps.get(i); 1703 Real r = stp.getAttribute(param); 1704 if (r != null) { 1705 double rr = r.getValue(); 1706 double azi = 0.0; 1707 List ringList = new ArrayList<StormTrackPoint>(); 1708 for (int j = 0; j < numberOfPoints; j++) { 1709 ringList.add(getCirclePoint(stp, rr, azi, dt)); 1710 azi = azi + angleDelta; 1711 } 1712 stracks.add(new StormTrack(track.getStormInfo(), ringWay, 1713 ringList, null)); 1714 } 1715 } 1716 1717 Data[] datas = new Data[stracks.size()]; 1718 int i = 0; 1719 for (StormTrack ringTrack : stracks) { 1720 datas[i++] = stormDisplayState.getStormTrackControl() 1721 .makeTrackField(ringTrack, null); 1722 } 1723 return Util.indexedField(datas, false); 1724 } 1725 1726 /** 1727 * _more_ 1728 * 1729 * @param stp 1730 * _more_ 1731 * @param r0 1732 * _more_ 1733 * @param azimuth 1734 * _more_ 1735 * @param dt 1736 * _more_ 1737 * 1738 * @return _more_ 1739 * 1740 * @throws VisADException 1741 * _more_ 1742 */ 1743 public StormTrackPoint getCirclePoint(StormTrackPoint stp, double r0, 1744 double azimuth, DateTime dt) throws VisADException { 1745 // 1746 1747 EarthLocation el = stp.getLocation(); 1748 double lat0 = el.getLatitude().getValue(); 1749 double lon0 = el.getLongitude().getValue(); 1750 // DateTime dt = stp.getTime(); 1751 LatLonPointImpl lp = Bearing.findPoint(lat0, lon0, azimuth, r0, null); 1752 1753 EarthLocation el1 = new EarthLocationLite(lp.getLatitude(), lp 1754 .getLongitude(), 0); 1755 StormTrackPoint stp1 = new StormTrackPoint(el1, dt, 0, null); 1756 1757 return stp1; 1758 } 1759 1760 /** 1761 * old 1762 * 1763 * @param track 1764 * _more_ 1765 * @param param 1766 * _more_ 1767 * 1768 * @return _more_ 1769 * 1770 * @throws VisADException 1771 * _more_ 1772 */ 1773 public StormTrack makeConeTrack_Old(StormTrack track, StormParam param) 1774 throws VisADException { 1775 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1776 int size = stps.size(); 1777 int numberOfPoint = size * 2 + 11; 1778 StormTrackPoint[] conePoints = new StormTrackPoint[numberOfPoint]; 1779 1780 StormTrackPoint stp1 = stps.get(0); 1781 conePoints[0] = stp1; // first point & last point 1782 conePoints[numberOfPoint - 1] = stp1; 1783 1784 StormTrackPoint stp2; 1785 StormTrackPoint stp; 1786 1787 // circle 1 to n 1788 1789 for (int i = 1; i < size; i++) { 1790 stp2 = stps.get(i); 1791 // right point 1792 stp = getPointToCircleTangencyPoint(stp1, stp2, param, true); 1793 conePoints[i] = stp; 1794 // left point 1795 stp = getPointToCircleTangencyPoint(stp1, stp2, param, false); 1796 conePoints[numberOfPoint - i - 1] = stp; 1797 stp1 = stp2; 1798 } 1799 1800 // end point half circle take 11 points 1801 StormTrackPoint last = stps.get(size - 1); 1802 EarthLocation lastEl = last.getLocation(); 1803 StormTrackPoint endSTP = conePoints[size - 1]; 1804 int ii = 0; 1805 while ((endSTP == null) && (ii < (size - 2))) { 1806 ii++; 1807 last = stps.get(size - 1 - ii); 1808 lastEl = last.getLocation(); 1809 endSTP = conePoints[size - 1 - ii]; 1810 } 1811 1812 if ((endSTP == null) || (ii == (size - 2))) { 1813 return null; 1814 } 1815 EarthLocation endEl = endSTP.getLocation(); 1816 1817 double ang = getCircleAngleRange(lastEl, endEl); 1818 1819 Real r = last.getAttribute(param); 1820 StormTrackPoint[] halfCircle = getHalfCircleTrackPoint(lastEl, ang, 1821 ((r != null) ? r.getValue() : 0), last.getTime()); 1822 1823 for (int i = 0; i < 11; i++) { 1824 conePoints[size + i] = halfCircle[i]; 1825 } 1826 1827 List coneList = new ArrayList<StormTrackPoint>(); 1828 for (int i = 0; i < numberOfPoint; i++) { 1829 if (conePoints[i] != null) { 1830 coneList.add(conePoints[i]); 1831 } 1832 } 1833 1834 return new StormTrack(track.getStormInfo(), 1835 new Way(getWay() + "_CONE"), coneList, null); 1836 1837 } 1838 1839 /** 1840 * construct the cone track as track of point to circle and circle to circle 1841 * 1842 * @param track 1843 * _more_ 1844 * @param param 1845 * _more_ 1846 * 1847 * @return _more_ 1848 * 1849 * @throws VisADException 1850 * _more_ 1851 */ 1852 public StormTrack makeConeTrack(StormTrack track, StormParam param) 1853 throws VisADException { 1854 1855 List<StormTrackPoint> stps = getRealTrackPoints(track, param); 1856 int size = stps.size(); 1857 int numberOfPoint = size * 2 + 100; 1858 List<StormTrackPoint> conePointsLeft = new ArrayList<StormTrackPoint>(); 1859 List<StormTrackPoint> conePointsRight = new ArrayList<StormTrackPoint>(); 1860 1861 StormTrackPoint stp1 = stps.get(0); 1862 conePointsRight.add(stp1); // first point & last point 1863 conePointsLeft.add(stp1); 1864 StormTrackPoint stp2 = stps.get(1); 1865 StormTrackPoint stp3 = stps.get(2); 1866 int nn = 3; 1867 // first point to circle 1868 List<StormTrackPoint> p2c = getPointToCircleTangencyPointA(stp1, stp2, 1869 stp3, param, true); 1870 while (p2c == null) { // need to find the first point with param value 1871 stp2 = stp3; 1872 if (nn < size) { 1873 stp3 = stps.get(nn); 1874 } else { 1875 stp3 = null; 1876 // return null; 1877 } 1878 p2c = getPointToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1879 nn++; 1880 if (nn >= size) { 1881 break; 1882 } 1883 } 1884 if (p2c != null) { 1885 conePointsRight.addAll(p2c); 1886 p2c = getPointToCircleTangencyPointA(stp1, stp2, stp3, param, false); 1887 conePointsLeft.addAll(p2c); 1888 } 1889 1890 // circle to circle 1 to n 1891 stp1 = stp2; 1892 stp2 = stp3; 1893 for (int i = nn; i < size; i++) { 1894 stp3 = stps.get(i); 1895 // right point 1896 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1897 if (p2c != null) { 1898 conePointsRight.addAll(p2c); 1899 // left point 1900 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, 1901 false); 1902 conePointsLeft.addAll(p2c); 1903 stp1 = stp2; // update the first point only after the valid 1904 // second point 1905 } 1906 1907 stp2 = stp3; 1908 } 1909 // last circle 1910 stp3 = null; 1911 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, true); 1912 if (p2c != null) { 1913 conePointsRight.addAll(p2c); 1914 // left point 1915 p2c = getCircleToCircleTangencyPointA(stp1, stp2, stp3, param, 1916 false); 1917 conePointsLeft.addAll(p2c); 1918 stp1 = stp2; 1919 } 1920 1921 // end point half circle take 11 points 1922 StormTrackPoint last = stp2; 1923 if (last == null) { 1924 last = stp1; 1925 } 1926 if (last == null) { 1927 return null; 1928 } 1929 EarthLocation lastEl = last.getLocation(); 1930 StormTrackPoint endSTP = conePointsRight 1931 .get(conePointsRight.size() - 1); 1932 /* 1933 * int ii = 0; while ((endSTP == null) && (ii < (size - 2))) { ii++; 1934 * last = stps.get(size - 1 - ii); lastEl = last.getLocation(); endSTP = 1935 * conePointsRight.get(size - 1 - ii); } 1936 * 1937 * if ((endSTP == null) || (ii == (size - 2))) { return null; } 1938 */ 1939 if (endSTP == null) { 1940 return null; 1941 } 1942 EarthLocation endEl = endSTP.getLocation(); 1943 1944 double ang = getCircleAngleRange(lastEl, endEl); 1945 1946 Real r = last.getAttribute(param); 1947 StormTrackPoint[] halfCircle = getHalfCircleTrackPoint(lastEl, ang, 1948 ((r != null) ? r.getValue() : 0), last.getTime()); 1949 1950 for (int i = 0; i < 11; i++) { 1951 } 1952 // merge three lists 1953 List<StormTrackPoint> coneList = new ArrayList<StormTrackPoint>(); 1954 int s1 = conePointsRight.size(); 1955 for (int i = 0; i < s1; i++) { 1956 if (conePointsRight.get(i) != null) { 1957 coneList.add(conePointsRight.get(i)); 1958 } 1959 } 1960 for (int i = 0; i < 11; i++) { 1961 coneList.add(halfCircle[i]); 1962 } 1963 int s2 = conePointsLeft.size(); 1964 for (int i = s2; i > 0; i--) { 1965 if (conePointsLeft.get(i - 1) != null) { 1966 coneList.add(conePointsLeft.get(i - 1)); 1967 } 1968 } 1969 1970 return new StormTrack(track.getStormInfo(), 1971 new Way(getWay() + "_CONE"), coneList, null); 1972 1973 } 1974 1975 /** 1976 * calculate the bearing of two storm track points 1977 * 1978 * @param sp1 1979 * _more_ 1980 * @param sp2 1981 * _more_ 1982 * 1983 * @return _more_ 1984 */ 1985 public Bearing getStormPoinsBearing(StormTrackPoint sp1, StormTrackPoint sp2) { 1986 EarthLocation el1 = sp1.getLocation(); 1987 EarthLocation el2 = sp2.getLocation(); 1988 return Bearing.calculateBearing(el1.getLatitude().getValue(), el1 1989 .getLongitude().getValue(), el2.getLatitude().getValue(), el2 1990 .getLongitude().getValue(), null); 1991 1992 } 1993 1994 /** 1995 * get the tangency point to the circle of the second point and the third 1996 * point as its direction of adding additional points 1997 * 1998 * @param sp1 1999 * outside point 2000 * @param sp2 2001 * the center of the circle 2002 * @param sp3 2003 * _more_ 2004 * @param param 2005 * _more_ 2006 * @param right 2007 * _more_ 2008 * 2009 * @return _more_ 2010 * 2011 * @throws VisADException 2012 * _more_ 2013 */ 2014 public List<StormTrackPoint> getPointToCircleTangencyPointA( 2015 StormTrackPoint sp1, StormTrackPoint sp2, StormTrackPoint sp3, 2016 StormParam param, boolean right) throws VisADException { 2017 2018 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2019 if (sp3 == null) { 2020 return getPointToCircleTangencyPointB(sp1, sp2, param, right); 2021 } 2022 2023 EarthLocation el1 = sp1.getLocation(); 2024 EarthLocation el2 = sp2.getLocation(); 2025 EarthLocation el3 = sp3.getLocation(); 2026 2027 Real rl = sp2.getAttribute(param); 2028 double r = rl.getValue(); 2029 2030 if (Float.isNaN((float) r) || (r == 0.0)) { 2031 return null; 2032 } 2033 2034 double lat1 = el1.getLatitude().getValue(); 2035 double lon1 = el1.getLongitude().getValue(); 2036 2037 double lat2 = el2.getLatitude().getValue(); 2038 double lon2 = el2.getLongitude().getValue(); 2039 2040 double lat3 = el3.getLatitude().getValue(); 2041 double lon3 = el3.getLongitude().getValue(); 2042 2043 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2044 Bearing c = Bearing.calculateBearing(lat2, lon2, lat3, lon3, null); 2045 double dist1 = b.getDistance(); 2046 2047 if (dist1 < r) { // first point is inside the circle 2048 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2049 right)); 2050 return trackPoints; 2051 } 2052 2053 double af = getCircleAngleRange(el1, el2); 2054 double ddt = Math.abs(b.getAngle() - c.getAngle()); 2055 double bt = getCircleTangencyAngle(el1, el2, r); 2056 2057 af = af * 180.0 / Math.PI; 2058 bt = bt * 180.0 / Math.PI; 2059 if (right) { 2060 af = af - 90; 2061 } else { 2062 af = af + 90; 2063 } 2064 // change angle to azimuth 2065 double az = af; 2066 if ((af <= 90) && (af >= 0)) { 2067 az = 90 - af; 2068 } else if ((af > 90) && (af <= 180)) { 2069 az = 360 + (90 - af); 2070 } else if ((af < 0) && (af >= -180)) { 2071 az = 90 - af; 2072 } else if ((af > 180) && (af <= 360)) { 2073 az = 450 - af; 2074 } else if ((af < -180) && (af >= -360)) { 2075 az = -270 - af; 2076 } 2077 if (right) { 2078 az = az + bt; 2079 } else { 2080 az = az - bt; 2081 } 2082 2083 if (ddt > 270) { 2084 ddt = 360 - ddt; 2085 } else if (ddt > 180) { 2086 ddt = ddt - 180; 2087 } else if (ddt > 90) { 2088 ddt = ddt - 90; 2089 } 2090 2091 double dt = bt; 2092 2093 if (right) { 2094 if ((c.getAngle() < b.getAngle()) 2095 && (Math.abs(b.getAngle() - c.getAngle()) < 90)) { 2096 dt = bt + ddt; 2097 } else if ((c.getAngle() > b.getAngle()) 2098 && (Math.abs(b.getAngle() - c.getAngle()) > 180)) { 2099 dt = bt + ddt; 2100 } else { 2101 dt = bt - ddt; 2102 } 2103 } else { 2104 if ((c.getAngle() > b.getAngle()) 2105 && (Math.abs(b.getAngle() - c.getAngle()) < 90)) { 2106 dt = bt + ddt; 2107 } else if ((c.getAngle() < b.getAngle()) 2108 && (Math.abs(b.getAngle() - c.getAngle()) > 180)) { 2109 dt = bt + ddt; 2110 } else { 2111 dt = bt - ddt; 2112 } 2113 2114 } 2115 2116 int n = (int) dt / 5 + 1; 2117 if (n <= 0) { 2118 n = 1; 2119 } 2120 double dtt = dt / n; 2121 if (dtt < 0) { 2122 dtt = 0; 2123 n = 1; 2124 } 2125 for (int i = 0; i < n; i++) { 2126 2127 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2128 // add more points along the circle 2129 2130 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2131 .getLongitude(), 0); 2132 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2133 if (right) { 2134 az = az - dtt; 2135 } else { 2136 az = az + dtt; 2137 } 2138 } 2139 2140 return trackPoints; 2141 } 2142 2143 /** 2144 * get the tangency point to the circle of the second point 2145 * 2146 * @param sp1 2147 * _more_ 2148 * @param sp2 2149 * _more_ 2150 * @param param 2151 * _more_ 2152 * @param right 2153 * _more_ 2154 * 2155 * @return _more_ 2156 * 2157 * @throws VisADException 2158 * _more_ 2159 */ 2160 public List<StormTrackPoint> getPointToCircleTangencyPointB( 2161 StormTrackPoint sp1, StormTrackPoint sp2, StormParam param, 2162 boolean right) throws VisADException { 2163 2164 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2165 2166 if (sp2 == null) { 2167 return null; 2168 } 2169 EarthLocation el1 = sp1.getLocation(); 2170 EarthLocation el2 = sp2.getLocation(); 2171 2172 Real rl = sp2.getAttribute(param); 2173 double r = rl.getValue(); 2174 2175 if (Float.isNaN((float) r) || (r == 0.0)) { 2176 return null; 2177 } 2178 2179 double lat1 = el1.getLatitude().getValue(); 2180 double lon1 = el1.getLongitude().getValue(); 2181 2182 double lat2 = el2.getLatitude().getValue(); 2183 double lon2 = el2.getLongitude().getValue(); 2184 2185 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2186 double dist1 = b.getDistance(); 2187 2188 if (dist1 < r) { // first point is inside the circle 2189 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2190 right)); 2191 return trackPoints; 2192 } 2193 2194 double af = getCircleAngleRange(el1, el2); 2195 double bt = getCircleTangencyAngle(el1, el2, r); 2196 2197 af = af * 180.0 / Math.PI; 2198 bt = bt * 180.0 / Math.PI; 2199 if (right) { 2200 af = af - 90; 2201 } else { 2202 af = af + 90; 2203 } 2204 // change angle to azimuth 2205 double az = af; 2206 if ((af <= 90) && (af >= 0)) { 2207 az = 90 - af; 2208 } else if ((af > 90) && (af <= 180)) { 2209 az = 360 + (90 - af); 2210 } else if ((af < 0) && (af >= -180)) { 2211 az = 90 - af; 2212 } else if ((af > 180) && (af <= 360)) { 2213 az = 450 - af; 2214 } else if ((af < -180) && (af >= -360)) { 2215 az = -270 - af; 2216 } 2217 if (right) { 2218 az = az + bt; 2219 } else { 2220 az = az - bt; 2221 } 2222 2223 double dt = bt; 2224 2225 int n = (int) dt / 5 + 1; 2226 double dtt = dt / n; 2227 for (int i = 0; i < n; i++) { 2228 2229 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2230 // add more points along the circle 2231 2232 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2233 .getLongitude(), 0); 2234 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2235 if (right) { 2236 az = az - dtt; 2237 } else { 2238 az = az + dtt; 2239 } 2240 } 2241 2242 return trackPoints; 2243 } 2244 2245 /** 2246 * get the approximate tangency points of circle to the circle 2247 * 2248 * @param sp1 2249 * outside point 2250 * @param sp2 2251 * the center of the circle 2252 * @param sp3 2253 * _more_ 2254 * @param param 2255 * _more_ 2256 * @param right 2257 * _more_ 2258 * 2259 * @return _more_ 2260 * 2261 * @throws VisADException 2262 * _more_ 2263 */ 2264 public List<StormTrackPoint> getCircleToCircleTangencyPointA( 2265 StormTrackPoint sp1, StormTrackPoint sp2, StormTrackPoint sp3, 2266 StormParam param, boolean right) throws VisADException { 2267 2268 List<StormTrackPoint> trackPoints = new ArrayList<StormTrackPoint>(); 2269 if (sp3 == null) { 2270 if (sp2 == null) { 2271 return null; 2272 } 2273 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2274 right)); 2275 return trackPoints; 2276 } 2277 2278 EarthLocation el1 = sp1.getLocation(); 2279 EarthLocation el2 = sp2.getLocation(); 2280 EarthLocation el3 = sp3.getLocation(); 2281 2282 Real rl = sp2.getAttribute(param); 2283 double r = rl.getValue(); 2284 2285 if (Float.isNaN((float) r) || (r == 0.0)) { 2286 return null; 2287 } 2288 2289 double lat1 = el1.getLatitude().getValue(); 2290 double lon1 = el1.getLongitude().getValue(); 2291 2292 double lat2 = el2.getLatitude().getValue(); 2293 double lon2 = el2.getLongitude().getValue(); 2294 2295 double lat3 = el3.getLatitude().getValue(); 2296 double lon3 = el3.getLongitude().getValue(); 2297 2298 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2299 double dist1 = b.getDistance(); 2300 Bearing c = Bearing.calculateBearing(lat2, lon2, lat3, lon3, null); 2301 double x = Math.abs(c.getAngle() - b.getAngle()); 2302 2303 if (right) { 2304 if ((c.getAngle() > b.getAngle()) || (x > 180)) { 2305 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2306 right)); 2307 return trackPoints; 2308 } 2309 } 2310 2311 if (!right) { 2312 if ((c.getAngle() < b.getAngle()) && (x < 90)) { 2313 trackPoints.add(getPointToCircleTangencyPoint(sp1, sp2, param, 2314 right)); 2315 return trackPoints; 2316 } 2317 } 2318 double af = getCircleAngleRange(el1, el2); 2319 double dt = 0; // = Math.abs(b.getAngle() - c.getAngle()); 2320 2321 if (x > 270) { 2322 dt = 360 - x; 2323 } else if (x > 180) { 2324 dt = x - 180; 2325 } else if (x > 90) { 2326 dt = x - 90; 2327 } else { 2328 dt = x; 2329 } 2330 2331 af = af * 180.0 / Math.PI; 2332 if (right) { 2333 af = af - 90; 2334 } else { 2335 af = af + 90; 2336 } 2337 // change angle to azimuth 2338 double az = af; 2339 if ((af <= 90) && (af >= 0)) { 2340 az = 90 - af; 2341 } else if ((af > 90) && (af <= 180)) { 2342 az = 360 + (90 - af); 2343 } else if ((af < 0) && (af >= -180)) { 2344 az = 90 - af; 2345 } else if ((af > 180) && (af <= 360)) { 2346 az = 450 - af; 2347 } else if ((af < -180) && (af >= -360)) { 2348 az = -270 - af; 2349 } 2350 2351 int n = (int) dt / 5 + 1; 2352 double dtt = dt / n; 2353 2354 for (int i = 0; i < n; i++) { 2355 2356 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, az, r, null); 2357 // add more points along the circle 2358 2359 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2360 .getLongitude(), 0); 2361 trackPoints.add(new StormTrackPoint(el, sp1.getTime(), 0, null)); 2362 if (right) { 2363 az = az - dtt; 2364 } else { 2365 az = az + dtt; 2366 } 2367 } 2368 2369 return trackPoints; 2370 } 2371 2372 /** 2373 * get the 90 degree point to the line of the two points 2374 * 2375 * @param sp1 2376 * _more_ 2377 * @param sp2 2378 * _more_ 2379 * @param param 2380 * _more_ 2381 * @param right 2382 * _more_ 2383 * 2384 * @return _more_ 2385 * 2386 * @throws VisADException 2387 * _more_ 2388 */ 2389 public StormTrackPoint getPointToCircleTangencyPoint(StormTrackPoint sp1, 2390 StormTrackPoint sp2, StormParam param, boolean right) 2391 throws VisADException { 2392 2393 EarthLocation el1 = sp1.getLocation(); 2394 EarthLocation el2 = sp2.getLocation(); 2395 2396 Real rl = sp2.getAttribute(param); 2397 double r = rl.getValue(); 2398 2399 if (Float.isNaN((float) r) || (r == 0.0)) { 2400 return null; 2401 } 2402 2403 double lat2 = el2.getLatitude().getValue(); 2404 double lon2 = el2.getLongitude().getValue(); 2405 2406 double af = getCircleAngleRange(el1, el2); 2407 af = af * 180.0 / Math.PI; 2408 if (right) { 2409 af = af - 90; 2410 } else { 2411 af = af + 90; 2412 } 2413 // change angle to azimuth 2414 if ((af <= 90) && (af >= 0)) { 2415 af = 90 - af; 2416 } else if ((af > 90) && (af <= 180)) { 2417 af = 360 + (90 - af); 2418 } else if ((af < 0) && (af >= -180)) { 2419 af = 90 - af; 2420 } else if ((af > 180) && (af <= 360)) { 2421 af = 450 - af; 2422 } else if ((af < -180) && (af >= -360)) { 2423 af = -270 - af; 2424 } 2425 2426 LatLonPointImpl lp1 = Bearing.findPoint(lat2, lon2, af, r, null); 2427 2428 EarthLocation el = new EarthLocationLite(lp1.getLatitude(), lp1 2429 .getLongitude(), 0); 2430 StormTrackPoint sp = new StormTrackPoint(el, sp1.getTime(), 0, null); 2431 return sp; 2432 } 2433 2434 /** 2435 * _more_ 2436 * 2437 * @param c 2438 * _more_ 2439 * @param d 2440 * _more_ 2441 * @param r 2442 * _more_ 2443 * 2444 * @return _more_ 2445 */ 2446 public double getCircleTangencyAngle(EarthLocation c, EarthLocation d, 2447 double r) { 2448 2449 double lat1 = c.getLatitude().getValue(); 2450 double lon1 = c.getLongitude().getValue(); 2451 2452 double lat2 = d.getLatitude().getValue(); 2453 double lon2 = d.getLongitude().getValue(); 2454 2455 Bearing b = Bearing.calculateBearing(lat1, lon1, lat2, lon2, null); 2456 double dist = b.getDistance(); 2457 double a = Math.asin(r / dist); 2458 2459 return a; 2460 2461 } 2462 2463 /** 2464 * _more_ 2465 * 2466 * @param c 2467 * _more_ 2468 * @param d 2469 * _more_ 2470 * 2471 * @return _more_ 2472 */ 2473 public double getCircleAngleRange(EarthLocation c, EarthLocation d) { 2474 2475 double lat1 = c.getLatitude().getValue(); 2476 double lon1 = c.getLongitude().getValue(); 2477 LatLonPointImpl p1 = new LatLonPointImpl(lat1, lon1); 2478 2479 double lat2 = d.getLatitude().getValue(); 2480 double lon2 = d.getLongitude().getValue(); 2481 LatLonPointImpl p2 = new LatLonPointImpl(lat2, lon2); 2482 2483 LatLonProjection pj1 = new LatLonProjection(); 2484 ProjectionPoint pp1 = pj1.latLonToProj(p1); 2485 LatLonProjection pj2 = new LatLonProjection(); 2486 ProjectionPoint pp2 = pj2.latLonToProj(p2); 2487 double dx = pp2.getX() - pp1.getX(); 2488 double dy = pp2.getY() - pp1.getY(); 2489 2490 double a = Math.atan2(dy, dx); 2491 2492 return a; 2493 } 2494 2495 /** 2496 * _more_ 2497 * 2498 * @param c 2499 * _more_ 2500 * @param angle 2501 * _more_ 2502 * @param r 2503 * _more_ 2504 * @param dt 2505 * _more_ 2506 * 2507 * @return _more_ 2508 * 2509 * @throws VisADException 2510 * _more_ 2511 */ 2512 public StormTrackPoint[] getHalfCircleTrackPoint(EarthLocation c, 2513 double angle, double r, DateTime dt) throws VisADException { 2514 // return 10 track point 2515 int size = 11; 2516 2517 StormTrackPoint[] track = new StormTrackPoint[size]; 2518 2519 double lat0 = c.getLatitude().getValue(); 2520 double lon0 = c.getLongitude().getValue(); 2521 2522 for (int i = 0; i < size; i++) { 2523 double af = (angle + (i + 1) * 15 * Math.PI / 180.0) * 180.0 2524 / Math.PI; 2525 // change angle to azimuth 2526 if ((af <= 90) && (af >= 0)) { 2527 af = 90 - af; 2528 } else if ((af > 90) && (af <= 180)) { 2529 af = 360 + (90 - af); 2530 } else if ((af < 0) && (af >= -180)) { 2531 af = 90 - af; 2532 } else if ((af > 180) && (af <= 360)) { 2533 af = 450 - af; 2534 } else if ((af < -180) && (af >= -360)) { 2535 af = -270 - af; 2536 } 2537 2538 LatLonPointImpl lp = Bearing.findPoint(lat0, lon0, af, r, null); 2539 2540 EarthLocation el = new EarthLocationLite(lp.getLatitude(), lp 2541 .getLongitude(), 0); 2542 StormTrackPoint sp = new StormTrackPoint(el, dt, 0, null); 2543 2544 track[i] = sp; 2545 } 2546 2547 return track; 2548 } 2549 2550 /** 2551 * _more_ 2552 * 2553 * @param c 2554 * _more_ 2555 * @param angle 2556 * _more_ 2557 * @param r 2558 * _more_ 2559 * @param dt 2560 * _more_ 2561 * 2562 * @return _more_ 2563 * 2564 * @throws VisADException 2565 * _more_ 2566 */ 2567 public StormTrackPoint[] getHalfCircleTrackPointOld(EarthLocation c, 2568 double angle, double r, DateTime dt) throws VisADException { 2569 // return 10 track point 2570 int size = 11; 2571 2572 StormTrackPoint[] track = new StormTrackPoint[size]; 2573 FlatEarth e = new FlatEarth(); 2574 ProjectionPointImpl p0 = e.latLonToProj(c.getLatitude().getValue(), c 2575 .getLongitude().getValue()); 2576 2577 for (int i = 0; i < size; i++) { 2578 double af = angle + i * 15 * Math.PI / 180.0; 2579 double x = p0.getX() + r * Math.cos(af); 2580 double y = p0.getY() + r * Math.sin(af); 2581 2582 ProjectionPoint pp = new ProjectionPointImpl(x, y); 2583 LatLonPointImpl lp = new LatLonPointImpl(); 2584 FlatEarth e3 = new FlatEarth(); 2585 LatLonPoint lp11 = e3.projToLatLon(pp, lp); 2586 EarthLocation el = new EarthLocationLite(lp11.getLatitude(), lp11 2587 .getLongitude(), 0); 2588 StormTrackPoint sp = new StormTrackPoint(el, dt, 0, null); 2589 2590 track[i] = sp; 2591 } 2592 2593 return track; 2594 } 2595 2596 }