001/*
002 * $Id: PolarOrbitTrackControl.java,v 1.25 2011/03/24 16:06:32 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.control;
032
033import edu.wisc.ssec.mcidasv.data.GroundStations;
034import edu.wisc.ssec.mcidasv.data.PolarOrbitTrackDataSource;
035import edu.wisc.ssec.mcidasv.data.adde.sgp4.AstroConst;
036import edu.wisc.ssec.mcidasv.data.hydra.CurveDrawer;
037import edu.wisc.ssec.mcidasv.util.McVGuiUtils;
038import edu.wisc.ssec.mcidasv.util.McVGuiUtils.Position;
039
040import java.awt.Color;
041import java.awt.Component;
042import java.awt.Container;
043import java.awt.Dimension;
044import java.awt.Insets;
045import java.awt.event.ActionEvent;
046import java.awt.event.ActionListener;
047import java.awt.event.FocusEvent;
048import java.awt.event.FocusListener;
049import java.awt.event.KeyEvent;
050import java.awt.event.KeyListener;
051import java.lang.Math;
052import java.rmi.RemoteException;
053import java.util.ArrayList;
054import java.util.List;
055
056import javax.swing.JComboBox;
057import javax.swing.JComponent;
058import javax.swing.JLabel;
059import javax.swing.JPanel;
060import javax.swing.JSlider;
061import javax.swing.JTextField;
062import javax.swing.event.ChangeEvent;
063import javax.swing.event.ChangeListener;
064
065import org.slf4j.Logger;
066import org.slf4j.LoggerFactory;
067
068import ucar.unidata.data.DataChoice;
069import ucar.unidata.data.DataInstance;
070import ucar.unidata.data.DataSourceImpl;
071import ucar.unidata.idv.control.DisplayControlImpl;
072//import ucar.unidata.idv.control.ZSlider;
073import ucar.unidata.ui.LatLonWidget;
074import ucar.unidata.util.GuiUtils;
075import ucar.unidata.util.GuiUtils.ColorSwatch;
076import ucar.unidata.util.PreferenceList;
077import ucar.unidata.view.geoloc.NavigatedDisplay;
078import ucar.visad.display.CompositeDisplayable;
079import ucar.visad.display.Displayable;
080import ucar.visad.display.TextDisplayable;
081
082import visad.Data;
083import visad.DisplayRealType;
084import visad.Gridded2DSet;
085import visad.MathType;
086import visad.RealTuple;
087import visad.RealTupleType;
088import visad.SampledSet;
089import visad.Text;
090import visad.TextControl;
091import visad.TextControl.Justification;
092import visad.TextType;
093import visad.Tuple;
094import visad.TupleType;
095import visad.UnionSet;
096import visad.VisADException;
097import visad.georef.EarthLocation;
098import visad.georef.EarthLocationTuple;
099import visad.georef.LatLonPoint;
100import visad.georef.LatLonTuple;
101
102/**
103 * {@link ucar.unidata.idv.control.PolarOrbitTrackControl} with some McIDAS-V
104 * specific extensions. Namely parameter sets and support for inverted 
105 * parameter defaults.
106 */
107public class PolarOrbitTrackControl extends DisplayControlImpl {
108
109    private static final Logger logger = LoggerFactory.getLogger(PolarOrbitTrackControl.class);
110
111    /** The spacing used in the grid layout */
112    protected static final int GRID_SPACING = 3;
113
114    /** Used by derived classes when they do a GuiUtils.doLayout */
115    protected static final Insets GRID_INSETS = new Insets(GRID_SPACING,
116                                                    GRID_SPACING,
117                                                    GRID_SPACING,
118                                                    GRID_SPACING);
119    /**
120     * position slider
121     */
122//    private ZSlider levelSlider = null;
123    private double latitude;
124    private double longitude;
125    private double altitude;
126    private JPanel fontSizePanel;
127    private JPanel colorPanel;
128    private JPanel antColorPanel;
129    private JPanel locationPanel;
130    private JPanel latLonAltPanel;
131
132    private int locationIndex = -1;
133    private List stations;
134    private List lats;
135    private List lons;
136    private List alts;
137
138    /** Property name to get the list or urls */
139    public final String PREF_GROUNDSTATIONS = "mcv.groundstations";
140
141    /** Manages the pull down list of urls */
142    private static PreferenceList prefList;
143
144    private JComboBox locationComboBox;
145    private JTextField locationEditor;
146
147    private String station = "";
148    private TextDisplayable groundStationDsp;
149
150    private static final int defaultAntAngle = 5;
151    private int angle = defaultAntAngle;
152
153    /** Input for lat/lon center point */
154    protected LatLonWidget latLonWidget = new LatLonWidget();
155
156    private JTextField latFld;
157    private JTextField lonFld;
158    private JTextField altitudeFld = new JTextField(" ", 5);
159    private JTextField antennaAngle = new JTextField(" 5", 5);
160
161//    private ChangeListener sizeListener;
162    private ActionListener fontSizeChange;
163    private FocusListener fontSizeFocusChange;
164
165    /** Font size control */
166    private static final int SLIDER_MAX = 10;
167    private static final int SLIDER_MIN = 1;
168    private static final int SLIDER_WIDTH = 150;
169    private static final int SLIDER_HEIGHT = 16;
170
171    private JSlider fontSizeSlider;
172    private JTextField fontSizeFld = new JTextField();
173
174    private CompositeDisplayable trackDsp;
175    private CompositeDisplayable circleDsp;
176    private static final TupleType TUPTYPE = makeTupleType();
177
178    private int fontSize;
179    private int defaultSize = 3;
180    private ColorSwatch colorSwatch;
181    private Color color;
182    private Color defaultColor = Color.GREEN;
183    private ColorSwatch antColorSwatch;
184    private Color antColor;
185    private Color defaultAntColor = Color.WHITE;
186    private PolarOrbitTrackDataSource dataSource;
187
188    private CurveDrawer coverageCircle;
189    private double satelliteAltitude = 0.0;
190
191    private double centerAlt = 0.0;
192    private double centerLat = 0.0;
193    private double centerLon = 0.0;
194    private double satZ = 0.0;
195    private NavigatedDisplay navDsp = null;
196
197
198    public PolarOrbitTrackControl() {
199        super();
200        logger.trace("created new tlecontrol={}", Integer.toHexString(hashCode()));
201    }
202
203    @Override public boolean init(DataChoice dataChoice) 
204        throws VisADException, RemoteException 
205    {
206        try {
207            trackDsp = new CompositeDisplayable();
208            circleDsp = new CompositeDisplayable();
209        } catch (Exception e) {
210            System.out.println("problem creating composite displayable e=" + e);
211            return false;
212        }
213        boolean result = super.init((DataChoice)this.getDataChoices().get(0));
214
215        String dispName = getDisplayName();
216        setDisplayName(getLongParamName() + " " + dispName);
217
218        Data data = getData(getDataInstance());
219        createTrackDisplay(data);
220        this.dataSource = getDataSource();
221        try {
222            navDsp = getNavigatedDisplay();
223            EarthLocation earthLoc = navDsp.getCenterPoint();
224            LatLonPoint llp = earthLoc.getLatLonPoint();
225            centerLat = llp.getLatitude().getValue();
226            centerLon = llp.getLongitude().getValue();
227            centerAlt = dataSource.getNearestAltToGroundStation(centerLat, centerLon)/1000.0;
228            EarthLocationTuple elt = new EarthLocationTuple(centerLat, centerLon, centerAlt);
229            double[] xyz = navDsp.getSpatialCoordinates((EarthLocation)elt).getValues();
230            satZ = xyz[2]/5.0;
231            applyTrackPosition();
232        } catch (Exception e) {
233            System.out.println("get display center e=" + e);
234        }
235        return result;
236    }
237
238    private void createTrackDisplay(Data data) {
239        try {
240            this.fontSize = getFontSize();
241            this.color = getColor();
242            List<String> dts = new ArrayList();
243            if (data instanceof Tuple) {
244                Data[] dataArr = ((Tuple)data).getComponents();
245                int npts = dataArr.length;
246                float[][] latlon = new float[2][npts];
247                float fSize = this.fontSize/10.f;
248                for (int i=0; i<npts; i++) {
249                    Tuple t = (Tuple)dataArr[i];
250                    Data[] tupleComps = t.getComponents();
251                    String str = ((Text)tupleComps[0]).getValue();
252                    dts.add(str);
253                    int indx = str.indexOf(" ") + 1;
254                    String subStr = "- " + str.substring(indx, indx+5);
255
256                    TextDisplayable time = new TextDisplayable(TextType.Generic);
257                    time.setJustification(TextControl.Justification.LEFT);
258                    time.setVerticalJustification(TextControl.Justification.CENTER);
259                    time.setColor(this.color);
260                    
261                    addDisplayable(time, FLAG_COLORTABLE);
262                    LatLonTuple llt = (LatLonTuple)tupleComps[1];
263                    double dlat = llt.getLatitude().getValue();
264                    double dlon = llt.getLongitude().getValue();
265                    RealTuple lonLat =
266                        new RealTuple(RealTupleType.SpatialEarth2DTuple,
267                            new double[] { dlon, dlat });
268                    Tuple tup = new Tuple(TUPTYPE,
269                        new Data[] { lonLat, new Text(subStr)});
270                    time.setData(tup);
271                    this.trackDsp.addDisplayable(time);
272                    float lat = (float)dlat;
273                    float lon = (float)dlon;
274                    //System.out.println("    Time=" + subStr + " Lat=" + lat + " Lon=" + lon);
275                    latlon[0][i] = lat;
276                    latlon[1][i] = lon;
277                }
278                setDisplayableTextSize(this.fontSize);
279                Gridded2DSet track = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
280                           latlon, npts);
281                SampledSet[] set = new SampledSet[1];
282                set[0] = track;
283                UnionSet uset = new UnionSet(set);
284                CurveDrawer trackLines = new CurveDrawer(uset);
285                trackLines.setData(uset);
286                this.trackDsp.addDisplayable(trackLines);
287                this.trackDsp.setColor(this.color);
288                this.trackDsp.setLineWidth(2.0f);
289                addDisplayable(this.trackDsp, FLAG_COLORTABLE);
290                trackLines.setDrawingEnabled(false);
291            }
292        } catch (Exception e) {
293            System.out.println("getData e=" + e);
294        }
295        return;
296    }
297
298    private static TupleType makeTupleType() {
299        TupleType t = null;
300        try {
301            t = new TupleType(new MathType[] {RealTupleType.SpatialEarth2DTuple,
302                                              TextType.Generic});
303        } catch (Exception e) {
304            System.out.println("\nPolarOrbitTrackControl.makeTupleType e=" + e);
305        }
306        return t;
307    }
308
309    public JComponent makeColorBox(Color swatchColor) {
310        GuiUtils.ColorSwatch swatch = new GuiUtils.ColorSwatch(swatchColor,
311                                               "Color") {
312            public void userSelectedNewColor(Color c) {
313                try {
314                    getIdv().showWaitCursor();
315                    setColor(c);
316                    setBackground(c);
317                    getIdv().showNormalCursor();
318                } catch (Exception e) {
319                    System.out.println("\nsetColor e=" + e);
320                    setColor(defaultColor);
321                }
322            }
323        };
324        return swatch;
325    }
326
327    public JComponent makeAntColorBox(Color swatchAntColor) {
328        GuiUtils.ColorSwatch swatch = new GuiUtils.ColorSwatch(swatchAntColor,
329                                               "Color") {
330            public void userSelectedNewColor(Color c) {
331                try {
332                    getIdv().showWaitCursor();
333                    setAntColor(c);
334                    setBackground(c);
335                    getIdv().showNormalCursor();
336                } catch (Exception e) {
337                    System.out.println("\nsetAntColor e=" + e);
338                    setAntColor(defaultAntColor);
339                }
340            }
341        };
342        return swatch;
343    }
344
345    /**
346     * Called by doMakeWindow in DisplayControlImpl, which then calls its
347     * doMakeMainButtonPanel(), which makes more buttons.
348     *
349     * @return container of contents
350     */
351    public Container doMakeContents() {
352/*
353        JPanel topPanel = GuiUtils.leftCenter(
354                        new JLabel("Track Z-Position:  "),
355                        makePositionSlider());
356*/
357        this.fontSizeChange =new ActionListener() {
358            public void actionPerformed(ActionEvent ae) {
359                String str = fontSizeFld.getText();
360                int size = new Integer(str).intValue();
361                moveFontSizeSlider(size);
362                setDisplayableTextSize(size);
363            }
364        };
365        this.fontSizeFocusChange = new FocusListener() {
366            public void focusGained(FocusEvent fe) {
367            }
368            public void focusLost(FocusEvent fe) {
369                String str = fontSizeFld.getText();
370                int size = new Integer(str).intValue();
371                moveFontSizeSlider(size);
372                setDisplayableTextSize(size);
373            }
374        };
375
376        this.fontSizeSlider = GuiUtils.makeSlider(SLIDER_MIN, SLIDER_MAX, defaultSize,
377                                     this, "sliderChanged", true);
378        this.fontSizeSlider.setPreferredSize(new Dimension(SLIDER_WIDTH,SLIDER_HEIGHT));
379        this.fontSizeSlider.setMajorTickSpacing(1);
380        this.fontSizeSlider.setSnapToTicks(true);
381        int size = getSizeValue(this.fontSizeSlider);
382        setFontSize(size);
383        this.fontSizeFld = new JTextField(Integer.toString(size),3);
384        this.fontSizeFld.addFocusListener(this.fontSizeFocusChange);
385        this.fontSizeFld.addActionListener(this.fontSizeChange);
386        this.fontSizePanel = GuiUtils.doLayout( new Component[] {
387                 new JLabel("FontSize: "),
388                 this.fontSizeFld,
389                 new JLabel(" "),
390                 this.fontSizeSlider }, 4,
391                 GuiUtils.WT_N, GuiUtils.WT_N);
392
393        Color swatchColor = getColor();
394        colorSwatch = (GuiUtils.ColorSwatch)makeColorBox(swatchColor);
395        colorPanel = GuiUtils.doLayout(new Component[] {
396                           new JLabel("Set Color: "),
397                           colorSwatch }, 2,
398                           GuiUtils.WT_N, GuiUtils.WT_N);
399        JPanel groundStationPanel = makeGroundStationPanel();
400
401        Insets  dfltGridSpacing = new Insets(4, 0, 4, 0);
402        String  dfltLblSpacing  = " ";
403        List allComps = new ArrayList();
404
405//        allComps.add(topPanel);
406//        allComps.add(new JLabel(" "));
407//        allComps.add(new JLabel(" "));
408        allComps.add(fontSizePanel);
409        allComps.add(colorPanel);
410        allComps.add(new JLabel(" "));
411        allComps.add(new JLabel(" "));
412        allComps.add(locationPanel);
413        allComps.add(groundStationPanel);
414        GuiUtils.tmpInsets = GRID_INSETS;
415        JPanel dateTimePanel = GuiUtils.doLayout(allComps, 1, GuiUtils.WT_NY,
416                               GuiUtils.WT_N);
417        return GuiUtils.top(dateTimePanel);
418    }
419
420    private JPanel makeGroundStationPanel() {
421        locationComboBox = new JComboBox();
422        locationComboBox.setEditable(true);
423        locationEditor = (JTextField)locationComboBox.getEditor().getEditorComponent();
424        locationEditor.addKeyListener(new KeyListener() {
425            public void keyPressed(KeyEvent e) {}
426            public void keyReleased(KeyEvent e) {}
427            public void keyTyped(KeyEvent e) {
428                locationIndex = -1;
429            }
430        });
431
432        GroundStations gs = new GroundStations(null);
433        stations = gs.getStations();
434        GuiUtils.setListData(locationComboBox, stations);
435        lats = gs.getLatitudes();
436        lons = gs.getLongitudes();
437        alts = gs.getAltitudes();
438
439        locationComboBox.addActionListener(new ActionListener() {
440            public void actionPerformed(ActionEvent ae) {
441                setStation((String)locationComboBox.getSelectedItem());
442                locationIndex = locationComboBox.getSelectedIndex();
443                //System.out.println("\n locationIndex=" + locationIndex);
444                if (locationIndex < 0) {
445                    //locationIndex = 0;
446                    //locationComboBox.setSelectedIndex(locationIndex);
447                    try {
448                        Object stat = (Object)getStation();
449                        //System.out.println("\n station=" + stat);
450                        if (stations.contains(stat)) {
451                            locationIndex = stations.indexOf(stat);
452                            //System.out.println("\nstat at location: " + locationIndex);
453                            locationComboBox.setSelectedIndex(locationIndex);
454                        }
455/*
456                          else {
457                            locationIndex = 0;
458                            locationComboBox.insertItemAt(stat, locationIndex);
459                            locationComboBox.setSelectedIndex(locationIndex);
460                            stations.add(locationIndex, stat);
461                            locationEditor.setText(getStation());
462                            String zero = "";
463                            lats.add(locationIndex, zero);
464                            lons.add(locationIndex, zero);
465                            alts.add(locationIndex, zero);
466                            latLonWidget.setLatLon(" ", " ");
467                            altitudeFld.setText(zero);
468                            setAltitude();
469                        }
470*/
471                    } catch (Exception e) {
472                    }
473                } else {
474                    try {
475                        String str = (String)(lats.get(locationIndex));
476                        Double d = new Double(str);
477                        double dVal = d.doubleValue();
478                        latLonWidget.setLat(dVal);
479                        setLatitude();
480                        str = (String)(lons.get(locationIndex));
481                        d = new Double(str);
482                        dVal = d.doubleValue();
483                        latLonWidget.setLon(dVal);
484                        setLongitude();
485                        str = (String)(alts.get(locationIndex));
486                        altitudeFld.setText(str);
487                        setAltitude();
488                        int val = getAntennaAngle();
489                        setAntennaAngle(val);
490                    } catch (Exception e) {
491                    }
492                }
493                setSatelliteAltitude(dataSource.getNearestAltToGroundStation(latitude, longitude)/1000.0);
494                redrawCoverageCircle();
495            }
496        });
497
498        latFld = latLonWidget.getLatField();
499        lonFld = latLonWidget.getLonField();
500        String str = (String)(alts.get(0));
501        altitudeFld = new JTextField(str, 5);
502        str = (String)(lats.get(0));
503        Double d;
504        double dVal;
505        if (!str.equals(" ")) {
506            d = new Double(str);
507            dVal = d.doubleValue();
508            latLonWidget.setLat(dVal);
509        }
510        if (!str.equals(" ")) {
511            str = (String)(lons.get(0));
512            d = new Double(str);
513            dVal = d.doubleValue();
514            latLonWidget.setLon(dVal);
515        }
516
517        ActionListener latLonListener = new ActionListener() {
518            public void actionPerformed(ActionEvent ae) {
519                setLatitude();
520                setLongitude();
521                setAltitude();
522                redrawCoverageCircle();
523            }
524        };
525        FocusListener latLonFocusChange = new FocusListener() {
526            public void focusGained(FocusEvent fe) {
527                latFld.setCaretPosition(latFld.getText().length());
528                lonFld.setCaretPosition(lonFld.getText().length());
529            }
530            public void focusLost(FocusEvent fe) {
531                setLatitude();
532                setLongitude();
533                setAltitude();
534                redrawCoverageCircle();
535            }
536        };
537        latFld.addActionListener(latLonListener);
538        lonFld.addActionListener(latLonListener);
539        latFld.addFocusListener(latLonFocusChange);
540        lonFld.addFocusListener(latLonFocusChange);
541        antennaAngle.addActionListener(new ActionListener() {
542            public void actionPerformed(ActionEvent ae) {
543                String str = antennaAngle.getText();
544                Integer iVal = new Integer(str.trim());
545                int val = iVal.intValue();
546                setAntennaAngle(val);
547                redrawCoverageCircle();
548            }
549        });
550        antennaAngle.addFocusListener(new FocusListener() {
551            public void focusGained(FocusEvent fe) {
552            }
553            public void focusLost(FocusEvent fe) {
554                String str = antennaAngle.getText();
555                Integer iVal = new Integer(str.trim());
556                int val = iVal.intValue();
557                setAntennaAngle(val);
558                redrawCoverageCircle();
559            }
560        });
561        locationPanel = GuiUtils.doLayout(new Component[] {
562                           new JLabel("Ground Station: "),
563                           locationComboBox }, 2,
564                           GuiUtils.WT_N, GuiUtils.WT_N);
565        latLonAltPanel = GuiUtils.doLayout(new Component[] {
566                           latLonWidget,
567                           new JLabel(" Altitude: "), altitudeFld,
568                           new JLabel(" Antenna Angle: "), antennaAngle }, 5,
569                           GuiUtils.WT_N, GuiUtils.WT_N);
570
571        Color swatchAntColor = getAntColor();
572        antColorSwatch = (GuiUtils.ColorSwatch)makeAntColorBox(swatchAntColor);
573        antColorPanel = GuiUtils.doLayout(new Component[] {
574                           new JLabel("Set Color: "),
575                           antColorSwatch }, 2,
576                           GuiUtils.WT_N, GuiUtils.WT_N);
577
578        List allComps = new ArrayList();
579        allComps.add(locationPanel);
580        allComps.add(new JLabel(" "));
581        allComps.add(latLonAltPanel);
582        allComps.add(new JLabel(" "));
583        allComps.add(antColorPanel);
584        JPanel retPanel = GuiUtils.doLayout(allComps, 1, GuiUtils.WT_NY,
585                               GuiUtils.WT_N);
586        return GuiUtils.top(retPanel);
587    }
588
589    private void getGroundStation() {
590    }
591/*
592    private JComponent makePositionSlider() {
593        levelSlider = new ZSlider(this.satZ) {
594            public void valueHasBeenSet() {
595                applyTrackPosition();
596            }
597        };
598        return levelSlider.getContents();
599    }
600*/
601    /**
602     * Apply the map (height) position to the displays
603     */
604    private void applyTrackPosition() {
605        try {
606            DisplayRealType dispType = navDsp.getDisplayAltitudeType();
607            trackDsp.setConstantPosition(this.satZ, dispType);
608        } catch (Exception exc) {
609            //System.out.println("Setting track z-position exc=" + exc);
610        }
611    }
612
613    private void redrawCoverageCircle() {
614        try {
615
616            int num = circleDsp.displayableCount();
617            for (int i=0; i<num; i++) {
618                circleDsp.removeDisplayable(0);
619            }
620
621            if (drawCoverageCircle(Math.toRadians(this.latitude), Math.toRadians(this.longitude),
622                       this.satelliteAltitude, getAntColor()) != null) {
623                drawGroundStation();
624                circleDsp.setColor(getAntColor());
625                circleDsp.setLineWidth(1f);
626                circleDsp.addDisplayable(this.coverageCircle);
627                circleDsp.addDisplayable(this.groundStationDsp);
628                addDisplayable(circleDsp, FLAG_COLORTABLE);
629            }
630        } catch (Exception e) {
631            System.out.println("redrawCoverageCircle e=" + e);
632        }
633    }
634
635    private CurveDrawer drawCoverageCircle(double lat, double lon, double satAlt, Color color) {
636        if (!(latLonWidget.isLatLonDefined())) return null;
637
638        double earthRadius = AstroConst.R_Earth/1000.0;
639        satAlt += earthRadius;
640        double pi = Math.PI;
641        double SAC = pi/2.0 + Math.toRadians(getAntennaAngle());
642        double sinASC = earthRadius * Math.sin(SAC) / satAlt;
643        double dist = earthRadius * (Math.PI - SAC - Math.asin(sinASC));
644        double rat = dist/earthRadius;
645
646        int npts = 360;
647        float[][] latlon = new float[2][npts];
648        double cosDist = Math.cos(rat);
649        double sinDist = Math.sin(rat);
650        double sinLat = Math.sin(lat);
651        double cosLat = Math.cos(lat);
652        double sinLon = -Math.sin(lon);
653        double cosLon = Math.cos(lon);
654        for (int i=0; i<npts; i++) {
655            double azimuth = Math.toRadians((double)i);
656            double cosBear = Math.cos(azimuth);
657            double sinBear = Math.sin(azimuth);
658            double z = cosDist * sinLat +
659                       sinDist * cosLat * cosBear;
660            double y = cosLat * cosLon * cosDist +
661                       sinDist * (sinLon*sinBear - sinLat*cosLon*cosBear);
662            double x = cosLat * sinLon * cosDist -
663                       sinDist * (cosLon*sinBear + sinLat*sinLon*cosBear);
664            double r = Math.sqrt(x*x + y*y);
665            double latRad = Math.atan2(z, r);
666            double lonRad = 0.0;
667            if (r > 0.0) lonRad = -Math.atan2(x, y);
668            latlon[0][i] = (float)Math.toDegrees(latRad);
669            latlon[1][i] = (float)Math.toDegrees(lonRad);
670        }
671        try {
672            Gridded2DSet circle = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
673                               latlon, npts);
674            SampledSet[] set = new SampledSet[1];
675            set[0] = circle;
676            UnionSet uset = new UnionSet(set);
677            this.coverageCircle = new CurveDrawer(uset);
678            this.coverageCircle.setLineStyle(1);
679            this.coverageCircle.setColor(getAntColor());
680            this.coverageCircle.setData(uset);
681            this.coverageCircle.setDrawingEnabled(false);
682        } catch (Exception e) {
683            System.out.println("drawCoverageCircle e=" + e);
684            return null;
685        }
686        return this.coverageCircle;
687    }
688
689    private int getSizeValue(JSlider slider) {
690        int value = slider.getValue();
691        if (value < SLIDER_MIN) {
692            value = SLIDER_MIN;
693        } else if (value > SLIDER_MAX) {
694            value = SLIDER_MAX;
695        }
696        return value;
697    }
698
699    public int getFontSize() {
700        if (this.fontSize < 1) this.fontSize = defaultSize;
701        return this.fontSize;
702    }
703
704    public void setFontSizeTextField(int size) {
705        size = setFontSize(size);
706        try {
707            if (this.fontSizeFld != null) {
708                this.fontSizeFld.setText(new Integer(size).toString());
709            }
710        } catch (Exception e) {
711            System.out.println("Exception in PolarOrbitTrackControl.setFontSizeTextField e=" + e);
712        }
713    }
714
715    private void moveFontSizeSlider(int size) {
716        size = setFontSize(size);
717        try {
718            if (this.fontSizeSlider != null) {
719                this.fontSizeSlider.setValue(size);
720            }
721        } catch (Exception e) {
722            System.out.println("Exception in PolarOrbitTrackControl.moveFontSizeSlider e=" + e);
723        }
724    }
725
726    private void setDisplayableTextSize(int size) {
727        size = setFontSize(size);
728        try {
729            float fSize = (float)size/10.0f;
730            int num = this.trackDsp.displayableCount()-1;
731            TextDisplayable textDsp = null;
732            for (int i=num; i>-1; i--) {
733                Displayable dsp = this.trackDsp.getDisplayable(i);
734                if (dsp instanceof TextDisplayable) {
735                    textDsp = (TextDisplayable)dsp;
736                    break;
737                }
738            }
739            if (textDsp != null) {
740                textDsp.setTextSize(fSize);
741            }
742        } catch (Exception e) {
743            System.out.println("Exception in PolarOrbitTrackControl.setDisplayableTextSize e=" + e);
744        }
745    }
746
747    public int setFontSize(int size) {
748        if (size < 1) size = defaultSize;
749        this.fontSize = size;
750        return this.fontSize;
751    }
752
753    public Color getColor() {
754        if (this.color == null) this.color = defaultColor;
755        return this.color;
756    }
757
758    public void setColor(Color color) {
759        if (this.color == null) this.color = defaultColor;
760        try {
761            this.trackDsp.setColor(color);
762            this.color = color;
763        } catch (Exception e) {
764            System.out.println("Exception in PolarOrbitTrackControl.setColor e=" + e);
765        }
766    }
767
768    public Color getAntColor() {
769        if (this.antColor == null) this.antColor = defaultAntColor;
770        return this.antColor;
771    }
772
773    public void setAntColor(Color color) {
774        if (color == null) color = defaultAntColor;
775        try {
776            this.antColor = color;
777            circleDsp.setColor(color);
778        } catch (Exception e) {
779            System.out.println("Exception in PolarOrbitTrackControl.setAntColor e=" + e);
780        }
781    }
782
783    public void setLatitude() {
784        this.latitude = latLonWidget.getLat();
785        //System.out.println("setLatitude: " + this.latitude);
786    }
787
788    public double getLatitude() {
789        return this.latitude;
790    }
791
792    public void setLongitude() {
793        this.longitude = latLonWidget.getLon();
794        //System.out.println("setLongitude: " + this.longitude);
795    }
796
797    public double getLongitude() {
798        return this.longitude;
799    }
800
801    public void setAltitude() {
802        String str = altitudeFld.getText();
803        //System.out.println("\nsetAltitude: str=" + str);
804        try {
805            Double d = new Double(str);
806            this.altitude = d.doubleValue();
807        } catch (Exception e) {
808            this.altitude = 0.0;
809        }
810    }
811
812    public void sliderChanged(int sliderValue) {
813        setFontSizeTextField(sliderValue);
814        setDisplayableTextSize(sliderValue);
815    }
816
817    public void setStation(String val) {
818        this.station = val.trim();
819        //System.out.println("setStation: " + this.station);
820    }
821
822    public String getStation() {
823        return this.station;
824    }
825
826    public void setAntennaAngle(int val) {
827        //System.out.println("\nsetAntennaAngle: val=" + val);
828        String str = " " + val;
829        antennaAngle.setText(str);
830        this.angle = val;
831    }
832
833    public int getAntennaAngle() {
834        //System.out.println("\ngetAntennaAngle:");
835        String str = antennaAngle.getText();
836        this.angle = new Integer(str.trim()).intValue();
837        if (this.angle < defaultAntAngle) this.angle = defaultAntAngle;
838        return this.angle;
839    }
840
841    private void setSatelliteAltitude(double val) {
842        //System.out.println("\nsetSatelliteAltitude: val=" + val);
843        this.satelliteAltitude = val;
844    }
845
846    private void drawGroundStation() {
847        try {
848            String str = "+" + getStation();
849            this.groundStationDsp = new TextDisplayable(TextType.Generic);
850            this.groundStationDsp.setJustification(TextControl.Justification.LEFT);
851            this.groundStationDsp.setVerticalJustification(TextControl.Justification.CENTER);
852            this.groundStationDsp.setTextSize(0.3f);
853            this.groundStationDsp.setColor(getAntColor());
854                    
855            //addDisplayable(this.groundStationDsp, FLAG_COLORTABLE);
856            //circleDsp.addDisplayable(this.groundStationDsp);
857            double dlat = getLatitude();
858            double dlon = getLongitude();
859            RealTuple lonLat =
860                new RealTuple(RealTupleType.SpatialEarth2DTuple,
861                    new double[] { dlon, dlat });
862            Tuple tup = new Tuple(TUPTYPE,
863                new Data[] { lonLat, new Text(str)});
864            this.groundStationDsp.setData(tup);
865        } catch (Exception e) {
866            System.out.println("drawGroundStation e=" + e);
867        }
868    }
869        
870    public PolarOrbitTrackDataSource getDataSource() {
871        DataSourceImpl ds = null;
872        List dataSources = getDataSources();
873        boolean gotit = false;
874        if (!dataSources.isEmpty()) {
875            int nsrc = dataSources.size();
876            for (int i=0; i<nsrc; i++) {
877                ds = (DataSourceImpl)dataSources.get(nsrc-i-1);
878                if (ds instanceof PolarOrbitTrackDataSource) {
879                    gotit = true;
880                    break;
881                }
882            }
883        }
884        if (!gotit) return null;
885        return (PolarOrbitTrackDataSource)ds;
886    }
887}