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 }