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