001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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 029package edu.wisc.ssec.mcidasv.control; 030 031import edu.wisc.ssec.mcidasv.McIdasPreferenceManager; 032 033import edu.wisc.ssec.mcidasv.data.GroundStations; 034import edu.wisc.ssec.mcidasv.data.PolarOrbitTrackDataSource; 035import edu.wisc.ssec.mcidasv.data.TimeRangeSelection; 036import edu.wisc.ssec.mcidasv.data.hydra.CurveDrawer; 037import edu.wisc.ssec.mcidasv.ui.ColorSwatchComponent; 038import edu.wisc.ssec.mcidasv.util.XmlUtil; 039 040import java.awt.Color; 041import java.awt.Container; 042import java.awt.Dimension; 043import java.awt.FlowLayout; 044import java.awt.Font; 045import java.awt.event.ActionEvent; 046import java.awt.event.ItemEvent; 047import java.lang.Math; 048import java.rmi.RemoteException; 049import java.util.ArrayList; 050import java.util.HashMap; 051import java.util.List; 052import java.util.TreeSet; 053 054import javax.swing.BorderFactory; 055import javax.swing.Box; 056import javax.swing.BoxLayout; 057import javax.swing.JButton; 058import javax.swing.JCheckBox; 059import javax.swing.JComboBox; 060import javax.swing.JLabel; 061import javax.swing.JOptionPane; 062import javax.swing.JPanel; 063import javax.swing.JSpinner; 064import javax.swing.JTextField; 065import javax.swing.SpinnerNumberModel; 066 067import name.gano.astro.AstroConst; 068import net.miginfocom.swing.MigLayout; 069 070import org.slf4j.Logger; 071import org.slf4j.LoggerFactory; 072import org.w3c.dom.Element; 073import org.w3c.dom.NodeList; 074 075import ucar.unidata.data.DataChoice; 076import ucar.unidata.data.DataSourceImpl; 077import ucar.unidata.idv.control.DisplayControlImpl; 078import ucar.unidata.ui.FontSelector; 079import ucar.unidata.util.GuiUtils; 080import ucar.unidata.util.IOUtil; 081import ucar.unidata.view.geoloc.NavigatedDisplay; 082import ucar.visad.UtcDate; 083import ucar.visad.Util; 084import ucar.visad.display.CompositeDisplayable; 085import ucar.visad.display.TextDisplayable; 086 087import visad.Data; 088import visad.DisplayRealType; 089import visad.Gridded2DSet; 090import visad.MathType; 091import visad.RealTuple; 092import visad.RealTupleType; 093import visad.SampledSet; 094import visad.Text; 095import visad.TextControl; 096import visad.TextType; 097import visad.Tuple; 098import visad.TupleType; 099import visad.UnionSet; 100import visad.VisADException; 101import visad.georef.EarthLocationTuple; 102import visad.georef.LatLonTuple; 103 104/** 105 * {@link ucar.unidata.idv.control.DisplayControlImpl} with some McIDAS-V 106 * specific extensions. Namely parameter sets and support for inverted 107 * parameter defaults. 108 */ 109 110public class PolarOrbitTrackControl extends DisplayControlImpl { 111 112 private static final Logger logger = LoggerFactory.getLogger(PolarOrbitTrackControl.class); 113 114 private JLabel satelliteName = new JLabel(""); 115 private static final JLabel kmLabel = new JLabel("km"); 116 private JTextField swathWidthFld = null; 117 private JPanel swathWidthPanel; 118 119 // Ground Station hashmap 120 private HashMap<String, EarthLocationTuple> stationMap = null; 121 122 private double latitude; 123 private double longitude; 124 private JPanel fontSizePanel; 125 private JPanel colorPanel; 126 private JPanel antColorPanel; 127 private JPanel locationPanel; 128 private JPanel latLonAltPanel; 129 130 /** Property name to get the list or URLs */ 131 public final String PREF_GROUNDSTATIONS = "mcv.groundstations"; 132 133 private JComboBox locationComboBox; 134 private JComboBox jcbStationsPlotted; 135 String [] lineStyles = new String[] { "_____", "_ _ _", ".....", "_._._" }; 136 private JComboBox jcbTrackLineStyle = new JComboBox(lineStyles); 137 private JComboBox jcbEdgeLineStyle = new JComboBox(lineStyles); 138 private JComboBox jcbStationLineStyle = new JComboBox(lineStyles); 139 private JCheckBox jcbLabels; 140 private JCheckBox jcbSwathEdges; 141 142 // names to distinguish checkbox event sources 143 private static final String CHECKBOX_LABELS = "CHECKBOX_LABELS"; 144 private static final String CHECKBOX_SWATH_EDGES = "CHECKBOX_SWATH_EDGES"; 145 146 private String station = ""; 147 148 private static final int SWATH_WIDTH_MIN = 1; 149 // TJJ Feb 2014 - need to determine max of any sensor. VIIRS is over 3000 km 150 private static final int SWATH_WIDTH_MAX = 4000; 151 private static final int DEFAULT_ANTENNA_ANGLE = 5; 152 private static final int MAX_ANTENNA_ANGLE = 90; 153 private int curAngle = DEFAULT_ANTENNA_ANGLE; 154 private static final double LABEL_DISTANCE_THRESHOLD = 5.0d; 155 156 private DataChoice dataChoice; 157 158 private JLabel latLabel; 159 private JLabel lonLabel; 160 private JLabel altLabel; 161 private JTextField antennaAngle = new JTextField("" + DEFAULT_ANTENNA_ANGLE, DEFAULT_ANTENNA_ANGLE); 162 163 // custom ground station UI components 164 JTextField customLat = null; 165 JTextField customLon = null; 166 JTextField customLab = null; 167 168 /** the font selectors, Orbit Track (ot) and Ground Station (gs) */ 169 private FontSelector otFontSelector; 170 private Font otCurFont = FontSelector.DEFAULT_FONT; 171 private FontSelector gsFontSelector; 172 private Font gsCurFont = FontSelector.DEFAULT_FONT; 173 174 // line width combo boxes, GS: Ground Station, SC: Swath Center, SE: Swath Edge 175 private JComboBox jcbGSLineWidth; 176 private JComboBox jcbSCLineWidth; 177 private JComboBox jcbSELineWidth; 178 private JSpinner js = null; 179 180 private CompositeDisplayable trackDsp; 181 private CompositeDisplayable timeLabelDsp; 182 private CompositeDisplayable stationLabelDsp; 183 private CompositeDisplayable swathEdgeDsp; 184 private CompositeDisplayable circleDsp; 185 186 // time label variables 187 private static final int DEFAULT_LABEL_INTERVAL = 5; 188 private int labelInterval = DEFAULT_LABEL_INTERVAL; 189 190 private ColorSwatchComponent colorSwatch; 191 192 private static final Color DEFAULT_COLOR = Color.GREEN; 193 private Color curSwathColor = DEFAULT_COLOR; 194 private Color prvSwathColor = null; 195 196 private ColorSwatchComponent antColorSwatch; 197 private Color antColor; 198 private Color defaultAntColor = Color.MAGENTA; 199 private PolarOrbitTrackDataSource dataSource; 200 201 private double satelliteAltitude = 0.0; 202 203 private double trackZ = 0.0d; 204 private double gsZ = 0.0d; 205 private NavigatedDisplay navDsp = null; 206 private TextType otTextType = null; 207 private TextType gsTextType = null; 208 private double curWidth = 0.0d; 209 private double prvWidth = 0.0d; 210 // TODO: event handler for ground station controls needs conditional handling checks 211 // e.g., utilize the unused variable below 212 private int prvStationLineStyle = -1; 213 private int prvTrackLineStyle = -1; 214 private int prvEdgeLineStyle = -1; 215 private int curTrackLineStyle = -1; 216 private int curEdgeLineStyle = -1; 217 private static final float FONT_SCALE_FACTOR = 12.0f; 218 219 // line width for drawing track center and swath edges 220 private float prvSwathCenterWidth = 2.0f; 221 private float curSwathCenterWidth = 2.0f; 222 private float prvSwathEdgeWidth = 1.0f; 223 private float curSwathEdgeWidth = 1.0f; 224 225 /** Path to the McV swathwidths.xml */ 226 private static final String SWATH_WIDTHS = "/edu/wisc/ssec/mcidasv/resources/swathwidths.xml"; 227 private static final String TAG_SATELLITE = "satellite"; 228 private static final String ATTR_NAME = "name"; 229 private static final String ATTR_WIDTH = "width"; 230 231 private static final String SWATH_MODS = "OrbitTrack"; 232 private static final String STATION_MODS = "GroundStation"; 233 private static final String STATION_ADD = "AddStation"; 234 private static final String STATION_REM = "RemStation"; 235 private static final String CUSTOM_ADD = "AddCustom"; 236 237 private Element root = null; 238 239 public PolarOrbitTrackControl() { 240 super(); 241 logger.trace("created new PolarOrbitTrackControl..."); 242 setAttributeFlags(FLAG_COLORTABLE); 243 try { 244 final String xml = 245 IOUtil.readContents(SWATH_WIDTHS, McIdasPreferenceManager.class); 246 root = XmlUtil.getRoot(xml); 247 } catch (Exception e) { 248 logger.error("problem reading swathwidths.xml"); 249 e.printStackTrace(); 250 } 251 } 252 253 /** 254 * Deal with action events 255 * 256 * @param ae the ActionEvent fired when the user applies changes 257 */ 258 259 public void actionPerformed(ActionEvent ae) { 260 261 // user trying to add a custom ground station 262 if (CUSTOM_ADD.equals(ae.getActionCommand())) { 263 logger.debug("Custom Ground Station..."); 264 String labStr = customLab.getText(); 265 if ((labStr == null) || (labStr.isEmpty())) { 266 JOptionPane.showMessageDialog(null, 267 "Please provide a label for the custom ground station."); 268 return; 269 } 270 float fLat; 271 float fLon; 272 try { 273 fLat = Float.parseFloat(customLat.getText()); 274 fLon = Float.parseFloat(customLon.getText()); 275 } catch (NumberFormatException nfe) { 276 JOptionPane.showMessageDialog(null, 277 "Latitude and Longitude must be floating point numbers, please correct."); 278 return; 279 } 280 if ((fLat < -90) || (fLat > 90)) { 281 JOptionPane.showMessageDialog(null, 282 "Latitude is out of valid range: " + fLat); 283 return; 284 } 285 if ((fLon < -180) || (fLon > 180)) { 286 JOptionPane.showMessageDialog(null, 287 "Longitude is out of valid range: " + fLon); 288 return; 289 } 290 // last check, is this label already used? 291 int numPlotted = jcbStationsPlotted.getItemCount(); 292 for (int i = 0; i < numPlotted; i++) { 293 String s = (String) jcbStationsPlotted.getItemAt(i); 294 if ((s != null) && s.equals(station)) { 295 JOptionPane.showMessageDialog(null, 296 "A station with this label has already been plotted: " + s); 297 return; 298 } 299 } 300 // if we made it this far, fields are valid, we can create a custom ground station 301 // create new earth location, add it to stations plotted, set index, 302 jcbStationsPlotted.addItem(labStr); 303 jcbStationsPlotted.setSelectedItem(labStr); 304 // make an Earth location 305 double dAlt = dataSource.getNearestAltToGroundStation(latitude, longitude) / 1000.0; 306 EarthLocationTuple elt = null; 307 try { 308 elt = new EarthLocationTuple(fLat, fLon, dAlt); 309 } catch (RemoteException e) { 310 e.printStackTrace(); 311 } catch (VisADException e) { 312 e.printStackTrace(); 313 } 314 stationMap.put(labStr, elt); 315 plotCoverageCircles(); 316 updateDisplayList(); 317 return; 318 } 319 320 // user trying to add a new ground station to those plotted on display 321 if (STATION_ADD.equals(ae.getActionCommand())) { 322 logger.debug("Add Station..."); 323 String station = (String) locationComboBox.getSelectedItem(); 324 boolean alreadyPlotted = false; 325 int numPlotted = jcbStationsPlotted.getItemCount(); 326 for (int i = 0; i < numPlotted; i++) { 327 String s = (String) jcbStationsPlotted.getItemAt(i); 328 if ((s != null) && s.equals(station)) { 329 alreadyPlotted = true; 330 break; 331 } 332 } 333 if (alreadyPlotted) { 334 JOptionPane.showMessageDialog(null, 335 "Station already plotted on display: " + station); 336 } else { 337 jcbStationsPlotted.addItem(station); 338 jcbStationsPlotted.setSelectedItem(station); 339 plotCoverageCircles(); 340 } 341 updateDisplayList(); 342 return; 343 } 344 345 // user removing a ground station from the display 346 if (STATION_REM.equals(ae.getActionCommand())) { 347 logger.debug("Rem Station..."); 348 String station = (String) jcbStationsPlotted.getSelectedItem(); 349 if (station == null) { 350 JOptionPane.showMessageDialog(null, 351 "Nothing to remove"); 352 } else { 353 jcbStationsPlotted.removeItem(station); 354 plotCoverageCircles(); 355 } 356 updateDisplayList(); 357 return; 358 } 359 360 // swath-related changes 361 if (SWATH_MODS.equals(ae.getActionCommand())) { 362 logger.debug("Apply Swath Mods..."); 363 364 boolean fontChanged = true; 365 boolean swathChanged = false; 366 curSwathCenterWidth = jcbSCLineWidth.getSelectedIndex() + 1; 367 curSwathEdgeWidth = jcbSELineWidth.getSelectedIndex() + 1; 368 if (curSwathCenterWidth != prvSwathCenterWidth) swathChanged = true; 369 if (curSwathEdgeWidth != prvSwathEdgeWidth) swathChanged = true; 370 371 curTrackLineStyle = jcbTrackLineStyle.getSelectedIndex(); 372 if (curTrackLineStyle != prvTrackLineStyle) swathChanged = true; 373 curEdgeLineStyle = jcbEdgeLineStyle.getSelectedIndex(); 374 if (curEdgeLineStyle != prvEdgeLineStyle) swathChanged = true; 375 376 curSwathColor = colorSwatch.getColor(); 377 if (! curSwathColor.equals(prvSwathColor)) swathChanged = true; 378 379 int newSwathWidth = validateSwathWidthField(); 380 if (newSwathWidth > 0) { 381 curWidth = newSwathWidth; 382 if (curWidth != prvWidth) swathChanged = true; 383 } 384 385 // update font attributes if necessary 386 Font f = otFontSelector.getFont(); 387 if (! f.equals(otCurFont)) { 388 otCurFont = f; 389 fontChanged = true; 390 } 391 392 // see if label interval has changed 393 SpinnerNumberModel snm = (SpinnerNumberModel) (js.getModel()); 394 int tmpLabelInterval = ((Integer) snm.getValue()).intValue(); 395 if ((tmpLabelInterval != labelInterval) || fontChanged) { 396 logger.debug("Label interval change from: " + labelInterval + 397 " to: " + tmpLabelInterval); 398 labelInterval = tmpLabelInterval; 399 try { 400 // remove the current set of labels 401 int numLabels = timeLabelDsp.displayableCount(); 402 for (int i = 0; i < numLabels; i++) { 403 timeLabelDsp.removeDisplayable(0); 404 } 405 // get the currently loaded data 406 Data data = getData(getDataInstance()); 407 if (data instanceof Tuple) { 408 Data[] dataArr = ((Tuple) data).getComponents(); 409 410 int npts = dataArr.length; 411 double distance = 0.0d; 412 LatLonTuple prvPoint = null; 413 414 for (int i = 0; i < npts; i++) { 415 Tuple t = (Tuple) dataArr[i]; 416 Data[] tupleComps = t.getComponents(); 417 418 LatLonTuple llt = (LatLonTuple) tupleComps[1]; 419 double dlat = llt.getLatitude().getValue(); 420 double dlon = llt.getLongitude().getValue(); 421 422 if ((i % labelInterval) == 0) { 423 424 if (prvPoint != null) { 425 distance = Util.distance(prvPoint, llt); 426 if (distance < LABEL_DISTANCE_THRESHOLD) { 427 continue; 428 } 429 } 430 431 String str = ((Text) tupleComps[0]).getValue(); 432 logger.debug("Adding time for str: " + str); 433 int indx = str.indexOf(" ") + 1; 434 String subStr = "- " + str.substring(indx, indx+5); 435 TextDisplayable time = new TextDisplayable(SWATH_MODS, otTextType); 436 time.setJustification(TextControl.Justification.LEFT); 437 time.setVerticalJustification(TextControl.Justification.CENTER); 438 time.setColor(curSwathColor); 439 time.setFont(otFontSelector.getFont()); 440 time.setTextSize((float) otFontSelector.getFontSize() / FONT_SCALE_FACTOR); 441 time.setSphere(inGlobeDisplay()); 442 443 RealTuple lonLat = 444 new RealTuple(RealTupleType.SpatialEarth2DTuple, 445 new double[] { dlon, dlat }); 446 Tuple tup = new Tuple(makeTupleType(SWATH_MODS), 447 new Data[] { lonLat, new Text(otTextType, subStr)}); 448 time.setData(tup); 449 timeLabelDsp.addDisplayable(time); 450 451 prvPoint = llt; 452 } 453 454 } 455 } 456 457 // check swath width field, update if necessary 458 if (swathChanged) changeSwathWidth(); 459 460 } catch (RemoteException re) { 461 re.printStackTrace(); 462 } catch (VisADException vade) { 463 vade.printStackTrace(); 464 } 465 } 466 467 updateDisplayList(); 468 return; 469 } 470 471 // Ground station mods 472 if (STATION_MODS.equals(ae.getActionCommand())) { 473 474 logger.debug("Apply Station Mods..."); 475 476 // flag indicates user changed some parameter 477 boolean somethingChanged = true; 478 479 setAntColor(antColorSwatch.getColor()); 480 481 // update font attributes if necessary 482 Font f = gsFontSelector.getFont(); 483 if (! f.equals(gsCurFont)) { 484 gsCurFont = f; 485 somethingChanged = true; 486 } 487 488 // validate antenna angle text field, redraw if necessary 489 String s = antennaAngle.getText(); 490 int newAngle = curAngle; 491 try { 492 newAngle = Integer.parseInt(s); 493 if (newAngle != curAngle) { 494 curAngle = newAngle; 495 somethingChanged = true; 496 } 497 } catch (NumberFormatException nfe) { 498 JOptionPane.showMessageDialog(latLonAltPanel, 499 "Antenna angle valid range is " + DEFAULT_ANTENNA_ANGLE + 500 " to " + MAX_ANTENNA_ANGLE + " degrees"); 501 return; 502 } 503 504 if (somethingChanged) { 505 plotCoverageCircles(); 506 } 507 508 updateDisplayList(); 509 return; 510 511 } 512 513 } 514 515 /** 516 * Apply the map (height) position to the displays 517 */ 518 519 private void applyTrackPosition() { 520 try { 521 DisplayRealType dispType = navDsp.getDisplayAltitudeType(); 522 trackDsp.setConstantPosition(trackZ, dispType); 523 timeLabelDsp.setConstantPosition(trackZ, dispType); 524 stationLabelDsp.setConstantPosition(gsZ, dispType); 525 } catch (Exception e) { 526 e.printStackTrace(); 527 } 528 } 529 530 private void changeSwathWidth() { 531 532 logger.debug("changeSwathWidth() in..."); 533 if ((curWidth != prvWidth) || 534 (curTrackLineStyle != prvTrackLineStyle) || 535 (curEdgeLineStyle != prvEdgeLineStyle) || 536 (curSwathCenterWidth != prvSwathCenterWidth) || 537 (curSwathEdgeWidth != prvSwathEdgeWidth) || 538 (curSwathColor != prvSwathColor)) { 539 prvWidth = curWidth; 540 prvSwathCenterWidth = curSwathCenterWidth; 541 prvSwathEdgeWidth = curSwathEdgeWidth; 542 prvSwathColor = curSwathColor; 543 prvTrackLineStyle = curTrackLineStyle; 544 prvEdgeLineStyle = curEdgeLineStyle; 545 try { 546 removeDisplayable(swathEdgeDsp); 547 removeDisplayable(trackDsp); 548 swathEdgeDsp = null; 549 trackDsp = null; 550 Data data = getData(getDataInstance()); 551 swathEdgeDsp = new CompositeDisplayable(); 552 trackDsp = new CompositeDisplayable(); 553 createTrackDisplay(data, true); 554 } catch (Exception e) { 555 e.printStackTrace(); 556 } 557 } 558 } 559 560 private void createTrackDisplay(Data data, boolean doTrack) { 561 logger.debug("createTrackDisplay() in..."); 562 try { 563 List<String> dts = new ArrayList<String>(); 564 if (data instanceof Tuple) { 565 Data[] dataArr = ((Tuple) data).getComponents(); 566 567 int npts = dataArr.length; 568 float[][] latlon = new float[2][npts]; 569 double distance = 0.0d; 570 LatLonTuple prvPoint = null; 571 572 for (int i = 0; i < npts; i++) { 573 Tuple t = (Tuple) dataArr[i]; 574 Data[] tupleComps = t.getComponents(); 575 576 LatLonTuple llt = (LatLonTuple) tupleComps[1]; 577 double dlat = llt.getLatitude().getValue(); 578 double dlon = llt.getLongitude().getValue(); 579 580 if (doTrack) { 581 if ((i % labelInterval) == 0) { 582 583 if (prvPoint != null) { 584 distance = Util.distance(prvPoint, llt); 585 logger.debug("Distance: " + distance); 586 if (distance < LABEL_DISTANCE_THRESHOLD) { 587 latlon[0][i] = (float) dlat; 588 latlon[1][i] = (float) dlon; 589 continue; 590 } 591 } 592 593 String str = ((Text) tupleComps[0]).getValue(); 594 dts.add(str); 595 int indx = str.indexOf(" ") + 1; 596 String subStr = "- " + str.substring(indx, indx + 5); 597 TextDisplayable time = new TextDisplayable(SWATH_MODS, otTextType); 598 time.setJustification(TextControl.Justification.LEFT); 599 time.setVerticalJustification(TextControl.Justification.CENTER); 600 time.setColor(curSwathColor); 601 time.setTextSize((float) otFontSelector.getFontSize() / FONT_SCALE_FACTOR); 602 time.setFont(otFontSelector.getFont()); 603 time.setSphere(inGlobeDisplay()); 604 605 RealTuple lonLat = 606 new RealTuple(RealTupleType.SpatialEarth2DTuple, 607 new double[] { dlon, dlat }); 608 Tuple tup = new Tuple(makeTupleType(SWATH_MODS), 609 new Data[] { lonLat, new Text(otTextType, subStr)}); 610 time.setData(tup); 611 timeLabelDsp.addDisplayable(time); 612 613 prvPoint = llt; 614 } 615 } 616 latlon[0][i] = (float) dlat; 617 latlon[1][i] = (float) dlon; 618 } 619 620 if (doTrack) { 621 Gridded2DSet track = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, 622 latlon, npts); 623 SampledSet[] set = new SampledSet[1]; 624 set[0] = track; 625 UnionSet uset = new UnionSet(set); 626 CurveDrawer trackLines = new CurveDrawer(uset); 627 trackLines.setData(uset); 628 trackLines.setDrawingEnabled(false); 629 trackLines.setLineStyle(jcbTrackLineStyle.getSelectedIndex()); 630 trackDsp.addDisplayable(trackLines); 631 trackDsp.setColor(curSwathColor); 632 trackDsp.setLineWidth(jcbSCLineWidth.getSelectedIndex() + 1); 633 634 addDisplayable(trackDsp, FLAG_COLORTABLE); 635 addDisplayable(timeLabelDsp, FLAG_COLORTABLE); 636 addDisplayable(stationLabelDsp, FLAG_COLORTABLE); 637 } 638 639 float[][][] crv = getSwath(latlon); 640 int npt = crv[0][0].length; 641 float[][] leftC = new float[2][npt]; 642 float[][] rightC = new float[2][npt]; 643 for (int i = 0; i < npt; i++) { 644 leftC[0][i] = crv[0][0][i]; 645 leftC[1][i] = crv[0][1][i]; 646 rightC[0][i] = crv[1][0][i]; 647 rightC[1][i] = crv[1][1][i]; 648 } 649 Gridded2DSet left = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, 650 leftC, npt); 651 SampledSet[] lSet = new SampledSet[1]; 652 lSet[0] = left; 653 UnionSet lUSet = new UnionSet(lSet); 654 CurveDrawer leftLines = new CurveDrawer(lUSet); 655 leftLines.setLineStyle(jcbEdgeLineStyle.getSelectedIndex()); 656 leftLines.setData(lUSet); 657 swathEdgeDsp.addDisplayable(leftLines); 658 leftLines.setDrawingEnabled(false); 659 660 Gridded2DSet right = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, 661 rightC, npt); 662 SampledSet[] rSet = new SampledSet[1]; 663 rSet[0] = right; 664 UnionSet rUSet = new UnionSet(rSet); 665 CurveDrawer rightLines = new CurveDrawer(rUSet); 666 rightLines.setLineStyle(jcbEdgeLineStyle.getSelectedIndex()); 667 rightLines.setData(rUSet); 668 swathEdgeDsp.addDisplayable(rightLines); 669 rightLines.setDrawingEnabled(false); 670 671 swathEdgeDsp.setColor(curSwathColor); 672 swathEdgeDsp.setLineWidth(curSwathEdgeWidth); 673 addDisplayable(swathEdgeDsp, FLAG_COLORTABLE); 674 } 675 } catch (Exception e) { 676 e.printStackTrace(); 677 } 678 return; 679 } 680 681 /** 682 * Called by doMakeWindow in DisplayControlImpl, which then calls its 683 * doMakeMainButtonPanel(), which makes more buttons. 684 * 685 * @return container of contents 686 */ 687 688 public Container doMakeContents() { 689 690 fontSizePanel = new JPanel(); 691 fontSizePanel.setLayout(new BoxLayout(fontSizePanel, BoxLayout.Y_AXIS)); 692 JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEADING)); 693 jcbLabels = new JCheckBox("Labels On/Off"); 694 jcbLabels.setSelected(true); 695 jcbLabels.setName(CHECKBOX_LABELS); 696 jcbLabels.addItemListener(this); 697 labelPanel.add(jcbLabels); 698 699 // same row, add label interval spinner 700 Integer defaultInterval = new Integer(5); 701 Integer minInterval = new Integer(1); 702 Integer maxInterval = new Integer(120); 703 Integer intervalStep = new Integer(1); 704 SpinnerNumberModel snm = 705 new SpinnerNumberModel(defaultInterval, minInterval, maxInterval, intervalStep); 706 JLabel intervalLabel = new JLabel("Label Interval:"); 707 JLabel intervalUnits = new JLabel("minutes"); 708 js = new JSpinner(snm); 709 labelPanel.add(Box.createHorizontalStrut(5)); 710 labelPanel.add(intervalLabel); 711 labelPanel.add(js); 712 labelPanel.add(intervalUnits); 713 714 // line style for drawing swath track and width edges 715 jcbTrackLineStyle.addActionListener(this); 716 // init to solid 717 jcbTrackLineStyle.setSelectedIndex(0); 718 curTrackLineStyle = jcbTrackLineStyle.getSelectedIndex(); 719 720 jcbEdgeLineStyle.addActionListener(this); 721 // init to dashed 722 jcbEdgeLineStyle.setSelectedIndex(1); 723 curEdgeLineStyle = jcbEdgeLineStyle.getSelectedIndex(); 724 725 fontSizePanel.add(labelPanel); 726 JPanel botPanel = new JPanel(new FlowLayout(FlowLayout.LEADING)); 727 botPanel.add(new JLabel("Font: ")); 728 botPanel.add(otFontSelector.getComponent()); 729 fontSizePanel.add(botPanel); 730 731 colorSwatch = new ColorSwatchComponent(getStore(), curSwathColor, "Color"); 732 colorSwatch.setPreferredSize(new Dimension(30, 30)); 733 734 colorPanel = new JPanel(new FlowLayout(FlowLayout.LEADING)); 735 colorPanel.add(new JLabel("Color: ")); 736 colorPanel.add(colorSwatch); 737 738 colorPanel.add(Box.createHorizontalStrut(5)); 739 colorPanel.add(new JLabel("Track Width: ")); 740 colorPanel.add(jcbSCLineWidth); 741 742 colorPanel.add(Box.createHorizontalStrut(4)); 743 colorPanel.add(new JLabel("Track Style: ")); 744 colorPanel.add(jcbTrackLineStyle); 745 746 colorPanel.add(Box.createHorizontalStrut(5)); 747 colorPanel.add(new JLabel("Edge Width: ")); 748 colorPanel.add(jcbSELineWidth); 749 750 colorPanel.add(Box.createHorizontalStrut(4)); 751 colorPanel.add(new JLabel("Edge Style: ")); 752 colorPanel.add(jcbEdgeLineStyle); 753 754 JPanel groundStationPanel = makeGroundStationPanel(); 755 756 swathWidthPanel = makeSwathWidthPanel(); 757 758 JPanel outerPanel = new JPanel(new MigLayout()); 759 760 JPanel mainPanel = new JPanel(new MigLayout()); 761 mainPanel.setBorder(BorderFactory.createTitledBorder(" Swath Controls ")); 762 mainPanel.add(swathWidthPanel, "wrap"); 763 mainPanel.add(fontSizePanel, "wrap"); 764 mainPanel.add(colorPanel, "wrap"); 765 JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 766 JButton applySwathMods = new JButton("Apply"); 767 applySwathMods.setActionCommand(SWATH_MODS); 768 applySwathMods.addActionListener(this); 769 bottomPanel.add(applySwathMods); 770 mainPanel.add(bottomPanel); 771 772 outerPanel.add(mainPanel, "wrap"); 773 outerPanel.add(groundStationPanel, "wrap"); 774 775 return outerPanel; 776 } 777 778 private CurveDrawer makeCoverageCircle(double lat, double lon, double satAlt, Color color) { 779 780 /* mean Earth radius in km */ 781 double earthRadius = AstroConst.R_Earth_mean / 1000.0; 782 satAlt += earthRadius; 783 double SAC = Math.PI / 2.0 + Math.toRadians(curAngle); 784 double sinASC = earthRadius * Math.sin(SAC) / satAlt; 785 double dist = earthRadius * (Math.PI - SAC - Math.asin(sinASC)); 786 double rat = dist / earthRadius; 787 788 int npts = 360; 789 float[][] latlon = new float[2][npts]; 790 double cosDist = Math.cos(rat); 791 double sinDist = Math.sin(rat); 792 double sinLat = Math.sin(lat); 793 double cosLat = Math.cos(lat); 794 double sinLon = -Math.sin(lon); 795 double cosLon = Math.cos(lon); 796 for (int i = 0; i < npts; i++) { 797 double azimuth = Math.toRadians((double) i); 798 double cosBear = Math.cos(azimuth); 799 double sinBear = Math.sin(azimuth); 800 double z = cosDist * sinLat + 801 sinDist * cosLat * cosBear; 802 double y = cosLat * cosLon * cosDist + 803 sinDist * (sinLon * sinBear - sinLat * cosLon * cosBear); 804 double x = cosLat * sinLon * cosDist - 805 sinDist * (cosLon * sinBear + sinLat * sinLon * cosBear); 806 double r = Math.sqrt(x * x + y * y); 807 double latRad = Math.atan2(z, r); 808 double lonRad = 0.0; 809 if (r > 0.0) lonRad = -Math.atan2(x, y); 810 latlon[0][i] = (float) Math.toDegrees(latRad); 811 latlon[1][i] = (float) Math.toDegrees(lonRad); 812 } 813 CurveDrawer coverageCircle = null; 814 try { 815 Gridded2DSet circle = new Gridded2DSet(RealTupleType.LatitudeLongitudeTuple, 816 latlon, npts); 817 SampledSet[] set = new SampledSet[1]; 818 set[0] = circle; 819 UnionSet uset = new UnionSet(set); 820 coverageCircle = new CurveDrawer(uset); 821 coverageCircle.setLineWidth(jcbGSLineWidth.getSelectedIndex() + 1); 822 coverageCircle.setLineStyle(jcbStationLineStyle.getSelectedIndex()); 823 coverageCircle.setColor(getAntColor()); 824 coverageCircle.setData(uset); 825 coverageCircle.setDrawingEnabled(false); 826 coverageCircle.setConstantPosition(gsZ, navDsp.getDisplayAltitudeType()); 827 } catch (Exception e) { 828 e.printStackTrace(); 829 return null; 830 } 831 return coverageCircle; 832 } 833 834 public Color getAntColor() { 835 if (antColor == null) antColor = defaultAntColor; 836 return antColor; 837 } 838 839 public PolarOrbitTrackDataSource getDataSource() { 840 DataSourceImpl ds = null; 841 List dataSources = getDataSources(); 842 boolean gotit = false; 843 if (!dataSources.isEmpty()) { 844 int nsrc = dataSources.size(); 845 for (int i = 0; i < nsrc; i++) { 846 ds = (DataSourceImpl) dataSources.get(nsrc - i - 1); 847 if (ds instanceof PolarOrbitTrackDataSource) { 848 gotit = true; 849 break; 850 } 851 } 852 } 853 if (!gotit) return null; 854 return (PolarOrbitTrackDataSource) ds; 855 } 856 857 /* (non-Javadoc) 858 * @see ucar.unidata.idv.control.DisplayControlImpl#getDisplayListData() 859 */ 860 @Override 861 protected Data getDisplayListData() { 862 // get time range that was specified in the Field Selector 863 String startTime = (String) getDataInstance().getDataSelection().getProperties().get(TimeRangeSelection.PROP_BEGTIME); 864 String endTime = (String) getDataInstance().getDataSelection().getProperties().get(TimeRangeSelection.PROP_ENDTIME); 865 866 // get the template used for the Display Properties Layer Label 867 String labelTemplate = getDisplayListTemplate(); 868 869 // see if time macro is enabled 870 boolean hasTimeMacro = UtcDate.containsTimeMacro(labelTemplate); 871 872 // fetch the label superclass would normally generate 873 Data data = super.getDisplayListData(); 874 875 // if so, modify label with time range for this selection 876 if (hasTimeMacro) { 877 try { 878 TextType tt = TextType.getTextType(DISPLAY_LIST_NAME); 879 data = new Text(tt, data.toString() + startTime + " - " + endTime); 880 } catch (VisADException vade) { 881 vade.printStackTrace(); 882 } 883 } 884 885 // return either original or modified data object 886 return data; 887 } 888 889 public double getLatitude() { 890 return latitude; 891 } 892 893 public double getLongitude() { 894 return longitude; 895 } 896 897 public String getStation() { 898 return station; 899 } 900 901 private float[][][] getSwath(float[][] track) { 902 double earthRadius = AstroConst.R_Earth_mean / 1000.0; 903 int npt = track[0].length-1; 904 float[][][] ret = new float[2][2][npt - 1]; 905 try { 906 int indx = 0; 907 for (int i = 1; i < npt; i++) { 908 double latA = Math.toRadians(track[0][i - 1]); 909 double lonA = Math.toRadians(track[1][i - 1]); 910 911 double latB = Math.toRadians(track[0][i + 1]); 912 double lonB = Math.toRadians(track[1][i + 1]); 913 914 double diffLon = lonB - lonA; 915 double bX = Math.cos(latB) * Math.cos(diffLon); 916 double bY = Math.cos(latB) * Math.sin(diffLon); 917 double xFac = Math.cos(latA) + bX; 918 double latC = Math.atan2(Math.sin(latA) + Math.sin(latB), Math.sqrt(xFac * xFac + bY * bY)); 919 double lonC = lonA + Math.atan2(bY, xFac); 920 921 double bearing = Math.atan2(Math.sin(diffLon) * Math.cos(latB), 922 Math.cos(latA) * Math.sin(latB) - Math.sin(latA) * Math.cos(latB) * Math.cos(diffLon)) 923 + Math.PI / 2.0; 924 double dist = curWidth / 2.0; 925 dist /= earthRadius; 926 double lat = Math.asin(Math.sin(latC) * Math.cos(dist) + 927 Math.cos(latC) * Math.sin(dist) * Math.cos(bearing)); 928 double lon = lonC + Math.atan2(Math.sin(bearing) * Math.sin(dist) * Math.cos(latC), 929 Math.cos(dist) - Math.sin(latC) * Math.sin(lat)); 930 float latD = (float) Math.toDegrees(lat); 931 float lonD = (float) Math.toDegrees(lon); 932 933 bearing += Math.PI; 934 lat = Math.asin(Math.sin(latC) * Math.cos(dist) + 935 Math.cos(latC) * Math.sin(dist) * Math.cos(bearing)); 936 lon = lonC + Math.atan2(Math.sin(bearing) * Math.sin(dist) * Math.cos(latC), 937 Math.cos(dist) - Math.sin(latC) * Math.sin(lat)); 938 float latE = (float) Math.toDegrees(lat); 939 float lonE = (float) Math.toDegrees(lon); 940 941 ret[0][0][indx] = latD; 942 ret[0][1][indx] = lonD; 943 944 ret[1][0][indx] = latE; 945 ret[1][1][indx] = lonE; 946 ++indx; 947 } 948 } catch (Exception e) { 949 e.printStackTrace(); 950 return null; 951 } 952 return ret; 953 } 954 955 @Override public boolean init(DataChoice dataChoice) 956 throws VisADException, RemoteException 957 { 958 logger.debug("init() in..."); 959 960 PolarOrbitTrackDataSource potdc = getDataSource(); 961 962 // validate time range before going ahead with control initialization 963 if (! potdc.getTrs().begTimeOk()) { 964 JOptionPane.showMessageDialog(null, 965 "Invalid start time, must follow format HH:MM:SS", 966 "Time Range Selection Error", JOptionPane.ERROR_MESSAGE); 967 return false; 968 } 969 970 if (! potdc.getTrs().endTimeOk()) { 971 JOptionPane.showMessageDialog(null, 972 "Invalid end time, must follow format HH:MM:SS", 973 "Time Range Selection Error", JOptionPane.ERROR_MESSAGE); 974 return false; 975 } 976 977 if (! potdc.getTrs().timeRangeOk()) { 978 JOptionPane.showMessageDialog(null, 979 "Invalid time range selection, please correct", 980 "Time Range Selection Error", JOptionPane.ERROR_MESSAGE); 981 return false; 982 } 983 984 // instantiate components we need to exist at initialization 985 latLabel = new JLabel(); 986 lonLabel = new JLabel(); 987 altLabel = new JLabel(); 988 String [] lineWidths = {"1", "2", "3", "4"}; 989 jcbGSLineWidth = new JComboBox(lineWidths); 990 jcbSCLineWidth = new JComboBox(lineWidths); 991 // initialize swath center (track line) to width 2 992 jcbSCLineWidth.setSelectedIndex(1); 993 jcbEdgeLineStyle.setSelectedIndex(1); 994 jcbSELineWidth = new JComboBox(lineWidths); 995 otFontSelector = new FontSelector(FontSelector.COMBOBOX_UI, false, false); 996 otFontSelector.setFont(FontSelector.DEFAULT_FONT); 997 gsFontSelector = new FontSelector(FontSelector.COMBOBOX_UI, false, false); 998 gsFontSelector.setFont(FontSelector.DEFAULT_FONT); 999 this.dataChoice = dataChoice; 1000 String choiceName = dataChoice.getName(); 1001 NodeList nodeList = root.getElementsByTagName(TAG_SATELLITE); 1002 int num = nodeList.getLength(); 1003 if (num > 0) { 1004 for (int i = 0; i < num; i++) { 1005 Element n = (Element) (nodeList.item(i)); 1006 String satName = n.getAttribute(ATTR_NAME); 1007 if (satName.equals(choiceName)) { 1008 String strWidth = n.getAttribute(ATTR_WIDTH); 1009 if (strWidth.isEmpty()) strWidth = "0"; 1010 Double dWidth = new Double(strWidth); 1011 curWidth = dWidth.doubleValue(); 1012 break; 1013 } 1014 } 1015 } 1016 try { 1017 trackDsp = new CompositeDisplayable(); 1018 timeLabelDsp = new CompositeDisplayable(); 1019 stationLabelDsp = new CompositeDisplayable(); 1020 swathEdgeDsp = new CompositeDisplayable(); 1021 circleDsp = new CompositeDisplayable(); 1022 } catch (Exception e) { 1023 logger.error("problem creating composite displayable"); 1024 e.printStackTrace(); 1025 return false; 1026 } 1027 boolean result = super.init((DataChoice) this.getDataChoices().get(0)); 1028 1029 String dispName = getDisplayName(); 1030 setDisplayName(getLongParamName() + " " + dispName); 1031 logger.debug("Setting display name: " + getDisplayName()); 1032 try { 1033 String longName = getLongParamName().replaceAll(" ", ""); 1034 otTextType = new TextType(SWATH_MODS + longName); 1035 gsTextType = new TextType(STATION_MODS + longName); 1036 } catch (Exception e) { 1037 e.printStackTrace(); 1038 otTextType = TextType.Generic; 1039 gsTextType = TextType.Generic; 1040 } 1041 1042 Data data = getData(getDataInstance()); 1043 createTrackDisplay(data, true); 1044 dataSource = getDataSource(); 1045 try { 1046 navDsp = getNavigatedDisplay(); 1047 float defaultZ = getMapViewManager().getDefaultMapPosition(); 1048 // we're just nudging a bit so tracks (and their labels) get drawn over 1049 // ground stations (and their labels), which get drawn over default map level 1050 // user can change this in map controls if they prefer maps on top 1051 gsZ = defaultZ + 0.25f; 1052 trackZ = defaultZ + 0.5f; 1053 // range on "map level" stuff is -1 to 1, stay within these limits 1054 if (trackZ > 1.0f) trackZ = 1.0f; 1055 if (gsZ > 1.0f) gsZ = 1.0f; 1056 applyTrackPosition(); 1057 } catch (Exception e) { 1058 logger.error("get display center e=" + e); 1059 } 1060 1061 return result; 1062 } 1063 1064 public void itemStateChanged(ItemEvent ie) { 1065 1066 // now we got multiple checkboxes, so first see which one applies 1067 String source = ((JCheckBox) ie.getSource()).getName(); 1068 try { 1069 if (source.equals(CHECKBOX_LABELS)) { 1070 if (ie.getStateChange() == ItemEvent.DESELECTED) { 1071 timeLabelDsp.setVisible(false); 1072 } else { 1073 timeLabelDsp.setVisible(true); 1074 } 1075 updateDisplayList(); 1076 } 1077 if (source.equals(CHECKBOX_SWATH_EDGES)) { 1078 if (ie.getStateChange() == ItemEvent.DESELECTED) { 1079 swathEdgeDsp.setVisible(false); 1080 } else { 1081 swathEdgeDsp.setVisible(true); 1082 } 1083 updateDisplayList(); 1084 } 1085 } catch (RemoteException re) { 1086 re.printStackTrace(); 1087 } catch (VisADException vade) { 1088 vade.printStackTrace(); 1089 } 1090 1091 } 1092 1093 private void labelGroundStation(String station) { 1094 try { 1095 String str = "+ " + station; 1096 logger.debug("Drawing station: " + str); 1097 1098 TextDisplayable groundStationDsp = new TextDisplayable(STATION_MODS, gsTextType); 1099 groundStationDsp.setJustification(TextControl.Justification.LEFT); 1100 groundStationDsp.setVerticalJustification(TextControl.Justification.CENTER); 1101 groundStationDsp.setColor(getAntColor()); 1102 groundStationDsp.setFont(gsFontSelector.getFont()); 1103 groundStationDsp.setTextSize((float) gsFontSelector.getFontSize() / FONT_SCALE_FACTOR); 1104 groundStationDsp.setSphere(inGlobeDisplay()); 1105 1106 double dlat = getLatitude(); 1107 double dlon = getLongitude(); 1108 RealTuple lonLat = 1109 new RealTuple(RealTupleType.SpatialEarth2DTuple, 1110 new double[] { dlon, dlat }); 1111 Tuple tup = new Tuple(makeTupleType(STATION_MODS), 1112 new Data[] { lonLat, new Text(gsTextType, str)}); 1113 groundStationDsp.setData(tup); 1114 groundStationDsp.setConstantPosition(gsZ, navDsp.getDisplayAltitudeType()); 1115 stationLabelDsp.addDisplayable(groundStationDsp); 1116 } catch (Exception e) { 1117 e.printStackTrace(); 1118 } 1119 } 1120 1121 private JPanel makeGroundStationPanel() { 1122 1123 JPanel jp = new JPanel(new MigLayout()); 1124 jp.setBorder(BorderFactory.createTitledBorder(" Ground Station Controls ")); 1125 1126 // line style for drawing coverage circles 1127// jcbStationLineStyle = new JComboBox(new String[] { "_____", "_ _ _", ".....", "_._._" }); 1128 jcbStationLineStyle = new JComboBox(lineStyles); 1129 jcbStationLineStyle.addActionListener(this); 1130 jcbStationLineStyle.setSelectedIndex(1); 1131 prvStationLineStyle = jcbStationLineStyle.getSelectedIndex(); 1132 1133 locationComboBox = new JComboBox(); 1134 jcbStationsPlotted = new JComboBox(); 1135 1136 // Ground Stations are now a natural-order map (alphabetical) 1137 GroundStations gs = new GroundStations(null); 1138 stationMap = gs.getGroundStations(); 1139 TreeSet<String> keySet = new TreeSet<String>(stationMap.keySet()); 1140 1141 GuiUtils.setListData(locationComboBox, keySet.toArray()); 1142 1143 // initialize with first Earth Location in our map 1144 if (! stationMap.isEmpty()) { 1145 EarthLocationTuple elt = stationMap.get(locationComboBox.getSelectedItem()); 1146 latLabel.setText(elt.getLatitude().toString()); 1147 lonLabel.setText(elt.getLongitude().toString()); 1148 altLabel.setText(elt.getAltitude().toString()); 1149 } 1150 1151 locationPanel = new JPanel(); 1152 locationPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 1153 locationPanel.add(new JLabel("Ground Stations Available:")); 1154 locationPanel.add(locationComboBox); 1155 JButton addButton = new JButton("Add Selected"); 1156 addButton.setActionCommand(STATION_ADD); 1157 addButton.addActionListener(this); 1158 locationPanel.add(addButton); 1159 1160 JPanel customPanel = new JPanel(); 1161 customPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 1162 customPanel.add(new JLabel("Custom Ground Station: Label: ")); 1163 customLab = new JTextField(6); 1164 customPanel.add(customLab); 1165 customPanel.add(new JLabel("Latitude: ")); 1166 customLat = new JTextField(6); 1167 customPanel.add(customLat); 1168 customPanel.add(new JLabel("Longitude: ")); 1169 customLon = new JTextField(6); 1170 customPanel.add(customLon); 1171 JButton customButton = new JButton("Add Custom"); 1172 customButton.setActionCommand(CUSTOM_ADD); 1173 customButton.addActionListener(this); 1174 customPanel.add(customButton); 1175 1176 JPanel plottedStationsPanel = new JPanel(); 1177 plottedStationsPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 1178 plottedStationsPanel.add(new JLabel("Ground Stations Plotted:")); 1179 plottedStationsPanel.add(jcbStationsPlotted); 1180 JButton remButton = new JButton("Remove Selected"); 1181 remButton.setActionCommand(STATION_REM); 1182 remButton.addActionListener(this); 1183 plottedStationsPanel.add(remButton); 1184 1185 latLonAltPanel = new JPanel(); 1186 latLonAltPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 1187 1188 latLonAltPanel.add(new JLabel("Last Station Plotted, Latitude: ")); 1189 latLonAltPanel.add(latLabel); 1190 latLonAltPanel.add(Box.createHorizontalStrut(5)); 1191 1192 latLonAltPanel.add(new JLabel("Longitude: ")); 1193 latLonAltPanel.add(lonLabel); 1194 latLonAltPanel.add(Box.createHorizontalStrut(5)); 1195 1196 latLonAltPanel.add(new JLabel("Altitude: ")); 1197 latLonAltPanel.add(altLabel); 1198 1199 JPanel gsFontPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 1200 gsFontPanel.add(new JLabel("Font: ")); 1201 gsFontPanel.add(gsFontSelector.getComponent()); 1202 1203 Color swatchAntColor = getAntColor(); 1204 antColorSwatch = new ColorSwatchComponent(getStore(), swatchAntColor, "Color"); 1205 antColorSwatch.setPreferredSize(new Dimension(30, 30)); 1206 1207 antColorPanel = new JPanel(); 1208 antColorPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); 1209 antColorPanel.add(new JLabel("Color: ")); 1210 antColorPanel.add(antColorSwatch); 1211 1212 antColorPanel.add(Box.createHorizontalStrut(5)); 1213 antColorPanel.add(new JLabel("Line Width: ")); 1214 antColorPanel.add(jcbGSLineWidth); 1215 1216 antColorPanel.add(Box.createHorizontalStrut(5)); 1217 antColorPanel.add(new JLabel("Line Style: ")); 1218 antColorPanel.add(jcbStationLineStyle); 1219 1220 antColorPanel.add(Box.createHorizontalStrut(5)); 1221 antColorPanel.add(new JLabel("Antenna Angle: ")); 1222 antColorPanel.add(antennaAngle); 1223 1224 jp.add(locationPanel, "wrap"); 1225 jp.add(customPanel, "wrap"); 1226 jp.add(plottedStationsPanel, "wrap"); 1227 jp.add(latLonAltPanel, "wrap"); 1228 jp.add(Box.createVerticalStrut(5), "wrap"); 1229 jp.add(gsFontPanel, "wrap"); 1230 jp.add(antColorPanel, "wrap"); 1231 1232 JPanel bottomRow = new JPanel(new FlowLayout(FlowLayout.LEFT)); 1233 JButton applyGroundStationMods = new JButton("Apply"); 1234 applyGroundStationMods.setActionCommand(STATION_MODS); 1235 applyGroundStationMods.addActionListener(this); 1236 bottomRow.add(applyGroundStationMods); 1237 jp.add(bottomRow); 1238 1239 return jp; 1240 } 1241 1242 private JPanel makeSwathWidthPanel() { 1243 if (dataChoice != null) 1244 satelliteName = new JLabel(dataChoice.getName()); 1245 Integer isw = new Integer((int) curWidth); 1246 swathWidthFld = new JTextField(isw.toString(), 5); 1247 1248 JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT)); 1249 1250 // first on this panel, check box to turn on/off swath line edges 1251 jcbSwathEdges = new JCheckBox("Swath Edges On/Off"); 1252 jcbSwathEdges.setSelected(true); 1253 jcbSwathEdges.setName(CHECKBOX_SWATH_EDGES); 1254 jcbSwathEdges.addItemListener(this); 1255 jp.add(jcbSwathEdges); 1256 1257 jp.add(Box.createHorizontalStrut(5)); 1258 jp.add(new JLabel("Satellite: ")); 1259 jp.add(satelliteName); 1260 jp.add(Box.createHorizontalStrut(5)); 1261 jp.add(new JLabel("Swath Width: ")); 1262 jp.add(swathWidthFld); 1263 jp.add(kmLabel); 1264 jp.add(Box.createHorizontalStrut(5)); 1265 1266 return jp; 1267 } 1268 1269 private TupleType makeTupleType(String prefix) { 1270 TupleType t = null; 1271 try { 1272 if (prefix.equals(SWATH_MODS)) 1273 t = new TupleType(new MathType[] {RealTupleType.SpatialEarth2DTuple, 1274 otTextType}); 1275 if (prefix.equals(STATION_MODS)) 1276 t = new TupleType(new MathType[] {RealTupleType.SpatialEarth2DTuple, 1277 gsTextType}); 1278 } catch (Exception e) { 1279 e.printStackTrace(); 1280 } 1281 return t; 1282 } 1283 1284 private void plotCoverageCircles() { 1285 try { 1286 1287 int num = circleDsp.displayableCount(); 1288 for (int i = 0; i < num; i++) { 1289 // yes, always 0th it's a queue 1290 circleDsp.removeDisplayable(0); 1291 } 1292 removeDisplayable(circleDsp); 1293 circleDsp = null; 1294 int numLabels = stationLabelDsp.displayableCount(); 1295 for (int i = 0; i < numLabels; i++) { 1296 // yes, always 0th it's a queue 1297 stationLabelDsp.removeDisplayable(0); 1298 } 1299 1300 int numPlotted = jcbStationsPlotted.getItemCount(); 1301 circleDsp = new CompositeDisplayable(); 1302 circleDsp.setConstantPosition(gsZ, navDsp.getDisplayAltitudeType()); 1303 for (int i = 0; i < numPlotted; i++) { 1304 String s = (String) jcbStationsPlotted.getItemAt(i); 1305 EarthLocationTuple elt = stationMap.get(s); 1306 latLabel.setText(elt.getLatitude().toString()); 1307 lonLabel.setText(elt.getLongitude().toString()); 1308 altLabel.setText(elt.getAltitude().toString()); 1309 // quick and easy way to limit sig digits to something not too crazy 1310 if (altLabel.getText().length() > 10) altLabel.setText(altLabel.getText().substring(0, 9)); 1311 latitude = Double.parseDouble(latLabel.getText()); 1312 longitude = Double.parseDouble(lonLabel.getText()); 1313 setSatelliteAltitude(dataSource.getNearestAltToGroundStation(latitude, longitude) / 1000.0); 1314 1315 CurveDrawer cd = makeCoverageCircle(Math.toRadians(latitude), Math.toRadians(longitude), 1316 satelliteAltitude, getAntColor()); 1317 if (cd != null) { 1318 logger.debug("Drawing ground station, station name: " + s); 1319 labelGroundStation(s); 1320 circleDsp.setColor(getAntColor()); 1321 cd.setLineWidth(jcbGSLineWidth.getSelectedIndex() + 1); 1322 circleDsp.addDisplayable(cd); 1323 } 1324 } 1325 addDisplayable(circleDsp, FLAG_COLORTABLE); 1326 1327 } catch (Exception e) { 1328 e.printStackTrace(); 1329 } 1330 } 1331 1332 public void setAntColor(Color c) { 1333 if (c == null) c = defaultAntColor; 1334 try { 1335 antColor = c; 1336 circleDsp.setColor(c); 1337 } catch (Exception e) { 1338 logger.error("Exception in PolarOrbitTrackControl.setAntColor e=" + e); 1339 } 1340 } 1341 1342 private void setSatelliteAltitude(double val) { 1343 satelliteAltitude = val; 1344 } 1345 1346 public void setStation(String val) { 1347 station = val.trim(); 1348 } 1349 1350 private int validateSwathWidthField() { 1351 String s = swathWidthFld.getText().trim(); 1352 int val = -1; 1353 try { 1354 val = Integer.parseInt(s); 1355 } catch (NumberFormatException nfe) { 1356 // throw up a dialog to tell user the problem 1357 JOptionPane.showMessageDialog(latLonAltPanel, 1358 "Invalid swath width: must be an integer value in km"); 1359 return -1; 1360 } 1361 1362 if ((val < SWATH_WIDTH_MIN) || (val > SWATH_WIDTH_MAX)) { 1363 // throw up a dialog to tell user the problem 1364 JOptionPane.showMessageDialog(latLonAltPanel, 1365 "Swath width valid range is " + SWATH_WIDTH_MIN + 1366 " to " + SWATH_WIDTH_MAX + " km"); 1367 return -1; 1368 } 1369 return val; 1370 } 1371 1372}