001    /*
002     * $Id: PolarOrbitTrackControl.java,v 1.42 2012/02/19 17:35:38 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
007     * Space Science and Engineering Center (SSEC)
008     * University of Wisconsin - Madison
009     * 1225 W. Dayton Street, Madison, WI 53706, USA
010     * https://www.ssec.wisc.edu/mcidas
011     * 
012     * All Rights Reserved
013     * 
014     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015     * some McIDAS-V source code is based on IDV and VisAD source code.  
016     * 
017     * McIDAS-V is free software; you can redistribute it and/or modify
018     * it under the terms of the GNU Lesser Public License as published by
019     * the Free Software Foundation; either version 3 of the License, or
020     * (at your option) any later version.
021     * 
022     * McIDAS-V is distributed in the hope that it will be useful,
023     * but WITHOUT ANY WARRANTY; without even the implied warranty of
024     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025     * GNU Lesser Public License for more details.
026     * 
027     * You should have received a copy of the GNU Lesser Public License
028     * along with this program.  If not, see http://www.gnu.org/licenses.
029     */
030    
031    package edu.wisc.ssec.mcidasv.control;
032    
033    import edu.wisc.ssec.mcidasv.McIdasPreferenceManager;
034    
035    import edu.wisc.ssec.mcidasv.data.GroundStations;
036    import edu.wisc.ssec.mcidasv.data.PolarOrbitTrackDataSource;
037    import edu.wisc.ssec.mcidasv.data.adde.sgp4.AstroConst;
038    import edu.wisc.ssec.mcidasv.data.hydra.CurveDrawer;
039    import edu.wisc.ssec.mcidasv.util.XmlUtil;
040    
041    import java.awt.BorderLayout;
042    import java.awt.Color;
043    import java.awt.Component;
044    import java.awt.Container;
045    import java.awt.Dimension;
046    import java.awt.FlowLayout;
047    import java.awt.Insets;
048    import java.awt.event.ActionEvent;
049    import java.awt.event.ActionListener;
050    import java.awt.event.FocusEvent;
051    import java.awt.event.FocusListener;
052    import java.awt.event.KeyEvent;
053    import java.awt.event.KeyListener;
054    import java.lang.Math;
055    import java.rmi.RemoteException;
056    import java.util.ArrayList;
057    import java.util.List;
058    
059    import javax.swing.Box;
060    import javax.swing.BoxLayout;
061    import javax.swing.JButton;
062    import javax.swing.JComboBox;
063    import javax.swing.JComponent;
064    import javax.swing.JLabel;
065    import javax.swing.JPanel;
066    import javax.swing.JSlider;
067    import javax.swing.JTextField;
068    
069    import org.slf4j.Logger;
070    import org.slf4j.LoggerFactory;
071    import org.w3c.dom.Element;
072    import org.w3c.dom.NodeList;
073    
074    import ucar.unidata.data.DataChoice;
075    import ucar.unidata.data.DataInstance;
076    import ucar.unidata.data.DataSourceImpl;
077    import ucar.unidata.idv.control.DisplayControlImpl;
078    import ucar.unidata.ui.LatLonWidget;
079    import ucar.unidata.util.GuiUtils;
080    import ucar.unidata.util.GuiUtils.ColorSwatch;
081    import ucar.unidata.util.IOUtil;
082    import ucar.unidata.view.geoloc.NavigatedDisplay;
083    import ucar.visad.display.CompositeDisplayable;
084    import ucar.visad.display.Displayable;
085    import ucar.visad.display.DisplayableData;
086    import ucar.visad.display.TextDisplayable;
087    
088    import visad.Data;
089    import visad.DisplayRealType;
090    import visad.Gridded2DSet;
091    import visad.MathType;
092    import visad.RealTuple;
093    import visad.RealTupleType;
094    import visad.SampledSet;
095    import visad.Text;
096    import visad.TextControl;
097    import visad.TextControl.Justification;
098    import visad.TextType;
099    import visad.Tuple;
100    import visad.TupleType;
101    import visad.UnionSet;
102    import visad.VisADException;
103    import visad.georef.EarthLocation;
104    import visad.georef.EarthLocationTuple;
105    import visad.georef.LatLonPoint;
106    import visad.georef.LatLonTuple;
107    
108    /**
109     * {@link ucar.unidata.idv.control.PolarOrbitTrackControl} with some McIDAS-V
110     * specific extensions. Namely parameter sets and support for inverted 
111     * parameter defaults.
112     */
113    public class PolarOrbitTrackControl extends DisplayControlImpl {
114    
115        private static final Logger logger = LoggerFactory.getLogger(PolarOrbitTrackControl.class);
116    
117        /** The spacing used in the grid layout */
118        protected static final int GRID_SPACING = 3;
119    
120        /** Used by derived classes when they do a GuiUtils.doLayout */
121        protected static final Insets GRID_INSETS = new Insets(GRID_SPACING,
122                                                        GRID_SPACING,
123                                                        GRID_SPACING,
124                                                        GRID_SPACING);
125        private JLabel satelliteName = new JLabel("           ");;
126        private static final JLabel kmLabel = new JLabel(" km");
127        private JTextField swathWidthFld = new JTextField(" ", 5);
128        private JPanel swathWidthPanel;
129        private JButton saveBtn;
130    
131        /**
132         * position slider
133         */
134        private double latitude;
135        private double longitude;
136        private double altitude;
137        private JPanel fontSizePanel;
138        private JPanel colorPanel;
139        private JPanel antColorPanel;
140        private JPanel locationPanel;
141        private JPanel latLonAltPanel;
142    
143        private int locationIndex = -1;
144        private List stations;
145        private List lats;
146        private List lons;
147        private List alts;
148    
149        /** Property name to get the list or urls */
150        public final String PREF_GROUNDSTATIONS = "mcv.groundstations";
151    
152        private JComboBox locationComboBox;
153        private JTextField locationEditor;
154    
155        private String station = "";
156        private TextDisplayable groundStationDsp;
157    
158        private static final int defaultAntAngle = 5;
159        private int angle = defaultAntAngle;
160    
161        private DataChoice dataChoice;
162    
163        /** Input for lat/lon center point */
164        protected LatLonWidget latLonWidget = new LatLonWidget();
165    
166        private JTextField latFld;
167        private JTextField lonFld;
168        private JTextField altitudeFld = new JTextField(" ", 5);
169        private JTextField antennaAngle = new JTextField(" 5", 5);
170    
171        private ActionListener fontSizeChange;
172        private FocusListener fontSizeFocusChange;
173    
174        /** Font size control */
175        private static final int SLIDER_MAX = 10;
176        private static final int SLIDER_MIN = 1;
177        private static final int SLIDER_WIDTH = 150;
178        private static final int SLIDER_HEIGHT = 16;
179    
180        private JSlider fontSizeSlider;
181        private JTextField fontSizeFld = new JTextField();
182    
183        private DisplayableData trackDisplay;
184        private CompositeDisplayable trackDsp;
185        private CompositeDisplayable swathDsp;
186        private CompositeDisplayable circleDsp;
187        private TupleType tupleType;
188    
189        private int fontSize;
190        private int defaultSize = 3;
191        private ColorSwatch colorSwatch;
192        private Color color;
193        private Color defaultColor = Color.GREEN;
194        private ColorSwatch antColorSwatch;
195        private Color antColor;
196        private Color defaultAntColor = Color.WHITE;
197        private PolarOrbitTrackDataSource dataSource;
198    
199        private CurveDrawer coverageCircle;
200        private double satelliteAltitude = 0.0;
201    
202        private double centerAlt = 0.0;
203        private double centerLat = 0.0;
204        private double centerLon = 0.0;
205        private double satZ = 0.0;
206        private int dTime;
207        private NavigatedDisplay navDsp = null;
208        private TextType textType = null;
209        private double width = 0.0;
210    
211        /** Path to the McV swathwidths.xml */
212        private static final String SWATH_WIDTHS = "/edu/wisc/ssec/mcidasv/resources/swathwidths.xml";
213        private static final String TAG_SATELLITE = "satellite";
214        private static final String ATTR_NAME = "name";
215        private static final String ATTR_WIDTH = "width";
216        private Element root = null;
217    
218        public PolarOrbitTrackControl() {
219            super();
220            logger.trace("created new tlecontrol={}", Integer.toHexString(hashCode()));
221            setAttributeFlags(FLAG_COLORTABLE);
222            try {
223                final String xml =
224                    IOUtil.readContents(SWATH_WIDTHS, McIdasPreferenceManager.class);
225                root = XmlUtil.getRoot(xml);
226            } catch (Exception e) {
227                System.out.println("problem reading swathwidths.xml e=" + e);
228            }
229        }
230    
231        @Override public boolean init(DataChoice dataChoice) 
232            throws VisADException, RemoteException 
233        {
234            this.dataChoice = dataChoice;
235            String choiceName = dataChoice.getName();
236            NodeList nodeList = root.getElementsByTagName(TAG_SATELLITE);
237            int num = nodeList.getLength();
238            if (num > 0) {
239                for (int i=0; i<num; i++) {
240                    Element n =(Element)(nodeList.item(i));
241                    String satName = n.getAttribute(ATTR_NAME);
242                    if (satName.equals(choiceName)) {
243                        String strWidth = n.getAttribute(ATTR_WIDTH);
244                        if (strWidth.isEmpty()) strWidth = "0";
245                        Double dWidth = new Double(strWidth);
246                        this.width = dWidth.doubleValue();
247                        break;
248                    }
249                }
250            }
251            try {
252                trackDsp = new CompositeDisplayable();
253                swathDsp = new CompositeDisplayable();
254                circleDsp = new CompositeDisplayable();
255            } catch (Exception e) {
256                System.out.println("problem creating composite displayable e=" + e);
257                return false;
258            }
259            boolean result = super.init((DataChoice)this.getDataChoices().get(0));
260    
261            this.tupleType = makeTupleType();
262    
263            Data data = getData(getDataInstance());
264            createTrackDisplay(data, true);
265            this.dataSource = getDataSource();
266            try {
267                navDsp = getNavigatedDisplay();
268                EarthLocation earthLoc = navDsp.getCenterPoint();
269                LatLonPoint llp = earthLoc.getLatLonPoint();
270                centerLat = llp.getLatitude().getValue();
271                centerLon = llp.getLongitude().getValue();
272                centerAlt = dataSource.getNearestAltToGroundStation(centerLat, centerLon)/1000.0;
273                EarthLocationTuple elt = new EarthLocationTuple(centerLat, centerLon, centerAlt);
274                double[] xyz = navDsp.getSpatialCoordinates((EarthLocation)elt).getValues();
275                satZ = xyz[2]/5.0;
276                applyTrackPosition();
277            } catch (Exception e) {
278                System.out.println("get display center e=" + e);
279            }
280    
281            return result;
282        }
283    
284        private void createTrackDisplay(Data data, boolean doTrack) {
285            try {
286                this.fontSize = getFontSize();
287                this.color = getColor();
288                List<String> dts = new ArrayList();
289                if (data instanceof Tuple) {
290                    Data[] dataArr = ((Tuple)data).getComponents();
291    
292                    int npts = dataArr.length;
293                    float[][] latlon = new float[2][npts];
294                    float fSize = this.fontSize/10.f;
295    
296                    for (int i=0; i<npts; i++) {
297                        Tuple t = (Tuple)dataArr[i];
298                        Data[] tupleComps = t.getComponents();
299    
300                        LatLonTuple llt = (LatLonTuple)tupleComps[1];
301                        double dlat = llt.getLatitude().getValue();
302                        double dlon = llt.getLongitude().getValue();
303    
304                        if (doTrack) {
305                            String str = ((Text)tupleComps[0]).getValue();
306                            dts.add(str);
307                            int indx = str.indexOf(" ") + 1;
308                            String subStr = "- " + str.substring(indx, indx+5);
309                            TextDisplayable time = new TextDisplayable(getTextType());
310                            time.setJustification(TextControl.Justification.LEFT);
311                            time.setVerticalJustification(TextControl.Justification.CENTER);
312                            time.setColor(this.color);
313     
314                            RealTuple lonLat =
315                                new RealTuple(RealTupleType.SpatialEarth2DTuple,
316                                    new double[] { dlon, dlat });
317                            if ((i % 5) == 0) {
318                                Tuple tup = new Tuple(getTupleType(),
319                                    new Data[] { lonLat, new Text(getTextType(), subStr)});
320                                time.setData(tup);
321                                this.trackDsp.addDisplayable(time);
322                            }
323                        }
324                        float lat = (float)dlat;
325                        float lon = (float)dlon;
326                        latlon[0][i] = lat;
327                        latlon[1][i] = lon;
328                    }
329    
330                    if (doTrack) {
331                        setDisplayableTextSize(this.fontSize);
332                        Gridded2DSet track = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
333                                   latlon, npts);
334                        SampledSet[] set = new SampledSet[1];
335                        set[0] = track;
336                        UnionSet uset = new UnionSet(set);
337                        CurveDrawer trackLines = new CurveDrawer(uset);
338                        trackLines.setData(uset);
339                        this.trackDsp.addDisplayable(trackLines);
340                        trackLines.setDrawingEnabled(false);
341                        this.trackDisplay = trackLines;
342    
343                        this.trackDsp.setColor(this.color);
344                        this.trackDsp.setLineWidth(2.0f);
345    
346                        addDisplayable(this.trackDsp, FLAG_COLORTABLE);
347                    }
348    
349                    float[][][] crv = getSwath(latlon);
350                    int npt = crv[0][0].length;
351                    float[][] leftC = new float[2][npt];
352                    float[][] rightC = new float[2][npt];
353                    for (int i=0; i<npt; i++) {
354                        leftC[0][i] = crv[0][0][i];
355                        leftC[1][i] = crv[0][1][i];
356                        rightC[0][i] = crv[1][0][i];
357                        rightC[1][i] = crv[1][1][i];
358                    }
359                    Gridded2DSet left = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
360                               leftC, npt);
361                    SampledSet[] lSet = new SampledSet[1];
362                    lSet[0] = left;
363                    UnionSet lUSet = new UnionSet(lSet);
364                    CurveDrawer leftLines = new CurveDrawer(lUSet);
365                    leftLines.setLineStyle(1);
366                    leftLines.setData(lUSet);
367                    swathDsp.addDisplayable(leftLines);
368                    leftLines.setDrawingEnabled(false);
369    
370                    Gridded2DSet right = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
371                               rightC, npt);
372                    SampledSet[] rSet = new SampledSet[1];
373                    rSet[0] = right;
374                    UnionSet rUSet = new UnionSet(rSet);
375                    CurveDrawer rightLines = new CurveDrawer(rUSet);
376                    rightLines.setLineStyle(1);
377                    rightLines.setData(rUSet);
378                    this.swathDsp.addDisplayable(rightLines);
379                    rightLines.setDrawingEnabled(false);
380    
381                    this.swathDsp.setColor(this.color);
382                    this.swathDsp.setLineWidth(1.0f);
383                    addDisplayable(this.swathDsp, FLAG_COLORTABLE);
384                }
385            } catch (Exception e) {
386                System.out.println("getData e=" + e);
387            }
388            return;
389        }
390    
391        private float[][][] getSwath(float[][] track) {
392            double earthRadius = AstroConst.R_Earth_mean/1000.0;
393            int npt = track[0].length-1;
394            int l2 = track.length;
395            float[][][] ret = new float[2][2][npt-1];
396            float zero = (float)0.0;
397            try {
398                int indx = 0;
399                for (int i=1; i<npt; i++) {
400                    double latA = Math.toRadians(track[0][i-1]);
401                    double lonA = Math.toRadians(track[1][i-1]);
402    
403                    double latB = Math.toRadians(track[0][i+1]);
404                    double lonB = Math.toRadians(track[1][i+1]);
405    
406                    double diffLon = lonB - lonA;
407                    double bX = Math.cos(latB) * Math.cos(diffLon);
408                    double bY = Math.cos(latB) * Math.sin(diffLon);
409                    double xFac = Math.cos(latA)+bX;
410                    double latC = Math.atan2(Math.sin(latA)+Math.sin(latB), Math.sqrt(xFac*xFac + bY*bY));
411                    double lonC = lonA + Math.atan2(bY, xFac);
412    
413                    double bearing = Math.atan2(Math.sin(diffLon)*Math.cos(latB),
414                                     Math.cos(latA)*Math.sin(latB)-Math.sin(latA)*Math.cos(latB)*Math.cos(diffLon))
415                                     + Math.PI/2.0;
416                    double dist = this.width/2.0;
417                    dist /= earthRadius;
418                    double lat = Math.asin(Math.sin(latC)*Math.cos(dist) +
419                                           Math.cos(latC)*Math.sin(dist)*Math.cos(bearing));
420                    double lon = lonC + Math.atan2(Math.sin(bearing)*Math.sin(dist)*Math.cos(latC),
421                                                   Math.cos(dist)-Math.sin(latC)*Math.sin(lat));
422                    float latD = (float)Math.toDegrees(lat);
423                    float lonD = (float)Math.toDegrees(lon);
424    
425                    bearing += Math.PI;
426                    lat = Math.asin(Math.sin(latC)*Math.cos(dist) +
427                                           Math.cos(latC)*Math.sin(dist)*Math.cos(bearing));
428                    lon = lonC + Math.atan2(Math.sin(bearing)*Math.sin(dist)*Math.cos(latC),
429                                                   Math.cos(dist)-Math.sin(latC)*Math.sin(lat));
430                    float latE = (float)Math.toDegrees(lat);
431                    float lonE = (float)Math.toDegrees(lon);
432    
433                    ret[0][0][indx] = latD;
434                    ret[0][1][indx] = lonD;
435    
436                    ret[1][0][indx] = latE;
437                    ret[1][1][indx] = lonE;
438                    ++indx;
439                }
440            } catch (Exception e) {
441                System.out.println("e=" + e);
442                return null;
443            }
444            return ret;
445        }
446    
447        private TupleType makeTupleType() {
448            TupleType t = null;
449            try {
450                t = new TupleType(new MathType[] {RealTupleType.SpatialEarth2DTuple,
451                                                  getTextType()});
452            } catch (Exception e) {
453                System.out.println("\nPolarOrbitTrackControl.makeTupleType e=" + e);
454                System.out.println("    textType=" + getTextType());
455            }
456            return t;
457        }
458    
459        public JComponent makeColorBox(Color swatchColor) {
460            GuiUtils.ColorSwatch swatch = new GuiUtils.ColorSwatch(swatchColor,
461                                                   "Color") {
462                public void userSelectedNewColor(Color c) {
463                    try {
464                        getIdv().showWaitCursor();
465                        setColor(c);
466                        setBackground(c);
467                        getIdv().showNormalCursor();
468                    } catch (Exception e) {
469                        System.out.println("\nsetColor e=" + e);
470                        setColor(defaultColor);
471                    }
472                }
473            };
474            return swatch;
475        }
476    
477        public JComponent makeAntColorBox(Color swatchAntColor) {
478            GuiUtils.ColorSwatch swatch = new GuiUtils.ColorSwatch(swatchAntColor,
479                                                   "Color") {
480                public void userSelectedNewColor(Color c) {
481                    try {
482                        getIdv().showWaitCursor();
483                        setAntColor(c);
484                        setBackground(c);
485                        getIdv().showNormalCursor();
486                    } catch (Exception e) {
487                        System.out.println("\nsetAntColor e=" + e);
488                        setAntColor(defaultAntColor);
489                    }
490                }
491            };
492            return swatch;
493        }
494    
495        /**
496         * Called by doMakeWindow in DisplayControlImpl, which then calls its
497         * doMakeMainButtonPanel(), which makes more buttons.
498         *
499         * @return container of contents
500         */
501        
502        public Container doMakeContents() {
503            this.fontSizeChange =new ActionListener() {
504                public void actionPerformed(ActionEvent ae) {
505                    String str = fontSizeFld.getText();
506                    int size = new Integer(str).intValue();
507                    moveFontSizeSlider(size);
508                    setDisplayableTextSize(size);
509                }
510            };
511            this.fontSizeFocusChange = new FocusListener() {
512                public void focusGained(FocusEvent fe) {
513                }
514                public void focusLost(FocusEvent fe) {
515                    String str = fontSizeFld.getText();
516                    int size = new Integer(str).intValue();
517                    moveFontSizeSlider(size);
518                    setDisplayableTextSize(size);
519                }
520            };
521    
522            this.fontSizeSlider = GuiUtils.makeSlider(SLIDER_MIN, SLIDER_MAX, defaultSize,
523                                         this, "sliderChanged", true);
524            this.fontSizeSlider.setPreferredSize(new Dimension(SLIDER_WIDTH,SLIDER_HEIGHT));
525            this.fontSizeSlider.setMajorTickSpacing(1);
526            this.fontSizeSlider.setSnapToTicks(true);
527            int size = getSizeValue(this.fontSizeSlider);
528            setFontSize(size);
529            this.fontSizeFld = new JTextField(Integer.toString(size),3);
530            this.fontSizeFld.addFocusListener(this.fontSizeFocusChange);
531            this.fontSizeFld.addActionListener(this.fontSizeChange);
532            
533            fontSizePanel = new JPanel();
534            fontSizePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
535            fontSizePanel.add(new JLabel("Font Size: "));
536            fontSizePanel.add(fontSizeFld);
537            fontSizePanel.add(fontSizeSlider);
538    
539            Color swatchColor = getColor();
540            colorSwatch = (GuiUtils.ColorSwatch)makeColorBox(swatchColor);
541            
542            colorPanel = new JPanel();
543            colorPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
544            colorPanel.add(new JLabel("Set Color: "));
545            colorPanel.add(colorSwatch);
546            
547            JPanel groundStationPanel = makeGroundStationPanel();
548    
549            Insets  dfltGridSpacing = new Insets(4, 0, 4, 0);
550            String  dfltLblSpacing  = " ";
551    
552            swathWidthPanel = makeSwathWidthPanel();
553            
554            JPanel outerPanel = new JPanel(new BorderLayout());
555            
556            JPanel mainPanel = new JPanel();
557            mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
558            mainPanel.add(swathWidthPanel);
559            mainPanel.add(fontSizePanel);
560            mainPanel.add(colorPanel);
561            mainPanel.add(locationPanel);
562            mainPanel.add(groundStationPanel);
563            mainPanel.add(latLonAltPanel);
564            mainPanel.add(antColorPanel);
565            
566            outerPanel.add(mainPanel, BorderLayout.NORTH);
567            
568            return outerPanel;
569        }
570    
571        private JPanel makeGroundStationPanel() {
572            locationComboBox = new JComboBox();
573            locationComboBox.setEditable(true);
574            locationEditor = (JTextField)locationComboBox.getEditor().getEditorComponent();
575            locationEditor.addKeyListener(new KeyListener() {
576                public void keyPressed(KeyEvent e) {}
577                public void keyReleased(KeyEvent e) {}
578                public void keyTyped(KeyEvent e) {
579                    locationIndex = -1;
580                }
581            });
582    
583            GroundStations gs = new GroundStations(null);
584            stations = gs.getStations();
585            GuiUtils.setListData(locationComboBox, stations);
586            lats = gs.getLatitudes();
587            lons = gs.getLongitudes();
588            alts = gs.getAltitudes();
589    
590            locationComboBox.addActionListener(new ActionListener() {
591                public void actionPerformed(ActionEvent ae) {
592                    setStation((String)locationComboBox.getSelectedItem());
593                    locationIndex = locationComboBox.getSelectedIndex();
594                    if (locationIndex < 0) {
595                        try {
596                            Object stat = (Object)getStation();
597                            if (stations.contains(stat)) {
598                                locationIndex = stations.indexOf(stat);
599                                locationComboBox.setSelectedIndex(locationIndex);
600                            }
601                        } catch (Exception e) {
602                        }
603                    } else {
604                        try {
605                            String str = (String)(lats.get(locationIndex));
606                            Double d = new Double(str);
607                            double dVal = d.doubleValue();
608                            latLonWidget.setLat(dVal);
609                            setLatitude();
610                            str = (String)(lons.get(locationIndex));
611                            d = new Double(str);
612                            dVal = d.doubleValue();
613                            latLonWidget.setLon(dVal);
614                            setLongitude();
615                            str = (String)(alts.get(locationIndex));
616                            altitudeFld.setText(str);
617                            setAltitude();
618                            int val = getAntennaAngle();
619                            setAntennaAngle(val);
620                        } catch (Exception e) {
621                        }
622                    }
623                    setSatelliteAltitude(dataSource.getNearestAltToGroundStation(latitude, longitude)/1000.0);
624                    dTime = dataSource.getDTime();
625                    redrawCoverageCircle();
626                }
627            });
628    
629            latFld = latLonWidget.getLatField();
630            lonFld = latLonWidget.getLonField();
631            String str = (String)(alts.get(0));
632            altitudeFld = new JTextField(str, 5);
633            str = (String)(lats.get(0));
634            Double d;
635            double dVal;
636            if (!str.equals(" ")) {
637                d = new Double(str);
638                dVal = d.doubleValue();
639                latLonWidget.setLat(dVal);
640            }
641            if (!str.equals(" ")) {
642                str = (String)(lons.get(0));
643                d = new Double(str);
644                dVal = d.doubleValue();
645                latLonWidget.setLon(dVal);
646            }
647    
648            ActionListener latLonListener = new ActionListener() {
649                public void actionPerformed(ActionEvent ae) {
650                    setLatitude();
651                    setLongitude();
652                    setAltitude();
653                    redrawCoverageCircle();
654                }
655            };
656            FocusListener latLonFocusChange = new FocusListener() {
657                public void focusGained(FocusEvent fe) {
658                    latFld.setCaretPosition(latFld.getText().length());
659                    lonFld.setCaretPosition(lonFld.getText().length());
660                }
661                public void focusLost(FocusEvent fe) {
662                    setLatitude();
663                    setLongitude();
664                    setAltitude();
665                    redrawCoverageCircle();
666                }
667            };
668            latFld.addActionListener(latLonListener);
669            lonFld.addActionListener(latLonListener);
670            latFld.addFocusListener(latLonFocusChange);
671            lonFld.addFocusListener(latLonFocusChange);
672            antennaAngle.addActionListener(new ActionListener() {
673                public void actionPerformed(ActionEvent ae) {
674                    String str = antennaAngle.getText();
675                    Integer iVal = new Integer(str.trim());
676                    int val = iVal.intValue();
677                    setAntennaAngle(val);
678                    redrawCoverageCircle();
679                }
680            });
681            antennaAngle.addFocusListener(new FocusListener() {
682                public void focusGained(FocusEvent fe) {
683                }
684                public void focusLost(FocusEvent fe) {
685                    String str = antennaAngle.getText();
686                    Integer iVal = new Integer(str.trim());
687                    int val = iVal.intValue();
688                    setAntennaAngle(val);
689                    redrawCoverageCircle();
690                }
691            });
692            
693            locationPanel = new JPanel();
694            locationPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
695            locationPanel.add(new JLabel("Ground Station:"));
696            locationPanel.add(locationComboBox);
697            
698            latLonAltPanel = new JPanel();
699            latLonAltPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
700            latLonAltPanel.add(latLonWidget);
701            latLonAltPanel.add(new JLabel("Altitude: "));
702            latLonAltPanel.add(altitudeFld);
703            latLonAltPanel.add(new JLabel("Antenna Angle: "));
704            latLonAltPanel.add(antennaAngle);
705    
706            Color swatchAntColor = getAntColor();
707            antColorSwatch = (GuiUtils.ColorSwatch)makeAntColorBox(swatchAntColor);
708            
709            antColorPanel = new JPanel();
710            antColorPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
711            antColorPanel.add(new JLabel("Set Color: "));
712            antColorPanel.add(antColorSwatch);
713            
714            return latLonAltPanel;
715        }
716    
717        private JPanel makeSwathWidthPanel() {
718            if (this.dataChoice != null)
719                satelliteName = new JLabel(this.dataChoice.getName());
720            Double dWidth = new Double(this.width);
721            swathWidthFld = new JTextField(dWidth.toString(), 6);
722            swathWidthFld.setHorizontalAlignment(JTextField.CENTER);
723            swathWidthFld.addActionListener(new ActionListener() {
724                public void actionPerformed(ActionEvent ae) {
725                    changeSwathWidth();
726                }
727            });
728            swathWidthFld.addFocusListener(new FocusListener() {
729                public void focusGained(FocusEvent fe) {
730                }
731                public void focusLost(FocusEvent fe) {
732                    changeSwathWidth();
733                }
734            });
735    
736            saveBtn = new JButton("Save");
737    
738            JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
739            jp.add(new JLabel("Satellite: "));
740            jp.add(satelliteName);
741            jp.add(Box.createHorizontalStrut(5));
742            jp.add(new JLabel("Swath Width: "));
743            jp.add(swathWidthFld);
744            jp.add(kmLabel);
745            jp.add(Box.createHorizontalStrut(5));
746            jp.add(saveBtn);
747            
748            return jp;
749        }
750    
751        private void changeSwathWidth() {
752            String str = swathWidthFld.getText();
753            Double dVal = new Double(str.trim());
754            double val = dVal.doubleValue();
755            setSwathWidth(val);
756            try {
757                removeDisplayable(swathDsp);
758                Data data = getData(getDataInstance());
759                swathDsp = new CompositeDisplayable();
760                createTrackDisplay(data, false);
761            } catch (Exception e) {
762                System.out.println("\nproblem redrawing swaths e=" + e);
763            }
764        }
765    
766        private void setSwathWidth(double val) {
767            this.width = val;
768        }
769    
770        /**
771         * Apply the map (height) position to the displays
772         */
773        
774        private void applyTrackPosition() {
775            try {
776                DisplayRealType dispType = navDsp.getDisplayAltitudeType();
777                trackDsp.setConstantPosition(this.satZ, dispType);
778            } catch (Exception exc) {
779                System.out.println("Setting track z-position exc=" + exc);
780            }
781        }
782    
783        private void redrawCoverageCircle() {
784            try {
785    
786                int num = circleDsp.displayableCount();
787                for (int i=0; i<num; i++) {
788                    circleDsp.removeDisplayable(0);
789                }
790    
791                if (drawCoverageCircle(Math.toRadians(this.latitude), Math.toRadians(this.longitude),
792                           this.satelliteAltitude, getAntColor()) != null) {
793                    drawGroundStation();
794                    circleDsp.setColor(getAntColor());
795                    circleDsp.setLineWidth(1f);
796                    circleDsp.addDisplayable(this.coverageCircle);
797                    circleDsp.addDisplayable(this.groundStationDsp);
798                    addDisplayable(circleDsp, FLAG_COLORTABLE);
799                }
800            } catch (Exception e) {
801                System.out.println("redrawCoverageCircle e=" + e);
802            }
803        }
804    
805        private CurveDrawer drawCoverageCircle(double lat, double lon, double satAlt, Color color) {
806            if (!(latLonWidget.isLatLonDefined())) return null;
807            /* mean earthRadius in km */
808            double earthRadius = AstroConst.R_Earth_mean/1000.0;
809            satAlt += earthRadius;
810            double pi = Math.PI;
811            double SAC = pi/2.0 + Math.toRadians(getAntennaAngle());
812            double sinASC = earthRadius * Math.sin(SAC) / satAlt;
813            double dist = earthRadius * (Math.PI - SAC - Math.asin(sinASC));
814            double rat = dist/earthRadius;
815    
816            int npts = 360;
817            float[][] latlon = new float[2][npts];
818            double cosDist = Math.cos(rat);
819            double sinDist = Math.sin(rat);
820            double sinLat = Math.sin(lat);
821            double cosLat = Math.cos(lat);
822            double sinLon = -Math.sin(lon);
823            double cosLon = Math.cos(lon);
824            for (int i=0; i<npts; i++) {
825                double azimuth = Math.toRadians((double)i);
826                double cosBear = Math.cos(azimuth);
827                double sinBear = Math.sin(azimuth);
828                double z = cosDist * sinLat +
829                           sinDist * cosLat * cosBear;
830                double y = cosLat * cosLon * cosDist +
831                           sinDist * (sinLon*sinBear - sinLat*cosLon*cosBear);
832                double x = cosLat * sinLon * cosDist -
833                           sinDist * (cosLon*sinBear + sinLat*sinLon*cosBear);
834                double r = Math.sqrt(x*x + y*y);
835                double latRad = Math.atan2(z, r);
836                double lonRad = 0.0;
837                if (r > 0.0) lonRad = -Math.atan2(x, y);
838                latlon[0][i] = (float)Math.toDegrees(latRad);
839                latlon[1][i] = (float)Math.toDegrees(lonRad);
840            }
841            try {
842                Gridded2DSet circle = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple,
843                                   latlon, npts);
844                SampledSet[] set = new SampledSet[1];
845                set[0] = circle;
846                UnionSet uset = new UnionSet(set);
847                this.coverageCircle = new CurveDrawer(uset);
848                this.coverageCircle.setLineStyle(1);
849                this.coverageCircle.setColor(getAntColor());
850                this.coverageCircle.setData(uset);
851                this.coverageCircle.setDrawingEnabled(false);
852            } catch (Exception e) {
853                System.out.println("drawCoverageCircle e=" + e);
854                return null;
855            }
856            return this.coverageCircle;
857        }
858    
859        private int getSizeValue(JSlider slider) {
860            int value = slider.getValue();
861            if (value < SLIDER_MIN) {
862                value = SLIDER_MIN;
863            } else if (value > SLIDER_MAX) {
864                value = SLIDER_MAX;
865            }
866            return value;
867        }
868    
869        public int getFontSize() {
870            if (this.fontSize < 1) this.fontSize = defaultSize;
871            return this.fontSize;
872        }
873    
874        public void setFontSizeTextField(int size) {
875            size = setFontSize(size);
876            try {
877                if (this.fontSizeFld != null) {
878                    this.fontSizeFld.setText(new Integer(size).toString());
879                }
880            } catch (Exception e) {
881                System.out.println("Exception in PolarOrbitTrackControl.setFontSizeTextField e=" + e);
882            }
883        }
884    
885        private void moveFontSizeSlider(int size) {
886            size = setFontSize(size);
887            try {
888                if (this.fontSizeSlider != null) {
889                    this.fontSizeSlider.setValue(size);
890                }
891            } catch (Exception e) {
892                System.out.println("Exception in PolarOrbitTrackControl.moveFontSizeSlider e=" + e);
893            }
894        }
895    
896        private void setDisplayableTextSize(int size) {
897            size = setFontSize(size);
898            try {
899                float fSize = (float)size/10.0f;
900                int num = this.trackDsp.displayableCount()-1;
901                TextDisplayable textDsp = null;
902                for (int i=num; i>-1; i--) {
903                    Displayable dsp = this.trackDsp.getDisplayable(i);
904                    if (dsp instanceof TextDisplayable) {
905                        textDsp = (TextDisplayable)dsp;
906                        break;
907                    }
908                }
909                if (textDsp != null) {
910                    textDsp.setTextSize(fSize);
911                }
912            } catch (Exception e) {
913                System.out.println("Exception in PolarOrbitTrackControl.setDisplayableTextSize e=" + e);
914            }
915        }
916    
917        public int setFontSize(int size) {
918            if (size < 1) size = defaultSize;
919            this.fontSize = size;
920            return this.fontSize;
921        }
922    
923        public Color getColor() {
924            if (this.color == null) this.color = defaultColor;
925            return this.color;
926        }
927    
928        public void setColor(Color color) {
929            if (this.color == null) this.color = defaultColor;
930            try {
931                this.trackDsp.setColor(color);
932                this.swathDsp.setColor(color);
933                this.color = color;
934            } catch (Exception e) {
935                System.out.println("Exception in PolarOrbitTrackControl.setColor e=" + e);
936            }
937        }
938    
939        public Color getAntColor() {
940            if (this.antColor == null) this.antColor = defaultAntColor;
941            return this.antColor;
942        }
943    
944        public void setAntColor(Color color) {
945            if (color == null) color = defaultAntColor;
946            try {
947                this.antColor = color;
948                circleDsp.setColor(color);
949            } catch (Exception e) {
950                System.out.println("Exception in PolarOrbitTrackControl.setAntColor e=" + e);
951            }
952        }
953    
954        public void setLatitude() {
955            this.latitude = latLonWidget.getLat();
956        }
957    
958        public double getLatitude() {
959            return this.latitude;
960        }
961    
962        public void setLongitude() {
963            this.longitude = latLonWidget.getLon();
964        }
965    
966        public double getLongitude() {
967            return this.longitude;
968        }
969    
970        public void setAltitude() {
971            String str = altitudeFld.getText();
972            try {
973                Double d = new Double(str);
974                this.altitude = d.doubleValue();
975            } catch (Exception e) {
976                this.altitude = 0.0;
977            }
978        }
979    
980        public void sliderChanged(int sliderValue) {
981            setFontSizeTextField(sliderValue);
982            setDisplayableTextSize(sliderValue);
983        }
984    
985        public void setStation(String val) {
986            this.station = val.trim();
987        }
988    
989        public String getStation() {
990            return this.station;
991        }
992    
993        public void setAntennaAngle(int val) {
994            String str = " " + val;
995            antennaAngle.setText(str);
996            this.angle = val;
997        }
998    
999        public int getAntennaAngle() {
1000            String str = antennaAngle.getText();
1001            this.angle = new Integer(str.trim()).intValue();
1002            if (this.angle < defaultAntAngle) this.angle = defaultAntAngle;
1003            return this.angle;
1004        }
1005    
1006        private void setSatelliteAltitude(double val) {
1007            this.satelliteAltitude = val;
1008        }
1009    
1010        private void drawGroundStation() {
1011            try {
1012                String str = "+" + getStation();
1013                this.groundStationDsp = new TextDisplayable(getTextType());
1014                this.groundStationDsp.setJustification(TextControl.Justification.LEFT);
1015                this.groundStationDsp.setVerticalJustification(TextControl.Justification.CENTER);
1016                float tSize = (float)getFontSize()/10.0f;
1017                this.groundStationDsp.setTextSize(tSize);
1018                this.groundStationDsp.setColor(getAntColor());
1019                        
1020                double dlat = getLatitude();
1021                double dlon = getLongitude();
1022                RealTuple lonLat =
1023                    new RealTuple(RealTupleType.SpatialEarth2DTuple,
1024                        new double[] { dlon, dlat });
1025                Tuple tup = new Tuple(getTupleType(),
1026                    new Data[] { lonLat, new Text(getTextType(), str)});
1027                this.groundStationDsp.setData(tup);
1028            } catch (Exception e) {
1029                System.out.println("drawGroundStation e=" + e);
1030            }
1031        }
1032            
1033        public PolarOrbitTrackDataSource getDataSource() {
1034            DataSourceImpl ds = null;
1035            List dataSources = getDataSources();
1036            boolean gotit = false;
1037            if (!dataSources.isEmpty()) {
1038                int nsrc = dataSources.size();
1039                for (int i=0; i<nsrc; i++) {
1040                    ds = (DataSourceImpl)dataSources.get(nsrc-i-1);
1041                    if (ds instanceof PolarOrbitTrackDataSource) {
1042                        gotit = true;
1043                        break;
1044                    }
1045                }
1046            }
1047            if (!gotit) return null;
1048            return (PolarOrbitTrackDataSource)ds;
1049        }
1050    
1051        private TextType getTextType() {
1052            if (this.textType == null) {
1053                String dispName = getDisplayName();
1054                setDisplayName(getLongParamName() + " " + dispName);
1055                try {
1056                    String longName = getLongParamName().replaceAll(" ", "");
1057                    this.textType = new TextType(longName);
1058                } catch (Exception e) {
1059                    this.textType = TextType.Generic;
1060                }
1061            }
1062            return this.textType;
1063        }
1064    
1065        private TupleType getTupleType() {
1066            return this.tupleType;
1067        }
1068    }