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.adde; 030 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.Map; 034import java.util.StringTokenizer; 035 036import edu.wisc.ssec.mcidas.McIDASUtil; 037import edu.wisc.ssec.mcidas.adde.AddeException; 038import edu.wisc.ssec.mcidas.adde.AddePointDataReader; 039 040import visad.DateTime; 041import visad.Unit; 042 043import ucar.unidata.beans.NonVetoableProperty; 044import ucar.unidata.data.sounding.SoundingAdapter; 045import ucar.unidata.data.sounding.SoundingAdapterImpl; 046import ucar.unidata.data.sounding.SoundingOb; 047import ucar.unidata.data.sounding.SoundingStation; 048import ucar.unidata.util.LogUtil; 049import ucar.unidata.util.Misc; 050import ucar.unidata.util.StringUtil; 051import ucar.visad.UtcDate; 052import ucar.visad.Util; 053import ucar.visad.quantities.GeopotentialAltitude; 054 055import edu.wisc.ssec.mcidasv.chooser.adde.AddeChooser; 056 057/** 058 * Class for retrieving upper air data from an ADDE remote server. Creates 059 * a SoundingOb for each of the stations on the remote server for the 060 * latest available data. 061 */ 062 063public class AddeSoundingAdapter extends SoundingAdapterImpl implements SoundingAdapter { 064 065 /** Default */ 066 private static final long serialVersionUID = 1L; 067 068 /** observed or satellite sounding? */ 069 private boolean satelliteSounding = false; 070 071 /** these are only really used for satellite soundings */ 072 private String satelliteTime = ""; 073 private String satellitePixel = ""; 074 075 /** parameter identifier */ 076 private static final String P_PARAM = "param"; 077 078 /** number of obs identifier */ 079 private static final String P_NUM = "num"; 080 081 /** all obs identifier */ 082 private static final String P_ALL = "all"; 083 084 /** number of obs identifier */ 085 private static final String P_POS = "pos"; 086 087 /** group identifier */ 088 private static final String P_GROUP = "group"; 089 090 /** descriptor identifier */ 091 private static final String P_DESCR = "descr"; 092 093 /** select identifier */ 094 private static final String P_SELECT = "select"; 095 096 /** URL type identifier */ 097 private static final String URL_ROOT = "/point"; 098 099 /** URL protocol identifier */ 100 private static final String URL_PROTOCOL = "adde"; 101 102 /** server property */ 103 private NonVetoableProperty serverProperty; 104 105 /** mandatory data set property */ 106 private NonVetoableProperty mandatoryDatasetProperty; 107 108 /** significant data set property */ 109 private NonVetoableProperty significantDatasetProperty; 110 111 /** stations property */ 112 private NonVetoableProperty stationsProperty; 113 114 /** sounding times property */ 115 private NonVetoableProperty soundingTimesProperty; 116 117 /** mandatory data group name */ 118 private String manGroup; 119 120 /** mandatory data descriptor */ 121 private String manDescriptor; 122 123 /** sig data group name */ 124 private String sigGroup = null; 125 126 /** sig data descriptor */ 127 private String sigDescriptor = null; 128 129 /** use main hours only */ 130 private boolean mainHours = false; 131 132 /** name of mandP pressure variable */ 133 private String prMandPVar = "p"; 134 135 /** name of mandP height variable */ 136 private String htMandPVar = "z"; 137 138 /** name of mandP temp variable */ 139 private String tpMandPVar = "t"; 140 141 /** name of mandP dewpoint variable */ 142 private String tdMandPVar = "td"; 143 144 /** name of mandP wind speed variable */ 145 private String spdMandPVar = "spd"; 146 147 /** name of mandP wind dir variable */ 148 private String dirMandPVar = "dir"; 149 150 /** name of day variable */ 151 private String dayVar = "day"; 152 153 /** name of time variable */ 154 private String timeVar = "time"; 155 156 /** name of station id variable */ 157 private String idVar = "idn"; 158 159 /** name of station latitude variable */ 160 private String latVar = "lat"; 161 162 /** name of station longitude variable */ 163 private String lonVar = "lon"; 164 165 /** name of station elevation variable */ 166 private String eleVar = "zs"; 167 168 /** server name */ 169 private String server; 170 171 /** mandatory dataset name */ 172 private String mandDataset; 173 174 /** significant dataset name */ 175 private String sigDataset; 176 177 /** default server */ 178 private String defaultServer = "adde.unidata.ucar.edu"; 179 180 /** default mandatory data set */ 181 private String defaultMandDataset = "rtptsrc/uppermand"; 182 183 /** default significant dataset */ 184 private String defaultSigDataset = "rtptsrc/uppersig"; 185 186 /** Accounting information */ 187 private static String user = "user"; 188 private static String proj = "0"; 189 190 protected boolean firstTime = true; 191 protected boolean retry = true; 192 193 /** Used to grab accounting information for a currently selected server. */ 194 private AddeChooser addeChooser; 195 196 /** 197 * Construct an empty AddeSoundingAdapter 198 */ 199 public AddeSoundingAdapter() { 200 super("AddeSoundingAdapter"); 201 } 202 203 /** 204 * Retrieve upper air data from a remote ADDE server using only 205 * mandatory data. 206 * 207 * @param server name or IP address of remote server 208 * 209 * @throws Exception (AddeException) if there is no data available or there 210 * is trouble connecting to the remote server 211 */ 212 public AddeSoundingAdapter(String server) throws Exception { 213 this(server, null); 214 } 215 216 /** 217 * Retrieve upper air data from a remote ADDE server using only 218 * mandatory data. 219 * 220 * @param server name or IP address of remote server 221 * @param dataset name of ADDE dataset (group/descriptor) 222 * 223 * @throws Exception (AddeException) if there is no data available or there 224 * is trouble connecting to the remote server 225 */ 226 public AddeSoundingAdapter(String server, String dataset) 227 throws Exception { 228 this(server, dataset, null); 229 } 230 231 /** 232 * Retrieve upper air data from a remote ADDE server using only 233 * mandatory data. 234 * 235 * @param server name or IP address of remote server 236 * @param mandDataset name of mandatory level upper air ADDE 237 * dataset (group/descriptor) 238 * @param sigDataset name of significant level upper air ADDE 239 * dataset (group/descriptor) 240 * 241 * @throws Exception (AddeException) if there is no data available 242 * or there is trouble connecting to the remote server 243 */ 244 public AddeSoundingAdapter(String server, String mandDataset, 245 String sigDataset) 246 throws Exception { 247 this(server, mandDataset, sigDataset, false); 248 } 249 250 /** 251 * Retrieve upper air data from a remote ADDE server using only 252 * mandatory data. 253 * 254 * @param server name or IP address of remote server 255 * @param mandDataset name of mandatory level upper air ADDE 256 * dataset (group/descriptor) 257 * @param sigDataset name of significant level upper air ADDE 258 * dataset (group/descriptor) 259 * @param mainHours only get data for main (00 & 12Z) hours 260 * 261 * @throws Exception (AddeException) if there is no data available 262 * or there is trouble connecting to the remote server 263 */ 264 public AddeSoundingAdapter(String server, String mandDataset, 265 String sigDataset, boolean mainHours) 266 throws Exception { 267 this(server, mandDataset, sigDataset, false, null); 268 } 269 270 271 public AddeSoundingAdapter(String server, String mandDataset, 272 String sigDataset, boolean mainHours, AddeChooser chooser) 273 throws Exception { 274 super("AddeSoundingAdapter"); 275 this.server = server; 276 this.mandDataset = mandDataset; 277 this.sigDataset = sigDataset; 278 this.mainHours = mainHours; 279 this.satelliteSounding = false; 280 this.satelliteTime = ""; 281 this.satellitePixel = ""; 282 this.addeChooser = chooser; 283 init(); 284 } 285 286 public AddeSoundingAdapter(String server, String mandDataset, 287 String sigDataset, String satelliteTime, String satellitePixel, AddeChooser chooser) 288 throws Exception 289 { 290 super("AddeSoundingAdapter"); 291 this.server = server; 292 this.mandDataset = mandDataset; 293 this.sigDataset = sigDataset; 294 this.mainHours = false; 295 this.satelliteSounding = true; 296 this.satelliteTime = satelliteTime; 297 this.satellitePixel = satellitePixel; 298 this.addeChooser = chooser; 299 init(); 300 } 301 302 /** 303 * Initialize the class. Populate the variable list and get 304 * the server and dataset information. 305 * 306 * @throws Exception problem occurred 307 */ 308 protected void init() throws Exception { 309 if (haveInitialized) { 310 return; 311 } 312 super.init(); 313 314 getVariables(); 315 316 if (server == null) { 317 server = defaultServer; 318 } 319 320 if (mandDataset == null) { 321 mandDataset = defaultMandDataset; 322 } 323 324 if (sigDataset == null) { 325 sigDataset = defaultSigDataset; 326 } 327 328 // set up the properties 329 addProperty(serverProperty = new NonVetoableProperty(this, "server")); 330 serverProperty.setValue(server); 331 332 addProperty(mandatoryDatasetProperty = new NonVetoableProperty(this, 333 "mandatoryDataset")); 334 mandatoryDatasetProperty.setValue(mandDataset); 335 336 addProperty(significantDatasetProperty = 337 new NonVetoableProperty(this, "significantDataset")); 338 significantDatasetProperty.setValue(sigDataset); 339 340 addProperty(stationsProperty = new NonVetoableProperty(this, 341 "stations")); 342 addProperty(soundingTimesProperty = new NonVetoableProperty(this, 343 "soundingTimes")); 344 loadStations(); 345 } 346 347 348 /** 349 * Utility method that calls McIDASUtil.intBitsToString 350 * to get a string to compare to the given parameter s 351 * 352 * @param v integer string value 353 * @param s string to compare 354 * @return true if they are equal 355 */ 356 private boolean intEqual(int v, String s) { 357 return (McIDASUtil.intBitsToString(v).equals(s)); 358 } 359 360 361 362 /** 363 * Return the given String in single quotes 364 * 365 * @param s Add single quotes to the string for select clauses. 366 * @return single quoted string (ex: 'foo') 367 */ 368 private String sQuote(String s) { 369 return "'" + s + "'"; 370 } 371 372 373 /** 374 * Assemble the URL from the given URL argument array. This turns around 375 * and calls {@link #makeUrl(String, String[])}, passing in the 376 * {@link #URL_ROOT} ({@literal "/point"}) and the {@code URL_ROOT} to use. 377 * 378 * @param args URL arguments, key value pairs 379 * (ex: {@code arg[0]=arg[1]&arg[2]=arg[3]...}) 380 * @return Associated URL. 381 */ 382 private String makeUrl(String[] args) { 383 return makeUrl(URL_ROOT, args); 384 } 385 386 /** 387 * Assemble the url from the given {@code urlRoot} and URL argument array. 388 * This returns: 389 * {@code "URL_PROTOCOL://server urlRoot ?arg[0]=arg[1]&arg[2]=arg[3]...} 390 * 391 * @param urlRoot Root for the URL 392 * @param args Key/value pair arguments. 393 * 394 * @return ADDE URL. 395 */ 396 private String makeUrl(String urlRoot, String[] args) { 397 return Misc.makeUrl(URL_PROTOCOL, server, urlRoot, args); 398 } 399 400 /** 401 * Update this adapter for new data 402 */ 403 public void update() { 404 checkInit(); 405 try { 406 loadStations(); 407 } catch (Exception exc) { 408 LogUtil.logException("Error updating AddeSoundingAdapter", exc); 409 } 410 } 411 412 413 /** 414 * Initialize the times, stations and soundings lists. 415 * Load the data into them. 416 */ 417 private void loadStations() { 418 times = new ArrayList<DateTime>(8); 419 stations = new ArrayList<SoundingStation>(100); 420 soundings = new ArrayList<SoundingOb>(100); 421 try { 422 if ((server != null) && (mandDataset != null)) { 423 loadStationsInner(); 424 } 425 } catch (Exception excp) { 426 if (firstTime) { 427 String aes = excp.toString(); 428 if ((aes.indexOf("Accounting data")) >= 0) { 429 if (addeChooser != null && addeChooser.canAccessServer()) { 430 Map<String, String> acctInfo = addeChooser.getAccountingInfo(); 431 user = acctInfo.get("user"); 432 proj = acctInfo.get("proj"); 433 } 434 } 435 firstTime = false; 436 update(); 437 } 438 } 439 stationsProperty.setValueAndNotifyListeners(stations); 440 soundingTimesProperty.setValueAndNotifyListeners(times); 441 } 442 443 /** 444 * Initialize the group and descriptor strings 445 */ 446 private void initGroupAndDescriptors() { 447 if (manGroup == null) { 448 StringTokenizer tok = new StringTokenizer(mandDataset, "/"); 449 if (tok.countTokens() != 2) { 450 throw new IllegalStateException( 451 "Illegal mandatory dataset name " + mandDataset); 452 } 453 manGroup = tok.nextToken(); 454 manDescriptor = tok.nextToken(); 455 } 456 if ((sigDataset != null) && (sigGroup == null)) { 457 StringTokenizer tok = new StringTokenizer(sigDataset, "/"); 458 if (tok.countTokens() != 2) { 459 throw new IllegalStateException( 460 "Illegal significant dataset name " + mandDataset); 461 } 462 sigGroup = tok.nextToken(); 463 sigDescriptor = tok.nextToken(); 464 } 465 } 466 467 468 /** 469 * Actually do the work of loading the stations 470 * 471 * @throws AddeException problem accessing data 472 */ 473 private void loadStationsInner() throws AddeException { 474 initGroupAndDescriptors(); 475 String request = ""; 476 if (!satelliteSounding) { 477 request = makeUrl(new String[] { 478 P_GROUP, manGroup, P_DESCR, manDescriptor, P_PARAM, 479 StringUtil.join(new String[] { 480 dayVar, timeVar, idVar, latVar, lonVar, eleVar 481 }), P_NUM, P_ALL, P_POS, P_ALL 482 }) + getManUserProj() + getStationsSelectString(); 483 } 484 else { 485 request = makeUrl(new String[] { 486 P_GROUP, manGroup, P_DESCR, manDescriptor, P_PARAM, 487 StringUtil.join(new String[] { 488 dayVar, timeVar, idVar, latVar, lonVar 489 }), P_NUM, P_ALL, P_POS, P_ALL 490 }) + getManUserProj() + getStationsSelectString(); 491 } 492 dbPrint(request); 493 494 //System.err.println("loading stations: " + request); 495 496 AddePointDataReader dataReader = new AddePointDataReader(request); 497 int[] scales = dataReader.getScales(); 498 int[][] data = dataReader.getData(); 499 500 for (int i = 0; i < data[0].length; i++) { 501 int day = data[0][i]; 502 int time = data[1][i]; 503 String wmoID = Integer.toString(data[2][i]); 504 double lat = scaleValue(data[3][i], scales[3]); 505 double lon = scaleValue(data[4][i], scales[4]); 506 lon = -lon; // change from McIDAS to eastPositive 507 double elev = 0; 508 if (!satelliteSounding) 509 elev = scaleValue(data[5][i], scales[5]); 510 try { 511 SoundingStation s = new SoundingStation(wmoID, lat, lon, 512 elev); 513 if ( !(stations.contains(s))) { 514 stations.add(s); 515 } 516 DateTime dt = new DateTime(McIDASUtil.mcDayTimeToSecs(day, 517 time)); 518 soundings.add(new SoundingOb(s, dt)); 519 if ( !times.contains(dt)) { 520 times.add(dt); 521 } 522 } catch (Exception vexcp) { 523 LogUtil.logException("Creating sounding", vexcp); 524 } 525 } 526 Collections.sort(times); 527 if (debug) { 528 System.out.println("Times:" + times); 529 } 530 } 531 532 /** 533 * Set the ADDE server name 534 * 535 * @param server server name or IP address 536 */ 537 public void setSource(String server) { 538 this.server = server; 539 if (serverProperty != null) { 540 serverProperty.setValue(server); 541 } 542 } 543 544 /** 545 * Get the source of the data (server) 546 * 547 * @return server name or IP address 548 */ 549 public String getSource() { 550 return server; 551 } 552 553 554 /** 555 * Set the mandatory data set name 556 * 557 * @param value mandatory data set name 558 */ 559 public void setMandDataset(String value) { 560 mandDataset = value; 561 } 562 563 /** 564 * Set the mandatory data set name 565 * 566 * @return the mandatory data set name 567 */ 568 public String getMandDataset() { 569 return mandDataset; 570 } 571 572 573 /** 574 * Set the significant data set name 575 * 576 * @param value the significant data set name 577 */ 578 public void setSigDataset(String value) { 579 sigDataset = value; 580 } 581 582 /** 583 * Get the significant data set name 584 * 585 * @return the significant data set name 586 */ 587 public String getSigDataset() { 588 return sigDataset; 589 } 590 591 592 /** 593 * Change behavior if we are looking at satellite soundings 594 */ 595 public void setSatelliteSounding(boolean flag) { 596 satelliteSounding = flag; 597 } 598 599 /** 600 * Are we looking at satellite soundings? 601 */ 602 public boolean getSatelliteSounding() { 603 return satelliteSounding; 604 } 605 606 607 /** 608 * Check to see if the RAOB has any data 609 * 610 * @param sound sounding to check 611 * @return a sounding with data 612 */ 613 public SoundingOb initSoundingOb(SoundingOb sound) { 614 if ( !sound.hasData()) { 615 setRAOBData(sound); 616 } 617 return sound; 618 } 619 620 /** 621 * Make the select string that will get this observation 622 * 623 * @param sound sounding to use 624 * @return select string 625 */ 626 private String makeSelectString(SoundingOb sound) { 627 return makeSelectString(sound.getStation().getIdentifier(), 628 sound.getTimestamp()); 629 } 630 631 632 /** 633 * Make a select string for the given station id and date 634 * 635 * @param wmoId station id 636 * @param date time of data 637 * @return ADDE select clause for the given parameters 638 */ 639 private String makeSelectString(String wmoId, DateTime date) { 640 String day = UtcDate.getYMD(date); 641 String time = UtcDate.getHHMM(date); 642 //int[] daytime = McIDASUtil.mcSecsToDayTime((long) date.getValue()); 643 return new String(idVar + " " + wmoId + ";" + dayVar + " " + day 644 + ";" + timeVar + " " + time); 645 } 646 647 /** 648 * Fills in the data for the RAOB 649 * 650 * @param sound sounding ob to set 651 */ 652 private void setRAOBData(SoundingOb sound) { 653 654 initGroupAndDescriptors(); 655 int numLevels; 656 Unit pUnit = null, 657 tUnit = null, 658 tdUnit = null, 659 spdUnit = null, 660 dirUnit = null, 661 zUnit = null; 662 float p[], t[], td[], z[], spd[], dir[]; 663 AddePointDataReader apdr; 664 665 String request = getMandatoryURL(sound); 666 667 dbPrint(request); 668 try { 669 if (sound.getMandatoryFile() != null) { 670 request = "file:" + sound.getMandatoryFile(); 671 // System.err.println ("using fixed mandatory url:" + request); 672 } 673 674 apdr = new AddePointDataReader(request); 675 int[] scales = apdr.getScales(); 676 String[] units = apdr.getUnits(); 677 int[][] data = apdr.getData(); 678 679 // Special case: GRET doesn't respond to SELECT DAY... 680 // Try again without it 681 if (satelliteSounding && data[0].length == 0) { 682 request = request.replaceAll("DAY [0-9-]+;?", ""); 683 apdr = new AddePointDataReader(request); 684 scales = apdr.getScales(); 685 units = apdr.getUnits(); 686 data = apdr.getData(); 687 } 688 689 numLevels = data[0].length; 690 if (numLevels > 0) { 691 dbPrint("Num mand pressure levels = " + numLevels); 692 // Get the their units 693 pUnit = getUnit(units[0]); 694 // NB: geopotential altitudes stored in units of length 695 zUnit = GeopotentialAltitude.getGeopotentialUnit( 696 getUnit(units[1])); 697 tUnit = getUnit(units[2]); 698 tdUnit = getUnit(units[3]); 699 // Satellite soundings don't have spd or dir 700 if (units.length > 4) { 701 spdUnit = getUnit(units[4]); 702 dirUnit = getUnit(units[5]); 703 } 704 else { 705 spdUnit = getUnit("MPS"); 706 dirUnit = getUnit("DEG"); 707 } 708 709 // initialize the arrays 710 p = new float[numLevels]; 711 z = new float[numLevels]; 712 t = new float[numLevels]; 713 td = new float[numLevels]; 714 spd = new float[numLevels]; 715 dir = new float[numLevels]; 716 717 // fill the arrays 718 for (int i = 0; i < numLevels; i++) { 719 p[i] = (float) scaleValue(data[0][i], scales[0]); 720 z[i] = (float) scaleValue(data[1][i], scales[1]); 721 t[i] = (float) scaleValue(data[2][i], scales[2]); 722 td[i] = (float) scaleValue(data[3][i], scales[3]); 723 // Satellite soundings don't have spd or dir 724 if (data.length > 4 && scales.length > 4) { 725 spd[i] = (float) scaleValue(data[4][i], scales[4]); 726 dir[i] = (float) scaleValue(data[5][i], scales[5]); 727 } 728 else { 729 if (i==0) spd[i] = dir[i] = (float) 0; 730 else spd[i] = dir[i] = (float) scaleValue(McIDASUtil.MCMISSING, 0); 731 } 732 } 733 if (debug) { 734 System.out.println("P[" + pUnit + "]\t" + "Z[" + zUnit 735 + "]\t" + "T[" + tUnit + "]\t" + "TD[" 736 + tdUnit + "]\t" + "SPD[" + spdUnit 737 + "]\t" + "DIR[" + dirUnit + "]"); 738 for (int i = 0; i < numLevels; i++) { 739 System.out.println(p[i] + "\t" + z[i] + "\t" + t[i] 740 + "\t" + td[i] + "\t" + spd[i] 741 + "\t" + dir[i]); 742 } 743 } 744 sound.getRAOB().setMandatoryPressureProfile(pUnit, p, tUnit, 745 t, tdUnit, td, spdUnit, spd, dirUnit, dir, zUnit, z); 746 } 747 } catch (Exception e) { 748 LogUtil.logException( 749 "Unable to set mandatory pressure data for station " 750 + sound.getStation(), e); 751 } 752 753 // Done if we have no sig data 754 if ((sigGroup == null) || (sigDescriptor == null)) { 755 return; 756 } 757 758 request = getSigURL(sound); 759 dbPrint(request); 760 761 // get the sig data 762 try { 763 if (sound.getSigFile() != null) { 764 request = "file:" + sound.getSigFile(); 765 // System.err.println ("using fixed sig url:" + request); 766 } 767 768 apdr = new AddePointDataReader(request); 769 int[][] data = apdr.getData(); 770 771 numLevels = data[0].length; 772 if (numLevels > 0) { 773 // Determine how many of each kind of level 774 int numSigW = 0; 775 int numSigT = 0; 776 for (int i = 0; i < data[0].length; i++) { 777 if (intEqual(data[0][i], "SIGT")) { 778 numSigT++; 779 } 780 if (intEqual(data[0][i], "SIGW")) { 781 numSigW++; 782 } 783 } 784 785 dbPrint("Num sig temperature levels = " + numSigT); 786 dbPrint("Num sig wind levels = " + numSigW); 787 788 789 // Get the units & initialize the arrays 790 pUnit = getUnit("mb"); 791 tUnit = getUnit("k"); 792 tdUnit = getUnit("k"); 793 // NB: geopotential altitudes stored in units of length 794 zUnit = 795 GeopotentialAltitude.getGeopotentialUnit(getUnit("m")); 796 spdUnit = getUnit("mps"); 797 dirUnit = getUnit("deg"); 798 799 p = new float[numSigT]; 800 t = new float[numSigT]; 801 td = new float[numSigT]; 802 z = new float[numSigW]; 803 spd = new float[numSigW]; 804 dir = new float[numSigW]; 805 806 // fill the arrays 807 int j = 0; // counter for sigT 808 int l = 0; // counter for sigW 809 for (int i = 0; i < numLevels; i++) { 810 if (intEqual(data[0][i], "SIGT")) { 811 p[j] = (float) scaleValue(data[3][i], 1); 812 t[j] = (float) scaleValue(data[1][i], 2); 813 td[j] = (float) scaleValue(data[2][i], 2); 814 j++; 815 } else if (intEqual(data[0][i], "SIGW")) { 816 z[l] = (data[3][i] == 0) 817 ? (float) ((SoundingStation) sound 818 .getStation()).getAltitudeAsDouble() 819 : (float) scaleValue(data[3][i], 0); 820 spd[l] = (float) scaleValue(data[2][i], 1); 821 dir[l] = (float) scaleValue(data[1][i], 0); 822 l++; 823 } 824 } 825 if (numSigT > 0) { 826 try { 827 if (debug) { 828 System.out.println("P[" + pUnit + "]\tT[" + tUnit 829 + "]\tTD[" + tdUnit + "]"); 830 for (int i = 0; i < numSigT; i++) { 831 System.out.println(p[i] + "\t" + t[i] + "\t" 832 + td[i]); 833 } 834 } 835 sound.getRAOB().setSignificantTemperatureProfile( 836 pUnit, p, tUnit, t, tdUnit, td); 837 } catch (Exception e) { 838 LogUtil.logException( 839 "Unable to set significant temperature data for station " 840 + sound.getStation(), e); 841 } 842 } 843 if (numSigW > 0) { 844 try { 845 if (debug) { 846 System.out.println("Z[" + zUnit + "]\tSPD[" 847 + spdUnit + "]\tDIR[" + dirUnit + "]"); 848 for (int i = 0; i < numSigW; i++) { 849 System.out.println(z[i] + "\t" + spd[i] 850 + "\t" + dir[i]); 851 } 852 } 853 sound.getRAOB().setSignificantWindProfile(zUnit, z, 854 spdUnit, spd, dirUnit, dir); 855 } catch (Exception e) { 856 LogUtil.logException( 857 "Unable to set significant wind data for station " 858 + sound.getStation(), e); 859 } 860 } 861 } 862 } catch (Exception e) { 863 LogUtil.logException( 864 "Unable to retrieve significant level data for station " 865 + sound.getStation(), e); 866 } 867 } 868 869 870 /** 871 * scale the values returned from the server 872 * 873 * @param value value to scale 874 * @param scale scale factor 875 * @return scaled value 876 */ 877 private double scaleValue(int value, int scale) { 878 return (value == McIDASUtil.MCMISSING) 879 ? Double.NaN 880 : (value / Math.pow(10.0, (double) scale)); 881 } 882 883 /** 884 * Gets the units of the variable. Now just a passthrough to 885 * ucar.visad.Util. 886 * 887 * @param unitName unit name 888 * @return corresponding Unit or null if can't be decoded 889 * @see ucar.visad.Util#parseUnit(String) 890 */ 891 private Unit getUnit(String unitName) { 892 try { 893 return Util.parseUnit(unitName); 894 } catch (Exception e) {} 895 return null; 896 } 897 898 /** 899 * Get a default value using this Adapter's prefix 900 * 901 * @param name name of property key 902 * @param dflt default value 903 * @return the default for that property or dflt if not in properties 904 */ 905 private String getDflt(String name, String dflt) { 906 return getDflt("AddeSoundingAdapter.", name, dflt); 907 } 908 909 /** 910 * Determines the names of the variables in the netCDF file that 911 * should be used. 912 */ 913 private void getVariables() { 914 // initialize the defaults for this object 915 try { 916 defaultServer = getDflt("serverName", defaultServer); 917 defaultMandDataset = getDflt("mandDataset", defaultMandDataset); 918 defaultSigDataset = getDflt("sigDataset", defaultSigDataset); 919 idVar = getDflt("stationIDVariable", idVar); 920 latVar = getDflt("latitudeVariable", latVar); 921 lonVar = getDflt("longitudeVariable", lonVar); 922 eleVar = getDflt("stationElevVariable", eleVar); 923 timeVar = getDflt("soundingTimeVariable", timeVar); 924 dayVar = getDflt("soundingDayVariable", dayVar); 925 926 prMandPVar = getDflt("mandPPressureVariable", prMandPVar); 927 htMandPVar = getDflt("mandPHeightVariable", htMandPVar); 928 tpMandPVar = getDflt("mandPTempVariable", tpMandPVar); 929 tdMandPVar = getDflt("mandPDewptVariable", tdMandPVar); 930 spdMandPVar = getDflt("mandPWindSpeedVariable", spdMandPVar); 931 dirMandPVar = getDflt("mandPWindDirVariable", dirMandPVar); 932 933 // Significant Temperature data 934 /* 935 numSigT = nc.get(getDflt("NetcdfSoundingAdapter.", "numSigTempLevels", "numSigT")); 936 if (numSigT != null) { 937 hasSigT = true; 938 prSigTVar = getDflt ("NetcdfSoundingAdapter.", "sigTPressureVariable", "prSigT"); 939 tpSigTVar = getDflt ("NetcdfSoundingAdapter.", "sigTTempVariable", "tpSigT"); 940 tdSigTVar = getDflt("NetcdfSoundingAdapter.", "sigTDewptVariable", "tdSigT"); 941 } 942 943 // Significant Wind data 944 numSigW = nc.get(getDflt("NetcdfSoundingAdapter.", "numSigWindLevels", "numSigW")); 945 if (numSigW != null) { 946 hasSigW = true; 947 htSigWVar = getDflt ("NetcdfSoundingAdapter.", "sigWHeightVariable", "htSigW"); 948 spdSigWVar = getDflt("NetcdfSoundingAdapter.", "sigWWindSpeedVariable", "wsSigW"); 949 dirSigWVar = getDflt("NetcdfSoundingAdapter.", "sigWWindDirVariable", "wdSigW"); 950 } 951 */ 952 } catch (Exception e) { 953 System.out.println("Unable to initialize defaults file"); 954 } 955 } 956 957 /** 958 * Get significant data ADDE user/project id for the data 959 * @return user/project string (ex: "id=idv proj=0") 960 */ 961 private String getSigUserProj() { 962 return getUserProj(new String(sigGroup + "/" 963 + sigDescriptor).toUpperCase()); 964 } 965 966 /** 967 * Make the mandatory levels URL for the given sounding 968 * 969 * @param sound sounding 970 * 971 * @return mandatory url 972 */ 973 public String getMandatoryURL(SoundingOb sound) { 974 String select = makeSelectString(sound); 975 String paramString; 976 if (!satelliteSounding) { 977 paramString = StringUtil.join(new String[] { 978 prMandPVar, htMandPVar, tpMandPVar, tdMandPVar, spdMandPVar, dirMandPVar 979 }); 980 } 981 else { 982 paramString = StringUtil.join(new String[] { 983 prMandPVar, htMandPVar, tpMandPVar, tdMandPVar 984 }); 985 } 986 String request = makeUrl(new String[] { 987 P_GROUP, manGroup, P_DESCR, manDescriptor, P_SELECT, 988 sQuote(select), P_PARAM, paramString, P_NUM, P_ALL, P_POS, P_ALL 989 }) + getManUserProj(); 990 991 return request; 992 993 } 994 995 /** 996 * Make the url for the significant levels for the sounding 997 * 998 * @param sound the sounding 999 * 1000 * @return sig url 1001 */ 1002 public String getSigURL(SoundingOb sound) { 1003 // If we haven't picked a sig dataset, act as though both are mandatory 1004 if (mandDataset.equals(sigDataset)) { 1005 return getMandatoryURL(sound); 1006 } 1007 1008 String select = makeSelectString(sound); 1009 String paramString; 1010 if (!satelliteSounding) { 1011 paramString = "type p1 p2 p3"; 1012 } 1013 else { 1014 paramString = StringUtil.join(new String[] { 1015 prMandPVar, htMandPVar, tpMandPVar, tdMandPVar 1016 }); 1017 } 1018 String request = makeUrl(new String[] { 1019 P_GROUP, sigGroup, P_DESCR, sigDescriptor, P_SELECT, 1020 sQuote(select), P_PARAM, paramString, P_NUM, P_ALL, P_POS, P_ALL 1021 }) + getSigUserProj(); 1022 1023 return request; 1024 } 1025 1026 1027 /** 1028 * Get mandatory data ADDE user/project id for the data 1029 * 1030 * @return user/project string (ex: "id=idv proj=0") 1031 */ 1032 private String getManUserProj() { 1033 return getUserProj(new String(manGroup + "/" 1034 + manDescriptor).toUpperCase()); 1035 } 1036 1037 /** 1038 * Get the user/project string for the given key 1039 * 1040 * @param key group/descriptor 1041 * 1042 * @return user/project string (ex: "id=idv proj=0") for the specified 1043 * dataset 1044 */ 1045 private String getUserProj(String key) { 1046 StringBuffer buf = new StringBuffer(); 1047 buf.append("&proj="); 1048 buf.append(getDflt("", key.toUpperCase().trim() + ".proj", proj)); 1049 buf.append("&user="); 1050 buf.append(getDflt("", key.toUpperCase().trim() + ".user", user)); 1051 buf.append("&compress=gzip"); 1052 //buf.append("&debug=true"); 1053 return buf.toString(); 1054 } 1055 1056 /** 1057 * Get the select string for use in loadStations 1058 * 1059 * @return select string 1060 */ 1061 private String getStationsSelectString() { 1062 StringBuffer buf; 1063 if (!satelliteSounding) { 1064 if ( !mainHours) { 1065 return ""; 1066 } 1067 buf = new StringBuffer(); 1068 buf.append("&SELECT='"); 1069 buf.append(timeVar + " 00,12'"); 1070 } 1071 else { 1072 buf = new StringBuffer(); 1073 buf.append("&SELECT='"); 1074 buf.append(timeVar + " " + satelliteTime); 1075 if (!satellitePixel.equals("")) { 1076 buf.append("; " + idVar + " " + satellitePixel); 1077 } 1078 buf.append("'"); 1079 } 1080 return buf.toString(); 1081 } 1082 1083 /** 1084 * test by running java ucar.unidata.data.sounding.AddeSoundingAdapter 1085 * 1086 * @param args array of arguments. Takes up to 3 arguments as 1087 * "server mandatory dataset significant dataset" 1088 * Use "x" for any of these arguments to use the default. 1089 */ 1090 public static void main(String[] args) { 1091 String server = "adde.unidata.ucar.edu"; 1092 String manset = "rtptsrc/uppermand"; 1093 String sigset = "rtptsrc/uppersig"; 1094 if (args.length > 0) { 1095 server = ( !(args[0].equalsIgnoreCase("x"))) 1096 ? args[0] 1097 : server; 1098 if (args.length > 1) { 1099 manset = ( !(args[1].equalsIgnoreCase("x"))) 1100 ? args[1] 1101 : manset; 1102 } 1103 if (args.length > 2) { 1104 sigset = ( !(args[2].equalsIgnoreCase("x"))) 1105 ? args[2] 1106 : sigset; 1107 } 1108 } 1109 // try { 1110 // AddeSoundingAdapter asa = 1111 // //new AddeSoundingAdapter(server, manset, sigset); 1112 // new AddeSoundingAdapter(); 1113 /* 1114 Thread.sleep(5000); 1115 asa.setServer("hurri.kean.edu"); 1116 Thread.sleep(5000); 1117 asa.setServer("adde.unidata.ucar.edu"); 1118 Thread.sleep(5000); 1119 asa.setMandatoryDataset("blizzard/uppermand"); 1120 */ 1121 // } 1122 // catch (Exception me) { 1123 // System.out.println(me); 1124 // } 1125 } 1126 1127 1128 /** 1129 * The string representation 1130 * @return The string 1131 */ 1132 public String toString() { 1133 return "SoundingAdapter:" + server; 1134 } 1135 1136}