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