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