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