001    /*
002     * $Id: AddeSoundingAdapter.java,v 1.4 2012/02/19 17:35:40 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
031    package edu.wisc.ssec.mcidasv.data.adde;
032    
033    
034    import java.util.ArrayList;
035    import java.util.Collections;
036    import java.util.Map;
037    import java.util.StringTokenizer;
038    
039    import edu.wisc.ssec.mcidas.McIDASUtil;
040    import edu.wisc.ssec.mcidas.adde.AddeException;
041    import edu.wisc.ssec.mcidas.adde.AddePointDataReader;
042    
043    import visad.DateTime;
044    import visad.Unit;
045    
046    import ucar.unidata.beans.NonVetoableProperty;
047    import ucar.unidata.data.sounding.SoundingAdapter;
048    import ucar.unidata.data.sounding.SoundingAdapterImpl;
049    import ucar.unidata.data.sounding.SoundingOb;
050    import ucar.unidata.data.sounding.SoundingStation;
051    import ucar.unidata.util.LogUtil;
052    import ucar.unidata.util.Misc;
053    import ucar.unidata.util.StringUtil;
054    import ucar.visad.UtcDate;
055    import ucar.visad.Util;
056    import ucar.visad.quantities.GeopotentialAltitude;
057    
058    import 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     */
065    public 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&nbsp;mandatory dataset&nbsp;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