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