001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.data; 030 031import java.io.BufferedReader; 032import java.io.File; 033import java.io.InputStreamReader; 034import java.net.MalformedURLException; 035import java.net.URI; 036import java.net.URL; 037import java.net.URLConnection; 038import java.nio.file.Paths; 039import java.rmi.RemoteException; 040import java.util.ArrayList; 041import java.util.Hashtable; 042import java.util.List; 043import java.util.Objects; 044import java.util.Vector; 045 046import javax.swing.JOptionPane; 047 048import com.google.common.net.InternetDomainName; 049import jsattrak.objects.SatelliteTleSGP4; 050import jsattrak.utilities.TLE; 051import name.gano.astro.propogators.sgp4_cssi.SGP4SatData; 052import name.gano.astro.time.Time; 053 054import org.slf4j.Logger; 055import org.slf4j.LoggerFactory; 056 057import ucar.unidata.data.DataCategory; 058import ucar.unidata.data.DataChoice; 059import ucar.unidata.data.DataSelection; 060import ucar.unidata.data.DataSelectionComponent; 061import ucar.unidata.data.DataSourceDescriptor; 062import ucar.unidata.data.DataSourceImpl; 063import ucar.unidata.data.DirectDataChoice; 064import ucar.unidata.idv.IntegratedDataViewer; 065import ucar.unidata.util.IOUtil; 066import ucar.unidata.util.StringUtil; 067import visad.Data; 068import visad.Text; 069import visad.Tuple; 070import visad.VisADException; 071import visad.georef.LatLonTuple; 072import edu.wisc.ssec.mcidas.adde.AddeTextReader; 073import edu.wisc.ssec.mcidasv.chooser.PolarOrbitTrackChooser; 074import edu.wisc.ssec.mcidasv.util.XmlUtil; 075 076/** 077 * Class for Two-Line-Element data sources, to plot orbit tracks 078 * on McIDAS-V display window. 079 * 080 * @author Gail Dengel and Tommy Jasmin 081 * @version $Revision$ 082 */ 083 084public class PolarOrbitTrackDataSource extends DataSourceImpl { 085 086 private static final Logger logger = 087 LoggerFactory.getLogger(PolarOrbitTrackDataSource.class); 088 089 private ArrayList<String> tleCards = new ArrayList<>(); 090 private ArrayList<String> choices = new ArrayList<>(); 091 092 private SGP4SatData data = new SGP4SatData(); 093 private TLE tle; 094 095 private Hashtable selectionProps; 096 097 /** time step between data points */ 098 private int dTime = 1; 099 100 private SatelliteTleSGP4 prop = null; 101 private double julDate0 = 0.0; 102 private double julDate1 = 0.0; 103 104 TimeRangeSelection trs = null; 105 106 /** 107 * Default bean constructor for persistence; does nothing. 108 */ 109 public PolarOrbitTrackDataSource() {} 110 111 /** 112 * Create a new PolarOrbitTrackDataSource 113 * 114 * @param descriptor descriptor for this source 115 * @param filename ADDE URL 116 * @param properties extra properties for this source 117 * 118 */ 119 120 public PolarOrbitTrackDataSource(DataSourceDescriptor descriptor, 121 String filename, 122 Hashtable properties) 123 { 124 super(descriptor, filename, null, properties); 125 tleCards = new ArrayList<>(); 126 choices = new ArrayList<>(); 127 128 // we dealing with a local file? 129 if (properties.containsKey(PolarOrbitTrackChooser.LOCAL_FILE_KEY)) { 130 File f = (File) (properties.get(PolarOrbitTrackChooser.LOCAL_FILE_KEY)); 131 logger.debug("Local file: {}", f.getName()); 132 URI uri = f.toURI(); 133 properties.put(PolarOrbitTrackChooser.URL_NAME_KEY, uri.toString()); 134 } 135 136 // not a file, must be URL or ADDE request 137 String key = PolarOrbitTrackChooser.TLE_SERVER_NAME_KEY; 138 if (properties.containsKey(key)) { 139 logger.debug("ADDE request..."); 140 Object server = properties.get(key); 141 key = PolarOrbitTrackChooser.TLE_GROUP_NAME_KEY; 142 Object group = properties.get(key); 143 key = PolarOrbitTrackChooser.TLE_USER_ID_KEY; 144 Object user = properties.get(key); 145 key = PolarOrbitTrackChooser.TLE_PROJECT_NUMBER_KEY; 146 Object proj = properties.get(key); 147 key = PolarOrbitTrackChooser.DATASET_NAME_KEY; 148 String descr = (String) properties.get(key); 149 String url = "adde://" + server + "/textdata?&PORT=112&COMPRESS=gzip&USER=" + user + "&PROJ=" + proj + "&GROUP=" + group + "&DESCR=" + descr; 150 AddeTextReader reader = new AddeTextReader(url); 151 List lines = null; 152 if ("OK".equals(reader.getStatus())) { 153 lines = reader.getLinesOfText(); 154 } 155 if (lines == null) { 156 notTLE(); 157 return; 158 } else { 159 String[] cards = StringUtil.listToStringArray(lines); 160 for (int i = 0; i < cards.length; i++) { 161 String str = cards[i]; 162 if (str.length() > 0) { 163 164 // TJJ Mar 2018 - only take records that match descriptor 165 // remove trailing description part of passed in descriptor 166 String satOnly = descr; 167 if (satOnly.indexOf(" ") > 0) { 168 satOnly = satOnly.substring(0, satOnly.indexOf(" ")); 169 } 170 if (str.startsWith(satOnly)) { 171 tleCards.add(XmlUtil.stripNonValidXMLCharacters(cards[i])); 172 173 // TJJ - wish I knew what these lines were all about! 174 // I think I added them at some point - skip blank lines maybe? 175 // Better just leave it for now. 176 int indx = cards[i].indexOf(" "); 177 if (indx < 0) { 178 choices.add(XmlUtil.stripNonValidXMLCharacters(cards[i])); 179 } 180 181 // Grab the next two lines and exit loop 182 tleCards.add(XmlUtil.stripNonValidXMLCharacters(cards[i + 1])); 183 tleCards.add(XmlUtil.stripNonValidXMLCharacters(cards[i + 2])); 184 break; 185 } 186 187 } 188 } 189 } 190 } else { 191 try { 192 key = PolarOrbitTrackChooser.URL_NAME_KEY; 193 String urlStr = (String)(properties.get(key)); 194 logger.debug("URL request: {}", urlStr); 195 196 URLConnection urlCon = IOUtil.getUrlConnection(urlStr); 197 if (urlStr.startsWith("file:/")) { 198 // "file:/" protocol prefix causes windows to barf 199 // Windows path will look like "file:/C:/temp/weather.txt" 200 String substr = urlStr.substring(6); 201 setName(Paths.get(substr).getFileName().toString()); 202 } else { 203 setName(makeNameForRemoteSource(urlStr)); 204 } 205 InputStreamReader isr = new InputStreamReader(urlCon.getInputStream()); 206 BufferedReader tleReader = new BufferedReader(isr); 207 String nextLine = null; 208 while ((nextLine = tleReader.readLine()) != null) { 209 if (!nextLine.isEmpty()) { 210 tleCards.add(XmlUtil.stripNonValidXMLCharacters(nextLine)); 211 if (nextLine.length() < 50) { 212 choices.add(XmlUtil.stripNonValidXMLCharacters(nextLine)); 213 } 214 } 215 } 216 } catch (Exception e) { 217 notTLE(); 218 logger.error("Could not complete URL request", e); 219 return; 220 } 221 } 222 checkFirstEntry(); 223 } 224 225 /** 226 * Create a nice looking name for this instance. 227 * 228 * <p>Given a URL like 229 * {@code http://celestrak.com/NORAD/elements/weather.txt}, this method 230 * will return {@code celestrak: /NORAD/elements/weather.txt}.</p> 231 * 232 * <p>If the hostname from {@code urlStr} could not be sufficiently reduced, 233 * this method will simply use the entire hostname. A URL like 234 * {@code http://adde.ssec.wisc.edu/weather.txt} will return 235 * {@code adde.ssec.wisc.edu: weather.txt}.</p> 236 * 237 * <p>If there was a problem parsing {@code urlStr}, the method will try 238 * to return the filename. A URL like 239 * {@code http://celestrak.com/NORAD/elements/weather.txt} would return 240 * {@code weather.txt}.</p> 241 * 242 * <p>If all of the above fails, {@code urlStr} will be returned.</p> 243 * 244 * @param urlStr URL of the TLE information. Cannot be {@code null}. 245 * 246 * @return Either the name as described above, or {@code null} if there was 247 * a problem. 248 */ 249 public static String makeNameForRemoteSource(String urlStr) { 250 Objects.requireNonNull(urlStr, "Cannot use a null URL string"); 251 String result; 252 try { 253 URL url = new URL(urlStr); 254 String host = url.getHost(); 255 String path = url.getPath(); 256 257 // thank you, guava! 258 InternetDomainName domain = InternetDomainName.from(host); 259 260 // suffix will be something like 'com' or 'co.uk', so suffixStart 261 // needs to start one character earlier to remove the trailing '.' 262 String suffix = domain.publicSuffix().toString(); 263 int suffixStart = host.indexOf(suffix) - 1; 264 String trimmed = host.substring(0, suffixStart); 265 266 // Trying this with 'http://adde.ssec.wisc.edu/weather.txt' will 267 // result in trimmed being 'adde.ssec.wisc', and I imagine there 268 // are more edge cases. With that in mind, we just use the hostname 269 // if it looks like trimmed doesn't look nice 270 if (trimmed.indexOf('.') > -1) { 271 result = host + ": " + path; 272 } else { 273 result = trimmed + ": " + path; 274 } 275 } catch (IllegalArgumentException e) { 276 // InternetDomainName.from() call likely failed; simply return 277 // original URL string as specified by the javadoc! 278 result = urlStr; 279 logger.warn("Problem with URL '"+urlStr+'\'', e); 280 } catch (MalformedURLException e) { 281 logger.error("Bad URL", e); 282 int lastSlash = urlStr.lastIndexOf('/'); 283 if (lastSlash > -1) { 284 // need the "+1" to get rid of the slash 285 result = urlStr.substring(lastSlash + 1); 286 } else { 287 result = urlStr; 288 } 289 } 290 return result; 291 } 292 293 private void checkFirstEntry() { 294 if (tleCards.isEmpty()) { 295 notTLE(); 296 return; 297 } 298 String card = (String) tleCards.get(1); 299 decodeCard1(card); 300 } 301 302 private int checksum(String str) { 303 int sum = 0; 304 byte[] bites = str.getBytes(); 305 for (int i = 0; i < bites.length; i++) { 306 int val = (int) bites[i]; 307 if ((val > 47) && (val < 58)) { 308 sum += val - 48; 309 } else if (val == 45) { 310 ++sum; 311 } 312 } 313 return sum % 10; 314 } 315 316 private int decodeCard1(String card) { 317 logger.debug("Decoding card: {}", card); 318 int satId = 0; 319 double ddd = 1.0; 320 double firstDev = 1.0; 321 int ephemerisType = 0; 322 int elementNumber = 0; 323 324 int ret = 0; 325 if (card.length() < 69) { 326 notTLE(); 327 return -1; 328 } 329 int ck1 = checksum(card.substring(0, 68)); 330 String str = card.substring(0, 1); 331 if (str.equals("1")) { 332 satId = getInt(2, 7, card); 333 //System.out.println(" satId = " + satId); 334 data.satnum = satId; 335 ++ret; 336 337 data.classification = card.substring(7, 8); 338 data.intldesg = card.substring(9, 17); 339 int yy = getInt(18, 20, card); 340 data.epochyr = yy; 341 ++ret; 342 343 ddd = getDouble(20, 32, card); 344 //System.out.println(" ddd = " + ddd); 345 data.epochdays = ddd; 346 ++ret; 347 348 firstDev = getDouble(33, 43, card); 349 //System.out.println(" firstDev = " + firstDev); 350 data.ndot = firstDev; 351 ++ret; 352 353 if((card.substring(44, 52)).equals(" ")) 354 { 355 data.nddot = 0; 356 data.nexp = 0; 357 } 358 else 359 { 360 data.nddot = getDouble(44, 50, card) / 1.0E5; 361 data.nexp = getInt(50, 52, card); 362 } 363 //System.out.println(" nddot=" + data.nddot); 364 //System.out.println(" nexp=" + data.nexp); 365 366 data.bstar = getDouble(53, 59, card) / 1.0E5; 367 data.ibexp = getInt(59, 61, card); 368 //System.out.println(" bstar=" + data.bstar); 369 //System.out.println(" ibexp=" + data.ibexp); 370 371 try { 372 ephemerisType = getInt(62, 63, card); 373 //System.out.println(" ephemerisType = " + ephemerisType); 374 data.numb = ephemerisType; 375 ++ret; 376 377 elementNumber = getInt(64, 68, card); 378 //System.out.println(" elementNumber = " + elementNumber); 379 data.elnum = elementNumber; 380 ++ret; 381 } catch (Exception e) { 382 logger.error("Warning: Error Reading numb or elnum from TLE line 1 sat#:" + data.satnum); 383 } 384 385 int check = card.codePointAt(68) - 48; 386 if (check != ck1) { 387 notTLE(); 388// logger.error("***** Failed checksum *****"); 389 ret = -1; 390 } 391 } 392 return ret; 393 } 394 395 private int decodeCard2(String card) { 396/* 397 System.out.println("\ndecodeCard2:"); 398 System.out.println(" card=" + card); 399 System.out.println(" length=" + card.length()); 400*/ 401 double inclination = 1.0; 402 double rightAscension = 1.0; 403 double eccentricity = 1.0; 404 double argOfPerigee = 1.0; 405 double meanAnomaly = 1.0; 406 double meanMotion = 1.0; 407 int revolutionNumber = 0; 408 409 int ret = 0; 410 //System.out.println("\n" + card); 411 if (card.length() < 69) { 412 notTLE(); 413 return -1; 414 } 415 int ck1 = checksum(card.substring(0, 68)); 416 String str = card.substring(0, 1); 417 if (str.equals("2")) { 418 int nsat = getInt(2, 7, card); 419 //System.out.println(" nsat = " + nsat + " data.satnum=" + data.satnum); 420 if (nsat != data.satnum) { 421 logger.error("Warning TLE line 2 Sat Num doesn't match line1 for sat: " + data.name); 422 } else { 423 inclination = getDouble(8, 16, card); 424 data.inclo = inclination; 425 //System.out.println(" inclo = " + data.inclo); 426 ++ret; 427 428 rightAscension = getDouble(17, 25, card); 429 data.nodeo = rightAscension; 430 //System.out.println(" nodeo = " + data.nodeo); 431 ++ret; 432 433 eccentricity = getDouble(26, 33, card) / 1.0E7; 434 data.ecco = eccentricity; 435 //System.out.println(" ecco = " + data.ecco); 436 ++ret; 437 438 argOfPerigee = getDouble(34, 42, card); 439 data.argpo = argOfPerigee; 440 //System.out.println(" argpo = " + data.argpo); 441 ++ret; 442 443 meanAnomaly = getDouble(43, 51, card); 444 data.mo = meanAnomaly; 445 //System.out.println(" mo = " + data.mo); 446 ++ret; 447 448 meanMotion = getDouble(52, 63, card); 449 data.no = meanMotion; 450 //System.out.println(" no = " + data.no); 451 ++ret; 452 453 try { 454 revolutionNumber = getInt(63, 68, card); 455 data.revnum = revolutionNumber; 456 //System.out.println(" revnum = " + data.revnum); 457 ++ret; 458 } catch (Exception e) { 459 logger.error("Warning: Error Reading revnum from TLE line 2 sat#:" + data.satnum + "\n" + e.toString()); 460 data.revnum = -1; 461 } 462 463 int check = card.codePointAt(68) - 48; 464 if (check != ck1) { 465 notTLE(); 466// logger.error("***** Failed checksum *****"); 467 ret = -1; 468 } 469 } 470 } 471 return ret; 472 } 473 474 /** 475 * Make the data choices associated with this source. 476 */ 477 protected void doMakeDataChoices() { 478 String category = "TLE"; 479 for (int i = 0; i < choices.size(); i++) { 480 String name = ((String) choices.get(i)).trim(); 481 addDataChoice( 482 new DirectDataChoice( 483 this, name, name, name, 484 DataCategory.parseCategories(category, false))); 485 } 486 } 487 488 /** 489 * Actually get the data identified by the given DataChoce. The default is 490 * to call the getDataInner that does not take the requestProperties. This 491 * allows other, non unidata.data DataSource-s (that follow the old API) 492 * to work. 493 * 494 * @param dataChoice The data choice that identifies the requested 495 * data. 496 * @param category The data category of the request. 497 * @param dataSelection Identifies any subsetting of the data. 498 * @param requestProperties Hashtable that holds any detailed request 499 * properties. 500 * 501 * @return The visad.Text object 502 * 503 * @throws RemoteException Java RMI problem 504 * @throws VisADException VisAD problem 505 */ 506 507 protected Data getDataInner(DataChoice dataChoice, DataCategory category, 508 DataSelection dataSelection, 509 Hashtable requestProperties) 510 throws VisADException, RemoteException { 511 512 boolean gotit = false; 513 int index = -1; 514 String choiceName = dataChoice.getName(); 515 String tleLine1 = ""; 516 String tleLine2 = ""; 517 518 while (!gotit) { 519 index++; 520 String name = ((String) tleCards.get(index)).trim(); 521 if (name.equals(choiceName)) { 522 data.name = name; 523/* 524 System.out.println("\n" + tleCards.get(index)); 525 System.out.println(tleCards.get(index+1)); 526 System.out.println(tleCards.get(index+2) + "\n"); 527*/ 528 index++; 529 String card = (String) tleCards.get(index); 530 tleLine1 = card; 531 int ncomps = decodeCard1(card); 532 if (ncomps < 0) return null; 533 index++; 534 card = (String) tleCards.get(index); 535 tleLine2 = card; 536 ncomps += decodeCard2(card); 537 gotit= true; 538 } 539 if (index+3 > tleCards.size()) gotit = true; 540 } 541 if (gotit == false) return null; 542 543 this.selectionProps = dataSelection.getProperties(); 544/* 545 Enumeration propEnum = this.selectionProps.keys(); 546 for (int i = 0; propEnum.hasMoreElements(); i++) { 547 String key = propEnum.nextElement().toString(); 548 String val = (String)this.selectionProps.get(key); 549 System.out.println("key=" + key + " val=" + val); 550 } 551*/ 552 tle = new TLE(choiceName, tleLine1, tleLine2); 553 554 String endStr = (String) this.selectionProps.get("ETime"); 555 Double dEnd = Double.valueOf(endStr); 556 double endJulianDate = dEnd.doubleValue(); 557 julDate1 = endJulianDate; 558 559 try { 560 prop = new SatelliteTleSGP4(tle.getSatName(), tle.getLine1(), 561 tle.getLine2()); 562 prop.setShowGroundTrack(false); 563 } catch (Exception e) { 564 logger.error("Error Creating SGP4 Satellite", e); 565 // WTF 566 //System.exit(1); 567 } 568 569 Time time = new Time( 570 (Integer.parseInt((String)this.selectionProps.get("Year"))), 571 (Integer.parseInt((String)this.selectionProps.get("Month"))), 572 (Integer.parseInt((String)this.selectionProps.get("Day"))), 573 (Integer.parseInt((String)this.selectionProps.get("Hours"))), 574 (Integer.parseInt((String)this.selectionProps.get("Mins"))), 575 (Double.valueOf((String)this.selectionProps.get("Secs"))).doubleValue()); 576 double julianDate = time.getJulianDate(); 577 julDate0 = julianDate; 578 Vector v = new Vector(); 579 580 while (julianDate <= julDate1) { 581 // prop to the desired time 582 prop.propogate2JulDate(julianDate); 583 584 // get the lat/long/altitude [radians, radians, meters] 585 double[] lla = prop.getLLA(); 586 double lat = lla[0] * 180.0 / Math.PI; 587 double lon = lla[1] * 180.0 / Math.PI; 588 589/* 590 System.out.println(time.getDateTimeStr() + " Lat: " + lat 591 + " Lon: " + lon 592 + " Alt: " + alt); 593 */ 594 Tuple data = new Tuple(new Data[] { new Text(time.getDateTimeStr()), 595 new LatLonTuple( 596 lat, 597 lon 598 )} 599 ); 600 v.add(data); 601 time.add(Time.MINUTE, dTime); 602 julianDate = time.getJulianDate(); 603 } 604 605 return new Tuple((Data[]) v.toArray(new Data[v.size()]), false); 606 } 607 608 private double getDouble(int beg, int end, String card) { 609 String str = card.substring(beg, end); 610 str = str.trim(); 611 return (Double.valueOf(str)).doubleValue(); 612 } 613 614 public int getDTime() { 615 return dTime; 616 } 617 618 private int getInt(int beg, int end, String card) { 619 String str = card.substring(beg, end); 620 str = str.trim(); 621 int tmp = -1; 622 try { 623 tmp = Integer.valueOf(str); 624 } catch (NumberFormatException nfe) { 625 JOptionPane.showMessageDialog( 626 null, 627 "Error parsing integer value from TLE card, potentially corrupt data source.", 628 "Orbit Track Data Source Error", 629 JOptionPane.ERROR_MESSAGE 630 ); 631 } 632 return tmp; 633 } 634 635 /** 636 * choices needs to persist to support bundles 637 * @return the choices 638 */ 639 640 public ArrayList<String> getChoices() { 641 return choices; 642 } 643 644 /** 645 * choices needs to persist to support bundles 646 * @param choices the choices to set 647 */ 648 649 public void setChoices(ArrayList<String> choices) { 650 this.choices = choices; 651 } 652 653 /** 654 * tleCards needs to persist to support bundles 655 * @return the tleCards 656 */ 657 658 public ArrayList<String> getTleCards() { 659 return tleCards; 660 } 661 662 /** 663 * tleCards needs to persist to support bundles 664 * @param tleCards the tleCards to set 665 */ 666 667 public void setTleCards(ArrayList<String> tleCards) { 668 this.tleCards = tleCards; 669 } 670 671 /** 672 * @return the trs 673 */ 674 public TimeRangeSelection getTrs() { 675 return trs; 676 } 677 678 public double getNearestAltToGroundStation(double gsLat, double gsLon) { 679 double retAlt = 0.0; 680 Time time = new Time( 681 (Integer.parseInt((String)this.selectionProps.get("Year"))), 682 (Integer.parseInt((String)this.selectionProps.get("Month"))), 683 (Integer.parseInt((String)this.selectionProps.get("Day"))), 684 (Integer.parseInt((String)this.selectionProps.get("Hours"))), 685 (Integer.parseInt((String)this.selectionProps.get("Mins"))), 686 (Double.valueOf((String)this.selectionProps.get("Secs"))).doubleValue()); 687 688 double minDist = 999999.99; 689 double julianDate = julDate0; 690 691 while (julianDate <= julDate1) { 692 // prop to the desired time 693 prop.propogate2JulDate(julianDate); 694 695 // get the lat/long/altitude [radians, radians, meters] 696 double[] lla = prop.getLLA(); 697 double lat = lla[0] * 180.0 / Math.PI; 698 double lon = lla[1] * 180.0 / Math.PI; 699 double alt = lla[2]; 700 //System.out.println(" " + time.getDateTimeStr() + ": lat=" + lat + " lon=" + lon + " alt=" + alt); 701 702 double latDiff = (gsLat - lat) * (gsLat - lat); 703 double lonDiff = (gsLon - lon) * (gsLon - lon); 704 double dist = Math.sqrt(latDiff+lonDiff); 705 if (dist < minDist) { 706 minDist = dist; 707 retAlt = alt; 708 } 709 time.add(Time.MINUTE, dTime); 710 julianDate = time.getJulianDate(); 711 } 712 713 return retAlt; 714 } 715 716 @Override public boolean canSaveDataToLocalDisk() { 717 return true; 718 } 719 720 @Override protected List saveDataToLocalDisk(String filePrefix, 721 Object loadId, 722 boolean changeLinks) 723 throws Exception 724 { 725 Hashtable props = getProperties(); 726 List result; 727 if (props.containsKey(PolarOrbitTrackChooser.URL_NAME_KEY)) { 728 List<String> urls = new ArrayList<>(1); 729 urls.add((String)props.get(PolarOrbitTrackChooser.URL_NAME_KEY)); 730 result = IOUtil.writeTo(urls, filePrefix, ".txt", loadId); 731 String newUrl = "file:"+result.get(0); 732 props.put(PolarOrbitTrackChooser.URL_NAME_KEY, newUrl); 733 } else { 734 result = super.saveDataToLocalDisk(filePrefix, loadId, changeLinks); 735 } 736 return result; 737 } 738 739 protected void initDataSelectionComponents( 740 List<DataSelectionComponent> components, final DataChoice dataChoice) { 741/* 742 System.out.println("\ninitDataSelectionComponents:"); 743 System.out.println(" components=" + components); 744 System.out.println(" dataChoice=" + dataChoice); 745 System.out.println(" categories=" + dataChoice.getCategories()); 746 System.out.println(" displayCategory=" + dataChoice.getDisplayCategory()); 747*/ 748 clearTimes(); 749 IntegratedDataViewer idv = getDataContext().getIdv(); 750 idv.showWaitCursor(); 751 try { 752 trs = new TimeRangeSelection(this); 753 if (selectionProps != null) { 754 trs.applyFromDataSelectionProperties(selectionProps); 755 } 756 components.add(trs); 757 } catch (Exception e) { 758 logger.error("problem creating TimeRangeSelection e=" + e); 759 } 760 idv.showNormalCursor(); 761 } 762 763 private void notTLE() { 764 tleCards = new ArrayList<>(); 765 choices = new ArrayList<>(); 766 setInError(true, "\nSource does not contain TLE data"); 767 } 768 769 public void setDTime(int val) { 770 dTime = val; 771 } 772 773 /** 774 * Show the dialog 775 * 776 * @param initTabName What tab should we show. May be null. 777 * @param modal Is dialog modal 778 * 779 * @return success 780 */ 781 782 public boolean showPropertiesDialog(String initTabName, boolean modal) { 783 boolean ret = super.showPropertiesDialog(initTabName, modal); 784 return ret; 785 } 786 787 public void setSelectionProps(Hashtable newProperties) { 788 selectionProps.putAll(newProperties); 789 } 790 791} 792