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