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&nbsp;mandatory dataset&nbsp;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